Matter SDK Coverage Report
Current view: top level - app - WriteClient.cpp (source / functions) Coverage Total Hit
Test: SHA:6c8f029dd2432dc900f1c9245c324e69bd79e40a Lines: 88.7 % 300 266
Test Date: 2026-08-08 07:40:09 Functions: 95.5 % 22 21

            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              : /**
      20              :  *    @file
      21              :  *      This file defines the initiator side of a CHIP Write Interaction.
      22              :  *
      23              :  */
      24              : 
      25              : #include "lib/core/CHIPError.h"
      26              : #include <app/AppConfig.h>
      27              : #include <app/InteractionModelEngine.h>
      28              : #include <app/TimedRequest.h>
      29              : #include <app/WriteClient.h>
      30              : 
      31              : namespace chip {
      32              : namespace app {
      33              : 
      34          962 : void WriteClient::Close()
      35              : {
      36          962 :     MoveToState(State::AwaitingDestruction);
      37          962 :     mExchangeCtx.Release();
      38              : 
      39          962 :     if (mpCallback)
      40              :     {
      41          962 :         mpCallback->OnDone(this);
      42              :     }
      43          962 : }
      44              : 
      45         3864 : CHIP_ERROR WriteClient::ProcessWriteResponseMessage(System::PacketBufferHandle && payload)
      46              : {
      47         3864 :     CHIP_ERROR err = CHIP_NO_ERROR;
      48         3864 :     System::PacketBufferTLVReader reader;
      49         3864 :     TLV::TLVReader attributeStatusesReader;
      50         3864 :     WriteResponseMessage::Parser writeResponse;
      51         3864 :     AttributeStatusIBs::Parser attributeStatusesParser;
      52              : 
      53         3864 :     reader.Init(std::move(payload));
      54              : 
      55         3864 :     ReturnErrorOnFailure(writeResponse.Init(reader));
      56              : 
      57              : #if CHIP_CONFIG_IM_PRETTY_PRINT
      58         3863 :     TEMPORARY_RETURN_IGNORED writeResponse.PrettyPrint();
      59              : #endif
      60              : 
      61         3863 :     err = writeResponse.GetWriteResponses(&attributeStatusesParser);
      62         7726 :     if (err == CHIP_END_OF_TLV)
      63              :     {
      64            0 :         return CHIP_NO_ERROR;
      65              :     }
      66         3863 :     ReturnErrorOnFailure(err);
      67              : 
      68         3863 :     attributeStatusesParser.GetReader(&attributeStatusesReader);
      69              : 
      70        18118 :     while (CHIP_NO_ERROR == (err = attributeStatusesReader.Next()))
      71              :     {
      72         5196 :         VerifyOrReturnError(TLV::AnonymousTag() == attributeStatusesReader.GetTag(), err = CHIP_ERROR_INVALID_TLV_TAG);
      73              : 
      74         5196 :         AttributeStatusIB::Parser element;
      75              : 
      76         5196 :         ReturnErrorOnFailure(element.Init(attributeStatusesReader));
      77         5196 :         ReturnErrorOnFailure(ProcessAttributeStatusIB(element));
      78              :     }
      79              : 
      80              :     // if we have exhausted this container
      81         7726 :     if (CHIP_END_OF_TLV == err)
      82              :     {
      83         3863 :         err = CHIP_NO_ERROR;
      84              :     }
      85         3863 :     ReturnErrorOnFailure(err);
      86         3863 :     return writeResponse.ExitContainer();
      87         3864 : }
      88              : 
      89         8177 : CHIP_ERROR WriteClient::PrepareAttributeIB(const ConcreteDataAttributePath & aPath)
      90              : {
      91         8177 :     AttributeDataIBs::Builder & writeRequests  = mWriteRequestBuilder.GetWriteRequests();
      92         8177 :     AttributeDataIB::Builder & attributeDataIB = writeRequests.CreateAttributeDataIBBuilder();
      93         8177 :     ReturnErrorOnFailure(writeRequests.GetError());
      94         8155 :     if (aPath.mDataVersion.HasValue())
      95              :     {
      96           12 :         attributeDataIB.DataVersion(aPath.mDataVersion.Value());
      97           12 :         mHasDataVersion = true;
      98              :     }
      99         8155 :     ReturnErrorOnFailure(attributeDataIB.GetError());
     100         8155 :     AttributePathIB::Builder & path = attributeDataIB.CreatePath();
     101              : 
     102              :     // We are using kInvalidEndpointId just for group write requests. This is not the correct use of ConcreteDataAttributePath.
     103              :     // TODO: update AttributePathParams or ConcreteDataAttributePath for a class supports both nullable list index and missing
     104              :     // endpoint id.
     105         8155 :     if (aPath.mEndpointId != kInvalidEndpointId)
     106              :     {
     107         8155 :         path.Endpoint(aPath.mEndpointId);
     108              :     }
     109         8155 :     path.Cluster(aPath.mClusterId).Attribute(aPath.mAttributeId);
     110         8155 :     if (aPath.IsListItemOperation())
     111              :     {
     112         6901 :         if (aPath.mListOp == ConcreteDataAttributePath::ListOperation::AppendItem)
     113              :         {
     114         6901 :             path.ListIndex(DataModel::NullNullable);
     115              :         }
     116              :         else
     117              :         {
     118              :             // We do not support other list operations (i.e. update, delete etc) for now.
     119            0 :             return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
     120              :         }
     121              :     }
     122         8155 :     ReturnErrorOnFailure(path.EndOfAttributePathIB());
     123              : 
     124         7683 :     return CHIP_NO_ERROR;
     125              : }
     126              : 
     127         5425 : CHIP_ERROR WriteClient::FinishAttributeIB()
     128              : {
     129         5425 :     AttributeDataIB::Builder & attributeDataIB = mWriteRequestBuilder.GetWriteRequests().GetAttributeDataIBBuilder();
     130         5425 :     ReturnErrorOnFailure(attributeDataIB.EndOfAttributeDataIB());
     131         5393 :     MoveToState(State::AddAttribute);
     132         5393 :     return CHIP_NO_ERROR;
     133              : }
     134              : 
     135         8153 : TLV::TLVWriter * WriteClient::GetAttributeDataIBTLVWriter()
     136              : {
     137         8153 :     return mWriteRequestBuilder.GetWriteRequests().GetAttributeDataIBBuilder().GetWriter();
     138              : }
     139              : 
     140         4087 : CHIP_ERROR WriteClient::FinalizeMessage(bool aHasMoreChunks)
     141              : {
     142         4087 :     System::PacketBufferHandle packet;
     143         4087 :     VerifyOrReturnError(mState == State::AddAttribute, CHIP_ERROR_INCORRECT_STATE);
     144              : 
     145         4087 :     TLV::TLVWriter * writer = mWriteRequestBuilder.GetWriter();
     146         4087 :     VerifyOrReturnError(writer != nullptr, CHIP_ERROR_INCORRECT_STATE);
     147         4087 :     ReturnErrorOnFailure(writer->UnreserveBuffer(kReservedSizeForTLVEncodingOverhead));
     148              : 
     149         4087 :     ReturnErrorOnFailure(mWriteRequestBuilder.GetWriteRequests().EndOfAttributeDataIBs());
     150              : 
     151         4087 :     ReturnErrorOnFailure(mWriteRequestBuilder.MoreChunkedMessages(aHasMoreChunks).EndOfWriteRequestMessage());
     152         4087 :     ReturnErrorOnFailure(mMessageWriter.Finalize(&packet));
     153         4087 :     mChunks.AddToEnd(std::move(packet));
     154         4087 :     return CHIP_NO_ERROR;
     155         4087 : }
     156              : 
     157         1205 : CHIP_ERROR WriteClient::EnsureMessage()
     158              : {
     159         1205 :     if (mState != State::AddAttribute)
     160              :     {
     161         1071 :         return StartNewMessage();
     162              :     }
     163          134 :     return CHIP_NO_ERROR;
     164              : }
     165              : 
     166         4190 : CHIP_ERROR WriteClient::StartNewMessage()
     167              : {
     168         4190 :     uint16_t reservedSize = 0;
     169              : 
     170         4190 :     if (mState == State::AddAttribute)
     171              :     {
     172         3119 :         ReturnErrorOnFailure(FinalizeMessage(true));
     173              :     }
     174              : 
     175              :     // Per the Matter specification a chunked Write Request cannot be part of a Timed Write Interaction, and it cannot
     176              :     // suppress the response: intermediate WriteResponses are required to pace the chunks, so SuppressResponse must be
     177              :     // false when the request is chunked.
     178         4190 :     VerifyOrReturnError(!((mTimedWriteTimeoutMs.HasValue() || mSuppressResponse) && !mChunks.IsNull()),
     179              :                         CHIP_ERROR_INVALID_MESSAGE_TYPE);
     180              : 
     181         4189 :     System::PacketBufferHandle packet = System::PacketBufferHandle::New(kMaxSecureSduLengthBytes);
     182         4189 :     VerifyOrReturnError(!packet.IsNull(), CHIP_ERROR_NO_MEMORY);
     183              : 
     184              :     // Always limit the size of the packet to fit within kMaxSecureSduLengthBytes regardless of the available buffer capacity.
     185         4189 :     if (packet->AvailableDataLength() > kMaxSecureSduLengthBytes)
     186              :     {
     187            0 :         reservedSize = static_cast<uint16_t>(packet->AvailableDataLength() - kMaxSecureSduLengthBytes);
     188              :     }
     189              : 
     190              :     // ... and we need to reserve some extra space for the MIC field.
     191         4189 :     reservedSize = static_cast<uint16_t>(reservedSize + Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES);
     192              : 
     193              :     // ... and the overhead for end of AttributeDataIBs (end of container), more chunks flag, end of WriteRequestMessage (another
     194              :     // end of container).
     195         4189 :     reservedSize = static_cast<uint16_t>(reservedSize + kReservedSizeForTLVEncodingOverhead);
     196              : 
     197              : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
     198              :     // ... and for unit tests.
     199         4189 :     reservedSize = static_cast<uint16_t>(reservedSize + mReservedSize);
     200              : #endif
     201              : 
     202         4189 :     mMessageWriter.Init(std::move(packet));
     203              : 
     204         4189 :     ReturnErrorOnFailure(mMessageWriter.ReserveBuffer(reservedSize));
     205              : 
     206         4189 :     ReturnErrorOnFailure(mWriteRequestBuilder.Init(&mMessageWriter));
     207         4189 :     mWriteRequestBuilder.SuppressResponse(mSuppressResponse);
     208         4189 :     mWriteRequestBuilder.TimedRequest(mTimedRequestFieldValue);
     209         4189 :     ReturnErrorOnFailure(mWriteRequestBuilder.GetError());
     210         4189 :     mWriteRequestBuilder.CreateWriteRequests();
     211         4189 :     ReturnErrorOnFailure(mWriteRequestBuilder.GetError());
     212              : 
     213         4189 :     TLV::TLVWriter * writer = mWriteRequestBuilder.GetWriter();
     214         4189 :     VerifyOrReturnError(writer != nullptr, CHIP_ERROR_INCORRECT_STATE);
     215              : 
     216         4189 :     return CHIP_NO_ERROR;
     217         4189 : }
     218              : 
     219          950 : CHIP_ERROR WriteClient::TryPutSinglePreencodedAttributeWritePayload(const ConcreteDataAttributePath & attributePath,
     220              :                                                                     const TLV::TLVReader & data)
     221              : {
     222          950 :     TLV::TLVReader dataToWrite;
     223          950 :     dataToWrite.Init(data);
     224              : 
     225          950 :     TLV::TLVWriter * writer = nullptr;
     226              : 
     227          950 :     ReturnErrorOnFailure(PrepareAttributeIB(attributePath));
     228          816 :     VerifyOrReturnError((writer = GetAttributeDataIBTLVWriter()) != nullptr, CHIP_ERROR_INCORRECT_STATE);
     229          816 :     ReturnErrorOnFailure(writer->CopyElement(TLV::ContextTag(AttributeDataIB::Tag::kData), dataToWrite));
     230          755 :     ReturnErrorOnFailure(FinishAttributeIB());
     231          741 :     return CHIP_NO_ERROR;
     232              : }
     233              : 
     234          741 : CHIP_ERROR WriteClient::PutSinglePreencodedAttributeWritePayload(const chip::app::ConcreteDataAttributePath & attributePath,
     235              :                                                                  const TLV::TLVReader & data)
     236              : {
     237          741 :     TLV::TLVWriter backupWriter;
     238              : 
     239          741 :     mWriteRequestBuilder.GetWriteRequests().Checkpoint(backupWriter);
     240              : 
     241              :     // First attempt to write this attribute.
     242          741 :     CHIP_ERROR err = TryPutSinglePreencodedAttributeWritePayload(attributePath, data);
     243         2014 :     if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
     244              :     {
     245              :         // If it failed with no memory, then we create a new chunk for it.
     246          209 :         mWriteRequestBuilder.GetWriteRequests().Rollback(backupWriter);
     247          209 :         ReturnErrorOnFailure(StartNewMessage());
     248          209 :         err = TryPutSinglePreencodedAttributeWritePayload(attributePath, data);
     249              :         // Since we have created a new chunk for this element, the encode is expected to succeed.
     250              :     }
     251          741 :     return err;
     252              : }
     253              : 
     254          179 : CHIP_ERROR WriteClient::PutPreencodedAttribute(const ConcreteDataAttributePath & attributePath, const TLV::TLVReader & data,
     255              :                                                TestListEncodingOverride testListEncodingOverride)
     256              : {
     257          179 :     ReturnErrorOnFailure(EnsureMessage());
     258              : 
     259              :     // ListIndex is missing and the data is an array -- we are writing a whole list.
     260          179 :     if (!attributePath.IsListOperation() && data.GetType() == TLV::TLVType::kTLVType_Array)
     261              :     {
     262          173 :         TLV::TLVReader dataReader;
     263          173 :         TLV::TLVReader valueReader;
     264          173 :         uint16_t encodedItemCount      = 0;
     265          173 :         ConcreteDataAttributePath path = attributePath;
     266              : 
     267              :         // By convention, and as tested against all cluster servers, clients have historically encoded an empty list as a
     268              :         // ReplaceAll, (i.e. the entire attribute contents are cleared before appending the new list’s items). However, this
     269              :         // behavior can be problematic, especially for the ACL attribute; sending an empty ReplaceAll list can cause clients to be
     270              :         // locked out. This is because the empty list first deletes all existing ACL entries, and if the new (malformed) ACL is
     271              :         // rejected, the server is left without valid (or with incomplete) ACLs.
     272              :         // SOLUTION: we treat ACL as an exception and avoid encoding an empty ReplaceAll list. Instead, we pack as many ACL entries
     273              :         // as possible into the ReplaceAll list, and send  any remaining entries in subsequent chunks are part of the AppendItem
     274              :         // list operation.
     275              :         // TODO (#38270): Generalize this behavior; send a non-empty ReplaceAll list for all clusters in a later Matter version and
     276              :         // enforce all clusters to support it in testing and in certification.
     277          173 :         bool encodeEmptyListAsReplaceAll = (path.mClusterId != Clusters::AccessControl::Id) ||
     278              :             (testListEncodingOverride == TestListEncodingOverride::kForceLegacyEncoding);
     279              : 
     280          173 :         if (encodeEmptyListAsReplaceAll)
     281              :         {
     282          112 :             ReturnErrorOnFailure(EncodeSingleAttributeDataIB(path, DataModel::List<uint8_t>()));
     283              :         }
     284              :         else
     285              :         {
     286              : 
     287           61 :             dataReader.Init(data);
     288           61 :             TEMPORARY_RETURN_IGNORED dataReader.OpenContainer(valueReader);
     289           61 :             bool chunkingNeeded = false;
     290              : 
     291              :             // Encode as many list-items as possible into a single AttributeDataIB, which will be included in a single
     292              :             // WriteRequestMessage chunk.
     293           61 :             ReturnErrorOnFailure(
     294              :                 TryPutPreencodedAttributeWritePayloadIntoList(path, valueReader, chunkingNeeded, encodedItemCount));
     295              : 
     296              :             // If all list items fit perfectly into a single AttributeDataIB, there is no need for any `append-item` or chunking,
     297              :             // and we can exit early.
     298           61 :             VerifyOrReturnError(chunkingNeeded, CHIP_NO_ERROR);
     299              : 
     300              :             // Start a new WriteRequest chunk, as there are still remaining list items to encode. These remaining items will be
     301              :             // appended one by one, each into its own AttributeDataIB. Unlike the first chunk (which contains only one
     302              :             // AttributeDataIB), subsequent chunks may contain multiple AttributeDataIBs if space allows it.
     303           43 :             ReturnErrorOnFailure(StartNewMessage());
     304              :         }
     305          155 :         path.mListOp = ConcreteDataAttributePath::ListOperation::AppendItem;
     306              : 
     307              :         // We will restart iterating on ValueReader, only appending the items we need to append.
     308          155 :         dataReader.Init(data);
     309          155 :         TEMPORARY_RETURN_IGNORED dataReader.OpenContainer(valueReader);
     310              : 
     311          155 :         CHIP_ERROR err            = CHIP_NO_ERROR;
     312          155 :         uint16_t currentItemCount = 0;
     313              : 
     314         2722 :         while ((err = valueReader.Next()) == CHIP_NO_ERROR)
     315              :         {
     316         1206 :             currentItemCount++;
     317              : 
     318         1206 :             if (currentItemCount <= encodedItemCount)
     319              :             {
     320              :                 // Element already encoded via `TryPutPreencodedAttributeWritePayloadIntoList`
     321          471 :                 continue;
     322              :             }
     323              : 
     324          735 :             ReturnErrorOnFailure(PutSinglePreencodedAttributeWritePayload(path, valueReader));
     325              :         }
     326              : 
     327          310 :         if (err == CHIP_END_OF_TLV)
     328              :         {
     329          155 :             err = CHIP_NO_ERROR;
     330              :         }
     331          155 :         return err;
     332              :     }
     333              : 
     334              :     // We are writing a non-list attribute, or we are writing a single element of a list.
     335            6 :     return PutSinglePreencodedAttributeWritePayload(attributePath, data);
     336              : }
     337              : 
     338          470 : CHIP_ERROR WriteClient::EnsureListStarted(const ConcreteDataAttributePath & attributePath)
     339              : {
     340          470 :     TLV::TLVWriter backupWriter;
     341          470 :     mWriteRequestBuilder.GetWriteRequests().Checkpoint(backupWriter);
     342              : 
     343          470 :     CHIP_ERROR err = TryToStartList(attributePath);
     344         1408 :     if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
     345              :     {
     346              :         // If it failed with no memory, then we create a new chunk for it.
     347            2 :         mWriteRequestBuilder.GetWriteRequests().Rollback(backupWriter);
     348            2 :         ReturnErrorOnFailure(StartNewMessage());
     349            2 :         ReturnErrorOnFailure(TryToStartList(attributePath));
     350              :     }
     351              : 
     352          470 :     return CHIP_NO_ERROR;
     353              : }
     354              : 
     355          472 : CHIP_ERROR WriteClient::TryToStartList(const ConcreteDataAttributePath & attributePath)
     356              : {
     357              : 
     358              :     // TODO (#38414) : Move reservation/unreservtion of Buffer for TLV Writing to AttributeDataIB Builder instead of WriteClient
     359          472 :     ReturnErrorOnFailure(mMessageWriter.ReserveBuffer(kReservedSizeForEndOfListAttributeIB));
     360              : 
     361          470 :     ReturnErrorOnFailure(PrepareAttributeIB(attributePath));
     362              : 
     363          470 :     TLV::TLVWriter * writer = GetAttributeDataIBTLVWriter();
     364          470 :     VerifyOrReturnError(writer != nullptr, CHIP_ERROR_INCORRECT_STATE);
     365              : 
     366              :     TLV::TLVType outerType;
     367          470 :     ReturnErrorOnFailure(writer->StartContainer(TLV::ContextTag(AttributeDataIB::Tag::kData), TLV::kTLVType_Array, outerType));
     368              : 
     369          470 :     VerifyOrReturnError(outerType == kAttributeDataIBType, CHIP_ERROR_INCORRECT_STATE);
     370              : 
     371          470 :     return CHIP_NO_ERROR;
     372              : }
     373              : 
     374          470 : CHIP_ERROR WriteClient::EnsureListEnded()
     375              : {
     376          470 :     TLV::TLVWriter * writer = GetAttributeDataIBTLVWriter();
     377          470 :     VerifyOrReturnError(writer != nullptr, CHIP_ERROR_INCORRECT_STATE);
     378              : 
     379              :     // Undo the reservation made in EnsureListStarted() to free up space for the EndOfContainer TLV Elements
     380              :     // (for both the List and AttributeDataIB).
     381          470 :     ReturnErrorOnFailure(writer->UnreserveBuffer(kReservedSizeForEndOfListAttributeIB));
     382          470 :     ReturnErrorOnFailure(writer->EndContainer(kAttributeDataIBType));
     383              : 
     384          470 :     return FinishAttributeIB();
     385              : }
     386              : 
     387              : CHIP_ERROR
     388           61 : WriteClient::TryPutPreencodedAttributeWritePayloadIntoList(const ConcreteDataAttributePath & attributePath,
     389              :                                                            TLV::TLVReader & valueReader, bool & outChunkingNeeded,
     390              :                                                            ListIndex & outEncodedItemCount)
     391              : {
     392              : 
     393           61 :     ReturnErrorOnFailure(EnsureListStarted(attributePath));
     394              : 
     395           61 :     AttributeDataIB::Builder & attributeDataIB = mWriteRequestBuilder.GetWriteRequests().GetAttributeDataIBBuilder();
     396           61 :     TLV::TLVWriter backupWriter;
     397           61 :     CHIP_ERROR err      = CHIP_NO_ERROR;
     398           61 :     outEncodedItemCount = 0;
     399              : 
     400         1784 :     while ((err = valueReader.Next()) == CHIP_NO_ERROR)
     401              :     {
     402              :         // Try to put all the list items into the list we just started, until we either run out of items
     403              :         // or run out of space.
     404              :         // Make sure that if we run out of space we don't leave a partially-encoded list item around.
     405          874 :         attributeDataIB.Checkpoint(backupWriter);
     406          874 :         err = attributeDataIB.GetWriter()->CopyElement(TLV::AnonymousTag(), valueReader);
     407              : 
     408         2579 :         if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
     409              :         {
     410              :             // Rollback through the attributeDataIB, which also resets the Builder's error state.
     411              :             // This returns the object to the state it was in before attempting to copy the element.
     412           43 :             attributeDataIB.Rollback(backupWriter);
     413           43 :             outChunkingNeeded = true;
     414           43 :             err               = CHIP_NO_ERROR;
     415           43 :             break;
     416              :         }
     417          831 :         ReturnErrorOnFailure(err);
     418          831 :         outEncodedItemCount++;
     419              :     }
     420          165 :     VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_NO_ERROR, err);
     421              : 
     422           61 :     return EnsureListEnded();
     423              : }
     424              : 
     425            0 : const char * WriteClient::GetStateStr() const
     426              : {
     427              : #if CHIP_DETAIL_LOGGING
     428            0 :     switch (mState)
     429              :     {
     430            0 :     case State::Initialized:
     431            0 :         return "Initialized";
     432              : 
     433            0 :     case State::AddAttribute:
     434            0 :         return "AddAttribute";
     435              : 
     436            0 :     case State::AwaitingTimedStatus:
     437            0 :         return "AwaitingTimedStatus";
     438              : 
     439            0 :     case State::AwaitingResponse:
     440            0 :         return "AwaitingResponse";
     441              : 
     442            0 :     case State::ResponseReceived:
     443            0 :         return "ResponseReceived";
     444              : 
     445            0 :     case State::AwaitingDestruction:
     446            0 :         return "AwaitingDestruction";
     447              :     }
     448              : #endif // CHIP_DETAIL_LOGGING
     449            0 :     return "N/A";
     450              : }
     451              : 
     452        14100 : void WriteClient::MoveToState(const State aTargetState)
     453              : {
     454        14100 :     mState = aTargetState;
     455        14100 :     ChipLogDetail(DataManagement, "WriteClient moving to [%10.10s]", GetStateStr());
     456        14100 : }
     457              : 
     458          968 : CHIP_ERROR WriteClient::SendWriteRequest(const SessionHandle & session, System::Clock::Timeout timeout)
     459              : {
     460          968 :     CHIP_ERROR err = CHIP_NO_ERROR;
     461              : 
     462          968 :     VerifyOrExit(mState == State::AddAttribute, err = CHIP_ERROR_INCORRECT_STATE);
     463              : 
     464          968 :     err = FinalizeMessage(false /* hasMoreChunks */);
     465          968 :     SuccessOrExit(err);
     466              : 
     467              :     {
     468              :         // Create a new exchange context.
     469          968 :         auto exchange = mpExchangeMgr->NewContext(session, this);
     470          968 :         VerifyOrExit(exchange != nullptr, err = CHIP_ERROR_NO_MEMORY);
     471              : 
     472          968 :         mExchangeCtx.Grab(exchange);
     473              :     }
     474              : 
     475          968 :     VerifyOrReturnError(!(mExchangeCtx->IsGroupExchangeContext() && mHasDataVersion), CHIP_ERROR_INVALID_MESSAGE_TYPE);
     476              : 
     477          968 :     if (timeout == System::Clock::kZero)
     478              :     {
     479          968 :         ReturnErrorOnFailure(mExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime));
     480              :     }
     481              :     else
     482              :     {
     483            0 :         mExchangeCtx->SetResponseTimeout(timeout);
     484              :     }
     485              : 
     486          968 :     if (mTimedWriteTimeoutMs.HasValue())
     487              :     {
     488            0 :         err = TimedRequest::Send(mExchangeCtx.Get(), mTimedWriteTimeoutMs.Value());
     489            0 :         SuccessOrExit(err);
     490            0 :         MoveToState(State::AwaitingTimedStatus);
     491              :     }
     492              :     else
     493              :     {
     494          968 :         err = SendWriteRequest();
     495          968 :         SuccessOrExit(err);
     496              :     }
     497              : 
     498          968 : exit:
     499         1936 :     if (err != CHIP_NO_ERROR)
     500              :     {
     501            0 :         ChipLogError(DataManagement, "Write client failed to SendWriteRequest: %" CHIP_ERROR_FORMAT, err.Format());
     502              :     }
     503              :     else
     504              :     {
     505              :         // TODO: Ideally this would happen async, but to make sure that we
     506              :         // handle this object dying (e.g. due to IM enging shutdown) while the
     507              :         // async bits are pending we'd need to malloc some state bit that we can
     508              :         // twiddle if we die.  For now just do the OnDone callback sync.
     509          968 :         if (session->IsGroupSession() || (mSuppressResponse && mState != State::AwaitingTimedStatus))
     510              :         {
     511              :             // Always shutdown on Group communication
     512            6 :             ChipLogDetail(DataManagement, "Closing on group Communication ");
     513              : 
     514              :             // Tell the application to release the object.
     515              :             // TODO: Consumers expect to hand off ownership of the WriteClient and wait for OnDone
     516              :             // after SendWriteRequest returns success.  Calling OnDone before returning is weird.
     517              :             // Need to refactor the code to avoid this.
     518            6 :             Close();
     519              :         }
     520              :     }
     521              : 
     522          968 :     return err;
     523              : }
     524              : 
     525         3882 : CHIP_ERROR WriteClient::SendWriteRequest()
     526              : {
     527              :     using namespace Protocols::InteractionModel;
     528              :     using namespace Messaging;
     529              : 
     530         3882 :     System::PacketBufferHandle data = mChunks.PopHead();
     531              : 
     532         3882 :     bool isGroupWrite = mExchangeCtx->IsGroupExchangeContext();
     533         3882 :     if (!mChunks.IsNull() && (isGroupWrite || mSuppressResponse))
     534              :     {
     535              :         // Reject this request if we have more than one chunk (mChunks is not null after PopHead()) and either this is a
     536              :         // group exchange context or the response is suppressed; a chunked write requires intermediate WriteResponses.
     537            0 :         return CHIP_ERROR_INCORRECT_STATE;
     538              :     }
     539              : 
     540         3882 :     if (mSuppressResponse)
     541              :     {
     542            4 :         return mExchangeCtx->SendMessage(MsgType::WriteRequest, std::move(data), SendMessageFlags::kNone);
     543              :     }
     544              : 
     545              :     // kExpectResponse is ignored by ExchangeContext in case of groupcast
     546         3878 :     ReturnErrorOnFailure(mExchangeCtx->SendMessage(MsgType::WriteRequest, std::move(data), SendMessageFlags::kExpectResponse));
     547         3878 :     MoveToState(State::AwaitingResponse);
     548         3878 :     return CHIP_NO_ERROR;
     549         3882 : }
     550              : 
     551         3867 : CHIP_ERROR WriteClient::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
     552              :                                           System::PacketBufferHandle && aPayload)
     553              : {
     554              :     using namespace Protocols::InteractionModel;
     555              : 
     556         3867 :     if (mState == State::AwaitingResponse)
     557              :     {
     558              :         // NOTE: if we have more chunks (i.e. `!mChunks.IsNull()`), then the
     559              :         //       SendWriteRequest() call below will move back to an AwaitingResponse state
     560         3867 :         MoveToState(State::ResponseReceived);
     561              :     }
     562              : 
     563         3867 :     CHIP_ERROR err          = CHIP_NO_ERROR;
     564         3867 :     bool sendStatusResponse = false;
     565              :     // Assert that the exchange context matches the client's current context.
     566              :     // This should never fail because even if SendWriteRequest is called
     567              :     // back-to-back, the second call will call Close() on the first exchange,
     568              :     // which clears the OnMessageReceived callback.
     569         3867 :     VerifyOrExit(apExchangeContext == mExchangeCtx.Get(), err = CHIP_ERROR_INCORRECT_STATE);
     570              : 
     571         3867 :     sendStatusResponse = true;
     572              : 
     573         3867 :     if (mState == State::AwaitingTimedStatus)
     574              :     {
     575            0 :         if (aPayloadHeader.HasMessageType(MsgType::StatusResponse))
     576              :         {
     577            0 :             CHIP_ERROR statusError = CHIP_NO_ERROR;
     578            0 :             SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError));
     579            0 :             sendStatusResponse = false;
     580            0 :             SuccessOrExit(err = statusError);
     581            0 :             err = SendWriteRequest();
     582              :         }
     583              :         else
     584              :         {
     585            0 :             err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
     586              :         }
     587              :         // Skip all other processing here (which is for the response to the
     588              :         // write request), no matter whether err is success or not.
     589            0 :         goto exit;
     590              :     }
     591              : 
     592         3867 :     if (aPayloadHeader.HasMessageType(MsgType::WriteResponse))
     593              :     {
     594         3862 :         err = ProcessWriteResponseMessage(std::move(aPayload));
     595         3862 :         SuccessOrExit(err);
     596         3861 :         sendStatusResponse = false;
     597         3861 :         if (!mChunks.IsNull())
     598              :         {
     599              :             // Send the next chunk.
     600         2914 :             SuccessOrExit(err = SendWriteRequest());
     601              :         }
     602              :     }
     603            5 :     else if (aPayloadHeader.HasMessageType(MsgType::StatusResponse))
     604              :     {
     605            4 :         CHIP_ERROR statusError = CHIP_NO_ERROR;
     606            7 :         SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError));
     607            3 :         SuccessOrExit(err = statusError);
     608            0 :         err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
     609              :     }
     610              :     else
     611              :     {
     612            1 :         err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
     613              :     }
     614              : 
     615         3867 : exit:
     616         3867 :     if (mpCallback != nullptr)
     617              :     {
     618         7734 :         if (err != CHIP_NO_ERROR)
     619              :         {
     620            6 :             mpCallback->OnError(this, err);
     621              :         }
     622              :     }
     623              : 
     624         3867 :     if (sendStatusResponse)
     625              :     {
     626            6 :         TEMPORARY_RETURN_IGNORED StatusResponse::Send(Status::InvalidAction, apExchangeContext, false /*aExpectResponse*/);
     627              :     }
     628              : 
     629         3867 :     if (mState != State::AwaitingResponse)
     630              :     {
     631          953 :         Close();
     632              :     }
     633              :     // Else we got a response to a Timed Request and just sent the write.
     634              : 
     635         3867 :     return err;
     636              : }
     637              : 
     638            1 : void WriteClient::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
     639              : {
     640            1 :     ChipLogError(DataManagement, "Time out! failed to receive write response from Exchange: " ChipLogFormatExchange,
     641              :                  ChipLogValueExchange(apExchangeContext));
     642              : 
     643            1 :     if (mpCallback != nullptr)
     644              :     {
     645            1 :         mpCallback->OnError(this, CHIP_ERROR_TIMEOUT);
     646              :     }
     647            1 :     Close();
     648            1 : }
     649              : 
     650         5196 : CHIP_ERROR WriteClient::ProcessAttributeStatusIB(AttributeStatusIB::Parser & aAttributeStatusIB)
     651              : {
     652         5196 :     CHIP_ERROR err = CHIP_NO_ERROR;
     653         5196 :     AttributePathIB::Parser attributePathParser;
     654         5196 :     StatusIB statusIB;
     655         5196 :     StatusIB::Parser StatusIBParser;
     656         5196 :     ConcreteDataAttributePath attributePath;
     657              : 
     658         5196 :     err = aAttributeStatusIB.GetPath(&attributePathParser);
     659         5196 :     SuccessOrExit(err);
     660              : 
     661         5196 :     err = attributePathParser.GetConcreteAttributePath(attributePath);
     662         5196 :     SuccessOrExit(err);
     663              : 
     664         5196 :     err = aAttributeStatusIB.GetErrorStatus(&(StatusIBParser));
     665        10392 :     if (CHIP_NO_ERROR == err)
     666              :     {
     667         5196 :         err = StatusIBParser.DecodeStatusIB(statusIB);
     668         5196 :         SuccessOrExit(err);
     669         5196 :         if (mpCallback != nullptr)
     670              :         {
     671         5196 :             mpCallback->OnResponse(this, attributePath, statusIB);
     672              :         }
     673              :     }
     674              : 
     675            0 : exit:
     676         5196 :     return err;
     677              : }
     678              : 
     679              : } // namespace app
     680              : } // namespace chip
        

Generated by: LCOV version 2.0-1