Matter SDK Coverage Report
Current view: top level - app - WriteClient.cpp (source / functions) Coverage Total Hit
Test: SHA:4cbce7f768f16e614f5a8ccb8cd93c92cbeae70d Lines: 69.8 % 298 208
Test Date: 2025-04-26 07:09:35 Functions: 81.8 % 22 18

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

Generated by: LCOV version 2.0-1