Matter SDK Coverage Report
Current view: top level - app - WriteClient.h (source / functions) Coverage Total Hit
Test: SHA:4cbce7f768f16e614f5a8ccb8cd93c92cbeae70d Lines: 0.0 % 22 0
Test Date: 2025-04-26 07:09:35 Functions: 0.0 % 8 0

            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              : #pragma once
      20              : 
      21              : #include <app-common/zap-generated/ids/Attributes.h>
      22              : #include <app-common/zap-generated/ids/Clusters.h>
      23              : #include <app/AttributePathParams.h>
      24              : #include <app/ConcreteAttributePath.h>
      25              : #include <app/InteractionModelTimeout.h>
      26              : #include <app/MessageDef/AttributeDataIBs.h>
      27              : #include <app/MessageDef/AttributeStatusIB.h>
      28              : #include <app/MessageDef/StatusIB.h>
      29              : #include <app/MessageDef/WriteRequestMessage.h>
      30              : #include <app/data-model/Encode.h>
      31              : #include <app/data-model/FabricScoped.h>
      32              : #include <app/data-model/List.h>
      33              : #include <lib/core/CHIPCore.h>
      34              : #include <lib/core/TLVDebug.h>
      35              : #include <lib/support/CodeUtils.h>
      36              : #include <lib/support/DLLUtil.h>
      37              : #include <lib/support/logging/CHIPLogging.h>
      38              : #include <messaging/ExchangeHolder.h>
      39              : #include <messaging/ExchangeMgr.h>
      40              : #include <messaging/Flags.h>
      41              : #include <platform/LockTracker.h>
      42              : #include <protocols/Protocols.h>
      43              : #include <system/SystemPacketBuffer.h>
      44              : #include <system/TLVPacketBufferBackingStore.h>
      45              : 
      46              : namespace chip {
      47              : namespace app {
      48              : 
      49              : class InteractionModelEngine;
      50              : 
      51              : /**
      52              :  *  @brief The write client represents the initiator side of a Write Interaction, and is responsible
      53              :  *  for generating one Write Request for a particular set of attributes, and handling the Write response.
      54              :  *  Consumer can allocate one write client, then call PrepareAttribute, insert attribute value, followed
      55              :  *  by FinishAttribute for every attribute it wants to insert in write request, then call SendWriteRequest
      56              :  *
      57              :  *  Note: When writing lists, you may receive multiple write status responses for a single list.
      58              :  *  Please see ChunkedWriteCallback.h for a high level API which will merge status codes for
      59              :  *  chunked write requests.
      60              :  *
      61              :  */
      62              : class WriteClient : public Messaging::ExchangeDelegate
      63              : {
      64              : public:
      65              :     class Callback
      66              :     {
      67              :     public:
      68            0 :         virtual ~Callback() = default;
      69              : 
      70              :         /**
      71              :          * OnResponse will be called when a write response has been received
      72              :          * and processed for the given path.
      73              :          *
      74              :          * The WriteClient object MUST continue to exist after this call is completed. The application shall wait until it
      75              :          * receives an OnDone call before it shuts down the object.
      76              :          *
      77              :          * @param[in] apWriteClient   The write client object that initiated the write transaction.
      78              :          * @param[in] aPath           The attribute path field in write response.
      79              :          * @param[in] attributeStatus Attribute-specific status, containing an InteractionModel::Status code as well as
      80              :          *                            an optional cluster-specific status code.
      81              :          */
      82            0 :         virtual void OnResponse(const WriteClient * apWriteClient, const ConcreteDataAttributePath & aPath,
      83              :                                 StatusIB attributeStatus)
      84            0 :         {}
      85              : 
      86              :         /**
      87              :          * OnError will be called when an error occurs *after* a successful call to SendWriteRequest(). The following
      88              :          * errors will be delivered through this call in the aError field:
      89              :          *
      90              :          * - CHIP_ERROR_TIMEOUT: A response was not received within the expected response timeout.
      91              :          * - CHIP_ERROR_*TLV*: A malformed, non-compliant response was received from the server.
      92              :          * - CHIP_ERROR encapsulating a StatusIB: If we got a non-path-specific
      93              :          *   status response from the server.  In that case, constructing
      94              :          *   a StatusIB from the error can be used to extract the status.
      95              :          * - CHIP_ERROR*: All other cases.
      96              :          *
      97              :          * The WriteClient object MUST continue to exist after this call is completed. The application shall wait until it
      98              :          * receives an OnDone call before it shuts down the object.
      99              :          *
     100              :          * @param[in] apWriteClient The write client object that initiated the attribute write transaction.
     101              :          * @param[in] aError        A system error code that conveys the overall error code.
     102              :          */
     103            0 :         virtual void OnError(const WriteClient * apWriteClient, CHIP_ERROR aError) {}
     104              : 
     105              :         /**
     106              :          * OnDone will be called when WriteClient has finished all work and is reserved for future WriteClient ownership change.
     107              :          * (#10366) Users may use this function to release their own objects related to this write interaction.
     108              :          *
     109              :          * This function will:
     110              :          *      - Always be called exactly *once* for a given WriteClient instance.
     111              :          *      - Be called even in error circumstances.
     112              :          *      - Only be called after a successful call to SendWriteRequest has been made.
     113              :          *
     114              :          * @param[in] apWriteClient The write client object of the terminated write transaction.
     115              :          */
     116              :         virtual void OnDone(WriteClient * apWriteClient) = 0;
     117              :     };
     118              : 
     119              :     /**
     120              :      *  Construct the client object. Within the lifetime
     121              :      *  of this instance.
     122              :      *
     123              :      *  @param[in]    apExchangeMgr    A pointer to the ExchangeManager object.
     124              :      *  @param[in]    apCallback       Callback set by application.
     125              :      *  @param[in]    aTimedWriteTimeoutMs If provided, do a timed write using this timeout.
     126              :      *  @param[in]    aSuppressResponse If provided, set SuppressResponse field to the provided value
     127              :      */
     128              :     WriteClient(Messaging::ExchangeManager * apExchangeMgr, Callback * apCallback, const Optional<uint16_t> & aTimedWriteTimeoutMs,
     129              :                 bool aSuppressResponse = false) :
     130              :         mpExchangeMgr(apExchangeMgr),
     131              :         mExchangeCtx(*this), mpCallback(apCallback), mTimedWriteTimeoutMs(aTimedWriteTimeoutMs),
     132              :         mSuppressResponse(aSuppressResponse)
     133              :     {
     134              :         assertChipStackLockedByCurrentThread();
     135              :     }
     136              : 
     137              : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
     138              :     WriteClient(Messaging::ExchangeManager * apExchangeMgr, Callback * apCallback, const Optional<uint16_t> & aTimedWriteTimeoutMs,
     139              :                 uint16_t aReservedSize) :
     140              :         mpExchangeMgr(apExchangeMgr),
     141              :         mExchangeCtx(*this), mpCallback(apCallback), mTimedWriteTimeoutMs(aTimedWriteTimeoutMs), mReservedSize(aReservedSize)
     142              :     {
     143              :         assertChipStackLockedByCurrentThread();
     144              :     }
     145              : #endif
     146              : 
     147            0 :     ~WriteClient() { assertChipStackLockedByCurrentThread(); }
     148              : 
     149              :     /**
     150              :      *  Encode an attribute value that can be directly encoded using DataModel::Encode. Will create a new chunk when necessary.
     151              :      */
     152              :     template <class T>
     153              :     CHIP_ERROR EncodeAttribute(const AttributePathParams & attributePath, const T & value,
     154              :                                const Optional<DataVersion> & aDataVersion = NullOptional)
     155              :     {
     156              :         ReturnErrorOnFailure(EnsureMessage());
     157              : 
     158              :         // Here, we are using kInvalidEndpointId for missing endpoint id, which is used when sending group write requests.
     159              :         return EncodeSingleAttributeDataIB(
     160              :             ConcreteDataAttributePath(attributePath.HasWildcardEndpointId() ? kInvalidEndpointId : attributePath.mEndpointId,
     161              :                                       attributePath.mClusterId, attributePath.mAttributeId, aDataVersion),
     162              :             value);
     163              :     }
     164              : 
     165              :     /**
     166              :      *  Encode a possibly-chunked list attribute value.  Will create a new chunk when necessary.
     167              :      *
     168              :      * Note: As an exception, for attributes in the Access Control cluster, this method will attempt to encode as many list items
     169              :      * as possible into a single AttributeDataIB with Change set to REPLACE.
     170              :      * If the list is too large, the WriteRequest will be chunked and remaining items will be encoded as individual AttributeDataIBs
     171              :      * with Change set to ADD, chunking them as needed.
     172              :      *
     173              :      */
     174              :     template <class T>
     175              :     CHIP_ERROR EncodeAttribute(const AttributePathParams & attributePath, const DataModel::List<T> & listValue,
     176              :                                const Optional<DataVersion> & aDataVersion = NullOptional)
     177              :     {
     178              :         // Here, we are using kInvalidEndpointId for missing endpoint id, which is used when sending group write requests.
     179              :         ConcreteDataAttributePath path =
     180              :             ConcreteDataAttributePath(attributePath.HasWildcardEndpointId() ? kInvalidEndpointId : attributePath.mEndpointId,
     181              :                                       attributePath.mClusterId, attributePath.mAttributeId, aDataVersion);
     182              : 
     183              :         ListIndex firstItemToAppendIndex = 0;
     184              :         uint16_t encodedItemCount        = 0;
     185              :         bool chunkingNeeded              = false;
     186              : 
     187              :         // By convention, and as tested against all cluster servers, clients have historically encoded an empty list as a
     188              :         // ReplaceAll, (i.e. the entire attribute contents are cleared before appending the new list’s items). However, this
     189              :         // behavior can be problematic, especially for the ACL attribute; sending an empty ReplaceAll list can cause clients to be
     190              :         // locked out. This is because the empty list first deletes all existing ACL entries, and if the new (malformed) ACL is
     191              :         // rejected, the server is left without valid (or with incomplete) ACLs.
     192              :         // SOLUTION: we treat ACL as an exception and avoid encoding an empty ReplaceAll list. Instead, we pack as many ACL entries
     193              :         // as possible into the ReplaceAll list, and send  any remaining entries in subsequent chunks are part of the AppendItem
     194              :         // list operation.
     195              :         // TODO (#38270): Generalize this behavior; send a non-empty ReplaceAll list for all clusters in a later Matter version and
     196              :         // enforce all clusters to support it in testing and in certification.
     197              :         bool encodeEmptyListAsReplaceAll = !(path.mClusterId == Clusters::AccessControl::Id);
     198              : 
     199              :         ReturnErrorOnFailure(EnsureMessage());
     200              : 
     201              :         if (encodeEmptyListAsReplaceAll)
     202              :         {
     203              :             ReturnErrorOnFailure(EncodeSingleAttributeDataIB(path, DataModel::List<uint8_t>()));
     204              :         }
     205              :         else
     206              :         {
     207              :             // Encode as many list-items as possible into a single AttributeDataIB, which will be included in a single
     208              :             // WriteRequestMessage chunk.
     209              :             ReturnErrorOnFailure(TryEncodeListIntoSingleAttributeDataIB(path, listValue, chunkingNeeded, encodedItemCount));
     210              : 
     211              :             // If all list items fit perfectly into a single AttributeDataIB, there is no need for any `append-item` or chunking,
     212              :             // and we can exit early.
     213              :             VerifyOrReturnError(chunkingNeeded, CHIP_NO_ERROR);
     214              : 
     215              :             // Start a new WriteRequest chunk, as there are still remaining list items to encode. These remaining items will be
     216              :             // appended one by one, each into its own AttributeDataIB. Unlike the first chunk (which contains only one
     217              :             // AttributeDataIB), subsequent chunks may contain multiple AttributeDataIBs if space allows it.
     218              :             ReturnErrorOnFailure(StartNewMessage());
     219              :             firstItemToAppendIndex = encodedItemCount;
     220              :         }
     221              : 
     222              :         path.mListOp = ConcreteDataAttributePath::ListOperation::AppendItem;
     223              : 
     224              :         for (ListIndex i = firstItemToAppendIndex; i < listValue.size(); i++)
     225              :         {
     226              :             ReturnErrorOnFailure(EncodeSingleAttributeDataIB(path, listValue[i]));
     227              :         }
     228              : 
     229              :         return CHIP_NO_ERROR;
     230              :     }
     231              : 
     232              :     /**
     233              :      * Encode a Nullable attribute value.  This needs a separate overload so it can dispatch to the right
     234              :      * EncodeAttribute when writing a nullable list.
     235              :      */
     236              :     template <class T>
     237              :     CHIP_ERROR EncodeAttribute(const AttributePathParams & attributePath, const DataModel::Nullable<T> & value,
     238              :                                const Optional<DataVersion> & aDataVersion = NullOptional)
     239              :     {
     240              :         ReturnErrorOnFailure(EnsureMessage());
     241              : 
     242              :         if (value.IsNull())
     243              :         {
     244              :             // Here, we are using kInvalidEndpointId to for missing endpoint id, which is used when sending group write requests.
     245              :             return EncodeSingleAttributeDataIB(
     246              :                 ConcreteDataAttributePath(attributePath.HasWildcardEndpointId() ? kInvalidEndpointId : attributePath.mEndpointId,
     247              :                                           attributePath.mClusterId, attributePath.mAttributeId, aDataVersion),
     248              :                 value);
     249              :         }
     250              : 
     251              :         return EncodeAttribute(attributePath, value.Value(), aDataVersion);
     252              :     }
     253              : 
     254              :     /**
     255              :      * Encode an attribute value which is already encoded into a TLV. The TLVReader is expected to be initialized and the read head
     256              :      * is expected to point to the element to be encoded.
     257              :      *
     258              :      * Note: When encoding lists with this function, you may receive more than one write status for a single list. You can refer
     259              :      * to ChunkedWriteCallback.h for a high level API which will merge status codes for chunked write requests.
     260              :      */
     261              :     CHIP_ERROR PutPreencodedAttribute(const ConcreteDataAttributePath & attributePath, const TLV::TLVReader & data);
     262              : 
     263              :     /**
     264              :      *  Once SendWriteRequest returns successfully, the WriteClient will
     265              :      *  handle calling Shutdown on itself once it decides it's done with waiting
     266              :      *  for a response (i.e. times out or gets a response). Client can specify
     267              :      *  the maximum time to wait for response (in milliseconds) via timeout parameter.
     268              :      *  If the timeout is missing or is set to System::Clock::kZero, a value based on the MRP timeouts of the session will be used.
     269              :      *  If SendWriteRequest is never called, or the call fails, the API
     270              :      *  consumer is responsible for calling Shutdown on the WriteClient.
     271              :      */
     272              :     CHIP_ERROR SendWriteRequest(const SessionHandle & session, System::Clock::Timeout timeout = System::Clock::kZero);
     273              : 
     274              :     /**
     275              :      *  Returns true if the WriteRequest is Chunked.
     276              :      *  WARNING: This method is only used for UnitTests. It should only be called AFTER a call
     277              :      * EncodeAttribute/PutPreencodedAttribute AND BEFORE a call to SendWriteRequest(); only during this window does
     278              :      * "!mChunks.IsNull()" reliably indicate that the WriteRequest is chunked.
     279              :      */
     280              :     bool IsWriteRequestChunked() const { return !mChunks.IsNull(); };
     281              : 
     282              : private:
     283              :     friend class TestWriteInteraction;
     284              :     friend class InteractionModelEngine;
     285              :     enum class State
     286              :     {
     287              :         Initialized = 0,     // The client has been initialized
     288              :         AddAttribute,        // The client has added attribute and ready for a SendWriteRequest
     289              :         AwaitingTimedStatus, // Sent a Tiemd Request, waiting for response.
     290              :         AwaitingResponse,    // The client has sent out the write request message
     291              :         ResponseReceived,    // We have gotten a response after sending write request
     292              :         AwaitingDestruction, // The object has completed its work and is awaiting destruction by the application.
     293              :     };
     294              : 
     295              :     CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
     296              :                                  System::PacketBufferHandle && aPayload) override;
     297              :     void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override;
     298              : 
     299              :     void MoveToState(const State aTargetState);
     300              :     CHIP_ERROR ProcessWriteResponseMessage(System::PacketBufferHandle && payload);
     301              :     CHIP_ERROR ProcessAttributeStatusIB(AttributeStatusIB::Parser & aAttributeStatusIB);
     302              :     const char * GetStateStr() const;
     303              : 
     304              :     // TODO (#38453) Clarify and fix the API contract of EnsureListStarted, TryToStartList and EnsureListEnded; in the case of
     305              :     // encoding failure, should we just undo buffer reservation? rollback to a checkpoint that we create within EnsureListStarted?
     306              :     // or just leave the WriteClient in a bad state.
     307              :     /**
     308              :      *     A wrapper for TryToStartList which will start a new chunk when TryToStartList fails with CHIP_ERROR_NO_MEMORY or
     309              :      * CHIP_ERROR_BUFFER_TOO_SMALL.
     310              :      *
     311              :      * @note Must always be followed by a call to EnsureListEnded(), to undo buffer reservation that took place within
     312              :      * it, and properly close TLV Containers.
     313              :      */
     314              :     CHIP_ERROR EnsureListStarted(const ConcreteDataAttributePath & attributePath);
     315              : 
     316              :     /**
     317              :      * Prepare the Encoding of an Attribute with List DataType into an AttributeDataIB.
     318              :      *
     319              :      */
     320              :     CHIP_ERROR TryToStartList(const ConcreteDataAttributePath & attributePath);
     321              : 
     322              :     /**
     323              :      * Complete the Encoding of an Attribute with List DataType into an AttributeDataIB.
     324              :      *
     325              :      * @note Must always be called after EnsureListStarted(), even in cases of encoding failures; to undo buffer reservation that
     326              :      * took place in EnsureListStarted.
     327              :      */
     328              :     CHIP_ERROR EnsureListEnded();
     329              : 
     330              :     /**
     331              :      *  Encode an attribute value that can be directly encoded using DataModel::Encode.
     332              :      */
     333              :     template <class T, std::enable_if_t<!DataModel::IsFabricScoped<T>::value, int> = 0>
     334            0 :     CHIP_ERROR TryEncodeSingleAttributeDataIB(const ConcreteDataAttributePath & attributePath, const T & value)
     335              :     {
     336            0 :         chip::TLV::TLVWriter * writer = nullptr;
     337              : 
     338            0 :         ReturnErrorOnFailure(PrepareAttributeIB(attributePath));
     339            0 :         VerifyOrReturnError((writer = GetAttributeDataIBTLVWriter()) != nullptr, CHIP_ERROR_INCORRECT_STATE);
     340            0 :         ReturnErrorOnFailure(DataModel::Encode(*writer, chip::TLV::ContextTag(chip::app::AttributeDataIB::Tag::kData), value));
     341            0 :         ReturnErrorOnFailure(FinishAttributeIB());
     342              : 
     343            0 :         return CHIP_NO_ERROR;
     344              :     }
     345              : 
     346              :     template <class T, std::enable_if_t<DataModel::IsFabricScoped<T>::value, int> = 0>
     347              :     CHIP_ERROR TryEncodeSingleAttributeDataIB(const ConcreteDataAttributePath & attributePath, const T & value)
     348              :     {
     349              :         chip::TLV::TLVWriter * writer = nullptr;
     350              : 
     351              :         ReturnErrorOnFailure(PrepareAttributeIB(attributePath));
     352              :         VerifyOrReturnError((writer = GetAttributeDataIBTLVWriter()) != nullptr, CHIP_ERROR_INCORRECT_STATE);
     353              :         ReturnErrorOnFailure(
     354              :             DataModel::EncodeForWrite(*writer, chip::TLV::ContextTag(chip::app::AttributeDataIB::Tag::kData), value));
     355              :         ReturnErrorOnFailure(FinishAttributeIB());
     356              : 
     357              :         return CHIP_NO_ERROR;
     358              :     }
     359              : 
     360              :     template <class T>
     361              :     CHIP_ERROR TryEncodeListIntoSingleAttributeDataIB(const ConcreteDataAttributePath & attributePath,
     362              :                                                       const DataModel::List<T> & list, bool & outChunkingNeeded,
     363              :                                                       uint16_t & outEncodedItemCount)
     364              :     {
     365              :         ReturnErrorOnFailure(EnsureListStarted(attributePath));
     366              : 
     367              :         AttributeDataIB::Builder & attributeDataIB = mWriteRequestBuilder.GetWriteRequests().GetAttributeDataIBBuilder();
     368              :         TLV::TLVWriter backupWriter;
     369              :         outEncodedItemCount = 0;
     370              : 
     371              :         for (auto & item : list)
     372              :         {
     373              :             // Try to put all the list items into the list we just started, until we either run out of items
     374              :             // or run out of space.
     375              :             // Make sure that if we run out of space we don't leave a partially-encoded list item around.
     376              :             attributeDataIB.Checkpoint(backupWriter);
     377              :             CHIP_ERROR err = CHIP_NO_ERROR;
     378              : 
     379              :             if constexpr (DataModel::IsFabricScoped<T>::value)
     380              :             {
     381              :                 err = DataModel::EncodeForWrite(*attributeDataIB.GetWriter(), TLV::AnonymousTag(), item);
     382              :             }
     383              :             else
     384              :             {
     385              :                 err = DataModel::Encode(*attributeDataIB.GetWriter(), TLV::AnonymousTag(), item);
     386              :             }
     387              : 
     388              :             if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
     389              :             {
     390              :                 // Rollback through the attributeDataIB, which also resets the Builder's error state.
     391              :                 // This returns the object to the state it was in before attempting to encode the list item.
     392              :                 attributeDataIB.Rollback(backupWriter);
     393              :                 outChunkingNeeded = true;
     394              :                 break;
     395              :             }
     396              :             ReturnErrorOnFailure(err);
     397              :             outEncodedItemCount++;
     398              :         }
     399              : 
     400              :         return EnsureListEnded();
     401              :     }
     402              : 
     403              :     /**
     404              :      * A wrapper for TryEncodeSingleAttributeDataIB which will start a new chunk when failed with CHIP_ERROR_NO_MEMORY or
     405              :      * CHIP_ERROR_BUFFER_TOO_SMALL.
     406              :      *
     407              :      * NOTE: This method must not be used for encoding non-empty lists, even if the template accepts a list type.
     408              :      * For such cases, use TryEncodeListIntoSingleAttributeDataIB as part of a suitable encoding strategy,
     409              :      * since it has a different contract and has different usage expectations.
     410              :      */
     411              :     template <class T>
     412            0 :     CHIP_ERROR EncodeSingleAttributeDataIB(const ConcreteDataAttributePath & attributePath, const T & value)
     413              :     {
     414            0 :         TLV::TLVWriter backupWriter;
     415              : 
     416            0 :         mWriteRequestBuilder.GetWriteRequests().Checkpoint(backupWriter);
     417              : 
     418              :         // First attempt to write this attribute.
     419            0 :         CHIP_ERROR err = TryEncodeSingleAttributeDataIB(attributePath, value);
     420            0 :         if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
     421              :         {
     422              :             // If it failed with no memory, then we create a new chunk for it.
     423            0 :             mWriteRequestBuilder.GetWriteRequests().Rollback(backupWriter);
     424            0 :             ReturnErrorOnFailure(StartNewMessage());
     425            0 :             ReturnErrorOnFailure(TryEncodeSingleAttributeDataIB(attributePath, value));
     426              :         }
     427              :         else
     428              :         {
     429            0 :             ReturnErrorOnFailure(err);
     430              :         }
     431              : 
     432            0 :         return CHIP_NO_ERROR;
     433              :     }
     434              : 
     435              :     /**
     436              :      * Encode a preencoded attribute data, returns TLV encode error if the remaining space of current chunk is too small for the
     437              :      * AttributeDataIB.
     438              :      */
     439              :     CHIP_ERROR TryPutSinglePreencodedAttributeWritePayload(const ConcreteDataAttributePath & attributePath,
     440              :                                                            const TLV::TLVReader & data);
     441              : 
     442              :     /**
     443              :      * Encode a preencoded attribute data, will try to create a new chunk when necessary.
     444              :      */
     445              :     CHIP_ERROR PutSinglePreencodedAttributeWritePayload(const ConcreteDataAttributePath & attributePath,
     446              :                                                         const TLV::TLVReader & data);
     447              : 
     448              :     /**
     449              :      * Encodes preencoded attribute data into a list, that will be decoded by cluster servers as a REPLACE Change.
     450              :      * Returns outChunkingNeeded = true if it was not possible to fit all the data into a single list.
     451              :      */
     452              :     CHIP_ERROR TryPutPreencodedAttributeWritePayloadIntoList(const chip::app::ConcreteDataAttributePath & attributePath,
     453              :                                                              TLV::TLVReader & valueReader, bool & outChunkingNeeded,
     454              :                                                              uint16_t & outEncodedItemCount);
     455              :     CHIP_ERROR EnsureMessage();
     456              : 
     457              :     /**
     458              :      * Called internally to signal the completion of all work on this object, gracefully close the
     459              :      * exchange (by calling into the base class) and finally, signal to the application that it's
     460              :      * safe to release this object.
     461              :      */
     462              :     void Close();
     463              : 
     464              :     /**
     465              :      * This forcibly closes the exchange context if a valid one is pointed to. Such a situation does
     466              :      * not arise during normal message processing flows that all normally call Close() above. This can only
     467              :      * arise due to application-initiated destruction of the object when this object is handling receiving/sending
     468              :      * message payloads.
     469              :      */
     470              :     void Abort();
     471              : 
     472              :     // Send our queued-up Write Request message.  Assumes the exchange is ready
     473              :     // and mPendingWriteData is populated.
     474              :     CHIP_ERROR SendWriteRequest();
     475              : 
     476              :     // Encodes the header of an AttributeDataIB, a special case for attributePath is its EndpointId can be kInvalidEndpointId, this
     477              :     // is used when sending group write requests.
     478              :     // TODO(#14935) Update AttributePathParams to support more list operations.
     479              :     CHIP_ERROR PrepareAttributeIB(const ConcreteDataAttributePath & attributePath);
     480              :     CHIP_ERROR FinishAttributeIB();
     481              :     TLV::TLVWriter * GetAttributeDataIBTLVWriter();
     482              : 
     483              :     /**
     484              :      * Create a new message (or a new chunk) for the write request.
     485              :      */
     486              :     CHIP_ERROR StartNewMessage();
     487              : 
     488              :     /**
     489              :      * Finalize Write Request Message TLV Builder and retrieve final data from tlv builder for later sending
     490              :      */
     491              :     CHIP_ERROR FinalizeMessage(bool aHasMoreChunks);
     492              : 
     493              :     Messaging::ExchangeManager * mpExchangeMgr = nullptr;
     494              :     Messaging::ExchangeHolder mExchangeCtx;
     495              :     Callback * mpCallback = nullptr;
     496              :     State mState          = State::Initialized;
     497              :     System::PacketBufferTLVWriter mMessageWriter;
     498              :     WriteRequestMessage::Builder mWriteRequestBuilder;
     499              :     // TODO Maybe we should change PacketBufferTLVWriter so we can finalize it
     500              :     // but have it hold on to the buffer, and get the buffer from it later.
     501              :     // Then we could avoid this extra pointer-sized member.
     502              :     System::PacketBufferHandle mPendingWriteData;
     503              :     // If mTimedWriteTimeoutMs has a value, we are expected to do a timed
     504              :     // write.
     505              :     Optional<uint16_t> mTimedWriteTimeoutMs;
     506              :     bool mSuppressResponse = false;
     507              : 
     508              :     // A list of buffers, one buffer for each chunk.
     509              :     System::PacketBufferHandle mChunks;
     510              : 
     511              :     // TODO: This file might be compiled with different build flags on Darwin platform (when building WriteClient.cpp and
     512              :     // CHIPClustersObjc.mm), which will cause undefined behavior when building write requests. Uncomment the #if and #endif after
     513              :     // resolving it.
     514              :     // #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
     515              :     uint16_t mReservedSize = 0;
     516              :     // #endif
     517              : 
     518              :     /**
     519              :      * Below we define several const variables for encoding overheads.
     520              :      * WriteRequestMessage =
     521              :      * {
     522              :      *  timedRequest = false,
     523              :      *  AttributeDataIBs =
     524              :      *  [
     525              :      *     AttributeDataIB =             \
     526              :      *     {                              |
     527              :      *        DataVersion = 0x0,          |
     528              :      *        AttributePathIB =           |
     529              :      *        {                           |
     530              :      *           Endpoint = 0x2,          |  "atomically" encoded via
     531              :      *           Cluster = 0x50f,          > EncodeAttribute or
     532              :      *           Attribute = 0x0000_0006, |  PutPreencodedAttribute
     533              :      *           ListIndex = Null,        |
     534              :      *        }                           |
     535              :      *        Data = ...                  |
     536              :      *     },                             /
     537              :      *     (...)
     538              :      *  ],                           <-- 1 byte  "end of AttributeDataIB" (end of container)
     539              :      *  moreChunkedMessages = false, <-- 2 bytes "kReservedSizeForMoreChunksFlag"
     540              :      *  InteractionModelRevision = 1,<-- 3 bytes "kReservedSizeForIMRevision"
     541              :      * }                             <-- 1 byte  "end of WriteRequestMessage" (end of container)
     542              :      */
     543              : 
     544              :     // Reserved size for the MoreChunks boolean flag, which takes up 1 byte for the control tag and 1 byte for the context tag.
     545              :     static constexpr uint16_t kReservedSizeForMoreChunksFlag = 1 + 1;
     546              :     // End Of Container (0x18) uses one byte.
     547              :     static constexpr uint16_t kReservedSizeForEndOfContainer = 1;
     548              :     // Reserved size for the uint8_t InteractionModelRevision flag, which takes up 1 byte for the control tag and 1 byte for the
     549              :     // context tag, 1 byte for value
     550              :     static constexpr uint16_t kReservedSizeForIMRevision = 1 + 1 + 1;
     551              :     // Reserved buffer for TLV level overhead (the overhead for end of AttributeDataIBs (end of container), more chunks flag, end
     552              :     // of WriteRequestMessage (another end of container)).
     553              :     static constexpr uint16_t kReservedSizeForTLVEncodingOverhead = kReservedSizeForIMRevision + kReservedSizeForMoreChunksFlag +
     554              :         kReservedSizeForEndOfContainer + kReservedSizeForEndOfContainer;
     555              :     bool mHasDataVersion = false;
     556              : 
     557              :     static constexpr uint16_t kReservedSizeForEndOfListAttributeIB =
     558              :         kReservedSizeForEndOfContainer + AttributeDataIB::Builder::GetSizeToEndAttributeDataIB();
     559              : 
     560              :     static constexpr TLV::TLVType kAttributeDataIBType = TLV::kTLVType_Structure;
     561              : };
     562              : 
     563              : } // namespace app
     564              : } // namespace chip
        

Generated by: LCOV version 2.0-1