Matter SDK Coverage Report
Current view: top level - app - ReadHandler.cpp (source / functions) Coverage Total Hit
Test: SHA:fd700f088ee2d8b07407ec02bd01329ac87109ab Lines: 84.5 % 426 360
Test Date: 2026-01-14 08:11:11 Functions: 90.6 % 32 29

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2020 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              : #include <app/AppConfig.h>
      19              : #include <app/AttributePathExpandIterator.h>
      20              : #include <app/InteractionModelEngine.h>
      21              : #include <app/MessageDef/EventPathIB.h>
      22              : #include <app/MessageDef/StatusResponseMessage.h>
      23              : #include <app/MessageDef/SubscribeRequestMessage.h>
      24              : #include <app/MessageDef/SubscribeResponseMessage.h>
      25              : #include <app/data-model-provider/Provider.h>
      26              : #include <app/icd/server/ICDServerConfig.h>
      27              : #include <lib/core/TLVUtilities.h>
      28              : #include <lib/support/CodeUtils.h>
      29              : #include <messaging/ExchangeContext.h>
      30              : 
      31              : #include <app/ReadHandler.h>
      32              : #include <app/reporting/Engine.h>
      33              : 
      34              : #if CHIP_CONFIG_ENABLE_ICD_SERVER
      35              : #include <app/icd/server/ICDConfigurationData.h> //nogncheck
      36              : #endif
      37              : 
      38              : namespace chip {
      39              : namespace app {
      40              : using Status = Protocols::InteractionModel::Status;
      41              : 
      42           33 : uint16_t ReadHandler::GetPublisherSelectedIntervalLimit()
      43              : {
      44              : #if CHIP_CONFIG_ENABLE_ICD_SERVER
      45              :     // We don't need to check for precision loss since the max value of the IdleModeDuration can fit inside a uint16_t
      46              :     const auto idleModeDuration =
      47              :         std::chrono::duration_cast<System::Clock::Seconds16>(ICDConfigurationData::GetInstance().GetIdleModeDuration()).count();
      48              :     return std::max(idleModeDuration, kSubscriptionMaxIntervalPublisherLimit);
      49              : #else
      50           33 :     return kSubscriptionMaxIntervalPublisherLimit;
      51              : #endif
      52              : }
      53              : 
      54         1136 : ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext,
      55         1136 :                          InteractionType aInteractionType, Observer * observer) :
      56         1136 :     mExchangeCtx(*this),
      57         2272 :     mManagementCallback(apCallback)
      58              : {
      59         1136 :     VerifyOrDie(apExchangeContext != nullptr);
      60              : 
      61         1136 :     mExchangeCtx.Grab(apExchangeContext);
      62              : #if CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
      63              :     // TODO: this should be replaced by a pointer to the InteractionModelEngine that created the ReadHandler
      64              :     // once InteractionModelEngine is no longer a singleton (see issue 23625)
      65              :     mExchangeMgr = apExchangeContext->GetExchangeMgr();
      66              : #endif // CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
      67              : 
      68         1136 :     mInteractionType            = aInteractionType;
      69         1136 :     mLastWrittenEventsBytes     = 0;
      70         1136 :     mTransactionStartGeneration = mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().GetDirtySetGeneration();
      71         1136 :     mFlags.ClearAll();
      72         1136 :     SetStateFlag(ReadHandlerFlags::PrimingReports);
      73              : 
      74         1136 :     mSessionHandle.Grab(mExchangeCtx->GetSessionHandle());
      75              : 
      76         1136 :     VerifyOrDie(observer != nullptr);
      77         1136 :     mObserver = observer;
      78         1136 : }
      79              : 
      80              : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
      81            0 : ReadHandler::ReadHandler(ManagementCallback & apCallback, Observer * observer) :
      82            0 :     mExchangeCtx(*this), mManagementCallback(apCallback)
      83              : {
      84            0 :     mInteractionType = InteractionType::Subscribe;
      85            0 :     mFlags.ClearAll();
      86              : 
      87            0 :     VerifyOrDie(observer != nullptr);
      88            0 :     mObserver = observer;
      89            0 : }
      90              : 
      91            0 : void ReadHandler::OnSubscriptionResumed(const SessionHandle & sessionHandle,
      92              :                                         SubscriptionResumptionSessionEstablisher & resumptionSessionEstablisher)
      93              : {
      94            0 :     mSubscriptionId          = resumptionSessionEstablisher.mSubscriptionInfo.mSubscriptionId;
      95            0 :     mMinIntervalFloorSeconds = resumptionSessionEstablisher.mSubscriptionInfo.mMinInterval;
      96            0 :     mMaxInterval             = resumptionSessionEstablisher.mSubscriptionInfo.mMaxInterval;
      97            0 :     SetStateFlag(ReadHandlerFlags::FabricFiltered, resumptionSessionEstablisher.mSubscriptionInfo.mFabricFiltered);
      98              : 
      99              :     // Move dynamically allocated attributes and events from the SubscriptionInfo struct into
     100              :     // the object pool managed by the IM engine
     101            0 :     for (size_t i = 0; i < resumptionSessionEstablisher.mSubscriptionInfo.mAttributePaths.AllocatedSize(); i++)
     102              :     {
     103            0 :         AttributePathParams params = resumptionSessionEstablisher.mSubscriptionInfo.mAttributePaths[i].GetParams();
     104            0 :         CHIP_ERROR err = mManagementCallback.GetInteractionModelEngine()->PushFrontAttributePathList(mpAttributePathList, params);
     105            0 :         if (err != CHIP_NO_ERROR)
     106              :         {
     107            0 :             Close();
     108            0 :             return;
     109              :         }
     110              :     }
     111            0 :     for (size_t i = 0; i < resumptionSessionEstablisher.mSubscriptionInfo.mEventPaths.AllocatedSize(); i++)
     112              :     {
     113            0 :         EventPathParams params = resumptionSessionEstablisher.mSubscriptionInfo.mEventPaths[i].GetParams();
     114            0 :         CHIP_ERROR err = mManagementCallback.GetInteractionModelEngine()->PushFrontEventPathParamsList(mpEventPathList, params);
     115            0 :         if (err != CHIP_NO_ERROR)
     116              :         {
     117            0 :             Close();
     118            0 :             return;
     119              :         }
     120              :     }
     121              : 
     122            0 :     mSessionHandle.Grab(sessionHandle);
     123              : 
     124            0 :     SetStateFlag(ReadHandlerFlags::ActiveSubscription);
     125              : 
     126            0 :     auto * appCallback = mManagementCallback.GetAppCallback();
     127            0 :     if (appCallback)
     128              :     {
     129            0 :         appCallback->OnSubscriptionEstablished(*this);
     130              :     }
     131              :     // Notify the observer that a subscription has been resumed
     132            0 :     mObserver->OnSubscriptionEstablished(this);
     133              : 
     134            0 :     MoveToState(HandlerState::CanStartReporting);
     135              : 
     136            0 :     SingleLinkedListNode<AttributePathParams> * attributePath = mpAttributePathList;
     137            0 :     while (attributePath)
     138              :     {
     139            0 :         TEMPORARY_RETURN_IGNORED mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().SetDirty(
     140            0 :             attributePath->mValue);
     141            0 :         attributePath = attributePath->mpNext;
     142              :     }
     143              : }
     144              : 
     145              : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
     146              : 
     147         1136 : ReadHandler::~ReadHandler()
     148              : {
     149         1136 :     mObserver->OnReadHandlerDestroyed(this);
     150              : 
     151         1136 :     auto * appCallback = mManagementCallback.GetAppCallback();
     152         1136 :     if (mFlags.Has(ReadHandlerFlags::ActiveSubscription) && appCallback)
     153              :     {
     154          240 :         mFlags.Clear(ReadHandlerFlags::ActiveSubscription);
     155          240 :         appCallback->OnSubscriptionTerminated(*this);
     156              :     }
     157              : 
     158         1136 :     if (IsAwaitingReportResponse())
     159              :     {
     160            2 :         mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().OnReportConfirm();
     161              :     }
     162         1136 :     mManagementCallback.GetInteractionModelEngine()->ReleaseAttributePathList(mpAttributePathList);
     163         1136 :     mManagementCallback.GetInteractionModelEngine()->ReleaseEventPathList(mpEventPathList);
     164         1136 :     mManagementCallback.GetInteractionModelEngine()->ReleaseDataVersionFilterList(mpDataVersionFilterList);
     165         1136 : }
     166              : 
     167          868 : void ReadHandler::Close(CloseOptions options)
     168              : {
     169              : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
     170          868 :     if (IsType(InteractionType::Subscribe) && options == CloseOptions::kDropPersistedSubscription)
     171              :     {
     172          102 :         auto * subscriptionResumptionStorage = mManagementCallback.GetInteractionModelEngine()->GetSubscriptionResumptionStorage();
     173          102 :         if (subscriptionResumptionStorage)
     174              :         {
     175            0 :             TEMPORARY_RETURN_IGNORED subscriptionResumptionStorage->Delete(GetInitiatorNodeId(), GetAccessingFabricIndex(),
     176              :                                                                            mSubscriptionId);
     177              :         }
     178              :     }
     179              : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
     180              : 
     181              : #if CHIP_PROGRESS_LOGGING
     182          868 :     if (IsType(InteractionType::Subscribe))
     183              :     {
     184          116 :         const ScopedNodeId & peer = mSessionHandle ? mSessionHandle->GetPeer() : ScopedNodeId();
     185          116 :         ChipLogProgress(DataManagement, "Subscription id 0x%" PRIx32 " from node " ChipLogFormatScopedNodeId " torn down",
     186              :                         mSubscriptionId, ChipLogValueScopedNodeId(peer));
     187              :     }
     188              : #endif // CHIP_PROGRESS_LOGGING
     189              : 
     190          868 :     MoveToState(HandlerState::AwaitingDestruction);
     191          868 :     mManagementCallback.OnDone(*this);
     192          868 : }
     193              : 
     194         1090 : void ReadHandler::OnInitialRequest(System::PacketBufferHandle && aPayload)
     195              : {
     196         1090 :     CHIP_ERROR err = CHIP_NO_ERROR;
     197         1090 :     System::PacketBufferHandle response;
     198              : 
     199         1090 :     if (IsType(InteractionType::Subscribe))
     200              :     {
     201          303 :         err = ProcessSubscribeRequest(std::move(aPayload));
     202              :     }
     203              :     else
     204              :     {
     205          787 :         err = ProcessReadRequest(std::move(aPayload));
     206              :     }
     207              : 
     208         2180 :     if (err != CHIP_NO_ERROR)
     209              :     {
     210            8 :         Status status = Status::InvalidAction;
     211            8 :         if (err.IsIMStatus())
     212              :         {
     213            0 :             status = StatusIB(err).mStatus;
     214              :         }
     215            8 :         TEMPORARY_RETURN_IGNORED StatusResponse::Send(status, mExchangeCtx.Get(), /* aExpectResponse = */ false);
     216              :         // At this point we can't have a persisted subscription, since that
     217              :         // happens only when ProcessSubscribeRequest returns success. And our
     218              :         // subscription id is almost certainly not actually useful at this
     219              :         // point, either.  So don't try to mess with persisted subscriptions in
     220              :         // Close().
     221            8 :         Close(CloseOptions::kKeepPersistedSubscription);
     222              :     }
     223              :     else
     224              :     {
     225              :         // Force us to be in a dirty state so we get processed by the reporting
     226         1082 :         SetStateFlag(ReadHandlerFlags::ForceDirty);
     227              :     }
     228         1090 : }
     229              : 
     230         1231 : CHIP_ERROR ReadHandler::OnStatusResponse(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload,
     231              :                                          bool & aSendStatusResponse)
     232              : {
     233         1231 :     CHIP_ERROR err         = CHIP_NO_ERROR;
     234         1231 :     aSendStatusResponse    = true;
     235         1231 :     CHIP_ERROR statusError = CHIP_NO_ERROR;
     236         1231 :     SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError));
     237              :     // Since this is a valid Status Response message, we don't have to send a Status Response in reply to it.
     238         1229 :     aSendStatusResponse = false;
     239         1229 :     SuccessOrExit(err = statusError);
     240         1217 :     switch (mState)
     241              :     {
     242         1217 :     case HandlerState::AwaitingReportResponse:
     243         1217 :         if (IsChunkedReport())
     244              :         {
     245          856 :             mExchangeCtx->WillSendMessage();
     246              :         }
     247          361 :         else if (IsType(InteractionType::Subscribe))
     248              :         {
     249          361 :             if (IsPriming())
     250              :             {
     251          284 :                 err = SendSubscribeResponse();
     252              : 
     253          284 :                 SetStateFlag(ReadHandlerFlags::ActiveSubscription);
     254          284 :                 auto * appCallback = mManagementCallback.GetAppCallback();
     255          284 :                 if (appCallback)
     256              :                 {
     257          240 :                     appCallback->OnSubscriptionEstablished(*this);
     258              :                 }
     259          284 :                 mObserver->OnSubscriptionEstablished(this);
     260              :             }
     261              :         }
     262              :         else
     263              :         {
     264              :             //
     265              :             // We're done processing a read, so let's close out and return.
     266              :             //
     267            0 :             Close();
     268            0 :             return CHIP_NO_ERROR;
     269              :         }
     270              : 
     271         1217 :         MoveToState(HandlerState::CanStartReporting);
     272         1217 :         break;
     273              : 
     274            0 :     case HandlerState::CanStartReporting:
     275              :     case HandlerState::Idle:
     276              :     default:
     277            0 :         err = CHIP_ERROR_INCORRECT_STATE;
     278            0 :         break;
     279              :     }
     280              : 
     281         1231 : exit:
     282         1231 :     return err;
     283              : }
     284              : 
     285           31 : CHIP_ERROR ReadHandler::SendStatusReport(Protocols::InteractionModel::Status aStatus)
     286              : {
     287           31 :     VerifyOrReturnLogError(mState == HandlerState::CanStartReporting, CHIP_ERROR_INCORRECT_STATE);
     288           31 :     if (IsPriming() || IsChunkedReport())
     289              :     {
     290           31 :         mSessionHandle.Grab(mExchangeCtx->GetSessionHandle());
     291              :     }
     292              :     else
     293              :     {
     294            0 :         VerifyOrReturnLogError(!mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
     295            0 :         VerifyOrReturnLogError(mSessionHandle, CHIP_ERROR_INCORRECT_STATE);
     296              : #if CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
     297              :         auto exchange = mExchangeMgr->NewContext(mSessionHandle.Get().Value(), this);
     298              : #else  // CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
     299              :         auto exchange =
     300            0 :             mManagementCallback.GetInteractionModelEngine()->GetExchangeManager()->NewContext(mSessionHandle.Get().Value(), this);
     301              : #endif // CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
     302            0 :         VerifyOrReturnLogError(exchange != nullptr, CHIP_ERROR_INCORRECT_STATE);
     303            0 :         mExchangeCtx.Grab(exchange);
     304              :     }
     305              : 
     306           31 :     VerifyOrReturnLogError(mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
     307           31 :     return StatusResponse::Send(aStatus, mExchangeCtx.Get(), /* aExpectResponse = */ false);
     308              : }
     309              : 
     310         1953 : CHIP_ERROR ReadHandler::SendReportData(System::PacketBufferHandle && aPayload, bool aMoreChunks)
     311              : {
     312         1953 :     VerifyOrReturnLogError(mState == HandlerState::CanStartReporting, CHIP_ERROR_INCORRECT_STATE);
     313         1949 :     VerifyOrDie(!IsAwaitingReportResponse()); // Should not be reportable!
     314         1949 :     if (IsPriming() || IsChunkedReport())
     315              :     {
     316         1863 :         mSessionHandle.Grab(mExchangeCtx->GetSessionHandle());
     317              :     }
     318              :     else
     319              :     {
     320           86 :         VerifyOrReturnLogError(!mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
     321           86 :         VerifyOrReturnLogError(mSessionHandle, CHIP_ERROR_INCORRECT_STATE);
     322              : #if CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
     323              :         auto exchange = mExchangeMgr->NewContext(mSessionHandle.Get().Value(), this);
     324              : #else  // CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
     325              :         auto exchange =
     326           86 :             mManagementCallback.GetInteractionModelEngine()->GetExchangeManager()->NewContext(mSessionHandle.Get().Value(), this);
     327              : #endif // CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
     328           86 :         VerifyOrReturnLogError(exchange != nullptr, CHIP_ERROR_INCORRECT_STATE);
     329           84 :         mExchangeCtx.Grab(exchange);
     330              :     }
     331              : 
     332         1947 :     VerifyOrReturnLogError(mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
     333              : 
     334         1947 :     if (!IsReporting())
     335              :     {
     336         1135 :         mCurrentReportsBeginGeneration =
     337         1135 :             mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().GetDirtySetGeneration();
     338              :     }
     339         1947 :     SetStateFlag(ReadHandlerFlags::ChunkedReport, aMoreChunks);
     340         1947 :     bool responseExpected = IsType(InteractionType::Subscribe) || aMoreChunks;
     341              : 
     342         1947 :     mExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime);
     343         1947 :     CHIP_ERROR err = mExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::ReportData, std::move(aPayload),
     344              :                                                responseExpected ? Messaging::SendMessageFlags::kExpectResponse
     345              :                                                                 : Messaging::SendMessageFlags::kNone);
     346         3894 :     if (err == CHIP_NO_ERROR)
     347              :     {
     348         1945 :         if (responseExpected)
     349              :         {
     350         1241 :             MoveToState(HandlerState::AwaitingReportResponse);
     351              :         }
     352              :         else
     353              :         {
     354              :             // Make sure we're not treated as an in-flight report waiting for a
     355              :             // response by the reporting engine.
     356          704 :             mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().OnReportConfirm();
     357              :         }
     358              : 
     359              :         // If we just finished a non-priming subscription report, notify our observers.
     360              :         // Priming reports are handled when we send a SubscribeResponse.
     361         1945 :         if (IsType(InteractionType::Subscribe) && !IsPriming() && !IsChunkedReport())
     362              :         {
     363           82 :             mObserver->OnSubscriptionReportSent(this);
     364              :         }
     365              :     }
     366         1947 :     if (!aMoreChunks)
     367              :     {
     368         1081 :         mPreviousReportsBeginGeneration = mCurrentReportsBeginGeneration;
     369         1081 :         ClearForceDirtyFlag();
     370         1081 :         mManagementCallback.GetInteractionModelEngine()->ReleaseDataVersionFilterList(mpDataVersionFilterList);
     371              :     }
     372              : 
     373         1947 :     return err;
     374              : }
     375              : 
     376         1233 : CHIP_ERROR ReadHandler::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
     377              :                                           System::PacketBufferHandle && aPayload)
     378              : {
     379         1233 :     CHIP_ERROR err          = CHIP_NO_ERROR;
     380         1233 :     bool sendStatusResponse = true;
     381         1233 :     if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse))
     382              :     {
     383         1231 :         err = OnStatusResponse(apExchangeContext, std::move(aPayload), sendStatusResponse);
     384              :     }
     385              :     else
     386              :     {
     387            2 :         ChipLogDetail(DataManagement, "ReadHandler:: Msg type %d not supported", aPayloadHeader.GetMessageType());
     388            2 :         err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
     389              :     }
     390              : 
     391         1233 :     if (sendStatusResponse)
     392              :     {
     393            4 :         TEMPORARY_RETURN_IGNORED StatusResponse::Send(Status::InvalidAction, apExchangeContext, false /*aExpectResponse*/);
     394              :     }
     395              : 
     396         2466 :     if (err != CHIP_NO_ERROR)
     397              :     {
     398           16 :         Close();
     399              :     }
     400         1233 :     return err;
     401              : }
     402              : 
     403           10 : bool ReadHandler::IsFromSubscriber(Messaging::ExchangeContext & apExchangeContext) const
     404              : {
     405           10 :     return (IsType(InteractionType::Subscribe) &&
     406           30 :             GetInitiatorNodeId() == apExchangeContext.GetSessionHandle()->AsSecureSession()->GetPeerNodeId() &&
     407           20 :             GetAccessingFabricIndex() == apExchangeContext.GetSessionHandle()->GetFabricIndex());
     408              : }
     409              : 
     410            8 : void ReadHandler::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
     411              : {
     412            8 :     ChipLogError(DataManagement, "Time out! failed to receive status response from Exchange: " ChipLogFormatExchange,
     413              :                  ChipLogValueExchange(apExchangeContext));
     414              : #if CHIP_CONFIG_ENABLE_ICD_SERVER && CHIP_CONFIG_ENABLE_ICD_CIP && CHIP_CONFIG_ENABLE_ICD_CHECK_IN_ON_REPORT_TIMEOUT
     415              :     switch (mState)
     416              :     {
     417              :     case HandlerState::AwaitingReportResponse:
     418              :         if (IsType(InteractionType::Subscribe) && !IsPriming())
     419              :         {
     420              :             // Trigger check-in message when a non-priming subscription report times out.
     421              :             ChipLogError(DataManagement, "Trigger check-in message when non-priming subscription report times out");
     422              :             if (mSessionHandle)
     423              :             {
     424              :                 ICDNotifier::GetInstance().NotifySendCheckIn(GetSubjectDescriptor());
     425              :             }
     426              :             else
     427              :             {
     428              :                 ChipLogError(DataManagement, "Failed to get subject descriptor for sending check-in message on report timeout");
     429              :             }
     430              :         }
     431              :         break;
     432              :     case HandlerState::CanStartReporting:
     433              :     case HandlerState::Idle:
     434              :     default:
     435              :         break;
     436              :     }
     437              : #endif // CHIP_CONFIG_ENABLE_ICD_CIP && CHIP_CONFIG_ENABLE_ICD_SERVER
     438              : 
     439              : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
     440            8 :     Close(CloseOptions::kKeepPersistedSubscription);
     441              : #else
     442              :     Close();
     443              : #endif
     444            8 : }
     445              : 
     446          791 : CHIP_ERROR ReadHandler::ProcessReadRequest(System::PacketBufferHandle && aPayload)
     447              : {
     448          791 :     CHIP_ERROR err = CHIP_NO_ERROR;
     449          791 :     System::PacketBufferTLVReader reader;
     450              : 
     451          791 :     ReadRequestMessage::Parser readRequestParser;
     452          791 :     EventPathIBs::Parser eventPathListParser;
     453          791 :     EventFilterIBs::Parser eventFilterIBsParser;
     454          791 :     AttributePathIBs::Parser attributePathListParser;
     455              : 
     456          791 :     reader.Init(std::move(aPayload));
     457              : 
     458          791 :     ReturnErrorOnFailure(readRequestParser.Init(reader));
     459              : 
     460              :     // No need to pretty-print here.  We pretty-print read requests in the read
     461              :     // case of InteractionModelEngine::OnReadInitialRequest, so we do it even if
     462              :     // we reject a read request.
     463              : 
     464          791 :     err = readRequestParser.GetAttributeRequests(&attributePathListParser);
     465         1582 :     if (err == CHIP_END_OF_TLV)
     466              :     {
     467          122 :         err = CHIP_NO_ERROR;
     468              :     }
     469         1338 :     else if (err == CHIP_NO_ERROR)
     470              :     {
     471          669 :         ReturnErrorOnFailure(ProcessAttributePaths(attributePathListParser));
     472          669 :         DataVersionFilterIBs::Parser dataVersionFilterListParser;
     473          669 :         err = readRequestParser.GetDataVersionFilters(&dataVersionFilterListParser);
     474         1338 :         if (err == CHIP_END_OF_TLV)
     475              :         {
     476          589 :             err = CHIP_NO_ERROR;
     477              :         }
     478          160 :         else if (err == CHIP_NO_ERROR)
     479              :         {
     480           80 :             ReturnErrorOnFailure(ProcessDataVersionFilterList(dataVersionFilterListParser));
     481              :         }
     482              :     }
     483          791 :     ReturnErrorOnFailure(err);
     484          791 :     err = readRequestParser.GetEventRequests(&eventPathListParser);
     485         1582 :     if (err == CHIP_END_OF_TLV)
     486              :     {
     487          473 :         err = CHIP_NO_ERROR;
     488              :     }
     489          636 :     else if (err == CHIP_NO_ERROR)
     490              :     {
     491          318 :         ReturnErrorOnFailure(err);
     492          318 :         ReturnErrorOnFailure(ProcessEventPaths(eventPathListParser));
     493          318 :         err = readRequestParser.GetEventFilters(&eventFilterIBsParser);
     494          636 :         if (err == CHIP_END_OF_TLV)
     495              :         {
     496            3 :             err = CHIP_NO_ERROR;
     497              :         }
     498          630 :         else if (err == CHIP_NO_ERROR)
     499              :         {
     500          315 :             ReturnErrorOnFailure(ProcessEventFilters(eventFilterIBsParser));
     501              :         }
     502              :     }
     503          791 :     ReturnErrorOnFailure(err);
     504              : 
     505              :     bool isFabricFiltered;
     506          791 :     ReturnErrorOnFailure(readRequestParser.GetIsFabricFiltered(&isFabricFiltered));
     507          787 :     SetStateFlag(ReadHandlerFlags::FabricFiltered, isFabricFiltered);
     508          787 :     ReturnErrorOnFailure(readRequestParser.ExitContainer());
     509          787 :     MoveToState(HandlerState::CanStartReporting);
     510              : 
     511          787 :     mExchangeCtx->WillSendMessage();
     512              : 
     513              :     // There must be no code after the WillSendMessage() call that can cause
     514              :     // this method to return a failure.
     515              : 
     516          787 :     return CHIP_NO_ERROR;
     517          791 : }
     518              : 
     519          970 : CHIP_ERROR ReadHandler::ProcessAttributePaths(AttributePathIBs::Parser & aAttributePathListParser)
     520              : {
     521          970 :     CHIP_ERROR err = CHIP_NO_ERROR;
     522          970 :     TLV::TLVReader reader;
     523          970 :     aAttributePathListParser.GetReader(&reader);
     524         5022 :     while (CHIP_NO_ERROR == (err = reader.Next()))
     525              :     {
     526         1541 :         VerifyOrReturnError(TLV::AnonymousTag() == reader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG);
     527         1541 :         AttributePathParams attribute;
     528         1541 :         AttributePathIB::Parser path;
     529         1541 :         ReturnErrorOnFailure(path.Init(reader));
     530         1541 :         ReturnErrorOnFailure(path.ParsePath(attribute));
     531         1541 :         ReturnErrorOnFailure(
     532              :             mManagementCallback.GetInteractionModelEngine()->PushFrontAttributePathList(mpAttributePathList, attribute));
     533              :     }
     534              :     // if we have exhausted this container
     535         1940 :     if (CHIP_END_OF_TLV == err)
     536              :     {
     537          970 :         mManagementCallback.GetInteractionModelEngine()->RemoveDuplicateConcreteAttributePath(mpAttributePathList);
     538          970 :         mAttributePathExpandPosition = AttributePathExpandIterator::Position::StartIterating(mpAttributePathList);
     539          970 :         err                          = CHIP_NO_ERROR;
     540              :     }
     541          970 :     return err;
     542              : }
     543              : 
     544          145 : CHIP_ERROR ReadHandler::ProcessDataVersionFilterList(DataVersionFilterIBs::Parser & aDataVersionFilterListParser)
     545              : {
     546          145 :     CHIP_ERROR err = CHIP_NO_ERROR;
     547          145 :     TLV::TLVReader reader;
     548              : 
     549          145 :     aDataVersionFilterListParser.GetReader(&reader);
     550         5274 :     while (CHIP_NO_ERROR == (err = reader.Next()))
     551              :     {
     552         2492 :         VerifyOrReturnError(TLV::AnonymousTag() == reader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG);
     553         2492 :         DataVersionFilter versionFilter;
     554         2492 :         ClusterPathIB::Parser path;
     555         2492 :         DataVersionFilterIB::Parser filter;
     556         2492 :         ReturnErrorOnFailure(filter.Init(reader));
     557         2492 :         DataVersion version = 0;
     558         2492 :         ReturnErrorOnFailure(filter.GetDataVersion(&version));
     559         2492 :         versionFilter.mDataVersion.SetValue(version);
     560         2492 :         ReturnErrorOnFailure(filter.GetPath(&path));
     561         2492 :         ReturnErrorOnFailure(path.GetEndpoint(&(versionFilter.mEndpointId)));
     562         2492 :         ReturnErrorOnFailure(path.GetCluster(&(versionFilter.mClusterId)));
     563         2492 :         VerifyOrReturnError(versionFilter.IsValidDataVersionFilter(), CHIP_ERROR_IM_MALFORMED_DATA_VERSION_FILTER_IB);
     564         2492 :         ReturnErrorOnFailure(mManagementCallback.GetInteractionModelEngine()->PushFrontDataVersionFilterList(
     565              :             mpDataVersionFilterList, versionFilter));
     566              :     }
     567              : 
     568          290 :     if (CHIP_END_OF_TLV == err)
     569              :     {
     570          145 :         err = CHIP_NO_ERROR;
     571              :     }
     572          145 :     return err;
     573              : }
     574              : 
     575          336 : CHIP_ERROR ReadHandler::ProcessEventPaths(EventPathIBs::Parser & aEventPathsParser)
     576              : {
     577          336 :     CHIP_ERROR err = CHIP_NO_ERROR;
     578          336 :     TLV::TLVReader reader;
     579          336 :     aEventPathsParser.GetReader(&reader);
     580         1670 :     while (CHIP_NO_ERROR == (err = reader.Next()))
     581              :     {
     582          499 :         VerifyOrReturnError(TLV::AnonymousTag() == reader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG);
     583          499 :         EventPathParams event;
     584          499 :         EventPathIB::Parser path;
     585          499 :         ReturnErrorOnFailure(path.Init(reader));
     586          499 :         ReturnErrorOnFailure(path.ParsePath(event));
     587          499 :         ReturnErrorOnFailure(mManagementCallback.GetInteractionModelEngine()->PushFrontEventPathParamsList(mpEventPathList, event));
     588              :     }
     589              : 
     590              :     // if we have exhausted this container
     591          672 :     if (CHIP_END_OF_TLV == err)
     592              :     {
     593          336 :         err = CHIP_NO_ERROR;
     594              :     }
     595          336 :     return err;
     596              : }
     597              : 
     598          315 : CHIP_ERROR ReadHandler::ProcessEventFilters(EventFilterIBs::Parser & aEventFiltersParser)
     599              : {
     600          315 :     CHIP_ERROR err = CHIP_NO_ERROR;
     601          315 :     TLV::TLVReader reader;
     602          315 :     aEventFiltersParser.GetReader(&reader);
     603              : 
     604         1260 :     while (CHIP_NO_ERROR == (err = reader.Next()))
     605              :     {
     606          315 :         VerifyOrReturnError(TLV::AnonymousTag() == reader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG);
     607          315 :         EventFilterIB::Parser filter;
     608          315 :         ReturnErrorOnFailure(filter.Init(reader));
     609              :         // this is for current node, and would have only one event filter.
     610          315 :         ReturnErrorOnFailure(filter.GetEventMin(&(mEventMin)));
     611              :     }
     612          630 :     if (CHIP_END_OF_TLV == err)
     613              :     {
     614          315 :         err = CHIP_NO_ERROR;
     615              :     }
     616          315 :     return err;
     617              : }
     618              : 
     619         4428 : const char * ReadHandler::GetStateStr() const
     620              : {
     621              : #if CHIP_DETAIL_LOGGING
     622         4428 :     switch (mState)
     623              :     {
     624            4 :     case HandlerState::Idle:
     625            4 :         return "Idle";
     626          868 :     case HandlerState::AwaitingDestruction:
     627          868 :         return "AwaitingDestruction";
     628         2315 :     case HandlerState::CanStartReporting:
     629         2315 :         return "CanStartReporting";
     630              : 
     631         1241 :     case HandlerState::AwaitingReportResponse:
     632         1241 :         return "AwaitingReportResponse";
     633              :     }
     634              : #endif // CHIP_DETAIL_LOGGING
     635            0 :     return "N/A";
     636              : }
     637              : 
     638         4428 : void ReadHandler::MoveToState(const HandlerState aTargetState)
     639              : {
     640         4428 :     if (aTargetState == mState)
     641              :     {
     642            0 :         return;
     643              :     }
     644              : 
     645         4428 :     if (IsAwaitingReportResponse() && aTargetState != HandlerState::AwaitingReportResponse)
     646              :     {
     647         1239 :         mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().OnReportConfirm();
     648              :     }
     649              : 
     650         4428 :     mState = aTargetState;
     651         4428 :     ChipLogDetail(DataManagement, "IM RH moving to [%s]", GetStateStr());
     652              : 
     653              :     //
     654              :     // If we just unblocked sending reports, let's go ahead and schedule the reporting
     655              :     // engine to run to kick that off.
     656              :     //
     657         4428 :     if (aTargetState == HandlerState::CanStartReporting)
     658              :     {
     659         2315 :         if (ShouldReportUnscheduled())
     660              :         {
     661         1921 :             TEMPORARY_RETURN_IGNORED mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().ScheduleRun();
     662              :         }
     663              :         else
     664              :         {
     665              :             // If we became reportable, the scheduler will schedule a run as soon as allowed
     666          394 :             mObserver->OnBecameReportable(this);
     667              :         }
     668              :     }
     669              : }
     670              : 
     671          888 : bool ReadHandler::CheckEventClean(EventManagement & aEventManager)
     672              : {
     673          888 :     if (mFlags.Has(ReadHandlerFlags::ChunkedReport))
     674              :     {
     675          525 :         if ((mLastScheduledEventNumber != 0) && (mEventMin <= mLastScheduledEventNumber))
     676              :         {
     677          519 :             return false;
     678              :         }
     679              :     }
     680              :     else
     681              :     {
     682          363 :         EventNumber lastEventNumber = aEventManager.GetLastEventNumber();
     683          363 :         if ((lastEventNumber != 0) && (mEventMin <= lastEventNumber))
     684              :         {
     685              :             // We have more events. snapshot last event number
     686          345 :             aEventManager.SetScheduledEventInfo(mLastScheduledEventNumber, mLastWrittenEventsBytes);
     687          345 :             return false;
     688              :         }
     689              :     }
     690           24 :     return true;
     691              : }
     692              : 
     693          284 : CHIP_ERROR ReadHandler::SendSubscribeResponse()
     694              : {
     695          284 :     System::PacketBufferHandle packet = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
     696          284 :     VerifyOrReturnLogError(!packet.IsNull(), CHIP_ERROR_NO_MEMORY);
     697              : 
     698          284 :     System::PacketBufferTLVWriter writer;
     699          284 :     writer.Init(std::move(packet));
     700              : 
     701          284 :     SubscribeResponseMessage::Builder response;
     702          284 :     ReturnErrorOnFailure(response.Init(&writer));
     703          284 :     ReturnErrorOnFailure(response.SubscriptionId(mSubscriptionId).MaxInterval(mMaxInterval).EndOfSubscribeResponseMessage());
     704              : 
     705          284 :     ReturnErrorOnFailure(writer.Finalize(&packet));
     706          284 :     VerifyOrReturnLogError(mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
     707              : 
     708          284 :     ClearStateFlag(ReadHandlerFlags::PrimingReports);
     709          284 :     return mExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::SubscribeResponse, std::move(packet));
     710          284 : }
     711              : 
     712          307 : CHIP_ERROR ReadHandler::ProcessSubscribeRequest(System::PacketBufferHandle && aPayload)
     713              : {
     714          307 :     System::PacketBufferTLVReader reader;
     715          307 :     reader.Init(std::move(aPayload));
     716              : 
     717          307 :     SubscribeRequestMessage::Parser subscribeRequestParser;
     718          307 :     ReturnErrorOnFailure(subscribeRequestParser.Init(reader));
     719              : 
     720              :     // No need to pretty-print here.  We pretty-print subscribe requests in the
     721              :     // subscribe case of InteractionModelEngine::OnReadInitialRequest, so we do
     722              :     // it even if we reject a subscribe request.
     723              : 
     724          307 :     AttributePathIBs::Parser attributePathListParser;
     725          307 :     CHIP_ERROR err = subscribeRequestParser.GetAttributeRequests(&attributePathListParser);
     726          614 :     if (err == CHIP_END_OF_TLV)
     727              :     {
     728            6 :         err = CHIP_NO_ERROR;
     729              :     }
     730          602 :     else if (err == CHIP_NO_ERROR)
     731              :     {
     732          301 :         ReturnErrorOnFailure(ProcessAttributePaths(attributePathListParser));
     733          301 :         DataVersionFilterIBs::Parser dataVersionFilterListParser;
     734          301 :         err = subscribeRequestParser.GetDataVersionFilters(&dataVersionFilterListParser);
     735          602 :         if (err == CHIP_END_OF_TLV)
     736              :         {
     737          236 :             err = CHIP_NO_ERROR;
     738              :         }
     739          130 :         else if (err == CHIP_NO_ERROR)
     740              :         {
     741           65 :             ReturnErrorOnFailure(ProcessDataVersionFilterList(dataVersionFilterListParser));
     742              :         }
     743              :     }
     744          307 :     ReturnErrorOnFailure(err);
     745              : 
     746          307 :     EventPathIBs::Parser eventPathListParser;
     747          307 :     err = subscribeRequestParser.GetEventRequests(&eventPathListParser);
     748          614 :     if (err == CHIP_END_OF_TLV)
     749              :     {
     750          289 :         err = CHIP_NO_ERROR;
     751              :     }
     752           36 :     else if (err == CHIP_NO_ERROR)
     753              :     {
     754           18 :         ReturnErrorOnFailure(ProcessEventPaths(eventPathListParser));
     755           18 :         EventFilterIBs::Parser eventFilterIBsParser;
     756           18 :         err = subscribeRequestParser.GetEventFilters(&eventFilterIBsParser);
     757           36 :         if (err == CHIP_END_OF_TLV)
     758              :         {
     759           18 :             err = CHIP_NO_ERROR;
     760              :         }
     761            0 :         else if (err == CHIP_NO_ERROR)
     762              :         {
     763            0 :             ReturnErrorOnFailure(ProcessEventFilters(eventFilterIBsParser));
     764              :         }
     765              :     }
     766          307 :     ReturnErrorOnFailure(err);
     767              : 
     768          307 :     ReturnErrorOnFailure(subscribeRequestParser.GetMinIntervalFloorSeconds(&mMinIntervalFloorSeconds));
     769          307 :     ReturnErrorOnFailure(subscribeRequestParser.GetMaxIntervalCeilingSeconds(&mSubscriberRequestedMaxInterval));
     770          307 :     mMaxInterval = mSubscriberRequestedMaxInterval;
     771              : 
     772          307 :     VerifyOrReturnError(mMinIntervalFloorSeconds <= mMaxInterval, CHIP_ERROR_INVALID_ARGUMENT);
     773              : 
     774              : #if CHIP_CONFIG_ENABLE_ICD_SERVER
     775              : 
     776              :     // To optimize ICD sleep/idle cycles, it is preferred to synchronize the subscription report interval
     777              :     // with the IdleModeDuration defined in the ICD Management Cluster.
     778              :     // We aim to use IdleModeDuration, but must respect the Min Interval Floor and Max Interval Ceiling.
     779              :     // Note: This behavior can be overridden using the OnSubscriptionRequested callback defined
     780              :     // in the application.
     781              : 
     782              :     // Per spec and static checks, GetIdleModeDuration max value fits a uint16_t.
     783              :     // The final selected interval must be a uint16_t but we use a uint32 here during calculation to prevent overflows.
     784              :     uint32_t preferredMaxInterval = ICDConfigurationData::GetInstance().GetIdleModeDuration().count();
     785              :     // Determine the max interval ceiling between GetPublisherSelectedIntervalLimit
     786              :     // and the subscriber-requested MaxInterval.
     787              :     uint16_t maxIntervalCeiling = std::max(GetPublisherSelectedIntervalLimit(), mMaxInterval);
     788              : 
     789              :     // Spec doesn't allow IdleModeDuration of 0 but make sure here to prevent div0
     790              :     if (preferredMaxInterval == 0)
     791              :     {
     792              :         preferredMaxInterval = maxIntervalCeiling;
     793              :     }
     794              :     else
     795              :     {
     796              :         // Must respect the minimal interval floor
     797              :         if (preferredMaxInterval < mMinIntervalFloorSeconds)
     798              :         {
     799              :             // Round up to the nearest multiple of IdleModeDuration that is >= mMinIntervalFloorSeconds.
     800              :             const uint32_t remainder = mMinIntervalFloorSeconds % preferredMaxInterval;
     801              :             preferredMaxInterval =
     802              :                 remainder == 0 ? mMinIntervalFloorSeconds : (mMinIntervalFloorSeconds + (preferredMaxInterval - remainder));
     803              :         }
     804              : 
     805              :         // Use the smallest interval between preferredMaxInterval and maxIntervalCeiling.
     806              :         // This also ensure that no overflow occurs when casting to uint16_t below
     807              :         preferredMaxInterval = std::min<uint32_t>(preferredMaxInterval, maxIntervalCeiling);
     808              :     }
     809              :     mMaxInterval = static_cast<uint16_t>(preferredMaxInterval);
     810              : #endif // CHIP_CONFIG_ENABLE_ICD_SERVER
     811              : 
     812              :     //
     813              :     // Notify the application (if requested) of the impending subscription and check whether we should still proceed to set it up.
     814              :     // This also provides the application an opportunity to modify the negotiated min/max intervals set above.
     815              :     //
     816          307 :     auto * appCallback = mManagementCallback.GetAppCallback();
     817          307 :     if (appCallback)
     818              :     {
     819          494 :         if (appCallback->OnSubscriptionRequested(*this, *mExchangeCtx->GetSessionHandle()->AsSecureSession()) != CHIP_NO_ERROR)
     820              :         {
     821            6 :             return CHIP_ERROR_TRANSACTION_CANCELED;
     822              :         }
     823              :     }
     824              : 
     825          301 :     ChipLogProgress(DataManagement, "Final negotiated min/max parameters: Min = %ds, Max = %ds", mMinIntervalFloorSeconds,
     826              :                     mMaxInterval);
     827              : 
     828              :     bool isFabricFiltered;
     829          301 :     ReturnErrorOnFailure(subscribeRequestParser.GetIsFabricFiltered(&isFabricFiltered));
     830          301 :     SetStateFlag(ReadHandlerFlags::FabricFiltered, isFabricFiltered);
     831          301 :     ReturnErrorOnFailure(Crypto::DRBG_get_bytes(reinterpret_cast<uint8_t *>(&mSubscriptionId), sizeof(mSubscriptionId)));
     832          301 :     ReturnErrorOnFailure(subscribeRequestParser.ExitContainer());
     833          301 :     MoveToState(HandlerState::CanStartReporting);
     834              : 
     835          301 :     mExchangeCtx->WillSendMessage();
     836              : 
     837              : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
     838          301 :     PersistSubscription();
     839              : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
     840              : 
     841          301 :     return CHIP_NO_ERROR;
     842          307 : }
     843              : 
     844          301 : void ReadHandler::PersistSubscription()
     845              : {
     846          301 :     auto * subscriptionResumptionStorage = mManagementCallback.GetInteractionModelEngine()->GetSubscriptionResumptionStorage();
     847          301 :     VerifyOrReturn(subscriptionResumptionStorage != nullptr);
     848              : 
     849              :     // TODO(#31873): We need to store the CAT information to enable better interactions with ICDs
     850            0 :     SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId         = GetInitiatorNodeId(),
     851            0 :                                                                          .mFabricIndex    = GetAccessingFabricIndex(),
     852            0 :                                                                          .mSubscriptionId = mSubscriptionId,
     853            0 :                                                                          .mMinInterval    = mMinIntervalFloorSeconds,
     854            0 :                                                                          .mMaxInterval    = mMaxInterval,
     855            0 :                                                                          .mFabricFiltered = IsFabricFiltered() };
     856            0 :     VerifyOrReturn(subscriptionInfo.SetAttributePaths(mpAttributePathList) == CHIP_NO_ERROR);
     857            0 :     VerifyOrReturn(subscriptionInfo.SetEventPaths(mpEventPathList) == CHIP_NO_ERROR);
     858              : 
     859            0 :     CHIP_ERROR err = subscriptionResumptionStorage->Save(subscriptionInfo);
     860            0 :     if (err != CHIP_NO_ERROR)
     861              :     {
     862            0 :         ChipLogError(DataManagement, "Failed to save subscription info error: '%" CHIP_ERROR_FORMAT, err.Format());
     863              :     }
     864            0 : }
     865              : 
     866         1168 : void ReadHandler::ResetPathIterator()
     867              : {
     868         1168 :     mAttributePathExpandPosition = AttributePathExpandIterator::Position::StartIterating(mpAttributePathList);
     869         1168 :     mAttributeEncoderState.Reset();
     870         1168 : }
     871              : 
     872          345 : void ReadHandler::AttributePathIsDirty(DataModel::Provider * apDataModel, const AttributePathParams & aAttributeChanged)
     873              : {
     874          345 :     mDirtyGeneration = mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().GetDirtySetGeneration();
     875              : 
     876              :     // We want to get the value, but not advance the iterator position.
     877          345 :     AttributePathExpandIterator::Position tempPosition = mAttributePathExpandPosition;
     878          345 :     ConcreteAttributePath path;
     879              : 
     880              :     // We won't reset the path iterator for every AttributePathIsDirty call to reduce the number of full data reports.
     881              :     // The iterator will be reset after finishing each report session.
     882              :     //
     883              :     // Here we just reset the iterator to the beginning of the current cluster, if the dirty path affects it.
     884              :     // This will ensure the reports are consistent within a single cluster generated from a single path in the request.
     885              : 
     886              :     // TODO (#16699): Currently we can only guarantee the reports generated from a single path in the request are consistent. The
     887              :     // data might be inconsistent if the user send a request with two paths from the same cluster. We need to clearify the behavior
     888              :     // or make it consistent.
     889          611 :     if (AttributePathExpandIterator(apDataModel, tempPosition).Next(path) &&
     890         1138 :         (aAttributeChanged.HasWildcardEndpointId() || aAttributeChanged.mEndpointId == path.mEndpointId) &&
     891          527 :         (aAttributeChanged.HasWildcardClusterId() || aAttributeChanged.mClusterId == path.mClusterId))
     892              :     {
     893          264 :         ChipLogDetail(DataManagement,
     894              :                       "The dirty path intersects the cluster we are currently reporting; reset the iterator to the beginning of "
     895              :                       "that cluster");
     896              :         // If we're currently in the middle of generating reports for a given cluster and that in turn is marked dirty, let's reset
     897              :         // our iterator to point back to the beginning of that cluster. This ensures that the receiver will get a coherent view of
     898              :         // the state of the cluster as present on the server
     899          264 :         mAttributePathExpandPosition.IterateFromTheStartOfTheCurrentClusterIfAttributeWildcard();
     900              : 
     901          264 :         mAttributeEncoderState.Reset();
     902              :     }
     903              : 
     904              :     // ReportScheduler will take care of verifying the reportability of the handler and schedule the run
     905          345 :     mObserver->OnBecameReportable(this);
     906          345 : }
     907              : 
     908        15249 : Transport::SecureSession * ReadHandler::GetSession() const
     909              : {
     910        15249 :     if (!mSessionHandle)
     911              :     {
     912            0 :         return nullptr;
     913              :     }
     914        15249 :     return mSessionHandle->AsSecureSession();
     915              : }
     916              : 
     917           19 : void ReadHandler::ForceDirtyState()
     918              : {
     919           19 :     SetStateFlag(ReadHandlerFlags::ForceDirty);
     920           19 : }
     921              : 
     922         6962 : void ReadHandler::SetStateFlag(ReadHandlerFlags aFlag, bool aValue)
     923              : {
     924         6962 :     bool oldReportable = ShouldStartReporting();
     925         6962 :     mFlags.Set(aFlag, aValue);
     926              : 
     927              :     // If we became reportable, schedule a reporting run.
     928         6962 :     if (!oldReportable && ShouldStartReporting())
     929              :     {
     930              :         // If we became reportable, the scheduler will schedule a run as soon as allowed
     931           13 :         mObserver->OnBecameReportable(this);
     932              :     }
     933         6962 : }
     934              : 
     935         1382 : void ReadHandler::ClearStateFlag(ReadHandlerFlags aFlag)
     936              : {
     937         1382 :     SetStateFlag(aFlag, false);
     938         1382 : }
     939              : 
     940         1980 : size_t ReadHandler::GetReportBufferMaxSize()
     941              : {
     942         1980 :     Transport::SecureSession * session = GetSession();
     943         1980 :     if (session && session->AllowsLargePayload())
     944              :     {
     945            0 :         return kMaxLargeSecureSduLengthBytes;
     946              :     }
     947         1980 :     return kMaxSecureSduLengthBytes;
     948              : }
     949              : 
     950              : } // namespace app
     951              : } // namespace chip
        

Generated by: LCOV version 2.0-1