Matter SDK Coverage Report
Current view: top level - app - CommandHandlerInterface.h (source / functions) Coverage Total Hit
Test: SHA:9f95d758cc04a404d4b85a9e3b8cc2551c3562e3 Lines: 100.0 % 11 11
Test Date: 2025-05-17 07:09:34 Functions: 100.0 % 6 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/CommandHandler.h>
      22              : #include <app/ConcreteClusterPath.h>
      23              : #include <app/ConcreteCommandPath.h>
      24              : #include <app/data-model/Decode.h>
      25              : #include <app/data-model/FabricScoped.h>
      26              : #include <app/data-model/List.h> // So we can encode lists
      27              : #include <lib/core/DataModelTypes.h>
      28              : #include <lib/support/Iterators.h>
      29              : 
      30              : #include <type_traits>
      31              : namespace chip {
      32              : namespace app {
      33              : 
      34              : /*
      35              :  * This interface permits applications to register a server-side command handler
      36              :  * at run-time for a given cluster. The handler can either be configured to handle all endpoints
      37              :  * for the given cluster or only handle a specific endpoint.
      38              :  *
      39              :  * If a command is not handled through this interface, it will default to invoking the generated DispatchSingleClusterCommand
      40              :  * instead.
      41              :  *
      42              :  */
      43              : class CommandHandlerInterface
      44              : {
      45              : public:
      46              :     struct HandlerContext
      47              :     {
      48              :     public:
      49            3 :         HandlerContext(CommandHandler & commandHandler, const ConcreteCommandPath & requestPath, TLV::TLVReader & aReader) :
      50            3 :             mCommandHandler(commandHandler), mRequestPath(requestPath), mPayload(aReader)
      51            3 :         {}
      52              : 
      53              :         void SetCommandHandled() { mCommandHandled = true; }
      54              :         void SetCommandNotHandled() { mCommandHandled = false; }
      55              : 
      56              :         /*
      57              :          * Returns a TLVReader positioned at the TLV struct that contains the payload of the command.
      58              :          *
      59              :          * If the reader is requested from the context, then we can assume there is an intention
      60              :          * to access the payload of this command and consequently, to handle this command.
      61              :          *
      62              :          * If this is not true, the application should call SetCommandNotHandled().
      63              :          *
      64              :          */
      65              :         TLV::TLVReader & GetReader()
      66              :         {
      67              :             SetCommandHandled();
      68              :             return mPayload;
      69              :         }
      70              : 
      71              :         CommandHandler & mCommandHandler;
      72              :         const ConcreteCommandPath & mRequestPath;
      73              :         TLV::TLVReader & mPayload;
      74              :         bool mCommandHandled = false;
      75              :     };
      76              : 
      77              :     /**
      78              :      * aEndpointId can be Missing to indicate that this object is meant to be
      79              :      * used with all endpoints.
      80              :      */
      81              :     CommandHandlerInterface(Optional<EndpointId> aEndpointId, ClusterId aClusterId) :
      82              :         mEndpointId(aEndpointId), mClusterId(aClusterId)
      83              :     {}
      84              : 
      85              :     virtual ~CommandHandlerInterface() {}
      86              : 
      87              :     /**
      88              :      * Callback that must be implemented to handle an invoke request.
      89              :      *
      90              :      * The callee is required to handle *all* errors that may occur during the handling of this command,
      91              :      * including errors like those encountered during decode and encode of the payloads as
      92              :      * well as application-specific errors. As part of handling the error, the callee is required
      93              :      * to handle generation of an appropriate status response.
      94              :      *
      95              :      * The only exception to this rule is if the HandleCommand helper method is used below - it will
      96              :      * help handle some of these cases (see below).
      97              :      *
      98              :      * @param [in] handlerContext Context that encapsulates the current invoke request.
      99              :      *                            Handlers are responsible for correctly calling SetCommandHandled()
     100              :      *                            on the context if they did handle the command.
     101              :      *
     102              :      *                            This is not necessary if the HandleCommand() method below is invoked.
     103              :      */
     104              :     virtual void InvokeCommand(HandlerContext & handlerContext) = 0;
     105              : 
     106              :     typedef Loop (*CommandIdCallback)(CommandId id, void * context);
     107              : 
     108              :     /**
     109              :      * Function that may be implemented to enumerate accepted (client-to-server)
     110              :      * commands for the given cluster.
     111              :      *
     112              :      * If this function returns CHIP_ERROR_NOT_IMPLEMENTED, the list of accepted
     113              :      * commands will come from the endpoint metadata for the cluster.
     114              :      *
     115              :      * If this function returns any other error, that will be treated as an
     116              :      * error condition by the caller, and handling will depend on the caller.
     117              :      *
     118              :      * Otherwise the list of accepted commands will be the list of values passed
     119              :      * to the provided callback.
     120              :      *
     121              :      * The implementation _must_ pass the provided context to the callback.
     122              :      *
     123              :      * If the callback returns Loop::Break, there must be no more calls to it.
     124              :      * This is used by callbacks that just look for a particular value in the
     125              :      * list.
     126              :      */
     127              :     virtual CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
     128              :     {
     129              :         return CHIP_ERROR_NOT_IMPLEMENTED;
     130              :     }
     131              : 
     132              :     /**
     133              :      * Function that may be implemented to enumerate generated (response)
     134              :      * commands for the given cluster.
     135              :      *
     136              :      * If this function returns CHIP_ERROR_NOT_IMPLEMENTED, the list of
     137              :      * generated commands will come from the endpoint metadata for the cluster.
     138              :      *
     139              :      * If this function returns any other error, that will be treated as an
     140              :      * error condition by the caller, and handling will depend on the caller.
     141              :      *
     142              :      * Otherwise the list of generated commands will be the list of values
     143              :      * passed to the provided callback.
     144              :      *
     145              :      * The implementation _must_ pass the provided context to the callback.
     146              :      *
     147              :      * If the callback returns Loop::Break, there must be no more calls to it.
     148              :      * This is used by callbacks that just look for a particular value in the
     149              :      * list.
     150              :      */
     151              :     virtual CHIP_ERROR EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
     152              :     {
     153              :         return CHIP_ERROR_NOT_IMPLEMENTED;
     154              :     }
     155              : 
     156              :     /**
     157              :      * Mechanism for keeping track of a chain of CommandHandlerInterface.
     158              :      */
     159           32 :     void SetNext(CommandHandlerInterface * aNext) { mNext = aNext; }
     160           62 :     CommandHandlerInterface * GetNext() const { return mNext; }
     161              : 
     162              :     /**
     163              :      * Check whether a this CommandHandlerInterface is relevant for a
     164              :      * particular endpoint+cluster.  An CommandHandlerInterface will be used
     165              :      * for an invoke from a particular cluster only when this function returns
     166              :      * true.
     167              :      */
     168           63 :     bool Matches(EndpointId aEndpointId, ClusterId aClusterId) const
     169              :     {
     170           63 :         return (!mEndpointId.HasValue() || mEndpointId.Value() == aEndpointId) && mClusterId == aClusterId;
     171              :     }
     172              : 
     173              :     /**
     174              :      * Check whether an CommandHandlerInterface is relevant for a particular
     175              :      * specific endpoint.  This is used to clean up overrides registered for an
     176              :      * endpoint that becomes disabled.
     177              :      */
     178           10 :     bool MatchesEndpoint(EndpointId aEndpointId) const { return mEndpointId.HasValue() && mEndpointId.Value() == aEndpointId; }
     179              : 
     180              :     /**
     181              :      * Check whether another CommandHandlerInterface wants to handle the same set of
     182              :      * commands as we do.
     183              :      */
     184           27 :     bool Matches(const CommandHandlerInterface & aOther) const
     185              :     {
     186           39 :         return mClusterId == aOther.mClusterId &&
     187           39 :             (!mEndpointId.HasValue() || !aOther.mEndpointId.HasValue() || mEndpointId.Value() == aOther.mEndpointId.Value());
     188              :     }
     189              : 
     190              : protected:
     191              :     /*
     192              :      * Helper function to automatically de-serialize the data payload into a cluster object
     193              :      * of type RequestT if the Cluster ID and Command ID in the context match. Upon successful
     194              :      * de-serialization, the provided function is invoked and passed in a reference to the cluster object.
     195              :      *
     196              :      * Any errors encountered in this function prior to calling func result in the automatic generation of a status response.
     197              :      * If `func` is called, the responsibility for doing so shifts to the callee to handle any further errors that are encountered.
     198              :      *
     199              :      * The provided function is expected to have the following signature:
     200              :      *  void Func(HandlerContext &handlerContext, const RequestT &requestPayload);
     201              :      */
     202              :     template <typename RequestT, typename FuncT,
     203              :               typename std::enable_if_t<!DataModel::IsFabricScoped<RequestT>::value, bool> = true>
     204              :     void HandleCommand(HandlerContext & handlerContext, FuncT func)
     205              :     {
     206              :         if (!handlerContext.mCommandHandled && (handlerContext.mRequestPath.mClusterId == RequestT::GetClusterId()) &&
     207              :             (handlerContext.mRequestPath.mCommandId == RequestT::GetCommandId()))
     208              :         {
     209              :             RequestT requestPayload;
     210              : 
     211              :             //
     212              :             // If the command matches what the caller is looking for, let's mark this as being handled
     213              :             // even if errors happen after this. This ensures that we don't execute any fall-back strategies
     214              :             // to handle this command since at this point, the caller is taking responsibility for handling
     215              :             // the command in its entirety, warts and all.
     216              :             //
     217              :             handlerContext.SetCommandHandled();
     218              : 
     219              :             if (DataModel::Decode(handlerContext.mPayload, requestPayload) != CHIP_NO_ERROR)
     220              :             {
     221              :                 handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath,
     222              :                                                          Protocols::InteractionModel::Status::InvalidCommand);
     223              :                 return;
     224              :             }
     225              : 
     226              :             func(handlerContext, requestPayload);
     227              :         }
     228              :     }
     229              : 
     230              :     /*
     231              :      * Helper function to automatically de-serialize the data payload into a cluster object
     232              :      * of type RequestT if the Cluster ID and Command ID in the context match. Upon successful
     233              :      * de-serialization, the provided function is invoked and passed in a reference to the cluster object.
     234              :      *
     235              :      * Any errors encountered in this function prior to calling func result in the automatic generation of a status response.
     236              :      * If `func` is called, the responsibility for doing so shifts to the callee to handle any further errors that are encountered.
     237              :      *
     238              :      * The provided function is expected to have the following signature:
     239              :      *  void Func(HandlerContext &handlerContext, const RequestT &requestPayload);
     240              :      */
     241              :     template <typename RequestT, typename FuncT, typename std::enable_if_t<DataModel::IsFabricScoped<RequestT>::value, bool> = true>
     242              :     void HandleCommand(HandlerContext & handlerContext, FuncT func)
     243              :     {
     244              :         if (!handlerContext.mCommandHandled && (handlerContext.mRequestPath.mClusterId == RequestT::GetClusterId()) &&
     245              :             (handlerContext.mRequestPath.mCommandId == RequestT::GetCommandId()))
     246              :         {
     247              :             RequestT requestPayload;
     248              : 
     249              :             //
     250              :             // If the command matches what the caller is looking for, let's mark this as being handled
     251              :             // even if errors happen after this. This ensures that we don't execute any fall-back strategies
     252              :             // to handle this command since at this point, the caller is taking responsibility for handling
     253              :             // the command in its entirety, warts and all.
     254              :             //
     255              :             handlerContext.SetCommandHandled();
     256              : 
     257              :             if (DataModel::Decode(handlerContext.mPayload, requestPayload,
     258              :                                   handlerContext.mCommandHandler.GetAccessingFabricIndex()) != CHIP_NO_ERROR)
     259              :             {
     260              :                 handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath,
     261              :                                                          Protocols::InteractionModel::Status::InvalidCommand);
     262              :                 return;
     263              :             }
     264              : 
     265              :             func(handlerContext, requestPayload);
     266              :         }
     267              :     }
     268              : 
     269              :     Optional<EndpointId> GetEndpointId() { return mEndpointId; }
     270              : 
     271              : private:
     272              :     Optional<EndpointId> mEndpointId;
     273              :     ClusterId mClusterId;
     274              :     CommandHandlerInterface * mNext = nullptr;
     275              : };
     276              : 
     277              : } // namespace app
     278              : } // namespace chip
        

Generated by: LCOV version 2.0-1