LCOV - code coverage report
Current view: top level - app - AttributeAccessInterface.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 60 63 95.2 %
Date: 2024-02-15 08:20:41 Functions: 25 29 86.2 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2021-2022 Project CHIP Authors
       4             :  *    All rights reserved.
       5             :  *
       6             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7             :  *    you may not use this file except in compliance with the License.
       8             :  *    You may obtain a copy of the License at
       9             :  *
      10             :  *        http://www.apache.org/licenses/LICENSE-2.0
      11             :  *
      12             :  *    Unless required by applicable law or agreed to in writing, software
      13             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15             :  *    See the License for the specific language governing permissions and
      16             :  *    limitations under the License.
      17             :  */
      18             : 
      19             : #pragma once
      20             : 
      21             : #include <access/SubjectDescriptor.h>
      22             : #include <app/ConcreteAttributePath.h>
      23             : #include <app/MessageDef/AttributeReportIBs.h>
      24             : #include <app/data-model/DecodableList.h>
      25             : #include <app/data-model/Decode.h>
      26             : #include <app/data-model/Encode.h>
      27             : #include <app/data-model/FabricScoped.h>
      28             : #include <app/data-model/List.h> // So we can encode lists
      29             : #include <app/data-model/TagBoundEncoder.h>
      30             : #include <app/util/basic-types.h>
      31             : #include <lib/core/Optional.h>
      32             : #include <lib/core/TLV.h>
      33             : #include <lib/support/logging/CHIPLogging.h>
      34             : 
      35             : /**
      36             :  * Callback class that clusters can implement in order to interpose custom
      37             :  * attribute-handling logic.  An AttributeAccessInterface instance is associated
      38             :  * with some specific cluster.  A single instance may be used for a specific
      39             :  * endpoint or for all endpoints.
      40             :  *
      41             :  * Instances of AttributeAccessInterface that are registered via
      42             :  * registerAttributeAccessOverride will be consulted before taking the normal
      43             :  * attribute access codepath and can use that codepath as a fallback if desired.
      44             :  */
      45             : namespace chip {
      46             : namespace app {
      47             : 
      48             : /**
      49             :  * The AttributeReportBuilder is a helper class for filling a single report in AttributeReportIBs.
      50             :  *
      51             :  * Possible usage of AttributeReportBuilder might be:
      52             :  *
      53             :  * AttributeReportBuilder builder;
      54             :  * ReturnErrorOnFailure(builder.PrepareAttribute(...));
      55             :  * ReturnErrorOnFailure(builder.Encode(...));
      56             :  * ReturnErrorOnFailure(builder.FinishAttribute());
      57             :  */
      58             : class AttributeReportBuilder
      59             : {
      60             : public:
      61             :     /**
      62             :      * PrepareAttribute encodes the "header" part of an attribute report including the path and data version.
      63             :      * Path will be encoded according to section 10.5.4.3.1 in the spec.
      64             :      * Note: Only append is supported currently (encode a null list index), other operations won't encode a list index in the
      65             :      * attribute path field.
      66             :      * TODO: Add support for encoding a single element in the list (path with a valid list index).
      67             :      */
      68             :     CHIP_ERROR PrepareAttribute(AttributeReportIBs::Builder & aAttributeReportIBs, const ConcreteDataAttributePath & aPath,
      69             :                                 DataVersion aDataVersion);
      70             : 
      71             :     /**
      72             :      * FinishAttribute encodes the "footer" part of an attribute report (it closes the containers opened in PrepareAttribute)
      73             :      */
      74             :     CHIP_ERROR FinishAttribute(AttributeReportIBs::Builder & aAttributeReportIBs);
      75             : 
      76             :     /**
      77             :      * EncodeValue encodes the value field of the report, it should be called exactly once.
      78             :      */
      79             :     template <typename T, std::enable_if_t<!DataModel::IsFabricScoped<T>::value, bool> = true, typename... Ts>
      80        1882 :     CHIP_ERROR EncodeValue(AttributeReportIBs::Builder & aAttributeReportIBs, TLV::Tag tag, T && item, Ts &&... aArgs)
      81             :     {
      82        1882 :         return DataModel::Encode(*(aAttributeReportIBs.GetAttributeReport().GetAttributeData().GetWriter()), tag, item,
      83        1882 :                                  std::forward<Ts>(aArgs)...);
      84             :     }
      85             : 
      86             :     template <typename T, std::enable_if_t<DataModel::IsFabricScoped<T>::value, bool> = true, typename... Ts>
      87             :     CHIP_ERROR EncodeValue(AttributeReportIBs::Builder & aAttributeReportIBs, TLV::Tag tag, FabricIndex accessingFabricIndex,
      88             :                            T && item, Ts &&... aArgs)
      89             :     {
      90             :         return DataModel::EncodeForRead(*(aAttributeReportIBs.GetAttributeReport().GetAttributeData().GetWriter()), tag,
      91             :                                         accessingFabricIndex, item, std::forward<Ts>(aArgs)...);
      92             :     }
      93             : };
      94             : 
      95             : /**
      96             :  * The AttributeValueEncoder is a helper class for filling report payloads into AttributeReportIBs.
      97             :  * The attribute value encoder can be initialized with a AttributeEncodeState for saving and recovering its state between encode
      98             :  * sessions (chunkings).
      99             :  *
     100             :  * When Encode returns recoverable errors (e.g. CHIP_ERROR_NO_MEMORY) the state can be used to initialize the AttributeValueEncoder
     101             :  * for future use on the same attribute path.
     102             :  */
     103             : class AttributeValueEncoder
     104             : {
     105             : public:
     106             :     class ListEncodeHelper
     107             :     {
     108             :     public:
     109        1114 :         ListEncodeHelper(AttributeValueEncoder & encoder) : mAttributeValueEncoder(encoder) {}
     110             : 
     111             :         template <typename T, std::enable_if_t<DataModel::IsFabricScoped<T>::value, bool> = true>
     112             :         CHIP_ERROR Encode(T && aArg) const
     113             :         {
     114             :             VerifyOrReturnError(aArg.GetFabricIndex() != kUndefinedFabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX);
     115             : 
     116             :             // If we are encoding for a fabric filtered attribute read and the fabric index does not match that present in the
     117             :             // request, skip encoding this list item.
     118             :             VerifyOrReturnError(!mAttributeValueEncoder.mIsFabricFiltered ||
     119             :                                     aArg.GetFabricIndex() == mAttributeValueEncoder.mAccessingFabricIndex,
     120             :                                 CHIP_NO_ERROR);
     121             :             return mAttributeValueEncoder.EncodeListItem(mAttributeValueEncoder.mAccessingFabricIndex, std::forward<T>(aArg));
     122             :         }
     123             : 
     124             :         template <typename T, std::enable_if_t<!DataModel::IsFabricScoped<T>::value, bool> = true>
     125        2078 :         CHIP_ERROR Encode(T && aArg) const
     126             :         {
     127        2078 :             return mAttributeValueEncoder.EncodeListItem(std::forward<T>(aArg));
     128             :         }
     129             : 
     130             :     private:
     131             :         AttributeValueEncoder & mAttributeValueEncoder;
     132             :     };
     133             : 
     134             :     class AttributeEncodeState
     135             :     {
     136             :     public:
     137        4553 :         AttributeEncodeState() : mAllowPartialData(false), mCurrentEncodingListIndex(kInvalidListIndex) {}
     138         391 :         bool AllowPartialData() const { return mAllowPartialData; }
     139             : 
     140             :     private:
     141             :         friend class AttributeValueEncoder;
     142             :         /**
     143             :          * When an attempt to encode an attribute returns an error, the buffer may contain tailing dirty data
     144             :          * (since the put was aborted).  The report engine normally rolls back the buffer to right before encoding
     145             :          * of the attribute started on errors.
     146             :          *
     147             :          * When chunking a list, EncodeListItem will atomically encode list items, ensuring that the
     148             :          * state of the buffer is valid to send (i.e. contains no trailing garbage), and return an error
     149             :          * if the list doesn't entirely fit.  In this situation, mAllowPartialData is set to communicate to the
     150             :          * report engine that it should not roll back the list items.
     151             :          *
     152             :          * TODO: There might be a better name for this variable.
     153             :          */
     154             :         bool mAllowPartialData = false;
     155             :         /**
     156             :          * If set to kInvalidListIndex, indicates that we have not encoded any data for the list yet and
     157             :          * need to start by encoding an empty list before we start encoding any list items.
     158             :          *
     159             :          * When set to a valid ListIndex value, indicates the index of the next list item that needs to be
     160             :          * encoded (i.e. the count of items encoded so far).
     161             :          */
     162             :         ListIndex mCurrentEncodingListIndex = kInvalidListIndex;
     163             :     };
     164             : 
     165        2471 :     AttributeValueEncoder(AttributeReportIBs::Builder & aAttributeReportIBsBuilder, FabricIndex aAccessingFabricIndex,
     166             :                           const ConcreteAttributePath & aPath, DataVersion aDataVersion, bool aIsFabricFiltered = false,
     167        2471 :                           const AttributeEncodeState & aState = AttributeEncodeState()) :
     168        2471 :         mAttributeReportIBsBuilder(aAttributeReportIBsBuilder),
     169        2471 :         mAccessingFabricIndex(aAccessingFabricIndex), mPath(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId),
     170        2471 :         mDataVersion(aDataVersion), mIsFabricFiltered(aIsFabricFiltered), mEncodeState(aState)
     171        2471 :     {}
     172             : 
     173             :     /**
     174             :      * Encode a single value.  This value will not be chunked; it will either be
     175             :      * entirely encoded or fail to be encoded.  Consumers are allowed to make
     176             :      * either one call to Encode or one call to EncodeList to handle a read.
     177             :      */
     178             :     template <typename... Ts>
     179             :     CHIP_ERROR Encode(Ts &&... aArgs)
     180             :     {
     181             :         mTriedEncode = true;
     182             :         return EncodeAttributeReportIB(std::forward<Ts>(aArgs)...);
     183             :     }
     184             : 
     185             :     /**
     186             :      * Encode an explicit null value.
     187             :      */
     188             :     CHIP_ERROR EncodeNull()
     189             :     {
     190             :         // Doesn't matter what type Nullable we use here.
     191             :         return Encode(DataModel::Nullable<uint8_t>());
     192             :     }
     193             : 
     194             :     /**
     195             :      * Encode an explicit empty list.
     196             :      */
     197             :     CHIP_ERROR EncodeEmptyList()
     198             :     {
     199             :         // Doesn't matter what type List we use here.
     200             :         return Encode(DataModel::List<uint8_t>());
     201             :     }
     202             : 
     203             :     /**
     204             :      * aCallback is expected to take a const auto & argument and Encode() on it as many times as needed to encode all the list
     205             :      * elements one by one.  If any of those Encode() calls returns failure, aCallback must stop encoding and return failure.  When
     206             :      * all items are encoded aCallback is expected to return success.
     207             :      *
     208             :      * aCallback may not be called.  Consumers must not assume it will be called.
     209             :      *
     210             :      * When EncodeList returns an error, the consumers must abort the encoding, and return the exact error to the caller.
     211             :      *
     212             :      * TODO: Can we hold a error state in the AttributeValueEncoder itself so functions in ember-compatibility-functions don't have
     213             :      * to rely on the above assumption?
     214             :      *
     215             :      * Consumers are allowed to make either one call to EncodeList or one call to Encode to handle a read.
     216             :      *
     217             :      */
     218             :     template <typename ListGenerator>
     219         715 :     CHIP_ERROR EncodeList(ListGenerator aCallback)
     220             :     {
     221         715 :         mTriedEncode = true;
     222             :         // Spec 10.5.4.3.1, 10.5.4.6 (Replace a list w/ Multiple IBs)
     223             :         // EmptyList acts as the beginning of the whole array type attribute report.
     224             :         // An empty list is encoded iff both mCurrentEncodingListIndex and mEncodeState.mCurrentEncodingListIndex are invalid
     225             :         // values. After encoding the empty list, mEncodeState.mCurrentEncodingListIndex and mCurrentEncodingListIndex are set to 0.
     226         715 :         ReturnErrorOnFailure(EnsureListStarted());
     227         665 :         CHIP_ERROR err = aCallback(ListEncodeHelper(*this));
     228             : 
     229             :         // Even if encoding list items failed, make sure we EnsureListEnded().
     230             :         // Since we encode list items atomically, in the case when we just
     231             :         // didn't fit the next item we want to make sure our list is properly
     232             :         // ended before the reporting engine starts chunking.
     233         665 :         EnsureListEnded();
     234         665 :         if (err == CHIP_NO_ERROR)
     235             :         {
     236             :             // The Encode procedure finished without any error, clear the state.
     237         621 :             mEncodeState = AttributeEncodeState();
     238             :         }
     239         665 :         return err;
     240             :     }
     241             : 
     242        2243 :     bool TriedEncode() const { return mTriedEncode; }
     243             : 
     244             :     /**
     245             :      * The accessing fabric index for this read or subscribe interaction.
     246             :      */
     247             :     FabricIndex AccessingFabricIndex() const { return mAccessingFabricIndex; }
     248             : 
     249             :     /**
     250             :      * AttributeValueEncoder is a short lived object, and the state is persisted by mEncodeState and restored by constructor.
     251             :      */
     252         228 :     const AttributeEncodeState & GetState() const { return mEncodeState; }
     253             : 
     254             : private:
     255             :     // We made EncodeListItem() private, and ListEncoderHelper will expose it by Encode()
     256             :     friend class ListEncodeHelper;
     257             : 
     258             :     template <typename... Ts>
     259        2078 :     CHIP_ERROR EncodeListItem(Ts &&... aArgs)
     260             :     {
     261             :         // EncodeListItem must be called after EnsureListStarted(), thus mCurrentEncodingListIndex and
     262             :         // mEncodeState.mCurrentEncodingListIndex are not invalid values.
     263        2078 :         if (mCurrentEncodingListIndex < mEncodeState.mCurrentEncodingListIndex)
     264             :         {
     265             :             // We have encoded this element in previous chunks, skip it.
     266         196 :             mCurrentEncodingListIndex++;
     267         196 :             return CHIP_NO_ERROR;
     268             :         }
     269             : 
     270        1882 :         TLV::TLVWriter backup;
     271        1882 :         mAttributeReportIBsBuilder.Checkpoint(backup);
     272             : 
     273             :         CHIP_ERROR err;
     274        1882 :         if (mEncodingInitialList)
     275             :         {
     276             :             // Just encode a single item, with an anonymous tag.
     277             :             AttributeReportBuilder builder;
     278        1718 :             err = builder.EncodeValue(mAttributeReportIBsBuilder, TLV::AnonymousTag(), std::forward<Ts>(aArgs)...);
     279             :         }
     280             :         else
     281             :         {
     282         164 :             err = EncodeAttributeReportIB(std::forward<Ts>(aArgs)...);
     283             :         }
     284        1882 :         if (err != CHIP_NO_ERROR)
     285             :         {
     286             :             // For list chunking, ReportEngine should not rollback the buffer when CHIP_ERROR_NO_MEMORY or similar error occurred.
     287             :             // However, the error might be raised in the middle of encoding procedure, then the buffer may contain partial data,
     288             :             // unclosed containers etc. This line clears all possible partial data and makes EncodeListItem is atomic.
     289          44 :             mAttributeReportIBsBuilder.Rollback(backup);
     290          44 :             return err;
     291             :         }
     292             : 
     293        1838 :         mCurrentEncodingListIndex++;
     294        1838 :         mEncodeState.mCurrentEncodingListIndex++;
     295        1838 :         mEncodedAtLeastOneListItem = true;
     296        1838 :         return CHIP_NO_ERROR;
     297             :     }
     298             : 
     299             :     /**
     300             :      * Builds a single AttributeReportIB in AttributeReportIBs.  The caller is
     301             :      * responsible for setting up mPath correctly.
     302             :      *
     303             :      * In particular, when we are encoding a single element in the list, mPath
     304             :      * must indicate a null list index to represent an "append" operation.
     305             :      * operation.
     306             :      */
     307             :     template <typename... Ts>
     308         164 :     CHIP_ERROR EncodeAttributeReportIB(Ts &&... aArgs)
     309             :     {
     310             :         AttributeReportBuilder builder;
     311         164 :         ReturnErrorOnFailure(builder.PrepareAttribute(mAttributeReportIBsBuilder, mPath, mDataVersion));
     312         164 :         ReturnErrorOnFailure(builder.EncodeValue(mAttributeReportIBsBuilder, TLV::ContextTag(AttributeDataIB::Tag::kData),
     313             :                                                  std::forward<Ts>(aArgs)...));
     314             : 
     315         164 :         return builder.FinishAttribute(mAttributeReportIBsBuilder);
     316             :     }
     317             : 
     318             :     /**
     319             :      * EnsureListStarted sets our mCurrentEncodingListIndex to 0, and:
     320             :      *
     321             :      * * If we are just starting the list, gets us ready to encode list items.
     322             :      *
     323             :      * * If we are continuing a chunked list, guarantees that mPath.mListOp is
     324             :      *   AppendItem after it returns.
     325             :      */
     326             :     CHIP_ERROR EnsureListStarted();
     327             : 
     328             :     /**
     329             :      * EnsureListEnded writes out the end of the list and our attribute data IB,
     330             :      * if we were encoding our initial list
     331             :      */
     332             :     void EnsureListEnded();
     333             : 
     334             :     bool mTriedEncode = false;
     335             :     AttributeReportIBs::Builder & mAttributeReportIBsBuilder;
     336             :     const FabricIndex mAccessingFabricIndex;
     337             :     ConcreteDataAttributePath mPath;
     338             :     DataVersion mDataVersion;
     339             :     bool mIsFabricFiltered = false;
     340             :     // mEncodingInitialList is true if we're encoding a list and we have not
     341             :     // started chunking it yet, so we're encoding a single attribute report IB
     342             :     // for the whole list, not one per item.
     343             :     bool mEncodingInitialList = false;
     344             :     // mEncodedAtLeastOneListItem becomes true once we successfully encode a list item.
     345             :     bool mEncodedAtLeastOneListItem = false;
     346             :     AttributeEncodeState mEncodeState;
     347             :     ListIndex mCurrentEncodingListIndex = kInvalidListIndex;
     348             : };
     349             : 
     350             : class AttributeValueDecoder
     351             : {
     352             : public:
     353        2434 :     AttributeValueDecoder(TLV::TLVReader & aReader, const Access::SubjectDescriptor & aSubjectDescriptor) :
     354        2434 :         mReader(aReader), mSubjectDescriptor(aSubjectDescriptor)
     355        2434 :     {}
     356             : 
     357             :     template <typename T, typename std::enable_if_t<!DataModel::IsFabricScoped<T>::value, bool> = true>
     358             :     CHIP_ERROR Decode(T & aArg)
     359             :     {
     360             :         mTriedDecode = true;
     361             :         return DataModel::Decode(mReader, aArg);
     362             :     }
     363             : 
     364             :     template <typename T, typename std::enable_if_t<DataModel::IsFabricScoped<T>::value, bool> = true>
     365             :     CHIP_ERROR Decode(T & aArg)
     366             :     {
     367             :         mTriedDecode = true;
     368             :         // The WriteRequest comes with no fabric index, this will happen when receiving a write request on a PASE session before
     369             :         // AddNOC.
     370             :         VerifyOrReturnError(AccessingFabricIndex() != kUndefinedFabricIndex, CHIP_IM_GLOBAL_STATUS(UnsupportedAccess));
     371             :         ReturnErrorOnFailure(DataModel::Decode(mReader, aArg));
     372             :         aArg.SetFabricIndex(AccessingFabricIndex());
     373             :         return CHIP_NO_ERROR;
     374             :     }
     375             : 
     376        2429 :     bool TriedDecode() const { return mTriedDecode; }
     377             : 
     378             :     /**
     379             :      * The accessing fabric index for this write interaction.
     380             :      */
     381             :     FabricIndex AccessingFabricIndex() const { return mSubjectDescriptor.fabricIndex; }
     382             : 
     383             :     /**
     384             :      * The accessing subject descriptor for this write interaction.
     385             :      */
     386             :     const Access::SubjectDescriptor & GetSubjectDescriptor() const { return mSubjectDescriptor; }
     387             : 
     388             : private:
     389             :     TLV::TLVReader & mReader;
     390             :     bool mTriedDecode = false;
     391             :     const Access::SubjectDescriptor mSubjectDescriptor;
     392             : };
     393             : 
     394             : class AttributeAccessInterface
     395             : {
     396             : public:
     397             :     /**
     398             :      * aEndpointId can be Missing to indicate that this object is meant to be
     399             :      * used with all endpoints.
     400             :      */
     401        2474 :     AttributeAccessInterface(Optional<EndpointId> aEndpointId, ClusterId aClusterId) :
     402        2474 :         mEndpointId(aEndpointId), mClusterId(aClusterId)
     403        2474 :     {}
     404        2474 :     virtual ~AttributeAccessInterface() {}
     405             : 
     406             :     /**
     407             :      * Callback for reading attributes.
     408             :      *
     409             :      * @param [in] aPath indicates which exact data is being read.
     410             :      * @param [in] aEncoder the AttributeValueEncoder to use for encoding the
     411             :      *             data.
     412             :      *
     413             :      * The implementation can do one of three things:
     414             :      *
     415             :      * 1) Return a failure.  This is treated as a failed read and the error is
     416             :      *    returned to the client, by converting it to a StatusIB.
     417             :      * 2) Return success and attempt to encode data using aEncoder.  The data is
     418             :      *    returned to the client.
     419             :      * 3) Return success and not attempt to encode any data using aEncoder.  In
     420             :      *    this case, Ember attribute access will happen for the read. This may
     421             :      *    involve reading from the attribute store or external attribute
     422             :      *    callbacks.
     423             :      */
     424             :     virtual CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) = 0;
     425             : 
     426             :     /**
     427             :      * Callback for writing attributes.
     428             :      *
     429             :      * @param [in] aPath indicates which exact data is being written.
     430             :      * @param [in] aDecoder the AttributeValueDecoder to use for decoding the
     431             :      *             data.
     432             :      *
     433             :      * The implementation can do one of three things:
     434             :      *
     435             :      * 1) Return a failure.  This is treated as a failed write and the error is
     436             :      *    sent to the client, by converting it to a StatusIB.
     437             :      * 2) Return success and attempt to decode from aDecoder.  This is
     438             :      *    treated as a successful write.
     439             :      * 3) Return success and not attempt to decode from aDecoder.  In
     440             :      *    this case, Ember attribute access will happen for the write. This may
     441             :      *    involve writing to the attribute store or external attribute
     442             :      *    callbacks.
     443             :      */
     444           0 :     virtual CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) { return CHIP_NO_ERROR; }
     445             : 
     446             :     /**
     447             :      * Indicates the start of a series of list operations. This function will be called before the first Write operation of a series
     448             :      * of consequence attribute data of the same attribute.
     449             :      *
     450             :      * 1) This function will be called if the client tries to set a nullable list attribute to null.
     451             :      * 2) This function will only be called once for a series of consequent attribute data (regardless the kind of list operation)
     452             :      * of the same attribute.
     453             :      *
     454             :      * @param [in] aPath indicates the path of the modified list.
     455             :      */
     456           0 :     virtual void OnListWriteBegin(const ConcreteAttributePath & aPath) {}
     457             : 
     458             :     /**
     459             :      * Indicates the end of a series of list operations. This function will be called after the last Write operation of a series
     460             :      * of consequence attribute data of the same attribute.
     461             :      *
     462             :      * 1) This function will be called if the client tries to set a nullable list attribute to null.
     463             :      * 2) This function will only be called once for a series of consequent attribute data (regardless the kind of list operation)
     464             :      * of the same attribute.
     465             :      * 3) When aWriteWasSuccessful is true, the data written must be consistent or the list is untouched.
     466             :      *
     467             :      * @param [in] aPath indicates the path of the modified list
     468             :      * @param [in] aWriteWasSuccessful indicates whether the delivered list is complete.
     469             :      *
     470             :      */
     471           0 :     virtual void OnListWriteEnd(const ConcreteAttributePath & aPath, bool aWriteWasSuccessful) {}
     472             : 
     473             :     /**
     474             :      * Mechanism for keeping track of a chain of AttributeAccessInterfaces.
     475             :      */
     476           3 :     void SetNext(AttributeAccessInterface * aNext) { mNext = aNext; }
     477          15 :     AttributeAccessInterface * GetNext() const { return mNext; }
     478             : 
     479             :     /**
     480             :      * Check whether a this AttributeAccessInterface is relevant for a
     481             :      * particular endpoint+cluster.  An AttributeAccessInterface will be used
     482             :      * for a read from a particular cluster only when this function returns
     483             :      * true.
     484             :      */
     485        5002 :     bool Matches(EndpointId aEndpointId, ClusterId aClusterId) const
     486             :     {
     487        5002 :         return (!mEndpointId.HasValue() || mEndpointId.Value() == aEndpointId) && mClusterId == aClusterId;
     488             :     }
     489             : 
     490             :     /**
     491             :      * Check whether an AttributeAccessInterface is relevant for a particular
     492             :      * specific endpoint.  This is used to clean up overrides registered for an
     493             :      * endpoint that becomes disabled.
     494             :      */
     495          15 :     bool MatchesEndpoint(EndpointId aEndpointId) const { return mEndpointId.HasValue() && mEndpointId.Value() == aEndpointId; }
     496             : 
     497             :     /**
     498             :      * Check whether another AttributeAccessInterface wants to handle the same set of
     499             :      * attributes as we do.
     500             :      */
     501           4 :     bool Matches(const AttributeAccessInterface & aOther) const
     502             :     {
     503           8 :         return mClusterId == aOther.mClusterId &&
     504           8 :             (!mEndpointId.HasValue() || !aOther.mEndpointId.HasValue() || mEndpointId.Value() == aOther.mEndpointId.Value());
     505             :     }
     506             : 
     507             : private:
     508             :     Optional<EndpointId> mEndpointId;
     509             :     ClusterId mClusterId;
     510             :     AttributeAccessInterface * mNext = nullptr;
     511             : };
     512             : 
     513             : } // namespace app
     514             : } // namespace chip

Generated by: LCOV version 1.14