Matter SDK Coverage Report
Current view: top level - app - WriteHandler.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 67.5 % 363 245
Test Date: 2025-01-17 19:00:11 Functions: 71.0 % 31 22

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

Generated by: LCOV version 2.0-1