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

Generated by: LCOV version 2.0-1