LCOV - code coverage report
Current view: top level - app - CommandSender.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 229 286 80.1 %
Date: 2024-02-15 08:20:41 Functions: 22 27 81.5 %

          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             :  *      This file defines objects for a CHIP IM Invoke Command Sender
      22             :  *
      23             :  */
      24             : 
      25             : #include "CommandSender.h"
      26             : #include "InteractionModelEngine.h"
      27             : #include "StatusResponse.h"
      28             : #include <app/TimedRequest.h>
      29             : #include <platform/LockTracker.h>
      30             : #include <protocols/Protocols.h>
      31             : #include <protocols/interaction_model/Constants.h>
      32             : 
      33             : namespace chip {
      34             : namespace app {
      35             : namespace {
      36             : 
      37             : // Gets the CommandRef if available. Error returned if we expected CommandRef and it wasn't
      38             : // provided in the response.
      39             : template <typename ParserT>
      40          31 : CHIP_ERROR GetRef(ParserT aParser, Optional<uint16_t> & aRef, bool commandRefExpected)
      41             : {
      42          31 :     CHIP_ERROR err = CHIP_NO_ERROR;
      43             :     uint16_t ref;
      44          31 :     err = aParser.GetRef(&ref);
      45             : 
      46          31 :     VerifyOrReturnError(err == CHIP_NO_ERROR || err == CHIP_END_OF_TLV, err);
      47          31 :     if (err == CHIP_END_OF_TLV)
      48             :     {
      49          31 :         if (commandRefExpected)
      50             :         {
      51           0 :             return CHIP_ERROR_INVALID_ARGUMENT;
      52             :         }
      53          31 :         aRef = NullOptional;
      54          31 :         return CHIP_NO_ERROR;
      55             :     }
      56             : 
      57           0 :     aRef = MakeOptional(ref);
      58           0 :     return CHIP_NO_ERROR;
      59             : }
      60             : 
      61             : } // namespace
      62             : 
      63          35 : CommandSender::CommandSender(Callback * apCallback, Messaging::ExchangeManager * apExchangeMgr, bool aIsTimedRequest,
      64          35 :                              bool aSuppressResponse) :
      65          35 :     mExchangeCtx(*this),
      66          35 :     mCallbackHandle(apCallback), mpExchangeMgr(apExchangeMgr), mSuppressResponse(aSuppressResponse), mTimedRequest(aIsTimedRequest)
      67             : {
      68          35 :     assertChipStackLockedByCurrentThread();
      69          35 : }
      70             : 
      71           2 : CommandSender::CommandSender(ExtendableCallback * apExtendableCallback, Messaging::ExchangeManager * apExchangeMgr,
      72           2 :                              bool aIsTimedRequest, bool aSuppressResponse) :
      73           2 :     mExchangeCtx(*this),
      74           2 :     mCallbackHandle(apExtendableCallback), mpExchangeMgr(apExchangeMgr), mSuppressResponse(aSuppressResponse),
      75           4 :     mTimedRequest(aIsTimedRequest), mUseExtendableCallback(true)
      76             : {
      77           2 :     assertChipStackLockedByCurrentThread();
      78           2 : }
      79             : 
      80          37 : CommandSender::~CommandSender()
      81             : {
      82          37 :     assertChipStackLockedByCurrentThread();
      83          37 : }
      84             : 
      85          37 : CHIP_ERROR CommandSender::AllocateBuffer()
      86             : {
      87          37 :     if (!mBufferAllocated)
      88             :     {
      89          35 :         mCommandMessageWriter.Reset();
      90             : 
      91          35 :         System::PacketBufferHandle commandPacket = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
      92          35 :         VerifyOrReturnError(!commandPacket.IsNull(), CHIP_ERROR_NO_MEMORY);
      93             : 
      94          35 :         mCommandMessageWriter.Init(std::move(commandPacket));
      95          35 :         ReturnErrorOnFailure(mInvokeRequestBuilder.InitWithEndBufferReserved(&mCommandMessageWriter));
      96             : 
      97          35 :         mInvokeRequestBuilder.SuppressResponse(mSuppressResponse).TimedRequest(mTimedRequest);
      98          35 :         ReturnErrorOnFailure(mInvokeRequestBuilder.GetError());
      99             : 
     100          35 :         mInvokeRequestBuilder.CreateInvokeRequests(/* aReserveEndBuffer = */ true);
     101          35 :         ReturnErrorOnFailure(mInvokeRequestBuilder.GetError());
     102             : 
     103          35 :         mBufferAllocated = true;
     104          35 :     }
     105             : 
     106          37 :     return CHIP_NO_ERROR;
     107             : }
     108             : 
     109          31 : CHIP_ERROR CommandSender::SendCommandRequestInternal(const SessionHandle & session, Optional<System::Clock::Timeout> timeout)
     110             : {
     111          31 :     VerifyOrReturnError(mState == State::AddedCommand, CHIP_ERROR_INCORRECT_STATE);
     112             : 
     113          30 :     ReturnErrorOnFailure(Finalize(mPendingInvokeData));
     114             : 
     115             :     // Create a new exchange context.
     116          30 :     auto exchange = mpExchangeMgr->NewContext(session, this);
     117          30 :     VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_NO_MEMORY);
     118             : 
     119          30 :     mExchangeCtx.Grab(exchange);
     120          30 :     VerifyOrReturnError(!mExchangeCtx->IsGroupExchangeContext(), CHIP_ERROR_INVALID_MESSAGE_TYPE);
     121             : 
     122          30 :     mExchangeCtx->SetResponseTimeout(timeout.ValueOr(session->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime)));
     123             : 
     124          30 :     if (mTimedInvokeTimeoutMs.HasValue())
     125             :     {
     126           0 :         ReturnErrorOnFailure(TimedRequest::Send(mExchangeCtx.Get(), mTimedInvokeTimeoutMs.Value()));
     127           0 :         MoveToState(State::AwaitingTimedStatus);
     128           0 :         return CHIP_NO_ERROR;
     129             :     }
     130             : 
     131          30 :     return SendInvokeRequest();
     132             : }
     133             : 
     134             : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
     135           0 : CHIP_ERROR CommandSender::TestOnlyCommandSenderTimedRequestFlagWithNoTimedInvoke(const SessionHandle & session,
     136             :                                                                                  Optional<System::Clock::Timeout> timeout)
     137             : {
     138           0 :     VerifyOrReturnError(mTimedRequest, CHIP_ERROR_INCORRECT_STATE);
     139           0 :     return SendCommandRequestInternal(session, timeout);
     140             : }
     141             : #endif
     142             : 
     143          31 : CHIP_ERROR CommandSender::SendCommandRequest(const SessionHandle & session, Optional<System::Clock::Timeout> timeout)
     144             : {
     145             : 
     146          31 :     if (mTimedRequest != mTimedInvokeTimeoutMs.HasValue())
     147             :     {
     148           0 :         ChipLogError(
     149             :             DataManagement,
     150             :             "Inconsistent timed request state in CommandSender: mTimedRequest (%d) != mTimedInvokeTimeoutMs.HasValue() (%d)",
     151             :             mTimedRequest, mTimedInvokeTimeoutMs.HasValue());
     152           0 :         return CHIP_ERROR_INCORRECT_STATE;
     153             :     }
     154          31 :     return SendCommandRequestInternal(session, timeout);
     155             : }
     156             : 
     157           0 : CHIP_ERROR CommandSender::SendGroupCommandRequest(const SessionHandle & session)
     158             : {
     159           0 :     VerifyOrReturnError(mState == State::AddedCommand, CHIP_ERROR_INCORRECT_STATE);
     160             : 
     161           0 :     ReturnErrorOnFailure(Finalize(mPendingInvokeData));
     162             : 
     163             :     // Create a new exchange context.
     164           0 :     auto exchange = mpExchangeMgr->NewContext(session, this);
     165           0 :     VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_NO_MEMORY);
     166             : 
     167           0 :     mExchangeCtx.Grab(exchange);
     168           0 :     VerifyOrReturnError(mExchangeCtx->IsGroupExchangeContext(), CHIP_ERROR_INVALID_MESSAGE_TYPE);
     169             : 
     170           0 :     ReturnErrorOnFailure(SendInvokeRequest());
     171             : 
     172           0 :     Close();
     173           0 :     return CHIP_NO_ERROR;
     174             : }
     175             : 
     176          30 : CHIP_ERROR CommandSender::SendInvokeRequest()
     177             : {
     178             :     using namespace Protocols::InteractionModel;
     179             :     using namespace Messaging;
     180             : 
     181          30 :     ReturnErrorOnFailure(
     182             :         mExchangeCtx->SendMessage(MsgType::InvokeCommandRequest, std::move(mPendingInvokeData), SendMessageFlags::kExpectResponse));
     183          30 :     MoveToState(State::AwaitingResponse);
     184             : 
     185          30 :     return CHIP_NO_ERROR;
     186             : }
     187             : 
     188          28 : CHIP_ERROR CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
     189             :                                             System::PacketBufferHandle && aPayload)
     190             : {
     191             :     using namespace Protocols::InteractionModel;
     192             : 
     193          28 :     if (mState == State::AwaitingResponse)
     194             :     {
     195          28 :         MoveToState(State::ResponseReceived);
     196             :     }
     197             : 
     198          28 :     CHIP_ERROR err           = CHIP_NO_ERROR;
     199          28 :     bool sendStatusResponse  = false;
     200          28 :     bool moreChunkedMessages = false;
     201          28 :     VerifyOrExit(apExchangeContext == mExchangeCtx.Get(), err = CHIP_ERROR_INCORRECT_STATE);
     202          28 :     sendStatusResponse = true;
     203             : 
     204          28 :     if (mState == State::AwaitingTimedStatus)
     205             :     {
     206           0 :         if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse))
     207             :         {
     208           0 :             CHIP_ERROR statusError = CHIP_NO_ERROR;
     209           0 :             SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError));
     210           0 :             sendStatusResponse = false;
     211           0 :             SuccessOrExit(err = statusError);
     212           0 :             err = SendInvokeRequest();
     213             :         }
     214             :         else
     215             :         {
     216           0 :             err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
     217             :         }
     218             :         // Skip all other processing here (which is for the response to the
     219             :         // invoke request), no matter whether err is success or not.
     220           0 :         goto exit;
     221             :     }
     222             : 
     223          28 :     if (aPayloadHeader.HasMessageType(MsgType::InvokeCommandResponse))
     224             :     {
     225          23 :         err = ProcessInvokeResponse(std::move(aPayload), moreChunkedMessages);
     226          23 :         SuccessOrExit(err);
     227          22 :         mInvokeResponseMessageCount++;
     228          22 :         if (moreChunkedMessages)
     229             :         {
     230           0 :             StatusResponse::Send(Status::Success, apExchangeContext, /*aExpectResponse = */ true);
     231           0 :             MoveToState(State::AwaitingResponse);
     232           0 :             return CHIP_NO_ERROR;
     233             :         }
     234          22 :         sendStatusResponse = false;
     235             :     }
     236           5 :     else if (aPayloadHeader.HasMessageType(MsgType::StatusResponse))
     237             :     {
     238           4 :         CHIP_ERROR statusError = CHIP_NO_ERROR;
     239           7 :         SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError));
     240           3 :         SuccessOrExit(err = statusError);
     241           0 :         err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
     242             :     }
     243             :     else
     244             :     {
     245           1 :         err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
     246             :     }
     247             : 
     248          28 : exit:
     249          28 :     if (err != CHIP_NO_ERROR)
     250             :     {
     251           6 :         OnErrorCallback(err);
     252             :     }
     253             : 
     254          28 :     if (sendStatusResponse)
     255             :     {
     256           6 :         StatusResponse::Send(Status::InvalidAction, apExchangeContext, /*aExpectResponse = */ false);
     257             :     }
     258             : 
     259          28 :     if (mState != State::AwaitingResponse)
     260             :     {
     261          28 :         Close();
     262             :     }
     263             :     // Else we got a response to a Timed Request and just sent the invoke.
     264             : 
     265          28 :     return err;
     266             : }
     267             : 
     268          25 : CHIP_ERROR CommandSender::ProcessInvokeResponse(System::PacketBufferHandle && payload, bool & moreChunkedMessages)
     269             : {
     270          25 :     CHIP_ERROR err = CHIP_NO_ERROR;
     271          25 :     System::PacketBufferTLVReader reader;
     272             :     TLV::TLVReader invokeResponsesReader;
     273          25 :     InvokeResponseMessage::Parser invokeResponseMessage;
     274          25 :     InvokeResponseIBs::Parser invokeResponses;
     275          25 :     bool suppressResponse = false;
     276             : 
     277          25 :     reader.Init(std::move(payload));
     278          25 :     ReturnErrorOnFailure(invokeResponseMessage.Init(reader));
     279             : 
     280             : #if CHIP_CONFIG_IM_PRETTY_PRINT
     281          24 :     invokeResponseMessage.PrettyPrint();
     282             : #endif
     283             : 
     284          24 :     ReturnErrorOnFailure(invokeResponseMessage.GetSuppressResponse(&suppressResponse));
     285          24 :     ReturnErrorOnFailure(invokeResponseMessage.GetInvokeResponses(&invokeResponses));
     286          24 :     invokeResponses.GetReader(&invokeResponsesReader);
     287             : 
     288          55 :     while (CHIP_NO_ERROR == (err = invokeResponsesReader.Next()))
     289             :     {
     290          31 :         VerifyOrReturnError(TLV::AnonymousTag() == invokeResponsesReader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG);
     291          31 :         InvokeResponseIB::Parser invokeResponse;
     292          31 :         ReturnErrorOnFailure(invokeResponse.Init(invokeResponsesReader));
     293          31 :         ReturnErrorOnFailure(ProcessInvokeResponseIB(invokeResponse));
     294             :     }
     295             : 
     296          24 :     err = invokeResponseMessage.GetMoreChunkedMessages(&moreChunkedMessages);
     297             :     // If the MoreChunkedMessages element is absent, we receive CHIP_END_OF_TLV. In this
     298             :     // case, per the specification, a default value of false is used.
     299          24 :     if (CHIP_END_OF_TLV == err)
     300             :     {
     301          24 :         moreChunkedMessages = false;
     302          24 :         err                 = CHIP_NO_ERROR;
     303             :     }
     304          24 :     ReturnErrorOnFailure(err);
     305             : 
     306          24 :     if (suppressResponse && moreChunkedMessages)
     307             :     {
     308           0 :         ChipLogError(DataManagement, "Spec violation! InvokeResponse has suppressResponse=true, and moreChunkedMessages=true");
     309             :         // TODO Is there a better error to return here?
     310           0 :         return CHIP_ERROR_INVALID_TLV_ELEMENT;
     311             :     }
     312             : 
     313             :     // if we have exhausted this container
     314          24 :     if (CHIP_END_OF_TLV == err)
     315             :     {
     316           0 :         err = CHIP_NO_ERROR;
     317             :     }
     318          24 :     ReturnErrorOnFailure(err);
     319          24 :     return invokeResponseMessage.ExitContainer();
     320          25 : }
     321             : 
     322           4 : void CommandSender::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
     323             : {
     324           4 :     ChipLogProgress(DataManagement, "Time out! failed to receive invoke command response from Exchange: " ChipLogFormatExchange,
     325             :                     ChipLogValueExchange(apExchangeContext));
     326             : 
     327             :     // TODO(#30453) When timeout occurs for batch commands what should be done? Should all individual
     328             :     // commands have a path specific error of timeout, or do we give or NoCommandResponse.
     329           4 :     OnErrorCallback(CHIP_ERROR_TIMEOUT);
     330             : 
     331           4 :     Close();
     332           4 : }
     333             : 
     334          32 : void CommandSender::Close()
     335             : {
     336          32 :     mSuppressResponse = false;
     337          32 :     mTimedRequest     = false;
     338          32 :     MoveToState(State::AwaitingDestruction);
     339             : 
     340          32 :     OnDoneCallback();
     341          32 : }
     342             : 
     343          31 : CHIP_ERROR CommandSender::ProcessInvokeResponseIB(InvokeResponseIB::Parser & aInvokeResponse)
     344             : {
     345          31 :     CHIP_ERROR err = CHIP_NO_ERROR;
     346             :     ClusterId clusterId;
     347             :     CommandId commandId;
     348             :     EndpointId endpointId;
     349             :     // Default to success when an invoke response is received.
     350          31 :     StatusIB statusIB;
     351             : 
     352             :     {
     353          31 :         bool commandRefExpected = (mFinishedCommandCount > 1);
     354          31 :         bool hasDataResponse    = false;
     355             :         TLV::TLVReader commandDataReader;
     356          31 :         Optional<uint16_t> commandRef;
     357             : 
     358          31 :         CommandStatusIB::Parser commandStatus;
     359          31 :         err = aInvokeResponse.GetStatus(&commandStatus);
     360          31 :         if (CHIP_NO_ERROR == err)
     361             :         {
     362          24 :             CommandPathIB::Parser commandPath;
     363          24 :             ReturnErrorOnFailure(commandStatus.GetPath(&commandPath));
     364          24 :             ReturnErrorOnFailure(commandPath.GetClusterId(&clusterId));
     365          24 :             ReturnErrorOnFailure(commandPath.GetCommandId(&commandId));
     366          24 :             ReturnErrorOnFailure(commandPath.GetEndpointId(&endpointId));
     367             : 
     368          24 :             StatusIB::Parser status;
     369          24 :             commandStatus.GetErrorStatus(&status);
     370          24 :             ReturnErrorOnFailure(status.DecodeStatusIB(statusIB));
     371          24 :             ReturnErrorOnFailure(GetRef(commandStatus, commandRef, commandRefExpected));
     372             :         }
     373           7 :         else if (CHIP_END_OF_TLV == err)
     374             :         {
     375           7 :             CommandDataIB::Parser commandData;
     376           7 :             CommandPathIB::Parser commandPath;
     377           7 :             ReturnErrorOnFailure(aInvokeResponse.GetCommand(&commandData));
     378           7 :             ReturnErrorOnFailure(commandData.GetPath(&commandPath));
     379           7 :             ReturnErrorOnFailure(commandPath.GetEndpointId(&endpointId));
     380           7 :             ReturnErrorOnFailure(commandPath.GetClusterId(&clusterId));
     381           7 :             ReturnErrorOnFailure(commandPath.GetCommandId(&commandId));
     382           7 :             commandData.GetFields(&commandDataReader);
     383           7 :             ReturnErrorOnFailure(GetRef(commandData, commandRef, commandRefExpected));
     384           7 :             err             = CHIP_NO_ERROR;
     385           7 :             hasDataResponse = true;
     386             :         }
     387             : 
     388          31 :         if (err != CHIP_NO_ERROR)
     389             :         {
     390           0 :             ChipLogError(DataManagement, "Received malformed Command Response, err=%" CHIP_ERROR_FORMAT, err.Format());
     391             :         }
     392             :         else
     393             :         {
     394          31 :             if (hasDataResponse)
     395             :             {
     396           7 :                 ChipLogProgress(DataManagement,
     397             :                                 "Received Command Response Data, Endpoint=%u Cluster=" ChipLogFormatMEI
     398             :                                 " Command=" ChipLogFormatMEI,
     399             :                                 endpointId, ChipLogValueMEI(clusterId), ChipLogValueMEI(commandId));
     400             :             }
     401             :             else
     402             :             {
     403          24 :                 ChipLogProgress(DataManagement,
     404             :                                 "Received Command Response Status for Endpoint=%u Cluster=" ChipLogFormatMEI
     405             :                                 " Command=" ChipLogFormatMEI " Status=0x%x",
     406             :                                 endpointId, ChipLogValueMEI(clusterId), ChipLogValueMEI(commandId),
     407             :                                 to_underlying(statusIB.mStatus));
     408             :             }
     409             :         }
     410          31 :         ReturnErrorOnFailure(err);
     411             : 
     412             :         // When using ExtendableCallbacks, we are adhering to a different API contract where path
     413             :         // specific errors are sent to the OnResponse callback. For more information on the history
     414             :         // of this issue please see https://github.com/project-chip/connectedhomeip/issues/30991
     415          31 :         if (statusIB.IsSuccess() || mUseExtendableCallback)
     416             :         {
     417          18 :             const ConcreteCommandPath concretePath = ConcreteCommandPath(endpointId, clusterId, commandId);
     418          18 :             ResponseData responseData              = { concretePath, statusIB };
     419          18 :             responseData.data                      = hasDataResponse ? &commandDataReader : nullptr;
     420          18 :             responseData.commandRef                = commandRef;
     421          18 :             OnResponseCallback(responseData);
     422          18 :         }
     423             :         else
     424             :         {
     425          13 :             OnErrorCallback(statusIB.ToChipError());
     426             :         }
     427          31 :     }
     428          31 :     return CHIP_NO_ERROR;
     429          31 : }
     430             : 
     431           0 : CHIP_ERROR CommandSender::SetCommandSenderConfig(CommandSender::ConfigParameters & aConfigParams)
     432             : {
     433             : #if CHIP_CONFIG_SENDING_BATCH_COMMANDS_ENABLED
     434           0 :     VerifyOrReturnError(mState == State::Idle, CHIP_ERROR_INCORRECT_STATE);
     435           0 :     VerifyOrReturnError(aConfigParams.remoteMaxPathsPerInvoke > 0, CHIP_ERROR_INVALID_ARGUMENT);
     436             : 
     437           0 :     mRemoteMaxPathsPerInvoke = aConfigParams.remoteMaxPathsPerInvoke;
     438           0 :     mBatchCommandsEnabled    = (aConfigParams.remoteMaxPathsPerInvoke > 1);
     439           0 :     return CHIP_NO_ERROR;
     440             : #else
     441             :     VerifyOrReturnError(aConfigParams.remoteMaxPathsPerInvoke == 1, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
     442             :     return CHIP_NO_ERROR;
     443             : #endif
     444             : }
     445             : 
     446          33 : CHIP_ERROR CommandSender::PrepareCommand(const CommandPathParams & aCommandPathParams,
     447             :                                          PrepareCommandParameters & aPrepareCommandParams)
     448             : {
     449          33 :     ReturnErrorOnFailure(AllocateBuffer());
     450             : 
     451             :     //
     452             :     // We must not be in the middle of preparing a command, and must not have already sent InvokeRequestMessage.
     453             :     //
     454          33 :     bool canAddAnotherCommand = (mState == State::AddedCommand && mBatchCommandsEnabled && mUseExtendableCallback);
     455          33 :     VerifyOrReturnError(mState == State::Idle || canAddAnotherCommand, CHIP_ERROR_INCORRECT_STATE);
     456          32 :     VerifyOrReturnError(mFinishedCommandCount < mRemoteMaxPathsPerInvoke, CHIP_ERROR_MAXIMUM_PATHS_PER_INVOKE_EXCEEDED);
     457             : 
     458          32 :     if (mBatchCommandsEnabled)
     459             :     {
     460           3 :         VerifyOrReturnError(aPrepareCommandParams.commandRef.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
     461           3 :         VerifyOrReturnError(aPrepareCommandParams.commandRef.Value() == mFinishedCommandCount, CHIP_ERROR_INVALID_ARGUMENT);
     462             :     }
     463             : 
     464          32 :     InvokeRequests::Builder & invokeRequests = mInvokeRequestBuilder.GetInvokeRequests();
     465          32 :     CommandDataIB::Builder & invokeRequest   = invokeRequests.CreateCommandData();
     466          32 :     ReturnErrorOnFailure(invokeRequests.GetError());
     467          32 :     CommandPathIB::Builder & path = invokeRequest.CreatePath();
     468          32 :     ReturnErrorOnFailure(invokeRequest.GetError());
     469          32 :     ReturnErrorOnFailure(path.Encode(aCommandPathParams));
     470             : 
     471          32 :     if (aPrepareCommandParams.startDataStruct)
     472             :     {
     473          17 :         ReturnErrorOnFailure(invokeRequest.GetWriter()->StartContainer(TLV::ContextTag(CommandDataIB::Tag::kFields),
     474             :                                                                        TLV::kTLVType_Structure, mDataElementContainerType));
     475             :     }
     476             : 
     477          32 :     MoveToState(State::AddingCommand);
     478          32 :     return CHIP_NO_ERROR;
     479             : }
     480             : 
     481          31 : CHIP_ERROR CommandSender::FinishCommand(FinishCommandParameters & aFinishCommandParams)
     482             : {
     483          31 :     if (mBatchCommandsEnabled)
     484             :     {
     485           3 :         VerifyOrReturnError(aFinishCommandParams.commandRef.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
     486           3 :         VerifyOrReturnError(aFinishCommandParams.commandRef.Value() == mFinishedCommandCount, CHIP_ERROR_INVALID_ARGUMENT);
     487             :     }
     488             : 
     489          31 :     return FinishCommandInternal(aFinishCommandParams);
     490             : }
     491             : 
     492          31 : CHIP_ERROR CommandSender::FinishCommandInternal(FinishCommandParameters & aFinishCommandParams)
     493             : {
     494          31 :     CHIP_ERROR err = CHIP_NO_ERROR;
     495             : 
     496          31 :     VerifyOrReturnError(mState == State::AddingCommand, err = CHIP_ERROR_INCORRECT_STATE);
     497             : 
     498          31 :     CommandDataIB::Builder & commandData = mInvokeRequestBuilder.GetInvokeRequests().GetCommandData();
     499             : 
     500          31 :     if (aFinishCommandParams.endDataStruct)
     501             :     {
     502          16 :         ReturnErrorOnFailure(commandData.GetWriter()->EndContainer(mDataElementContainerType));
     503             :     }
     504             : 
     505          31 :     if (aFinishCommandParams.commandRef.HasValue())
     506             :     {
     507           3 :         ReturnErrorOnFailure(commandData.Ref(aFinishCommandParams.commandRef.Value()));
     508             :     }
     509             : 
     510          31 :     ReturnErrorOnFailure(commandData.EndOfCommandDataIB());
     511             : 
     512          31 :     MoveToState(State::AddedCommand);
     513             : 
     514          31 :     mFinishedCommandCount++;
     515             : 
     516          31 :     if (aFinishCommandParams.timedInvokeTimeoutMs.HasValue())
     517             :     {
     518           0 :         SetTimedInvokeTimeoutMs(aFinishCommandParams.timedInvokeTimeoutMs);
     519             :     }
     520             : 
     521          31 :     return CHIP_NO_ERROR;
     522             : }
     523             : 
     524          32 : TLV::TLVWriter * CommandSender::GetCommandDataIBTLVWriter()
     525             : {
     526          32 :     if (mState != State::AddingCommand)
     527             :     {
     528           0 :         return nullptr;
     529             :     }
     530             : 
     531          32 :     return mInvokeRequestBuilder.GetInvokeRequests().GetCommandData().GetWriter();
     532             : }
     533             : 
     534           0 : void CommandSender::SetTimedInvokeTimeoutMs(const Optional<uint16_t> & aTimedInvokeTimeoutMs)
     535             : {
     536           0 :     if (!mTimedInvokeTimeoutMs.HasValue())
     537             :     {
     538           0 :         mTimedInvokeTimeoutMs = aTimedInvokeTimeoutMs;
     539             :     }
     540           0 :     else if (aTimedInvokeTimeoutMs.HasValue())
     541             :     {
     542           0 :         uint16_t newValue = std::min(mTimedInvokeTimeoutMs.Value(), aTimedInvokeTimeoutMs.Value());
     543           0 :         mTimedInvokeTimeoutMs.SetValue(newValue);
     544             :     }
     545           0 : }
     546             : 
     547           3 : size_t CommandSender::GetInvokeResponseMessageCount()
     548             : {
     549           3 :     return static_cast<size_t>(mInvokeResponseMessageCount);
     550             : }
     551             : 
     552          33 : CHIP_ERROR CommandSender::Finalize(System::PacketBufferHandle & commandPacket)
     553             : {
     554          33 :     VerifyOrReturnError(mState == State::AddedCommand, CHIP_ERROR_INCORRECT_STATE);
     555          33 :     ReturnErrorOnFailure(mInvokeRequestBuilder.GetInvokeRequests().EndOfInvokeRequests());
     556          33 :     ReturnErrorOnFailure(mInvokeRequestBuilder.EndOfInvokeRequestMessage());
     557          33 :     return mCommandMessageWriter.Finalize(&commandPacket);
     558             : }
     559             : 
     560         158 : const char * CommandSender::GetStateStr() const
     561             : {
     562             : #if CHIP_DETAIL_LOGGING
     563         158 :     switch (mState)
     564             :     {
     565           0 :     case State::Idle:
     566           0 :         return "Idle";
     567             : 
     568          32 :     case State::AddingCommand:
     569          32 :         return "AddingCommand";
     570             : 
     571          36 :     case State::AddedCommand:
     572          36 :         return "AddedCommand";
     573             : 
     574           0 :     case State::AwaitingTimedStatus:
     575           0 :         return "AwaitingTimedStatus";
     576             : 
     577          30 :     case State::AwaitingResponse:
     578          30 :         return "AwaitingResponse";
     579             : 
     580          28 :     case State::ResponseReceived:
     581          28 :         return "ResponseReceived";
     582             : 
     583          32 :     case State::AwaitingDestruction:
     584          32 :         return "AwaitingDestruction";
     585             :     }
     586             : #endif // CHIP_DETAIL_LOGGING
     587           0 :     return "N/A";
     588             : }
     589             : 
     590         158 : void CommandSender::MoveToState(const State aTargetState)
     591             : {
     592         158 :     mState = aTargetState;
     593         158 :     ChipLogDetail(DataManagement, "ICR moving to [%10.10s]", GetStateStr());
     594         158 : }
     595             : 
     596             : } // namespace app
     597             : } // namespace chip

Generated by: LCOV version 1.14