Matter SDK Coverage Report
Current view: top level - app - CommandSender.cpp (source / functions) Coverage Total Hit
Test: SHA:1560a87972ec2c7a76cec101927a563a6862bc2a Lines: 85.4 % 355 303
Test Date: 2025-03-30 07:08:27 Functions: 87.5 % 32 28

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

Generated by: LCOV version 2.0-1