3 * Copyright (c) 2020 Project CHIP Authors
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * This file defines objects for a CHIP IM Invoke Command Sender
25 #include "CommandSender.h"
27 #include "CommandHandler.h"
28 #include "InteractionModelEngine.h"
30 #include <protocols/secure_channel/Constants.h>
32 using GeneralStatusCode = chip::Protocols::SecureChannel::GeneralStatusCode;
37 CHIP_ERROR CommandSender::SendCommandRequest(NodeId aNodeId, Transport::AdminId aAdminId)
39 CHIP_ERROR err = CHIP_NO_ERROR;
41 err = FinalizeCommandsMessage();
44 ClearExistingExchangeContext();
46 // Create a new exchange context.
47 // TODO: temprary create a SecureSessionHandle from node id, will be fix in PR 3602
48 // TODO: Hard code keyID to 0 to unblock IM end-to-end test. Complete solution is tracked in issue:4451
49 mpExchangeCtx = mpExchangeMgr->NewContext({ aNodeId, 0, aAdminId }, this);
50 VerifyOrExit(mpExchangeCtx != nullptr, err = CHIP_ERROR_NO_MEMORY);
51 mpExchangeCtx->SetResponseTimeout(kImMessageTimeoutMsec);
53 err = mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::InvokeCommandRequest, std::move(mCommandMessageBuf),
54 Messaging::SendFlags(Messaging::SendMessageFlags::kExpectResponse));
56 MoveToState(CommandState::Sending);
59 if (err != CHIP_NO_ERROR)
61 ClearExistingExchangeContext();
63 ChipLogFunctError(err);
68 void CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader,
69 const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload)
71 CHIP_ERROR err = CHIP_NO_ERROR;
72 // Assert that the exchange context matches the client's current context.
73 // This should never fail because even if SendCommandRequest is called
74 // back-to-back, the second call will call Close() on the first exchange,
75 // which clears the OnMessageReceived callback.
77 VerifyOrDie(apExchangeContext == mpExchangeCtx);
79 // Verify that the message is an Invoke Command Response.
80 // If not, close the exchange and free the payload.
81 if (!aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::InvokeCommandResponse))
83 apExchangeContext->Close();
84 mpExchangeCtx = nullptr;
88 // Remove the EC from the app state now. OnMessageReceived can call
89 // SendCommandRequest and install a new one. We abort rather than close
90 // because we no longer care whether the echo request message has been
91 // acknowledged at the transport layer.
92 ClearExistingExchangeContext();
94 err = ProcessCommandMessage(std::move(aPayload), CommandRoleId::SenderId);
101 void CommandSender::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
103 ChipLogProgress(DataManagement, "Time out! failed to receive invoke command response from Exchange: %d",
104 apExchangeContext->GetExchangeId());
107 if (mpDelegate != nullptr)
109 mpDelegate->CommandResponseTimeout(this);
113 CHIP_ERROR CommandSender::ProcessCommandDataElement(CommandDataElement::Parser & aCommandElement)
115 CHIP_ERROR err = CHIP_NO_ERROR;
116 CommandPath::Parser commandPath;
117 chip::TLV::TLVReader commandDataReader;
118 chip::ClusterId clusterId;
119 chip::CommandId commandId;
120 chip::EndpointId endpointId;
121 Protocols::SecureChannel::GeneralStatusCode generalCode = Protocols::SecureChannel::GeneralStatusCode::kSuccess;
122 uint32_t protocolId = 0;
123 uint16_t protocolCode = 0;
124 StatusElement::Parser statusElementParser;
128 err = aCommandElement.GetCommandPath(&commandPath);
131 err = commandPath.GetClusterId(&clusterId);
134 err = commandPath.GetCommandId(&commandId);
137 err = commandPath.GetEndpointId(&endpointId);
140 err = aCommandElement.GetStatusElement(&statusElementParser);
141 if (CHIP_NO_ERROR == err)
143 // Response has status element since either there is error in command response or it is empty response
144 err = statusElementParser.CheckSchemaValidity();
147 err = statusElementParser.DecodeStatusElement(&generalCode, &protocolId, &protocolCode);
149 if (mpDelegate != nullptr)
151 mpDelegate->CommandResponseStatus(this, generalCode, protocolId, protocolCode, endpointId, clusterId, commandId,
155 else if (CHIP_END_OF_TLV == err)
157 err = aCommandElement.GetData(&commandDataReader);
159 // TODO(#4503): Should call callbacks of cluster that sends the command.
160 DispatchSingleClusterCommand(clusterId, commandId, endpointId, commandDataReader, this);
164 ChipLogFunctError(err);
165 if (err != CHIP_NO_ERROR && mpDelegate != nullptr)
167 mpDelegate->CommandResponseProtocolError(this, mCommandIndex);