Matter SDK Coverage Report
Current view: top level - app - WriteHandler.h (source / functions) Coverage Total Hit
Test: SHA:704d97f9c619242ad76fcf75aeabc67802fa72d4 Lines: 77.8 % 9 7
Test Date: 2026-05-18 07:37:39 Functions: 66.7 % 9 6

            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/AppConfig.h>
      22              : #include <app/AttributePathParams.h>
      23              : #include <app/ConcreteAttributePath.h>
      24              : #include <app/InteractionModelDelegatePointers.h>
      25              : #include <app/MessageDef/WriteResponseMessage.h>
      26              : #include <app/data-model-provider/ActionReturnStatus.h>
      27              : #include <app/data-model-provider/Provider.h>
      28              : #include <lib/core/CHIPCore.h>
      29              : #include <lib/core/TLVDebug.h>
      30              : #include <lib/support/BitFlags.h>
      31              : #include <lib/support/CodeUtils.h>
      32              : #include <lib/support/DLLUtil.h>
      33              : #include <lib/support/logging/CHIPLogging.h>
      34              : #include <messaging/ExchangeHolder.h>
      35              : #include <messaging/ExchangeMgr.h>
      36              : #include <messaging/Flags.h>
      37              : #include <protocols/Protocols.h>
      38              : #include <protocols/interaction_model/Constants.h>
      39              : #include <protocols/interaction_model/StatusCode.h>
      40              : #include <system/SystemPacketBuffer.h>
      41              : #include <system/TLVPacketBufferBackingStore.h>
      42              : 
      43              : namespace chip {
      44              : namespace app {
      45              : 
      46              : class WriteHandler;
      47              : 
      48              : class WriteHandlerDelegate
      49              : {
      50              : public:
      51          163 :     virtual ~WriteHandlerDelegate() = default;
      52              : 
      53              :     /**
      54              :      * Returns whether the write operation to the given path is in conflict with another write operation.
      55              :      * (i.e. another write transaction is in the middle of processing a chunked write to the given path.)
      56              :      */
      57              :     virtual bool HasConflictWriteRequests(const WriteHandler * apWriteHandler, const ConcreteAttributePath & aPath) = 0;
      58              : };
      59              : 
      60              : /**
      61              :  *  @brief The write handler is responsible for processing a write request and sending a write reply.
      62              :  */
      63              : class WriteHandler : public Messaging::ExchangeDelegate
      64              : {
      65              : public:
      66          656 :     WriteHandler() : mExchangeCtx(*this) {}
      67              : 
      68              :     /**
      69              :      *  Initialize the WriteHandler. Within the lifetime
      70              :      *  of this instance, this method is invoked once after object
      71              :      *  construction until a call to Close is made to terminate the
      72              :      *  instance.
      73              :      *
      74              :      *  @param[in] apProvider              A valid pointer to the model used to forward writes towards
      75              :      *  @param[in] apWriteHandlerDelegate  A Valid pointer to the WriteHandlerDelegate.
      76              :      *
      77              :      *  @retval #CHIP_ERROR_INVALID_ARGUMENT on invalid pointers
      78              :      *  @retval #CHIP_ERROR_INCORRECT_STATE If the state is not equal to
      79              :      *          kState_NotInitialized.
      80              :      *  @retval #CHIP_NO_ERROR On success.
      81              :      *
      82              :      */
      83              :     CHIP_ERROR Init(DataModel::Provider * apProvider, WriteHandlerDelegate * apWriteHandlerDelegate);
      84              : 
      85              :     /**
      86              :      *  Process a write request.  Parts of the processing may end up being asynchronous, but the WriteHandler
      87              :      *  guarantees that it will call Close on itself when processing is done (including if OnWriteRequest
      88              :      *  returns an error).
      89              :      *
      90              :      *  @param[in]    apExchangeContext    A pointer to the ExchangeContext.
      91              :      *  @param[in]    aPayload             A payload that has read request data
      92              :      *  @param[in]    aIsTimedWrite        Whether write is part of a timed interaction.
      93              :      *
      94              :      *  @retval Status.  Callers are expected to send a status response if the
      95              :      *  return status is not Status::Success.
      96              :      */
      97              :     Protocols::InteractionModel::Status OnWriteRequest(Messaging::ExchangeContext * apExchangeContext,
      98              :                                                        System::PacketBufferHandle && aPayload, bool aIsTimedWrite);
      99              : 
     100              :     /**
     101              :      *  Clean up state when we are done sending the write response.
     102              :      */
     103              :     void Close();
     104              : 
     105        23312 :     bool IsFree() const { return mState == State::Uninitialized; }
     106              : 
     107          656 :     ~WriteHandler() override = default;
     108              : 
     109              :     CHIP_ERROR ProcessAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader);
     110              :     CHIP_ERROR ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader);
     111              : 
     112              :     CHIP_ERROR AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::ClusterStatusCode & aStatus);
     113            0 :     CHIP_ERROR AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::Status aStatus)
     114              :     {
     115            0 :         return AddStatus(aPath, Protocols::InteractionModel::ClusterStatusCode{ aStatus });
     116              :     }
     117              : 
     118              :     CHIP_ERROR AddClusterSpecificSuccess(const ConcreteDataAttributePath & aAttributePathParams, ClusterStatus aClusterStatus);
     119              :     CHIP_ERROR AddClusterSpecificFailure(const ConcreteDataAttributePath & aAttributePathParams, ClusterStatus aClusterStatus);
     120              : 
     121              :     FabricIndex GetAccessingFabricIndex() const;
     122              : 
     123              :     /**
     124              :      * Check whether the WriteRequest we are handling is a timed write.
     125              :      */
     126        10400 :     bool IsTimedWrite() const { return mStateFlags.Has(StateBits::kIsTimedRequest); }
     127              : 
     128              :     bool MatchesExchangeContext(Messaging::ExchangeContext * apExchangeContext) const
     129              :     {
     130              :         return !IsFree() && mExchangeCtx.Get() == apExchangeContext;
     131              :     }
     132              : 
     133           22 :     bool IsCurrentlyProcessingWritePath(const ConcreteAttributePath & aPath)
     134              :     {
     135           22 :         return mProcessingAttributePath.HasValue() && mProcessingAttributePath.Value() == aPath;
     136              :     }
     137              : 
     138              : private:
     139              :     friend class TestWriteInteraction;
     140              :     enum class State : uint8_t
     141              :     {
     142              :         Uninitialized = 0, // The handler has not been initialized
     143              :         Initialized,       // The handler has been initialized and is ready
     144              :         AddStatus,         // The handler has added status code
     145              :         Sending,           // The handler has sent out the write response
     146              :     };
     147              :     using Status = Protocols::InteractionModel::Status;
     148              :     Status ProcessWriteRequest(System::PacketBufferHandle && aPayload, bool aIsTimedWrite);
     149              :     Status HandleWriteRequestMessage(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload,
     150              :                                      bool aIsTimedWrite);
     151              : 
     152              :     CHIP_ERROR FinalizeMessage(System::PacketBufferTLVWriter && aMessageWriter, System::PacketBufferHandle & packet);
     153              :     CHIP_ERROR SendWriteResponse(System::PacketBufferTLVWriter && aMessageWriter);
     154              : 
     155              :     void MoveToState(const State aTargetState);
     156              :     const char * GetStateStr() const;
     157              : 
     158              :     void DeliverListWriteBegin(const ConcreteAttributePath & aPath);
     159              :     void DeliverListWriteEnd(const ConcreteAttributePath & aPath, bool writeWasSuccessful);
     160              : 
     161              :     // Deliver the signal that we have delivered all list entries to the AttributeAccessInterface. This function will be called
     162              :     // after handling the last chunk of a series of write requests. Or the write handler was shutdown (usually due to transport
     163              :     // timeout).
     164              :     // This function will become no-op on group writes, since DeliverFinalListWriteEndForGroupWrite will clear the
     165              :     // mProcessingAttributePath after processing the AttributeDataIBs from the request.
     166              :     void DeliverFinalListWriteEnd(bool writeWasSuccessful);
     167              : 
     168              :     // Deliver the signal that we have delivered all list entries to the AttributeAccessInterface. This function will be called
     169              :     // after handling the last attribute in a group write request (since group writes will never be chunked writes). Or we failed to
     170              :     // process the group write request (usually due to malformed messages). This function should only be called by
     171              :     // ProcessGroupAttributeDataIBs.
     172              :     CHIP_ERROR DeliverFinalListWriteEndForGroupWrite(bool writeWasSuccessful);
     173              : 
     174              :     CHIP_ERROR AddStatusInternal(const ConcreteDataAttributePath & aPath, const StatusIB & aStatus);
     175              : 
     176              :     // ExchangeDelegate
     177              :     CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
     178              :                                  System::PacketBufferHandle && aPayload) override;
     179              :     void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override;
     180              : 
     181              :     /// Validate that a write is acceptable on the given path.
     182              :     ///
     183              :     /// Validates that ACL, writability and Timed interaction settings are ok.
     184              :     ///
     185              :     /// Returns a success status if all is ok, failure otherwise.
     186              :     DataModel::ActionReturnStatus CheckWriteAllowed(const Access::SubjectDescriptor & aSubject,
     187              :                                                     const ConcreteDataAttributePath & aPath);
     188              : 
     189              :     /// Validate that a write on the given path has aRequiredPrivilege.
     190              :     ///
     191              :     /// Returns a success status if the ACL check is successful, otherwise the relevant status will be returned.
     192              :     Status CheckWriteAccess(const Access::SubjectDescriptor & aSubject, const ConcreteAttributePath & aPath,
     193              :                             const Access::Privilege aRequiredPrivilege);
     194              : 
     195              :     // Write the given data to the given path
     196              :     CHIP_ERROR WriteClusterData(const Access::SubjectDescriptor & aSubject, const ConcreteDataAttributePath & aPath,
     197              :                                 TLV::TLVReader & aData);
     198              : 
     199              :     /// Checks whether the given path corresponds to a list attribute
     200              :     /// Return values:
     201              :     ///    true/false: valid attribute path, known if list or not
     202              :     ///    std::nulloptr - path not available/valid, unknown if attribute is a list or not
     203              :     std::optional<bool> IsListAttributePath(const ConcreteAttributePath & path);
     204              : 
     205              :     Messaging::ExchangeHolder mExchangeCtx;
     206              :     WriteResponseMessage::Builder mWriteResponseBuilder;
     207              :     Optional<ConcreteAttributePath> mProcessingAttributePath;
     208              : 
     209              :     DataModel::Provider * mDataModelProvider = nullptr;
     210              : 
     211              :     // Required for legacy-encoded ACL list writes; lets CheckWriteAccess skip the re-check on repeated same-path writes
     212              :     // Legacy-encoded ACL list write produces, in one WriteRequestMessage:
     213              :     //   #1 ReplaceAll([])  — empties ACL, removing the writer's own admin entry
     214              :     //   #2 AppendItem(item) — same path; its ACL re-check would be denied if not for
     215              :     //                          mLastSuccessfullyWrittenPath cache.
     216              :     std::optional<ConcreteAttributePath> mLastSuccessfullyWrittenPath;
     217              : 
     218              :     // This may be a "fake" pointer or a real delegate pointer, depending
     219              :     // on CHIP_CONFIG_STATIC_GLOBAL_INTERACTION_MODEL_ENGINE setting.
     220              :     //
     221              :     // When this is not a real pointer, it checks that the value is always
     222              :     // set to the global InteractionModelEngine and the size of this
     223              :     // member is 1 byte.
     224              :     InteractionModelDelegatePointer<WriteHandlerDelegate> mDelegate;
     225              : 
     226              :     // bit level enums to save storage for this object. InteractionModelEngine maintains
     227              :     // several of these objects, so every bit of storage multiplies storage usage.
     228              :     enum class StateBits : uint8_t
     229              :     {
     230              :         kIsTimedRequest            = 0x01,
     231              :         kSuppressResponse          = 0x02,
     232              :         kHasMoreChunks             = 0x04,
     233              :         kProcessingAttributeIsList = 0x08,
     234              :         // We record the Status when AddStatus is called to determine whether all data of a list write is accepted.
     235              :         // This value will be used by DeliverListWriteEnd and DeliverFinalListWriteEnd but it won't be used by group writes based on
     236              :         // the fact that the errors that won't be delivered to AttributeAccessInterface are:
     237              :         //  (1) Attribute not found
     238              :         //  (2) Access control failed
     239              :         //  (3) Write request to a read-only attribute
     240              :         //  (4) Data version mismatch
     241              :         //  (5) Not using timed write.
     242              :         //  Where (1)-(3) will be consistent among the whole list write request, while (4) and (5) are not appliable to group
     243              :         //  writes.
     244              :         kAttributeWriteSuccessful = 0x10,
     245              :     };
     246              : 
     247              :     BitFlags<StateBits> mStateFlags;
     248              :     State mState = State::Uninitialized;
     249              : };
     250              : 
     251              : } // namespace app
     252              : } // namespace chip
        

Generated by: LCOV version 2.0-1