Matter SDK Coverage Report
Current view: top level - controller - TypedReadCallback.h (source / functions) Coverage Total Hit
Test: SHA:c5aeaea5470ddad6464d5b62caddeb9f1efd6a41 Lines: 62.8 % 94 59
Test Date: 2025-07-25 07:10:31 Functions: 43.7 % 87 38

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2021 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 <app/AppConfig.h>
      22              : #include <app/BufferedReadCallback.h>
      23              : #include <app/ConcreteAttributePath.h>
      24              : #include <app/data-model/Decode.h>
      25              : #include <functional>
      26              : #include <lib/support/CHIPMem.h>
      27              : 
      28              : #if CHIP_CONFIG_ENABLE_READ_CLIENT
      29              : namespace chip {
      30              : namespace Controller {
      31              : 
      32              : /*
      33              :  * This provides an adapter class that implements ReadClient::Callback and provides three additional
      34              :  * features:
      35              :  *  1. The ability to pass in std::function closures to permit more flexible
      36              :  *     programming scenarios than are provided by the strict delegate interface
      37              :  *     stipulated by ReadClient::Callback.
      38              :  *
      39              :  *  2. Automatic decoding of attribute data provided in the TLVReader by
      40              :  *     ReadClient::Callback::OnAttributeData into a decoded cluster object.
      41              :  *
      42              :  *  3. Automatically representing all errors as a CHIP_ERROR (which might
      43              :  *     encapsulate a StatusIB).  This could be a path-specific error or it
      44              :  *     could be a general error for the entire request; the distinction is not
      45              :  *     that important, because we only have one path involved.  If the
      46              :  *     CHIP_ERROR encapsulates a StatusIB, constructing a StatusIB from it will
      47              :  *     extract the status.
      48              :  */
      49              : template <typename DecodableAttributeType>
      50              : class TypedReadAttributeCallback final : public app::ReadClient::Callback
      51              : {
      52              : public:
      53              :     using OnSuccessCallbackType =
      54              :         std::function<void(const app::ConcreteDataAttributePath & aPath, const DecodableAttributeType & aData)>;
      55              :     using OnErrorCallbackType = std::function<void(const app::ConcreteDataAttributePath * aPath, CHIP_ERROR aError)>;
      56              :     using OnDoneCallbackType  = std::function<void(TypedReadAttributeCallback * callback)>;
      57              :     using OnSubscriptionEstablishedCallbackType =
      58              :         std::function<void(const app::ReadClient & readClient, SubscriptionId aSubscriptionId)>;
      59              :     using OnResubscriptionAttemptCallbackType =
      60              :         std::function<void(const app::ReadClient & readClient, CHIP_ERROR aError, uint32_t aNextResubscribeIntervalMsec)>;
      61          232 :     TypedReadAttributeCallback(ClusterId aClusterId, AttributeId aAttributeId, OnSuccessCallbackType aOnSuccess,
      62              :                                OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone,
      63              :                                OnSubscriptionEstablishedCallbackType aOnSubscriptionEstablished = nullptr,
      64              :                                OnResubscriptionAttemptCallbackType aOnResubscriptionAttempt     = nullptr) :
      65          232 :         mClusterId(aClusterId),
      66          232 :         mAttributeId(aAttributeId), mOnSuccess(aOnSuccess), mOnError(aOnError), mOnDone(aOnDone),
      67          232 :         mOnSubscriptionEstablished(aOnSubscriptionEstablished), mOnResubscriptionAttempt(aOnResubscriptionAttempt),
      68          464 :         mBufferedReadAdapter(*this)
      69          232 :     {}
      70              : 
      71          232 :     ~TypedReadAttributeCallback()
      72              :     {
      73              :         // Ensure we release the ReadClient before we tear down anything else,
      74              :         // so it can call our OnDeallocatePaths properly.
      75          232 :         mReadClient = nullptr;
      76          232 :     }
      77              : 
      78          232 :     app::BufferedReadCallback & GetBufferedCallback() { return mBufferedReadAdapter; }
      79              : 
      80          231 :     void AdoptReadClient(Platform::UniquePtr<app::ReadClient> aReadClient) { mReadClient = std::move(aReadClient); }
      81              : 
      82              : private:
      83          233 :     void OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
      84              :                          const app::StatusIB & aStatus) override
      85              :     {
      86          233 :         if (mCalledCallback && mReadClient->IsReadType())
      87              :         {
      88            7 :             return;
      89              :         }
      90          226 :         mCalledCallback = true;
      91              : 
      92          226 :         CHIP_ERROR err = CHIP_NO_ERROR;
      93          214 :         DecodableAttributeType value;
      94              : 
      95              :         //
      96              :         // We shouldn't be getting list item operations in the provided path since that should be handled by the buffered read
      97              :         // callback. If we do, that's a bug.
      98              :         //
      99          226 :         VerifyOrDie(!aPath.IsListItemOperation());
     100              : 
     101          226 :         VerifyOrExit(aStatus.IsSuccess(), err = aStatus.ToChipError());
     102          224 :         VerifyOrExit(aPath.mClusterId == mClusterId && aPath.mAttributeId == mAttributeId, err = CHIP_ERROR_SCHEMA_MISMATCH);
     103          223 :         VerifyOrExit(apData != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
     104              : 
     105          223 :         SuccessOrExit(err = app::DataModel::Decode(*apData, value));
     106              : 
     107          223 :         mOnSuccess(aPath, value);
     108              : 
     109          226 :     exit:
     110          226 :         if (err != CHIP_NO_ERROR)
     111              :         {
     112            3 :             mOnError(&aPath, err);
     113              :         }
     114              :     }
     115              : 
     116           67 :     void OnError(CHIP_ERROR aError) override
     117              :     {
     118           67 :         if (mCalledCallback && mReadClient->IsReadType())
     119              :         {
     120            0 :             return;
     121              :         }
     122           67 :         mCalledCallback = true;
     123              : 
     124           67 :         mOnError(nullptr, aError);
     125              :     }
     126              : 
     127          231 :     void OnDone(app::ReadClient *) override { mOnDone(this); }
     128              : 
     129          204 :     void OnSubscriptionEstablished(SubscriptionId aSubscriptionId) override
     130              :     {
     131          204 :         if (mOnSubscriptionEstablished)
     132              :         {
     133          204 :             mOnSubscriptionEstablished(*mReadClient.get(), aSubscriptionId);
     134              :         }
     135          204 :     }
     136              : 
     137            6 :     CHIP_ERROR OnResubscriptionNeeded(chip::app::ReadClient * apReadClient, CHIP_ERROR aTerminationCause) override
     138              :     {
     139            6 :         ReturnErrorOnFailure(app::ReadClient::Callback::OnResubscriptionNeeded(apReadClient, aTerminationCause));
     140              : 
     141            6 :         if (mOnResubscriptionAttempt)
     142              :         {
     143            0 :             mOnResubscriptionAttempt(*mReadClient.get(), aTerminationCause, apReadClient->ComputeTimeTillNextSubscription());
     144              :         }
     145              : 
     146            6 :         return CHIP_NO_ERROR;
     147              :     }
     148              : 
     149          208 :     void OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) override
     150              :     {
     151          208 :         VerifyOrDie(aReadPrepareParams.mAttributePathParamsListSize == 1 &&
     152              :                     aReadPrepareParams.mpAttributePathParamsList != nullptr);
     153          208 :         chip::Platform::Delete<app::AttributePathParams>(aReadPrepareParams.mpAttributePathParamsList);
     154              : 
     155          208 :         if (aReadPrepareParams.mDataVersionFilterListSize == 1 && aReadPrepareParams.mpDataVersionFilterList != nullptr)
     156              :         {
     157           65 :             chip::Platform::Delete<app::DataVersionFilter>(aReadPrepareParams.mpDataVersionFilterList);
     158              :         }
     159          208 :     }
     160              : 
     161              :     ClusterId mClusterId;
     162              :     AttributeId mAttributeId;
     163              :     OnSuccessCallbackType mOnSuccess;
     164              :     OnErrorCallbackType mOnError;
     165              :     OnDoneCallbackType mOnDone;
     166              :     OnSubscriptionEstablishedCallbackType mOnSubscriptionEstablished;
     167              :     OnResubscriptionAttemptCallbackType mOnResubscriptionAttempt;
     168              :     app::BufferedReadCallback mBufferedReadAdapter;
     169              :     Platform::UniquePtr<app::ReadClient> mReadClient;
     170              :     // For reads, we ensure that we make only one data/error callback to our consumer.
     171              :     bool mCalledCallback = false;
     172              : };
     173              : 
     174              : template <typename DecodableEventType>
     175              : class TypedReadEventCallback final : public app::ReadClient::Callback
     176              : {
     177              : public:
     178              :     using OnSuccessCallbackType = std::function<void(const app::EventHeader & aEventHeader, const DecodableEventType & aData)>;
     179              :     using OnErrorCallbackType   = std::function<void(const app::EventHeader * apEventHeader, CHIP_ERROR aError)>;
     180              :     using OnDoneCallbackType    = std::function<void(app::ReadClient * apReadClient)>;
     181              :     using OnSubscriptionEstablishedCallbackType =
     182              :         std::function<void(const app::ReadClient & aReadClient, SubscriptionId aSubscriptionId)>;
     183              :     using OnResubscriptionAttemptCallbackType =
     184              :         std::function<void(const app::ReadClient & aReadClient, CHIP_ERROR aError, uint32_t aNextResubscribeIntervalMsec)>;
     185              : 
     186            1 :     TypedReadEventCallback(OnSuccessCallbackType aOnSuccess, OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone,
     187              :                            OnSubscriptionEstablishedCallbackType aOnSubscriptionEstablished = nullptr,
     188              :                            OnResubscriptionAttemptCallbackType aOnResubscriptionAttempt     = nullptr) :
     189            1 :         mOnSuccess(aOnSuccess),
     190            1 :         mOnError(aOnError), mOnDone(aOnDone), mOnSubscriptionEstablished(aOnSubscriptionEstablished),
     191            2 :         mOnResubscriptionAttempt(aOnResubscriptionAttempt)
     192            1 :     {}
     193              : 
     194            1 :     ~TypedReadEventCallback()
     195              :     {
     196              :         // Ensure we release the ReadClient before we tear down anything else,
     197              :         // so it can call our OnDeallocatePaths properly.
     198            1 :         mReadClient = nullptr;
     199            1 :     }
     200              : 
     201            1 :     void AdoptReadClient(Platform::UniquePtr<app::ReadClient> aReadClient) { mReadClient = std::move(aReadClient); }
     202              : 
     203              : private:
     204            0 :     void OnEventData(const app::EventHeader & aEventHeader, TLV::TLVReader * apData, const app::StatusIB * apStatus) override
     205              :     {
     206            0 :         if (mCalledCallback && mReadClient->IsReadType())
     207              :         {
     208            0 :             return;
     209              :         }
     210            0 :         mCalledCallback = true;
     211              : 
     212            0 :         CHIP_ERROR err = CHIP_NO_ERROR;
     213            0 :         DecodableEventType value;
     214              : 
     215              :         // Only one of the apData and apStatus can be non-null, so apStatus will always indicate a failure status when it is not
     216              :         // nullptr.
     217            0 :         VerifyOrExit(apStatus == nullptr, err = apStatus->ToChipError());
     218              : 
     219            0 :         VerifyOrExit(apData != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
     220              : 
     221            0 :         VerifyOrExit((aEventHeader.mPath.mEventId == value.GetEventId()) && (aEventHeader.mPath.mClusterId == value.GetClusterId()),
     222              :                      CHIP_ERROR_SCHEMA_MISMATCH);
     223            0 :         err = app::DataModel::Decode(*apData, value);
     224            0 :         SuccessOrExit(err);
     225              : 
     226            0 :         mOnSuccess(aEventHeader, value);
     227              : 
     228            0 :     exit:
     229            0 :         if (err != CHIP_NO_ERROR)
     230              :         {
     231            0 :             mOnError(&aEventHeader, err);
     232              :         }
     233              :     }
     234              : 
     235            0 :     void OnError(CHIP_ERROR aError) override
     236              :     {
     237            0 :         if (mCalledCallback && mReadClient->IsReadType())
     238              :         {
     239            0 :             return;
     240              :         }
     241            0 :         mCalledCallback = true;
     242              : 
     243            0 :         mOnError(nullptr, aError);
     244              :     }
     245              : 
     246            1 :     void OnDone(app::ReadClient * apReadClient) override
     247              :     {
     248            1 :         if (mOnDone != nullptr)
     249              :         {
     250            1 :             mOnDone(apReadClient);
     251              :         }
     252              : 
     253              :         // Always needs to be the last call
     254            1 :         chip::Platform::Delete(this);
     255            1 :     }
     256              : 
     257            0 :     void OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) override
     258              :     {
     259            0 :         VerifyOrDie(aReadPrepareParams.mEventPathParamsListSize == 1 && aReadPrepareParams.mpEventPathParamsList != nullptr);
     260            0 :         chip::Platform::Delete<app::EventPathParams>(aReadPrepareParams.mpEventPathParamsList);
     261            0 :     }
     262              : 
     263            0 :     void OnSubscriptionEstablished(SubscriptionId aSubscriptionId) override
     264              :     {
     265            0 :         if (mOnSubscriptionEstablished)
     266              :         {
     267            0 :             mOnSubscriptionEstablished(*mReadClient.get(), aSubscriptionId);
     268              :         }
     269            0 :     }
     270              : 
     271            0 :     CHIP_ERROR OnResubscriptionNeeded(chip::app::ReadClient * apReadClient, CHIP_ERROR aTerminationCause) override
     272              :     {
     273            0 :         ReturnErrorOnFailure(app::ReadClient::Callback::OnResubscriptionNeeded(apReadClient, aTerminationCause));
     274              : 
     275            0 :         if (mOnResubscriptionAttempt)
     276              :         {
     277            0 :             mOnResubscriptionAttempt(*mReadClient.get(), aTerminationCause, apReadClient->ComputeTimeTillNextSubscription());
     278              :         }
     279              : 
     280            0 :         return CHIP_NO_ERROR;
     281              :     }
     282              : 
     283              :     OnSuccessCallbackType mOnSuccess;
     284              :     OnErrorCallbackType mOnError;
     285              :     OnDoneCallbackType mOnDone;
     286              :     OnSubscriptionEstablishedCallbackType mOnSubscriptionEstablished;
     287              :     OnResubscriptionAttemptCallbackType mOnResubscriptionAttempt;
     288              :     Platform::UniquePtr<app::ReadClient> mReadClient;
     289              :     // For reads, we ensure that we make only one data/error callback to our consumer.
     290              :     bool mCalledCallback = false;
     291              : };
     292              : 
     293              : } // namespace Controller
     294              : } // namespace chip
     295              : #endif // CHIP_CONFIG_ENABLE_READ_CLIENT
        

Generated by: LCOV version 2.0-1