LCOV - code coverage report
Current view: top level - app - WriteHandler.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 219 322 68.0 %
Date: 2024-02-15 08:20:41 Functions: 23 27 85.2 %

          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             : #include "messaging/ExchangeContext.h"
      20             : #include <app/AppConfig.h>
      21             : #include <app/InteractionModelEngine.h>
      22             : #include <app/MessageDef/EventPathIB.h>
      23             : #include <app/StatusResponse.h>
      24             : #include <app/WriteHandler.h>
      25             : #include <app/reporting/Engine.h>
      26             : #include <app/util/MatterCallbacks.h>
      27             : #include <credentials/GroupDataProvider.h>
      28             : #include <lib/support/TypeTraits.h>
      29             : 
      30             : namespace chip {
      31             : namespace app {
      32             : 
      33             : using namespace Protocols::InteractionModel;
      34             : using Status                         = Protocols::InteractionModel::Status;
      35             : constexpr uint8_t kListAttributeType = 0x48;
      36             : 
      37         427 : CHIP_ERROR WriteHandler::Init()
      38             : {
      39         427 :     VerifyOrReturnError(!mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
      40             : 
      41         427 :     MoveToState(State::Initialized);
      42             : 
      43         427 :     mACLCheckCache.ClearValue();
      44         427 :     mProcessingAttributePath.ClearValue();
      45             : 
      46         427 :     return CHIP_NO_ERROR;
      47             : }
      48             : 
      49         427 : void WriteHandler::Close()
      50             : {
      51         427 :     VerifyOrReturn(mState != State::Uninitialized);
      52             : 
      53             :     // DeliverFinalListWriteEnd will be a no-op if we have called
      54             :     // DeliverFinalListWriteEnd in success conditions, so passing false for
      55             :     // wasSuccessful here is safe: if it does anything, we were in fact not
      56             :     // successful.
      57         427 :     DeliverFinalListWriteEnd(false /* wasSuccessful */);
      58         427 :     mExchangeCtx.Release();
      59         427 :     mSuppressResponse = false;
      60         427 :     MoveToState(State::Uninitialized);
      61             : }
      62             : 
      63        1735 : Status WriteHandler::HandleWriteRequestMessage(Messaging::ExchangeContext * apExchangeContext,
      64             :                                                System::PacketBufferHandle && aPayload, bool aIsTimedWrite)
      65             : {
      66        1735 :     System::PacketBufferHandle packet = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
      67        1735 :     VerifyOrReturnError(!packet.IsNull(), Status::Failure);
      68             : 
      69        1735 :     System::PacketBufferTLVWriter messageWriter;
      70        1735 :     messageWriter.Init(std::move(packet));
      71        1735 :     VerifyOrReturnError(mWriteResponseBuilder.Init(&messageWriter) == CHIP_NO_ERROR, Status::Failure);
      72             : 
      73        1735 :     mWriteResponseBuilder.CreateWriteResponses();
      74        1735 :     VerifyOrReturnError(mWriteResponseBuilder.GetError() == CHIP_NO_ERROR, Status::Failure);
      75             : 
      76        1735 :     Status status = ProcessWriteRequest(std::move(aPayload), aIsTimedWrite);
      77             : 
      78             :     // Do not send response on Group Write
      79        1735 :     if (status == Status::Success && !apExchangeContext->IsGroupExchangeContext())
      80             :     {
      81        1733 :         CHIP_ERROR err = SendWriteResponse(std::move(messageWriter));
      82        1733 :         if (err != CHIP_NO_ERROR)
      83             :         {
      84           0 :             status = Status::Failure;
      85             :         }
      86             :     }
      87             : 
      88        1735 :     return status;
      89        1735 : }
      90             : 
      91         427 : Status WriteHandler::OnWriteRequest(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload,
      92             :                                     bool aIsTimedWrite)
      93             : {
      94             :     //
      95             :     // Let's take over further message processing on this exchange from the IM.
      96             :     // This is only relevant during chunked requests.
      97             :     //
      98         427 :     mExchangeCtx.Grab(apExchangeContext);
      99             : 
     100         427 :     Status status = HandleWriteRequestMessage(apExchangeContext, std::move(aPayload), aIsTimedWrite);
     101             : 
     102             :     // The write transaction will be alive only when the message was handled successfully and there are more chunks.
     103         427 :     if (!(status == Status::Success && mHasMoreChunks))
     104             :     {
     105          36 :         Close();
     106             :     }
     107             : 
     108         427 :     return status;
     109             : }
     110             : 
     111        1309 : CHIP_ERROR WriteHandler::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
     112             :                                            System::PacketBufferHandle && aPayload)
     113             : {
     114        1309 :     CHIP_ERROR err = CHIP_NO_ERROR;
     115             : 
     116        1309 :     VerifyOrDieWithMsg(apExchangeContext == mExchangeCtx.Get(), DataManagement,
     117             :                        "Incoming exchange context should be same as the initial request.");
     118        1309 :     VerifyOrDieWithMsg(!apExchangeContext->IsGroupExchangeContext(), DataManagement,
     119             :                        "OnMessageReceived should not be called on GroupExchangeContext");
     120        1309 :     if (!aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::WriteRequest))
     121             :     {
     122           1 :         if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse))
     123             :         {
     124           0 :             CHIP_ERROR statusError = CHIP_NO_ERROR;
     125             :             // Parse the status response so we can log it properly.
     126           0 :             StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError);
     127             :         }
     128           1 :         ChipLogDetail(DataManagement, "Unexpected message type %d", aPayloadHeader.GetMessageType());
     129           1 :         StatusResponse::Send(Status::InvalidAction, apExchangeContext, false /*aExpectResponse*/);
     130           1 :         Close();
     131           1 :         return CHIP_ERROR_INVALID_MESSAGE_TYPE;
     132             :     }
     133             : 
     134             :     Status status =
     135        1308 :         HandleWriteRequestMessage(apExchangeContext, std::move(aPayload), false /* chunked write should not be timed write */);
     136        1308 :     if (status == Status::Success)
     137             :     {
     138             :         // We have no more chunks, the write response has been sent in HandleWriteRequestMessage, so close directly.
     139        1308 :         if (!mHasMoreChunks)
     140             :         {
     141         387 :             Close();
     142             :         }
     143             :     }
     144             :     else
     145             :     {
     146           0 :         err = StatusResponse::Send(status, apExchangeContext, false /*aExpectResponse*/);
     147           0 :         Close();
     148             :     }
     149        1308 :     return CHIP_NO_ERROR;
     150             : }
     151             : 
     152           2 : void WriteHandler::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
     153             : {
     154           2 :     ChipLogError(DataManagement, "Time out! failed to receive status response from Exchange: " ChipLogFormatExchange,
     155             :                  ChipLogValueExchange(apExchangeContext));
     156           2 :     Close();
     157           2 : }
     158             : 
     159        1733 : CHIP_ERROR WriteHandler::FinalizeMessage(System::PacketBufferTLVWriter && aMessageWriter, System::PacketBufferHandle & packet)
     160             : {
     161        1733 :     VerifyOrReturnError(mState == State::AddStatus, CHIP_ERROR_INCORRECT_STATE);
     162        1733 :     ReturnErrorOnFailure(mWriteResponseBuilder.GetWriteResponses().EndOfAttributeStatuses());
     163        1733 :     ReturnErrorOnFailure(mWriteResponseBuilder.EndOfWriteResponseMessage());
     164        1733 :     ReturnErrorOnFailure(aMessageWriter.Finalize(&packet));
     165        1733 :     return CHIP_NO_ERROR;
     166             : }
     167             : 
     168        1733 : CHIP_ERROR WriteHandler::SendWriteResponse(System::PacketBufferTLVWriter && aMessageWriter)
     169             : {
     170        1733 :     CHIP_ERROR err = CHIP_NO_ERROR;
     171        1733 :     System::PacketBufferHandle packet;
     172             : 
     173        1733 :     VerifyOrExit(mState == State::AddStatus, err = CHIP_ERROR_INCORRECT_STATE);
     174             : 
     175        1733 :     err = FinalizeMessage(std::move(aMessageWriter), packet);
     176        1733 :     SuccessOrExit(err);
     177             : 
     178        1733 :     VerifyOrExit(mExchangeCtx, err = CHIP_ERROR_INCORRECT_STATE);
     179        1733 :     mExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime);
     180        3466 :     err = mExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::WriteResponse, std::move(packet),
     181        1733 :                                     mHasMoreChunks ? Messaging::SendMessageFlags::kExpectResponse
     182             :                                                    : Messaging::SendMessageFlags::kNone);
     183        1733 :     SuccessOrExit(err);
     184             : 
     185        1733 :     MoveToState(State::Sending);
     186             : 
     187        1733 : exit:
     188        1733 :     return err;
     189        1733 : }
     190             : 
     191         406 : void WriteHandler::DeliverListWriteBegin(const ConcreteAttributePath & aPath)
     192             : {
     193         406 :     if (auto * attrOverride = GetAttributeAccessOverride(aPath.mEndpointId, aPath.mClusterId))
     194             :     {
     195         406 :         attrOverride->OnListWriteBegin(aPath);
     196             :     }
     197         406 : }
     198             : 
     199         413 : void WriteHandler::DeliverListWriteEnd(const ConcreteAttributePath & aPath, bool writeWasSuccessful)
     200             : {
     201         413 :     if (auto * attrOverride = GetAttributeAccessOverride(aPath.mEndpointId, aPath.mClusterId))
     202             :     {
     203         406 :         attrOverride->OnListWriteEnd(aPath, writeWasSuccessful);
     204             :     }
     205         413 : }
     206             : 
     207         848 : void WriteHandler::DeliverFinalListWriteEnd(bool writeWasSuccessful)
     208             : {
     209         848 :     if (mProcessingAttributePath.HasValue() && mProcessingAttributeIsList)
     210             :     {
     211         411 :         DeliverListWriteEnd(mProcessingAttributePath.Value(), writeWasSuccessful);
     212             :     }
     213         848 :     mProcessingAttributePath.ClearValue();
     214         848 : }
     215             : 
     216           0 : CHIP_ERROR WriteHandler::DeliverFinalListWriteEndForGroupWrite(bool writeWasSuccessful)
     217             : {
     218           0 :     VerifyOrReturnError(mProcessingAttributePath.HasValue() && mProcessingAttributeIsList, CHIP_NO_ERROR);
     219             : 
     220           0 :     Credentials::GroupDataProvider::GroupEndpoint mapping;
     221           0 :     Credentials::GroupDataProvider * groupDataProvider = Credentials::GetGroupDataProvider();
     222             :     Credentials::GroupDataProvider::EndpointIterator * iterator;
     223             : 
     224           0 :     GroupId groupId         = mExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
     225           0 :     FabricIndex fabricIndex = GetAccessingFabricIndex();
     226             : 
     227           0 :     auto processingConcreteAttributePath = mProcessingAttributePath.Value();
     228           0 :     mProcessingAttributePath.ClearValue();
     229             : 
     230           0 :     iterator = groupDataProvider->IterateEndpoints(fabricIndex);
     231           0 :     VerifyOrReturnError(iterator != nullptr, CHIP_ERROR_NO_MEMORY);
     232             : 
     233           0 :     while (iterator->Next(mapping))
     234             :     {
     235           0 :         if (groupId != mapping.group_id)
     236             :         {
     237           0 :             continue;
     238             :         }
     239             : 
     240           0 :         processingConcreteAttributePath.mEndpointId = mapping.endpoint_id;
     241             : 
     242           0 :         if (!InteractionModelEngine::GetInstance()->HasConflictWriteRequests(this, processingConcreteAttributePath))
     243             :         {
     244           0 :             DeliverListWriteEnd(processingConcreteAttributePath, writeWasSuccessful);
     245             :         }
     246             :     }
     247           0 :     iterator->Release();
     248           0 :     return CHIP_NO_ERROR;
     249             : }
     250             : namespace {
     251             : 
     252             : // To reduce the various use of previousProcessed.HasValue() && previousProcessed.Value() == nextAttribute to save code size.
     253        6996 : bool IsSameAttribute(const Optional<ConcreteAttributePath> & previousProcessed, const ConcreteDataAttributePath & nextAttribute)
     254             : {
     255        6996 :     return previousProcessed.HasValue() && previousProcessed.Value() == nextAttribute;
     256             : }
     257             : 
     258        2480 : bool ShouldReportListWriteEnd(const Optional<ConcreteAttributePath> & previousProcessed, bool previousProcessedAttributeIsList,
     259             :                               const ConcreteDataAttributePath & nextAttribute)
     260             : {
     261        2480 :     return previousProcessedAttributeIsList && !IsSameAttribute(previousProcessed, nextAttribute) && previousProcessed.HasValue();
     262             : }
     263             : 
     264        2480 : bool ShouldReportListWriteBegin(const Optional<ConcreteAttributePath> & previousProcessed, bool previousProcessedAttributeIsList,
     265             :                                 const ConcreteDataAttributePath & nextAttribute)
     266             : {
     267        2480 :     return !IsSameAttribute(previousProcessed, nextAttribute) && nextAttribute.IsListOperation();
     268             : }
     269             : 
     270             : } // namespace
     271             : 
     272        1733 : CHIP_ERROR WriteHandler::ProcessAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader)
     273             : {
     274        1733 :     CHIP_ERROR err = CHIP_NO_ERROR;
     275             : 
     276        1733 :     ReturnErrorCodeIf(!mExchangeCtx, CHIP_ERROR_INTERNAL);
     277        1733 :     const Access::SubjectDescriptor subjectDescriptor = mExchangeCtx->GetSessionHandle()->GetSubjectDescriptor();
     278             : 
     279        4221 :     while (CHIP_NO_ERROR == (err = aAttributeDataIBsReader.Next()))
     280             :     {
     281             :         chip::TLV::TLVReader dataReader;
     282        2488 :         AttributeDataIB::Parser element;
     283        2488 :         AttributePathIB::Parser attributePath;
     284        2488 :         ConcreteDataAttributePath dataAttributePath;
     285        2488 :         TLV::TLVReader reader = aAttributeDataIBsReader;
     286             : 
     287        2488 :         err = element.Init(reader);
     288        2488 :         SuccessOrExit(err);
     289             : 
     290        2488 :         err = element.GetPath(&attributePath);
     291        2488 :         SuccessOrExit(err);
     292             : 
     293        2488 :         err = attributePath.GetConcreteAttributePath(dataAttributePath);
     294        2488 :         SuccessOrExit(err);
     295             : 
     296        2488 :         err = element.GetData(&dataReader);
     297        2488 :         SuccessOrExit(err);
     298             : 
     299        2488 :         const auto attributeMetadata = GetAttributeMetadata(dataAttributePath);
     300        2488 :         bool currentAttributeIsList  = (attributeMetadata != nullptr && attributeMetadata->attributeType == kListAttributeType);
     301             : 
     302        2488 :         if (!dataAttributePath.IsListOperation() && currentAttributeIsList)
     303             :         {
     304         411 :             dataAttributePath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
     305             :         }
     306             : 
     307        7028 :         if (InteractionModelEngine::GetInstance()->HasConflictWriteRequests(this, dataAttributePath) ||
     308             :             // Per chunking protocol, we are processing the list entries, but the initial empty list is not processed, so we reject
     309             :             // it with Busy status code.
     310        4540 :             (dataAttributePath.IsListItemOperation() && !IsSameAttribute(mProcessingAttributePath, dataAttributePath)))
     311             :         {
     312           8 :             err = AddStatus(dataAttributePath, StatusIB(Status::Busy));
     313           8 :             continue;
     314             :         }
     315             : 
     316        2480 :         if (ShouldReportListWriteEnd(mProcessingAttributePath, mProcessingAttributeIsList, dataAttributePath))
     317             :         {
     318           2 :             DeliverListWriteEnd(mProcessingAttributePath.Value(), mAttributeWriteSuccessful);
     319             :         }
     320             : 
     321        2480 :         if (ShouldReportListWriteBegin(mProcessingAttributePath, mProcessingAttributeIsList, dataAttributePath))
     322             :         {
     323         406 :             DeliverListWriteBegin(dataAttributePath);
     324         406 :             mAttributeWriteSuccessful = true;
     325             :         }
     326             : 
     327        2480 :         mProcessingAttributeIsList = dataAttributePath.IsListOperation();
     328        2480 :         mProcessingAttributePath.SetValue(dataAttributePath);
     329             : 
     330        2480 :         MatterPreAttributeWriteCallback(dataAttributePath);
     331        2480 :         TLV::TLVWriter backup;
     332        2480 :         DataVersion version = 0;
     333        2480 :         mWriteResponseBuilder.GetWriteResponses().Checkpoint(backup);
     334        2480 :         err = element.GetDataVersion(&version);
     335        2480 :         if (CHIP_NO_ERROR == err)
     336             :         {
     337          12 :             dataAttributePath.mDataVersion.SetValue(version);
     338             :         }
     339        2468 :         else if (CHIP_END_OF_TLV == err)
     340             :         {
     341        2468 :             err = CHIP_NO_ERROR;
     342             :         }
     343        2480 :         SuccessOrExit(err);
     344        2480 :         err = WriteSingleClusterData(subjectDescriptor, dataAttributePath, dataReader, this);
     345        2480 :         if (err != CHIP_NO_ERROR)
     346             :         {
     347          10 :             mWriteResponseBuilder.GetWriteResponses().Rollback(backup);
     348          10 :             err = AddStatus(dataAttributePath, StatusIB(err));
     349             :         }
     350        2480 :         MatterPostAttributeWriteCallback(dataAttributePath);
     351        2480 :         SuccessOrExit(err);
     352        2488 :     }
     353             : 
     354        1733 :     if (CHIP_END_OF_TLV == err)
     355             :     {
     356        1733 :         err = CHIP_NO_ERROR;
     357             :     }
     358             : 
     359        1733 :     SuccessOrExit(err);
     360             : 
     361        1733 :     if (!mHasMoreChunks)
     362             :     {
     363         421 :         DeliverFinalListWriteEnd(mAttributeWriteSuccessful);
     364             :     }
     365             : 
     366        1312 : exit:
     367        1733 :     return err;
     368             : }
     369             : 
     370           0 : CHIP_ERROR WriteHandler::ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader)
     371             : {
     372           0 :     CHIP_ERROR err = CHIP_NO_ERROR;
     373             : 
     374           0 :     ReturnErrorCodeIf(!mExchangeCtx, CHIP_ERROR_INTERNAL);
     375             :     const Access::SubjectDescriptor subjectDescriptor =
     376           0 :         mExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetSubjectDescriptor();
     377             : 
     378           0 :     GroupId groupId    = mExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
     379           0 :     FabricIndex fabric = GetAccessingFabricIndex();
     380             : 
     381           0 :     while (CHIP_NO_ERROR == (err = aAttributeDataIBsReader.Next()))
     382             :     {
     383             :         chip::TLV::TLVReader dataReader;
     384           0 :         AttributeDataIB::Parser element;
     385           0 :         AttributePathIB::Parser attributePath;
     386           0 :         ConcreteDataAttributePath dataAttributePath;
     387           0 :         TLV::TLVReader reader = aAttributeDataIBsReader;
     388             : 
     389           0 :         Credentials::GroupDataProvider::GroupEndpoint mapping;
     390           0 :         Credentials::GroupDataProvider * groupDataProvider = Credentials::GetGroupDataProvider();
     391             :         Credentials::GroupDataProvider::EndpointIterator * iterator;
     392             : 
     393           0 :         err = element.Init(reader);
     394           0 :         SuccessOrExit(err);
     395             : 
     396           0 :         err = element.GetPath(&attributePath);
     397           0 :         SuccessOrExit(err);
     398             : 
     399           0 :         err = attributePath.GetGroupAttributePath(dataAttributePath);
     400           0 :         SuccessOrExit(err);
     401             : 
     402           0 :         err = element.GetData(&dataReader);
     403           0 :         SuccessOrExit(err);
     404             : 
     405           0 :         if (!dataAttributePath.IsListOperation() && dataReader.GetType() == TLV::TLVType::kTLVType_Array)
     406             :         {
     407           0 :             dataAttributePath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
     408             :         }
     409             : 
     410           0 :         ChipLogDetail(DataManagement,
     411             :                       "Received group attribute write for Group=%u Cluster=" ChipLogFormatMEI " attribute=" ChipLogFormatMEI,
     412             :                       groupId, ChipLogValueMEI(dataAttributePath.mClusterId), ChipLogValueMEI(dataAttributePath.mAttributeId));
     413             : 
     414           0 :         iterator = groupDataProvider->IterateEndpoints(fabric);
     415           0 :         VerifyOrExit(iterator != nullptr, err = CHIP_ERROR_NO_MEMORY);
     416             : 
     417             :         bool shouldReportListWriteEnd =
     418           0 :             ShouldReportListWriteEnd(mProcessingAttributePath, mProcessingAttributeIsList, dataAttributePath);
     419           0 :         bool shouldReportListWriteBegin = false; // This will be set below.
     420             : 
     421           0 :         const EmberAfAttributeMetadata * attributeMetadata = nullptr;
     422             : 
     423           0 :         while (iterator->Next(mapping))
     424             :         {
     425           0 :             if (groupId != mapping.group_id)
     426             :             {
     427           0 :                 continue;
     428             :             }
     429             : 
     430           0 :             dataAttributePath.mEndpointId = mapping.endpoint_id;
     431             : 
     432             :             // Try to get the metadata from for the attribute from one of the expanded endpoints (it doesn't really matter which
     433             :             // endpoint we pick, as long as it's valid) and update the path info according to it and recheck if we need to report
     434             :             // list write begin.
     435           0 :             if (attributeMetadata == nullptr)
     436             :             {
     437           0 :                 attributeMetadata = GetAttributeMetadata(dataAttributePath);
     438           0 :                 bool currentAttributeIsList =
     439           0 :                     (attributeMetadata != nullptr && attributeMetadata->attributeType == kListAttributeType);
     440           0 :                 if (!dataAttributePath.IsListOperation() && currentAttributeIsList)
     441             :                 {
     442           0 :                     dataAttributePath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
     443             :                 }
     444             :                 ConcreteDataAttributePath pathForCheckingListWriteBegin(kInvalidEndpointId, dataAttributePath.mClusterId,
     445           0 :                                                                         dataAttributePath.mEndpointId, dataAttributePath.mListOp,
     446           0 :                                                                         dataAttributePath.mListIndex);
     447             :                 shouldReportListWriteBegin =
     448           0 :                     ShouldReportListWriteBegin(mProcessingAttributePath, mProcessingAttributeIsList, pathForCheckingListWriteBegin);
     449           0 :             }
     450             : 
     451           0 :             if (shouldReportListWriteEnd)
     452             :             {
     453           0 :                 auto processingConcreteAttributePath        = mProcessingAttributePath.Value();
     454           0 :                 processingConcreteAttributePath.mEndpointId = mapping.endpoint_id;
     455           0 :                 if (!InteractionModelEngine::GetInstance()->HasConflictWriteRequests(this, processingConcreteAttributePath))
     456             :                 {
     457           0 :                     DeliverListWriteEnd(processingConcreteAttributePath, true /* writeWasSuccessful */);
     458             :                 }
     459             :             }
     460             : 
     461           0 :             if (InteractionModelEngine::GetInstance()->HasConflictWriteRequests(this, dataAttributePath))
     462             :             {
     463           0 :                 ChipLogDetail(DataManagement,
     464             :                               "Writing attribute endpoint=%u Cluster=" ChipLogFormatMEI " attribute=" ChipLogFormatMEI
     465             :                               " is conflict with other write transactions.",
     466             :                               mapping.endpoint_id, ChipLogValueMEI(dataAttributePath.mClusterId),
     467             :                               ChipLogValueMEI(dataAttributePath.mAttributeId));
     468           0 :                 continue;
     469             :             }
     470             : 
     471           0 :             if (shouldReportListWriteBegin)
     472             :             {
     473           0 :                 DeliverListWriteBegin(dataAttributePath);
     474             :             }
     475             : 
     476           0 :             ChipLogDetail(DataManagement,
     477             :                           "Processing group attribute write for endpoint=%u Cluster=" ChipLogFormatMEI
     478             :                           " attribute=" ChipLogFormatMEI,
     479             :                           mapping.endpoint_id, ChipLogValueMEI(dataAttributePath.mClusterId),
     480             :                           ChipLogValueMEI(dataAttributePath.mAttributeId));
     481             : 
     482           0 :             chip::TLV::TLVReader tmpDataReader(dataReader);
     483             : 
     484           0 :             MatterPreAttributeWriteCallback(dataAttributePath);
     485           0 :             err = WriteSingleClusterData(subjectDescriptor, dataAttributePath, tmpDataReader, this);
     486             : 
     487           0 :             if (err != CHIP_NO_ERROR)
     488             :             {
     489           0 :                 ChipLogError(DataManagement,
     490             :                              "WriteSingleClusterData Endpoint=%u Cluster=" ChipLogFormatMEI " Attribute =" ChipLogFormatMEI
     491             :                              " failed: %" CHIP_ERROR_FORMAT,
     492             :                              mapping.endpoint_id, ChipLogValueMEI(dataAttributePath.mClusterId),
     493             :                              ChipLogValueMEI(dataAttributePath.mAttributeId), err.Format());
     494             :             }
     495           0 :             MatterPostAttributeWriteCallback(dataAttributePath);
     496             :         }
     497             : 
     498           0 :         dataAttributePath.mEndpointId = kInvalidEndpointId;
     499           0 :         mProcessingAttributeIsList    = dataAttributePath.IsListOperation();
     500           0 :         mProcessingAttributePath.SetValue(dataAttributePath);
     501           0 :         iterator->Release();
     502           0 :     }
     503             : 
     504           0 :     if (CHIP_END_OF_TLV == err)
     505             :     {
     506           0 :         err = CHIP_NO_ERROR;
     507             :     }
     508             : 
     509           0 :     err = DeliverFinalListWriteEndForGroupWrite(true);
     510             : 
     511           0 : exit:
     512             :     // The DeliverFinalListWriteEndForGroupWrite above will deliver the successful state of the list write and clear the
     513             :     // mProcessingAttributePath making the following call no-op. So we call it again after the exit label to deliver a failure state
     514             :     // to the clusters. Ignore the error code since we need to deliver other more important failures.
     515           0 :     DeliverFinalListWriteEndForGroupWrite(false);
     516           0 :     return err;
     517             : }
     518             : 
     519        1735 : Status WriteHandler::ProcessWriteRequest(System::PacketBufferHandle && aPayload, bool aIsTimedWrite)
     520             : {
     521        1735 :     CHIP_ERROR err = CHIP_NO_ERROR;
     522        1735 :     System::PacketBufferTLVReader reader;
     523             : 
     524        1735 :     WriteRequestMessage::Parser writeRequestParser;
     525        1735 :     AttributeDataIBs::Parser AttributeDataIBsParser;
     526             :     TLV::TLVReader AttributeDataIBsReader;
     527             :     // Default to InvalidAction for our status; that's what we want if any of
     528             :     // the parsing of our overall structure or paths fails.  Once we have a
     529             :     // successfully parsed path, the only way we will get a failure return is if
     530             :     // our path handling fails to AddStatus on us.
     531             :     //
     532             :     // TODO: That's not technically InvalidAction, and we should probably make
     533             :     // our callees hand out Status as well.
     534        1735 :     Status status = Status::InvalidAction;
     535             : 
     536        1735 :     reader.Init(std::move(aPayload));
     537             : 
     538        1735 :     err = writeRequestParser.Init(reader);
     539        1735 :     SuccessOrExit(err);
     540             : 
     541             : #if CHIP_CONFIG_IM_PRETTY_PRINT
     542        1735 :     writeRequestParser.PrettyPrint();
     543             : #endif
     544        1735 :     err = writeRequestParser.GetSuppressResponse(&mSuppressResponse);
     545        1735 :     if (err == CHIP_END_OF_TLV)
     546             :     {
     547           4 :         err = CHIP_NO_ERROR;
     548             :     }
     549        1735 :     SuccessOrExit(err);
     550             : 
     551        1735 :     err = writeRequestParser.GetTimedRequest(&mIsTimedRequest);
     552        1735 :     SuccessOrExit(err);
     553             : 
     554        1735 :     err = writeRequestParser.GetMoreChunkedMessages(&mHasMoreChunks);
     555        1735 :     if (err == CHIP_ERROR_END_OF_TLV)
     556             :     {
     557           4 :         err = CHIP_NO_ERROR;
     558             :     }
     559        1735 :     SuccessOrExit(err);
     560             : 
     561        1735 :     if (mHasMoreChunks && (mExchangeCtx->IsGroupExchangeContext() || mIsTimedRequest))
     562             :     {
     563             :         // Sanity check: group exchange context should only have one chunk.
     564             :         // Also, timed requests should not have more than one chunk.
     565           0 :         ExitNow(err = CHIP_ERROR_INVALID_MESSAGE_TYPE);
     566             :     }
     567             : 
     568        1735 :     err = writeRequestParser.GetWriteRequests(&AttributeDataIBsParser);
     569        1735 :     SuccessOrExit(err);
     570             : 
     571        1735 :     if (mIsTimedRequest != aIsTimedWrite)
     572             :     {
     573             :         // The message thinks it should be part of a timed interaction but it's
     574             :         // not, or vice versa.  Spec says to Respond with UNSUPPORTED_ACCESS.
     575           2 :         status = Status::UnsupportedAccess;
     576           2 :         goto exit;
     577             :     }
     578             : 
     579        1733 :     AttributeDataIBsParser.GetReader(&AttributeDataIBsReader);
     580             : 
     581        1733 :     if (mExchangeCtx->IsGroupExchangeContext())
     582             :     {
     583           0 :         err = ProcessGroupAttributeDataIBs(AttributeDataIBsReader);
     584             :     }
     585             :     else
     586             :     {
     587        1733 :         err = ProcessAttributeDataIBs(AttributeDataIBsReader);
     588             :     }
     589        1733 :     SuccessOrExit(err);
     590        1733 :     SuccessOrExit(err = writeRequestParser.ExitContainer());
     591             : 
     592        1733 :     if (err == CHIP_NO_ERROR)
     593             :     {
     594        1733 :         status = Status::Success;
     595             :     }
     596             : 
     597           0 : exit:
     598        1735 :     if (err != CHIP_NO_ERROR)
     599             :     {
     600           0 :         ChipLogError(DataManagement, "Failed to process write request: %" CHIP_ERROR_FORMAT, err.Format());
     601             :     }
     602        3470 :     return status;
     603        1735 : }
     604             : 
     605        2476 : CHIP_ERROR WriteHandler::AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::Status aStatus)
     606             : {
     607        2476 :     return AddStatus(aPath, StatusIB(aStatus));
     608             : }
     609             : 
     610           0 : CHIP_ERROR WriteHandler::AddClusterSpecificSuccess(const ConcreteDataAttributePath & aPath, ClusterStatus aClusterStatus)
     611             : {
     612           0 :     return AddStatus(aPath, StatusIB(Status::Success, aClusterStatus));
     613             : }
     614             : 
     615           0 : CHIP_ERROR WriteHandler::AddClusterSpecificFailure(const ConcreteDataAttributePath & aPath, ClusterStatus aClusterStatus)
     616             : {
     617           0 :     return AddStatus(aPath, StatusIB(Status::Failure, aClusterStatus));
     618             : }
     619             : 
     620        2494 : CHIP_ERROR WriteHandler::AddStatus(const ConcreteDataAttributePath & aPath, const StatusIB & aStatus)
     621             : {
     622        2494 :     AttributeStatusIBs::Builder & writeResponses   = mWriteResponseBuilder.GetWriteResponses();
     623        2494 :     AttributeStatusIB::Builder & attributeStatusIB = writeResponses.CreateAttributeStatus();
     624             : 
     625        2494 :     if (!aStatus.IsSuccess())
     626             :     {
     627          33 :         mAttributeWriteSuccessful = false;
     628             :     }
     629             : 
     630        2494 :     ReturnErrorOnFailure(writeResponses.GetError());
     631             : 
     632        2494 :     AttributePathIB::Builder & path = attributeStatusIB.CreatePath();
     633        2494 :     ReturnErrorOnFailure(attributeStatusIB.GetError());
     634        2494 :     ReturnErrorOnFailure(path.Encode(aPath));
     635             : 
     636        2494 :     StatusIB::Builder & statusIBBuilder = attributeStatusIB.CreateErrorStatus();
     637        2494 :     ReturnErrorOnFailure(attributeStatusIB.GetError());
     638        2494 :     statusIBBuilder.EncodeStatusIB(aStatus);
     639        2494 :     ReturnErrorOnFailure(statusIBBuilder.GetError());
     640        2494 :     ReturnErrorOnFailure(attributeStatusIB.EndOfAttributeStatusIB());
     641             : 
     642        2494 :     MoveToState(State::AddStatus);
     643        2494 :     return CHIP_NO_ERROR;
     644             : }
     645             : 
     646           1 : FabricIndex WriteHandler::GetAccessingFabricIndex() const
     647             : {
     648           1 :     return mExchangeCtx->GetSessionHandle()->GetFabricIndex();
     649             : }
     650             : 
     651        5081 : const char * WriteHandler::GetStateStr() const
     652             : {
     653             : #if CHIP_DETAIL_LOGGING
     654        5081 :     switch (mState)
     655             :     {
     656         427 :     case State::Uninitialized:
     657         427 :         return "Uninitialized";
     658             : 
     659         427 :     case State::Initialized:
     660         427 :         return "Initialized";
     661             : 
     662        2494 :     case State::AddStatus:
     663        2494 :         return "AddStatus";
     664        1733 :     case State::Sending:
     665        1733 :         return "Sending";
     666             :     }
     667             : #endif // CHIP_DETAIL_LOGGING
     668           0 :     return "N/A";
     669             : }
     670             : 
     671        5081 : void WriteHandler::MoveToState(const State aTargetState)
     672             : {
     673        5081 :     mState = aTargetState;
     674        5081 :     ChipLogDetail(DataManagement, "IM WH moving to [%s]", GetStateStr());
     675        5081 : }
     676             : 
     677             : } // namespace app
     678             : } // namespace chip
     679             : 
     680        2480 : void __attribute__((weak)) MatterPreAttributeWriteCallback(const chip::app::ConcreteAttributePath & attributePath) {}
     681        2480 : void __attribute__((weak)) MatterPostAttributeWriteCallback(const chip::app::ConcreteAttributePath & attributePath) {}

Generated by: LCOV version 1.14