LCOV - code coverage report
Current view: top level - app - CommandHandler.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 19 24 79.2 %
Date: 2024-02-15 08:20:41 Functions: 7 11 63.6 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020 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             :  *      A handler for incoming Invoke interactions.
      22             :  *
      23             :  *      Allows adding responses to be sent in an InvokeResponse: see the various
      24             :  *      "Add*" methods.
      25             :  *
      26             :  *      Allows adding the responses asynchronously.  See the documentation
      27             :  *      for the CommandHandler::Handle class below.
      28             :  *
      29             :  */
      30             : 
      31             : #pragma once
      32             : 
      33             : #include "CommandPathRegistry.h"
      34             : #include "CommandResponseSender.h"
      35             : 
      36             : #include <app/ConcreteCommandPath.h>
      37             : #include <app/data-model/Encode.h>
      38             : #include <lib/core/CHIPCore.h>
      39             : #include <lib/core/TLV.h>
      40             : #include <lib/core/TLVDebug.h>
      41             : #include <lib/support/BitFlags.h>
      42             : #include <lib/support/CodeUtils.h>
      43             : #include <lib/support/DLLUtil.h>
      44             : #include <lib/support/Scoped.h>
      45             : #include <lib/support/logging/CHIPLogging.h>
      46             : #include <messaging/ExchangeHolder.h>
      47             : #include <messaging/Flags.h>
      48             : #include <protocols/Protocols.h>
      49             : #include <protocols/interaction_model/Constants.h>
      50             : #include <system/SystemPacketBuffer.h>
      51             : #include <system/TLVPacketBufferBackingStore.h>
      52             : 
      53             : #include <app/MessageDef/InvokeRequestMessage.h>
      54             : #include <app/MessageDef/InvokeResponseMessage.h>
      55             : 
      56             : namespace chip {
      57             : namespace app {
      58             : 
      59             : class CommandHandler
      60             : {
      61             : public:
      62             :     class Callback
      63             :     {
      64             :     public:
      65          40 :         virtual ~Callback() = default;
      66             : 
      67             :         /*
      68             :          * Method that signals to a registered callback that this object
      69             :          * has completed doing useful work and is now safe for release/destruction.
      70             :          */
      71             :         virtual void OnDone(CommandHandler & apCommandObj) = 0;
      72             : 
      73             :         /*
      74             :          * Upon processing of a CommandDataIB, this method is invoked to dispatch the command
      75             :          * to the right server-side handler provided by the application.
      76             :          */
      77             :         virtual void DispatchCommand(CommandHandler & apCommandObj, const ConcreteCommandPath & aCommandPath,
      78             :                                      TLV::TLVReader & apPayload) = 0;
      79             : 
      80             :         /*
      81             :          * Check to see if a command implementation exists for a specific
      82             :          * concrete command path.  If it does, Success will be returned.  If
      83             :          * not, one of UnsupportedEndpoint, UnsupportedCluster, or
      84             :          * UnsupportedCommand will be returned, depending on how the command
      85             :          * fails to exist.
      86             :          */
      87             :         virtual Protocols::InteractionModel::Status CommandExists(const ConcreteCommandPath & aCommandPath) = 0;
      88             :     };
      89             : 
      90             :     /**
      91             :      * Class that allows asynchronous command processing before sending a
      92             :      * response.  When such processing is desired:
      93             :      *
      94             :      * 1) Create a Handle initialized with the CommandHandler that delivered the
      95             :      *    incoming command.
      96             :      * 2) Ensure the Handle, or some Handle it's moved into via the move
      97             :      *    constructor or move assignment operator, remains alive during the
      98             :      *    course of the asynchronous processing.
      99             :      * 3) Ensure that the ConcreteCommandPath involved will be known when
     100             :      *    sending the response.
     101             :      * 4) When ready to send the response:
     102             :      *    * Ensure that no other Matter tasks are running in parallel (e.g. by
     103             :      *      running on the Matter event loop or holding the Matter stack lock).
     104             :      *    * Call Get() to get the CommandHandler.
     105             :      *    * Check that Get() did not return null.
     106             :      *    * Add the response to the CommandHandler via one of the Add* methods.
     107             :      *    * Let the Handle get destroyed, or manually call Handle::Release() if
     108             :      *      destruction of the Handle is not desirable for some reason.
     109             :      *
     110             :      * The Invoke Response will not be sent until all outstanding Handles have
     111             :      * been destroyed or have had Release called.
     112             :      */
     113             :     class Handle
     114             :     {
     115             :     public:
     116             :         Handle() {}
     117             :         Handle(const Handle & handle) = delete;
     118             :         Handle(Handle && handle)
     119             :         {
     120             :             mpHandler        = handle.mpHandler;
     121             :             mMagic           = handle.mMagic;
     122             :             handle.mpHandler = nullptr;
     123             :             handle.mMagic    = 0;
     124             :         }
     125             :         Handle(decltype(nullptr)) {}
     126             :         Handle(CommandHandler * handle);
     127           7 :         ~Handle() { Release(); }
     128             : 
     129             :         Handle & operator=(Handle && handle)
     130             :         {
     131             :             Release();
     132             :             mpHandler        = handle.mpHandler;
     133             :             mMagic           = handle.mMagic;
     134             :             handle.mpHandler = nullptr;
     135             :             handle.mMagic    = 0;
     136             :             return *this;
     137             :         }
     138             : 
     139             :         Handle & operator=(decltype(nullptr))
     140             :         {
     141             :             Release();
     142             :             return *this;
     143             :         }
     144             : 
     145             :         /**
     146             :          * Get the CommandHandler object it holds. Get() may return a nullptr if the CommandHandler object is holds is no longer
     147             :          * valid.
     148             :          */
     149             :         CommandHandler * Get();
     150             : 
     151             :         void Release();
     152             : 
     153             :     private:
     154             :         CommandHandler * mpHandler = nullptr;
     155             :         uint32_t mMagic            = 0;
     156             :     };
     157             : 
     158             :     // Previously we kept adding arguments with default values individually as parameters. This is because there
     159             :     // is legacy code outside of the SDK that would call PrepareCommand. With the new PrepareInvokeResponseCommand
     160             :     // replacing PrepareCommand, we took this opportunity to create a new parameter structure to make it easier to
     161             :     // add new parameters without there needing to be an ever increasing parameter list with defaults.
     162             :     struct InvokeResponseParameters
     163             :     {
     164             :         InvokeResponseParameters(ConcreteCommandPath aRequestCommandPath) : mRequestCommandPath(aRequestCommandPath) {}
     165             : 
     166             :         InvokeResponseParameters & SetStartOrEndDataStruct(bool aStartOrEndDataStruct)
     167             :         {
     168             :             mStartOrEndDataStruct = aStartOrEndDataStruct;
     169             :             return *this;
     170             :         }
     171             : 
     172             :         ConcreteCommandPath mRequestCommandPath;
     173             :         /**
     174             :          * Whether the method this is being provided to should start/end the TLV container for the CommandFields element
     175             :          * within CommandDataIB.
     176             :          */
     177             :         bool mStartOrEndDataStruct = true;
     178             :     };
     179             : 
     180             :     class TestOnlyMarker
     181             :     {
     182             :     };
     183             : 
     184             :     /*
     185             :      * Constructor.
     186             :      *
     187             :      * The callback passed in has to outlive this CommandHandler object.
     188             :      */
     189             :     CommandHandler(Callback * apCallback);
     190             : 
     191             :     /*
     192             :      * Constructor to override number of supported paths per invoke.
     193             :      *
     194             :      * The callback and command path registry passed in has to outlive this CommandHandler object.
     195             :      * For testing purposes.
     196             :      */
     197             :     CommandHandler(TestOnlyMarker aTestMarker, Callback * apCallback, CommandPathRegistry * apCommandPathRegistry);
     198             : 
     199             :     /*
     200             :      * Main entrypoint for this class to handle an invoke request.
     201             :      *
     202             :      * This function will always call the OnDone function above on the registered callback
     203             :      * before returning.
     204             :      *
     205             :      * isTimedInvoke is true if and only if this is part of a Timed Invoke
     206             :      * transaction (i.e. was preceded by a Timed Request).  If we reach here,
     207             :      * the timer verification has already been done.
     208             :      */
     209             :     void OnInvokeCommandRequest(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
     210             :                                 System::PacketBufferHandle && payload, bool isTimedInvoke);
     211             : 
     212             :     /**
     213             :      * Checks that all CommandDataIB within InvokeRequests satisfy the spec's general
     214             :      * constraints for CommandDataIB. Additionally checks that InvokeRequestMessage is
     215             :      * properly formatted.
     216             :      *
     217             :      * This also builds a registry that to ensure that all commands can be responded
     218             :      * to with the data required as per spec.
     219             :      */
     220             :     CHIP_ERROR ValidateInvokeRequestMessageAndBuildRegistry(InvokeRequestMessage::Parser & invokeRequestMessage);
     221             : 
     222             :     /**
     223             :      * Adds the given command status and returns any failures in adding statuses (e.g. out
     224             :      * of buffer space) to the caller
     225             :      */
     226             :     CHIP_ERROR FallibleAddStatus(const ConcreteCommandPath & aCommandPath, const Protocols::InteractionModel::Status aStatus,
     227             :                                  const char * context = nullptr);
     228             : 
     229             :     /**
     230             :      * Adds a status when the caller is unable to handle any failures. Logging is performed
     231             :      * and failure to register the status is checked with VerifyOrDie.
     232             :      */
     233             :     void AddStatus(const ConcreteCommandPath & aCommandPath, const Protocols::InteractionModel::Status aStatus,
     234             :                    const char * context = nullptr);
     235             : 
     236             :     CHIP_ERROR AddClusterSpecificSuccess(const ConcreteCommandPath & aCommandPath, ClusterStatus aClusterStatus);
     237             : 
     238             :     CHIP_ERROR AddClusterSpecificFailure(const ConcreteCommandPath & aCommandPath, ClusterStatus aClusterStatus);
     239             : 
     240             :     Protocols::InteractionModel::Status ProcessInvokeRequest(System::PacketBufferHandle && payload, bool isTimedInvoke);
     241             : 
     242             :     /**
     243             :      * This adds a new CommandDataIB element into InvokeResponses for the associated
     244             :      * aRequestCommandPath. This adds up until the `CommandFields` element within
     245             :      * `CommandDataIB`.
     246             :      *
     247             :      * This call will fail if CommandHandler is already in the middle of building a
     248             :      * CommandStatusIB or CommandDataIB (i.e. something has called Prepare*, without
     249             :      * calling Finish*), or is already sending InvokeResponseMessage.
     250             :      *
     251             :      * Upon success, the caller is expected to call `FinishCommand` once they have added
     252             :      * all the fields into the CommandFields element of CommandDataIB.
     253             :      *
     254             :      * @param [in] aResponseCommandPath the concrete response path that we are sending to Requester.
     255             :      * @param [in] aPrepareParameters struct containing paramters needs for preparing a command. Data
     256             :      *             such as request path, and whether this method should start the CommandFields element within
     257             :      *             CommandDataIB.
     258             :      */
     259             :     CHIP_ERROR PrepareInvokeResponseCommand(const ConcreteCommandPath & aResponseCommandPath,
     260             :                                             const InvokeResponseParameters & aPrepareParameters);
     261             : 
     262             :     [[deprecated("PrepareCommand now needs the requested command path. Please use PrepareInvokeResponseCommand")]] CHIP_ERROR
     263             :     PrepareCommand(const ConcreteCommandPath & aCommandPath, bool aStartDataStruct = true);
     264             : 
     265             :     /**
     266             :      * Finishes the CommandDataIB element within the InvokeResponses.
     267             :      *
     268             :      * Caller must have first successfully called `PrepareInvokeResponseCommand`.
     269             :      *
     270             :      * @param [in] aEndDataStruct end the TLV container for the CommandFields element within
     271             :      *             CommandDataIB. This should match the boolean passed into Prepare*.
     272             :      *
     273             :      * @return CHIP_ERROR_INCORRECT_STATE
     274             :      *                      If device has not previously successfully called
     275             :      *                      `PrepareInvokeResponseCommand`.
     276             :      * @return CHIP_ERROR_BUFFER_TOO_SMALL
     277             :      *                      If writing the values needed to finish the InvokeReponseIB
     278             :      *                      with the current contents of the InvokeResponseMessage
     279             :      *                      would exceed the limit. When this error occurs, it is possible
     280             :      *                      we have already closed some of the IB Builders that were
     281             :      *                      previously started in `PrepareInvokeResponseCommand`.
     282             :      * @return CHIP_ERROR_NO_MEMORY
     283             :      *                      If TLVWriter attempted to allocate an output buffer failed due to
     284             :      *                      lack of memory.
     285             :      * @return other        Other TLVWriter related errors. Typically occurs if
     286             :      *                      `GetCommandDataIBTLVWriter()` was called and used incorrectly.
     287             :      */
     288             :     // TODO(#30453): We should be able to eliminate the chances of OOM issues with reserve.
     289             :     // This will be completed in a follow up PR.
     290             :     CHIP_ERROR FinishCommand(bool aEndDataStruct = true);
     291             : 
     292             :     TLV::TLVWriter * GetCommandDataIBTLVWriter();
     293             : 
     294             :     /**
     295             :      * GetAccessingFabricIndex() may only be called during synchronous command
     296             :      * processing.  Anything that runs async (while holding a
     297             :      * CommandHandler::Handle or equivalent) must not call this method, because
     298             :      * it will not work right if the session we're using was evicted.
     299             :      */
     300             :     FabricIndex GetAccessingFabricIndex() const;
     301             : 
     302             :     /**
     303             :      * @brief Best effort to add InvokeResponse to InvokeResponseMessage.
     304             :      *
     305             :      * Tries to add response using lambda. Upon failure to add response, attempts
     306             :      * to rollback the InvokeResponseMessage to a known good state. If failure is due
     307             :      * to insufficient space in the current InvokeResponseMessage:
     308             :      *  - Finalizes the current InvokeResponseMessage.
     309             :      *  - Allocates a new InvokeResponseMessage.
     310             :      *  - Reattempts to add the InvokeResponse to the new InvokeResponseMessage.
     311             :      *
     312             :      * @param [in] addResponseFunction A lambda function responsible for adding the
     313             :      *             response to the current InvokeResponseMessage.
     314             :      */
     315             :     template <typename Function>
     316          37 :     CHIP_ERROR TryAddingResponse(Function && addResponseFunction)
     317             :     {
     318             :         // Invalidate any existing rollback backups. The addResponseFunction is
     319             :         // expected to create a new backup during either PrepareInvokeResponseCommand
     320             :         // or PrepareStatus execution. Direct invocation of
     321             :         // CreateBackupForResponseRollback is avoided since the buffer used by
     322             :         // InvokeResponseMessage might not be allocated until a Prepare* function
     323             :         // is called.
     324          37 :         mRollbackBackupValid = false;
     325          37 :         CHIP_ERROR err       = addResponseFunction();
     326          37 :         if (err == CHIP_NO_ERROR)
     327             :         {
     328          36 :             return CHIP_NO_ERROR;
     329             :         }
     330           1 :         ReturnErrorOnFailure(RollbackResponse());
     331             :         // If we failed to add a command due to lack of space in the
     332             :         // packet, we will make another attempt to add the response using
     333             :         // an additional InvokeResponseMessage.
     334           1 :         if (mState != State::AddedCommand || err != CHIP_ERROR_NO_MEMORY)
     335             :         {
     336           0 :             return err;
     337             :         }
     338           1 :         ReturnErrorOnFailure(FinalizeInvokeResponseMessageAndPrepareNext());
     339           1 :         err = addResponseFunction();
     340           1 :         if (err != CHIP_NO_ERROR)
     341             :         {
     342             :             // The return value of RollbackResponse is ignored, as we prioritize
     343             :             // conveying the error generated by addResponseFunction to the
     344             :             // caller.
     345           0 :             RollbackResponse();
     346             :         }
     347           1 :         return err;
     348             :     }
     349             : 
     350             :     /**
     351             :      * API for adding a data response.  The template parameter T is generally
     352             :      * expected to be a ClusterName::Commands::CommandName::Type struct, but any
     353             :      * object that can be encoded using the DataModel::Encode machinery and
     354             :      * exposes the right command id will work.
     355             :      *
     356             :      * @param [in] aRequestCommandPath the concrete path of the command we are
     357             :      *             responding to.
     358             :      * @param [in] aData the data for the response.
     359             :      */
     360             :     template <typename CommandData>
     361             :     CHIP_ERROR AddResponseData(const ConcreteCommandPath & aRequestCommandPath, const CommandData & aData)
     362             :     {
     363             :         return TryAddingResponse([&]() -> CHIP_ERROR { return TryAddResponseData(aRequestCommandPath, aData); });
     364             :     }
     365             : 
     366             :     /**
     367             :      * API for adding a response.  This will try to encode a data response (response command), and if that fails will encode a a
     368             :      * Protocols::InteractionModel::Status::Failure status response instead.
     369             :      *
     370             :      * The template parameter T is generally expected to be a ClusterName::Commands::CommandName::Type struct, but any object that
     371             :      * can be encoded using the DataModel::Encode machinery and exposes the right command id will work.
     372             :      *
     373             :      * Since the function will call AddStatus when it fails to encode the data, it cannot send any response when it fails to encode
     374             :      * a status code since another AddStatus call will also fail. The error from AddStatus will just be logged.
     375             :      *
     376             :      * @param [in] aRequestCommandPath the concrete path of the command we are
     377             :      *             responding to.
     378             :      * @param [in] aData the data for the response.
     379             :      */
     380             :     template <typename CommandData>
     381             :     void AddResponse(const ConcreteCommandPath & aRequestCommandPath, const CommandData & aData)
     382             :     {
     383             :         if (AddResponseData(aRequestCommandPath, aData) != CHIP_NO_ERROR)
     384             :         {
     385             :             AddStatus(aRequestCommandPath, Protocols::InteractionModel::Status::Failure);
     386             :         }
     387             :     }
     388             : 
     389             :     /**
     390             :      * Check whether the InvokeRequest we are handling is a timed invoke.
     391             :      */
     392           0 :     bool IsTimedInvoke() const { return mTimedRequest; }
     393             : 
     394             :     /**
     395             :      * Gets the inner exchange context object, without ownership.
     396             :      *
     397             :      * WARNING: This is dangerous, since it is directly interacting with the
     398             :      *          exchange being managed automatically by mResponseSender and
     399             :      *          if not done carefully, may end up with use-after-free errors.
     400             :      *
     401             :      * @return The inner exchange context, might be nullptr if no
     402             :      *         exchange context has been assigned or the context
     403             :      *         has been released.
     404             :      */
     405             :     Messaging::ExchangeContext * GetExchangeContext() const { return mResponseSender.GetExchangeContext(); }
     406             : 
     407             :     /**
     408             :      * @brief Flush acks right away for a slow command
     409             :      *
     410             :      * Some commands that do heavy lifting of storage/crypto should
     411             :      * ack right away to improve reliability and reduce needless retries. This
     412             :      * method can be manually called in commands that are especially slow to
     413             :      * immediately schedule an acknowledgement (if needed) since the delayed
     414             :      * stand-alone ack timer may actually not hit soon enough due to blocking command
     415             :      * execution.
     416             :      *
     417             :      */
     418             :     void FlushAcksRightAwayOnSlowCommand() { mResponseSender.FlushAcksRightNow(); }
     419             : 
     420             :     /**
     421             :      * GetSubjectDescriptor() may only be called during synchronous command
     422             :      * processing.  Anything that runs async (while holding a
     423             :      * CommandHandler::Handle or equivalent) must not call this method, because
     424             :      * it might not work right if the session we're using was evicted.
     425             :      */
     426          81 :     Access::SubjectDescriptor GetSubjectDescriptor() const
     427             :     {
     428          81 :         VerifyOrDie(!mGoneAsync);
     429          81 :         return mResponseSender.GetSubjectDescriptor();
     430             :     }
     431             : 
     432             : private:
     433             :     friend class TestCommandInteraction;
     434             :     friend class CommandHandler::Handle;
     435             : 
     436             :     enum class State : uint8_t
     437             :     {
     438             :         Idle,                ///< Default state that the object starts out in, where no work has commenced
     439             :         NewResponseMessage,  ///< mInvokeResponseBuilder is ready, with no responses added.
     440             :         Preparing,           ///< We are prepaing the command or status header.
     441             :         AddingCommand,       ///< In the process of adding a command.
     442             :         AddedCommand,        ///< A command has been completely encoded and is awaiting transmission.
     443             :         DispatchResponses,   ///< The command response(s) are being dispatched.
     444             :         AwaitingDestruction, ///< The object has completed its work and is awaiting destruction by the application.
     445             :     };
     446             : 
     447             :     void MoveToState(const State aTargetState);
     448             :     const char * GetStateStr() const;
     449             : 
     450             :     /**
     451             :      * Create a backup to enable rolling back to the state prior to ResponseData encoding in the event of failure.
     452             :      */
     453             :     void CreateBackupForResponseRollback();
     454             : 
     455             :     /**
     456             :      * Rollback the state to before encoding the current ResponseData (before calling PrepareInvokeResponseCommand / PrepareStatus)
     457             :      *
     458             :      * Requires CreateBackupForResponseRollback to be called at the start of PrepareInvokeResponseCommand / PrepareStatus
     459             :      */
     460             :     CHIP_ERROR RollbackResponse();
     461             : 
     462             :     /*
     463             :      * This forcibly closes the exchange context if a valid one is pointed to. Such a situation does
     464             :      * not arise during normal message processing flows that all normally call Close() above. This can only
     465             :      * arise due to application-initiated destruction of the object when this object is handling receiving/sending
     466             :      * message payloads.
     467             :      */
     468             :     void Abort();
     469             : 
     470             :     /**
     471             :      * IncrementHoldOff will increase the inner refcount of the CommandHandler.
     472             :      *
     473             :      * Users should use CommandHandler::Handle for management the lifespan of the CommandHandler.
     474             :      * DefRef should be released in reasonable time, and Close() should only be called when the refcount reached 0.
     475             :      */
     476             :     void IncrementHoldOff();
     477             : 
     478             :     /**
     479             :      * DecrementHoldOff is used by CommandHandler::Handle for decreasing the refcount of the CommandHandler.
     480             :      * When refcount reached 0, CommandHandler will send the response to the peer and shutdown.
     481             :      */
     482             :     void DecrementHoldOff();
     483             : 
     484             :     /*
     485             :      * Allocates a packet buffer used for encoding an invoke response payload.
     486             :      *
     487             :      * This can be called multiple times safely, as it will only allocate the buffer once for the lifetime
     488             :      * of this object.
     489             :      */
     490             :     CHIP_ERROR AllocateBuffer();
     491             : 
     492             :     /**
     493             :      * This will add a new CommandStatusIB element into InvokeResponses. It will put the
     494             :      * aCommandPath into the CommandPath element within CommandStatusIB.
     495             :      *
     496             :      * This call will fail if CommandHandler is already in the middle of building a
     497             :      * CommandStatusIB or CommandDataIB (i.e. something has called Prepare*, without
     498             :      * calling Finish*), or is already sending InvokeResponseMessage.
     499             :      *
     500             :      * Upon success, the caller is expected to call `FinishStatus` once they have encoded
     501             :      * StatusIB.
     502             :      *
     503             :      * @param [in] aCommandPath the concrete path of the command we are responding to.
     504             :      */
     505             :     CHIP_ERROR PrepareStatus(const ConcreteCommandPath & aCommandPath);
     506             : 
     507             :     /**
     508             :      * Finishes the CommandStatusIB element within the InvokeResponses.
     509             :      *
     510             :      * Caller must have first successfully called `PrepareStatus`.
     511             :      */
     512             :     CHIP_ERROR FinishStatus();
     513             : 
     514             :     CHIP_ERROR PrepareInvokeResponseCommand(const CommandPathRegistryEntry & apCommandPathRegistryEntry,
     515             :                                             const ConcreteCommandPath & aCommandPath, bool aStartDataStruct);
     516             : 
     517          15 :     CHIP_ERROR FinalizeLastInvokeResponseMessage() { return FinalizeInvokeResponseMessage(/* aHasMoreChunks = */ false); }
     518             : 
     519             :     CHIP_ERROR FinalizeInvokeResponseMessageAndPrepareNext();
     520             : 
     521             :     CHIP_ERROR FinalizeInvokeResponseMessage(bool aHasMoreChunks);
     522             : 
     523             :     /**
     524             :      * Called internally to signal the completion of all work on this object, gracefully close the
     525             :      * exchange (by calling into the base class) and finally, signal to a registerd callback that it's
     526             :      * safe to release this object.
     527             :      */
     528             :     void Close();
     529             : 
     530             :     /**
     531             :      * @brief Callback method invoked when CommandResponseSender has finished sending all messages.
     532             :      */
     533             :     static void HandleOnResponseSenderDone(void * context);
     534             : 
     535             :     /**
     536             :      * ProcessCommandDataIB is only called when a unicast invoke command request is received
     537             :      * It requires the endpointId in its command path to be able to dispatch the command
     538             :      */
     539             :     Protocols::InteractionModel::Status ProcessCommandDataIB(CommandDataIB::Parser & aCommandElement);
     540             : 
     541             :     /**
     542             :      * ProcessGroupCommandDataIB is only called when a group invoke command request is received
     543             :      * It doesn't need the endpointId in it's command path since it uses the GroupId in message metadata to find it
     544             :      */
     545             :     Protocols::InteractionModel::Status ProcessGroupCommandDataIB(CommandDataIB::Parser & aCommandElement);
     546             :     CHIP_ERROR StartSendingCommandResponses();
     547             : 
     548             :     CHIP_ERROR TryAddStatusInternal(const ConcreteCommandPath & aCommandPath, const StatusIB & aStatus);
     549             : 
     550             :     CHIP_ERROR AddStatusInternal(const ConcreteCommandPath & aCommandPath, const StatusIB & aStatus);
     551             : 
     552             :     /**
     553             :      * Non-templated function called before DataModel::Encode when attempting to add a response,
     554             :      * which does all the work needed before encoding the actual type-dependent data into the buffer.
     555             :      *
     556             :      * **Important:** If this function fails, the TLV buffer may be left in an inconsistent state.
     557             :      * Callers should create snapshots as necessary before invoking this function and implement
     558             :      * rollback mechanisms if needed.
     559             :      *
     560             :      * **Usage:** This function is intended to be called exclusively by TryAddResponseData. It was
     561             :      * factored out to optimize code size.
     562             :      *
     563             :      * @param aRequestCommandPath  The concrete path of the command being responded to.
     564             :      * @param aResponseCommandPath The concrete path of the command response.
     565             :      */
     566             :     CHIP_ERROR TryAddResponseDataPreEncode(const ConcreteCommandPath & aRequestCommandPath,
     567             :                                            const ConcreteCommandPath & aResponseCommandPath)
     568             :     {
     569             :         // Return early in case of requests targeted to a group, since they should not add a response.
     570             :         VerifyOrReturnValue(!IsGroupRequest(), CHIP_NO_ERROR);
     571             : 
     572             :         InvokeResponseParameters prepareParams(aRequestCommandPath);
     573             :         prepareParams.SetStartOrEndDataStruct(false);
     574             : 
     575             :         ScopedChange<bool> internalCallToAddResponse(mInternalCallToAddResponseData, true);
     576             :         return PrepareInvokeResponseCommand(aResponseCommandPath, prepareParams);
     577             :     }
     578             : 
     579             :     // TODO(#31627): It would be awesome if we could remove this template all together.
     580             :     /**
     581             :      * If this function fails, it may leave our TLV buffer in an inconsistent state.
     582             :      * Callers should snapshot as needed before calling this function, and roll back
     583             :      * as needed afterward.
     584             :      *
     585             :      * @param [in] aRequestCommandPath the concrete path of the command we are
     586             :      *             responding to.
     587             :      * @param [in] aData the data for the response.
     588             :      */
     589             :     template <typename CommandData>
     590             :     CHIP_ERROR TryAddResponseData(const ConcreteCommandPath & aRequestCommandPath, const CommandData & aData)
     591             :     {
     592             :         // This method, templated with CommandData, captures all the components needs
     593             :         // from CommandData with as little code as possible.
     594             :         //
     595             :         // Previously, non-essential code was unnecessarily templated, leading to
     596             :         // compilation and duplication N times. By isolating only the code segments
     597             :         // that genuinely require templating, minimizes duplicate compiled code.
     598             :         ConcreteCommandPath responseCommandPath = { aRequestCommandPath.mEndpointId, aRequestCommandPath.mClusterId,
     599             :                                                     CommandData::GetCommandId() };
     600             :         ReturnErrorOnFailure(TryAddResponseDataPreEncode(aRequestCommandPath, responseCommandPath));
     601             :         TLV::TLVWriter * writer = GetCommandDataIBTLVWriter();
     602             :         VerifyOrReturnError(writer != nullptr, CHIP_ERROR_INCORRECT_STATE);
     603             :         ReturnErrorOnFailure(DataModel::Encode(*writer, TLV::ContextTag(CommandDataIB::Tag::kFields), aData));
     604             : 
     605             :         // FinishCommand technically should be refactored out as it is not a command that needs templating.
     606             :         // But, because there is only a single function call, keeping it here takes less code. If there is
     607             :         // ever more code between DataModel::Encode and the end of this function, it should be broken out into
     608             :         // TryAddResponseDataPostEncode.
     609             :         return FinishCommand(/* aEndDataStruct = */ false);
     610             :     }
     611             : 
     612             :     /**
     613             :      * Check whether the InvokeRequest we are handling is targeted to a group.
     614             :      */
     615           0 :     bool IsGroupRequest() { return mGroupRequest; }
     616             : 
     617             :     /**
     618             :      * Sets the state flag to keep the information that request we are handling is targeted to a group.
     619             :      */
     620           0 :     void SetGroupRequest(bool isGroupRequest) { mGroupRequest = isGroupRequest; }
     621             : 
     622          37 :     CommandPathRegistry & GetCommandPathRegistry() const { return *mCommandPathRegistry; }
     623             : 
     624          36 :     size_t MaxPathsPerInvoke() const { return mMaxPathsPerInvoke; }
     625             : 
     626             :     Callback * mpCallback = nullptr;
     627             :     InvokeResponseMessage::Builder mInvokeResponseBuilder;
     628             :     TLV::TLVType mDataElementContainerType = TLV::kTLVType_NotSpecified;
     629             :     size_t mPendingWork                    = 0;
     630             : 
     631             :     chip::System::PacketBufferTLVWriter mCommandMessageWriter;
     632             :     TLV::TLVWriter mBackupWriter;
     633             :     size_t mMaxPathsPerInvoke = CHIP_CONFIG_MAX_PATHS_PER_INVOKE;
     634             :     // TODO(#30453): See if we can reduce this size for the default cases
     635             :     // TODO Allow flexibility in registration.
     636             :     BasicCommandPathRegistry<CHIP_CONFIG_MAX_PATHS_PER_INVOKE> mBasicCommandPathRegistry;
     637             :     CommandPathRegistry * mCommandPathRegistry = &mBasicCommandPathRegistry;
     638             :     Optional<uint16_t> mRefForResponse;
     639             : 
     640             :     chip::Callback::Callback<OnResponseSenderDone> mResponseSenderDone;
     641             :     CommandResponseSender mResponseSender;
     642             : 
     643             :     State mState = State::Idle;
     644             :     State mBackupState;
     645             :     ScopedChangeOnly<bool> mInternalCallToAddResponseData{ false };
     646             :     bool mSuppressResponse                 = false;
     647             :     bool mTimedRequest                     = false;
     648             :     bool mSentStatusResponse               = false;
     649             :     bool mGroupRequest                     = false;
     650             :     bool mBufferAllocated                  = false;
     651             :     bool mReserveSpaceForMoreChunkMessages = false;
     652             :     // TODO(#30453): We should introduce breaking change where calls to add CommandData
     653             :     // need to use AddResponse, and not CommandHandler primitives directly using
     654             :     // GetCommandDataIBTLVWriter.
     655             :     bool mRollbackBackupValid = false;
     656             :     // If mGoneAsync is true, we have finished out initial processing of the
     657             :     // incoming invoke.  After this point, our session could go away at any
     658             :     // time.
     659             :     bool mGoneAsync = false;
     660             : };
     661             : 
     662             : } // namespace app
     663             : } // namespace chip

Generated by: LCOV version 1.14