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

Generated by: LCOV version 2.0-1