3 * Copyright (c) 2020-2021 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 Base class for a CHIP IM Command
26 #include "CommandHandler.h"
27 #include "CommandSender.h"
28 #include "InteractionModelEngine.h"
29 #include <core/CHIPTLVDebug.hpp>
34 CHIP_ERROR Command::Init(Messaging::ExchangeManager * apExchangeMgr, InteractionModelDelegate * apDelegate)
36 CHIP_ERROR err = CHIP_NO_ERROR;
37 // Error if already initialized.
38 VerifyOrExit(apExchangeMgr != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
39 VerifyOrExit(mpExchangeMgr == nullptr, err = CHIP_ERROR_INCORRECT_STATE);
40 VerifyOrExit(mpExchangeCtx == nullptr, err = CHIP_ERROR_INCORRECT_STATE);
42 mpExchangeMgr = apExchangeMgr;
43 mpDelegate = apDelegate;
48 ChipLogFunctError(err);
52 CHIP_ERROR Command::Reset()
54 CHIP_ERROR err = CHIP_NO_ERROR;
56 ClearExistingExchangeContext();
58 if (mCommandMessageBuf.IsNull())
60 // TODO: Calculate the packet buffer size
61 mCommandMessageBuf = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
62 VerifyOrExit(!mCommandMessageBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
65 mCommandMessageWriter.Init(std::move(mCommandMessageBuf));
66 err = mInvokeCommandBuilder.Init(&mCommandMessageWriter);
69 mInvokeCommandBuilder.CreateCommandListBuilder();
70 MoveToState(CommandState::Initialized);
75 ChipLogFunctError(err);
80 CHIP_ERROR Command::ProcessCommandMessage(System::PacketBufferHandle && payload, CommandRoleId aCommandRoleId)
82 CHIP_ERROR err = CHIP_NO_ERROR;
83 chip::System::PacketBufferTLVReader reader;
84 chip::TLV::TLVReader commandListReader;
85 InvokeCommand::Parser invokeCommandParser;
86 CommandList::Parser commandListParser;
88 reader.Init(std::move(payload));
92 err = invokeCommandParser.Init(reader);
95 err = invokeCommandParser.CheckSchemaValidity();
98 err = invokeCommandParser.GetCommandList(&commandListParser);
101 commandListParser.GetReader(&commandListReader);
103 while (CHIP_NO_ERROR == (err = commandListReader.Next()))
105 VerifyOrExit(chip::TLV::AnonymousTag == commandListReader.GetTag(), err = CHIP_ERROR_INVALID_TLV_TAG);
106 VerifyOrExit(chip::TLV::kTLVType_Structure == commandListReader.GetType(), err = CHIP_ERROR_WRONG_TLV_TYPE);
108 CommandDataElement::Parser commandElement;
110 err = commandElement.Init(commandListReader);
113 err = commandElement.CheckSchemaValidity();
116 err = ProcessCommandDataElement(commandElement);
120 // if we have exhausted this container
121 if (CHIP_END_OF_TLV == err)
130 void Command::Shutdown()
132 VerifyOrExit(mState != CommandState::Uninitialized, );
133 mCommandMessageWriter.Reset();
134 mCommandMessageBuf = nullptr;
136 ClearExistingExchangeContext();
138 mpExchangeMgr = nullptr;
139 mpDelegate = nullptr;
140 MoveToState(CommandState::Uninitialized);
147 chip::TLV::TLVWriter & Command::CreateCommandDataElementTLVWriter()
149 mCommandDataBuf = chip::System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
150 if (mCommandDataBuf.IsNull())
152 ChipLogDetail(DataManagement, "Unable to allocate packet buffer");
155 mCommandDataWriter.Init(mCommandDataBuf.Retain());
157 return mCommandDataWriter;
160 CHIP_ERROR Command::AddCommand(chip::EndpointId aEndpointId, chip::GroupId aGroupId, chip::ClusterId aClusterId,
161 chip::CommandId aCommandId, BitFlags<CommandPathFlags> aFlags)
163 CommandParams commandParams(aEndpointId, aGroupId, aClusterId, aCommandId, aFlags);
165 return AddCommand(commandParams);
168 CHIP_ERROR Command::AddCommand(CommandParams & aCommandParams)
170 CHIP_ERROR err = CHIP_NO_ERROR;
171 const uint8_t * commandData = nullptr;
172 uint32_t commandLen = 0;
174 if (!mCommandDataBuf.IsNull())
176 commandData = mCommandDataBuf->Start();
177 commandLen = mCommandDataBuf->DataLength();
180 if (commandLen > 0 && commandData != nullptr)
182 // Command argument list can be empty.
183 VerifyOrExit(commandLen >= 2, err = CHIP_ERROR_INVALID_ARGUMENT);
184 VerifyOrExit(commandData[0] == chip::TLV::kTLVType_Structure, err = CHIP_ERROR_INVALID_ARGUMENT);
191 CommandDataElement::Builder commandDataElement =
192 mInvokeCommandBuilder.GetCommandListBuilder().CreateCommandDataElementBuilder();
193 CommandPath::Builder commandPath = commandDataElement.CreateCommandPathBuilder();
194 if (aCommandParams.Flags.Has(CommandPathFlags::kEndpointIdValid))
196 commandPath.EndpointId(aCommandParams.EndpointId);
199 if (aCommandParams.Flags.Has(CommandPathFlags::kGroupIdValid))
201 commandPath.GroupId(aCommandParams.GroupId);
204 commandPath.ClusterId(aCommandParams.ClusterId).CommandId(aCommandParams.CommandId).EndOfCommandPath();
206 err = commandPath.GetError();
209 if (commandLen > 0 && commandData != nullptr)
211 // Copy the application data into a new TLV structure field contained with the
212 // command structure. NOTE: The TLV writer will take care of moving the app data
213 // to the correct location within the buffer.
214 err = mInvokeCommandBuilder.GetWriter()->PutPreEncodedContainer(chip::TLV::ContextTag(CommandDataElement::kCsTag_Data),
215 chip::TLV::kTLVType_Structure, commandData, commandLen);
218 commandDataElement.EndOfCommandDataElement();
220 err = commandDataElement.GetError();
223 MoveToState(CommandState::AddCommand);
226 mCommandDataBuf = nullptr;
227 ChipLogFunctError(err);
231 CHIP_ERROR Command::AddStatusCode(const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, Protocols::Id aProtocolId,
232 const uint16_t aProtocolCode)
234 CHIP_ERROR err = CHIP_NO_ERROR;
235 StatusElement::Builder statusElementBuilder;
237 err = statusElementBuilder.Init(mInvokeCommandBuilder.GetWriter());
240 statusElementBuilder.EncodeStatusElement(aGeneralCode, aProtocolId.ToFullyQualifiedSpecForm(), aProtocolCode)
241 .EndOfStatusElement();
242 err = statusElementBuilder.GetError();
244 MoveToState(CommandState::AddCommand);
247 ChipLogFunctError(err);
251 CHIP_ERROR Command::ClearExistingExchangeContext()
253 // Discard any existing exchange context. Effectively we can only have one Echo exchange with
254 // a single node at any one time.
255 if (mpExchangeCtx != nullptr)
257 mpExchangeCtx->Abort();
258 mpExchangeCtx = nullptr;
261 return CHIP_NO_ERROR;
264 CHIP_ERROR Command::FinalizeCommandsMessage()
266 CHIP_ERROR err = CHIP_NO_ERROR;
268 mInvokeCommandBuilder.EndOfInvokeCommand();
269 err = mInvokeCommandBuilder.GetError();
272 err = mCommandMessageWriter.Finalize(&mCommandMessageBuf);
275 VerifyOrExit(mCommandMessageBuf->EnsureReservedSize(System::PacketBuffer::kDefaultHeaderReserve),
276 err = CHIP_ERROR_BUFFER_TOO_SMALL);
279 ChipLogFunctError(err);
283 const char * Command::GetStateStr() const
285 #if CHIP_DETAIL_LOGGING
288 case CommandState::Uninitialized:
289 return "Uninitialized";
291 case CommandState::Initialized:
292 return "Initialized";
294 case CommandState::AddCommand:
297 case CommandState::Sending:
300 #endif // CHIP_DETAIL_LOGGING
304 void Command::MoveToState(const CommandState aTargetState)
306 mState = aTargetState;
307 ChipLogDetail(DataManagement, "ICR moving to [%10.10s]", GetStateStr());
310 void Command::ClearState(void)
312 MoveToState(CommandState::Uninitialized);