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

Generated by: LCOV version 2.0-1