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)
36 CHIP_ERROR err = CHIP_NO_ERROR;
37 // Error if already initialized.
38 VerifyOrExit(mpExchangeMgr == nullptr, err = CHIP_ERROR_INCORRECT_STATE);
39 VerifyOrExit(mpExchangeCtx == nullptr, err = CHIP_ERROR_INCORRECT_STATE);
41 mpExchangeMgr = apExchangeMgr;
47 ChipLogFunctError(err);
51 CHIP_ERROR Command::Reset()
53 CHIP_ERROR err = CHIP_NO_ERROR;
55 ClearExistingExchangeContext();
57 if (mCommandMessageBuf.IsNull())
59 // TODO: Calculate the packet buffer size
60 mCommandMessageBuf = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLength);
61 VerifyOrExit(!mCommandMessageBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
64 mCommandMessageWriter.Init(std::move(mCommandMessageBuf));
65 err = mInvokeCommandBuilder.Init(&mCommandMessageWriter);
68 mInvokeCommandBuilder.CreateCommandListBuilder();
69 MoveToState(CommandState::Initialized);
72 ChipLogFunctError(err);
77 CHIP_ERROR Command::ProcessCommandMessage(System::PacketBufferHandle && payload, CommandRoleId aCommandRoleId)
79 CHIP_ERROR err = CHIP_NO_ERROR;
80 chip::System::PacketBufferTLVReader reader;
81 chip::TLV::TLVReader commandListReader;
82 InvokeCommand::Parser invokeCommandParser;
83 CommandList::Parser commandListParser;
85 reader.Init(std::move(payload));
89 err = invokeCommandParser.Init(reader);
92 err = invokeCommandParser.CheckSchemaValidity();
95 err = invokeCommandParser.GetCommandList(&commandListParser);
98 commandListParser.GetReader(&commandListReader);
100 while (CHIP_NO_ERROR == (err = commandListReader.Next()))
102 VerifyOrExit(chip::TLV::AnonymousTag == commandListReader.GetTag(), err = CHIP_ERROR_INVALID_TLV_TAG);
103 VerifyOrExit(chip::TLV::kTLVType_Structure == commandListReader.GetType(), err = CHIP_ERROR_WRONG_TLV_TYPE);
105 CommandDataElement::Parser commandElement;
107 err = commandElement.Init(commandListReader);
110 err = commandElement.CheckSchemaValidity();
113 err = ProcessCommandDataElement(commandElement);
117 // if we have exhausted this container
118 if (CHIP_END_OF_TLV == err)
127 void Command::Shutdown()
129 VerifyOrExit(mState != CommandState::Uninitialized, );
130 mCommandMessageWriter.Reset();
131 mCommandMessageBuf = nullptr;
133 ClearExistingExchangeContext();
135 mpExchangeMgr = nullptr;
136 MoveToState(CommandState::Uninitialized);
142 chip::TLV::TLVWriter & Command::CreateCommandDataElementTLVWriter()
144 mCommandDataBuf = chip::System::PacketBufferHandle::New(chip::app::kMaxSecureSduLength);
145 if (mCommandDataBuf.IsNull())
147 ChipLogDetail(DataManagement, "Unable to allocate packet buffer");
150 mCommandDataWriter.Init(mCommandDataBuf.Retain());
152 return mCommandDataWriter;
155 CHIP_ERROR Command::AddCommand(chip::EndpointId aEndpointId, chip::GroupId aGroupId, chip::ClusterId aClusterId,
156 chip::CommandId aCommandId, BitFlags<CommandPathFlags> aFlags)
158 CommandParams commandParams(aEndpointId, aGroupId, aClusterId, aCommandId, aFlags);
160 return AddCommand(commandParams);
163 CHIP_ERROR Command::AddCommand(CommandParams & aCommandParams)
165 CHIP_ERROR err = CHIP_NO_ERROR;
166 const uint8_t * apCommandData;
167 uint32_t apCommandLen;
169 apCommandData = mCommandDataBuf->Start();
170 apCommandLen = mCommandDataBuf->DataLength();
172 if (apCommandLen > 0)
174 // Command argument list can be empty.
175 VerifyOrExit(apCommandLen >= 2, err = CHIP_ERROR_INVALID_ARGUMENT);
176 VerifyOrExit(apCommandData[0] == chip::TLV::kTLVType_Structure, err = CHIP_ERROR_INVALID_ARGUMENT);
183 CommandDataElement::Builder commandDataElement =
184 mInvokeCommandBuilder.GetCommandListBuilder().CreateCommandDataElementBuilder();
185 CommandPath::Builder commandPath = commandDataElement.CreateCommandPathBuilder();
186 if (aCommandParams.Flags.Has(CommandPathFlags::kEndpointIdValid))
188 commandPath.EndpointId(aCommandParams.EndpointId);
191 if (aCommandParams.Flags.Has(CommandPathFlags::kGroupIdValid))
193 commandPath.GroupId(aCommandParams.GroupId);
196 commandPath.ClusterId(aCommandParams.ClusterId).CommandId(aCommandParams.CommandId).EndOfCommandPath();
198 err = commandPath.GetError();
201 if (apCommandLen > 0)
203 // Copy the application data into a new TLV structure field contained with the
204 // command structure. NOTE: The TLV writer will take care of moving the app data
205 // to the correct location within the buffer.
206 err = mInvokeCommandBuilder.GetWriter()->PutPreEncodedContainer(
207 chip::TLV::ContextTag(CommandDataElement::kCsTag_Data), chip::TLV::kTLVType_Structure, apCommandData, apCommandLen);
210 commandDataElement.EndOfCommandDataElement();
212 err = commandDataElement.GetError();
215 MoveToState(CommandState::AddCommand);
218 mCommandDataBuf = nullptr;
219 ChipLogFunctError(err);
223 CHIP_ERROR Command::AddStatusCode(const uint16_t aGeneralCode, const uint32_t aProtocolId, const uint16_t aProtocolCode,
224 const chip::ClusterId aClusterId)
226 CHIP_ERROR err = CHIP_NO_ERROR;
227 StatusElement::Builder statusElementBuilder;
229 err = statusElementBuilder.Init(mInvokeCommandBuilder.GetWriter());
232 statusElementBuilder.EncodeStatusElement(aGeneralCode, aProtocolId, aProtocolCode, aProtocolCode).EndOfStatusElement();
233 err = statusElementBuilder.GetError();
235 MoveToState(CommandState::AddCommand);
238 ChipLogFunctError(err);
242 CHIP_ERROR Command::ClearExistingExchangeContext()
244 // Discard any existing exchange context. Effectively we can only have one Echo exchange with
245 // a single node at any one time.
246 if (mpExchangeCtx != nullptr)
248 mpExchangeCtx->Abort();
249 mpExchangeCtx = nullptr;
252 return CHIP_NO_ERROR;
255 CHIP_ERROR Command::FinalizeCommandsMessage()
257 CHIP_ERROR err = CHIP_NO_ERROR;
259 mInvokeCommandBuilder.EndOfInvokeCommand();
260 err = mInvokeCommandBuilder.GetError();
263 err = mCommandMessageWriter.Finalize(&mCommandMessageBuf);
266 VerifyOrExit(mCommandMessageBuf->EnsureReservedSize(System::PacketBuffer::kDefaultHeaderReserve),
267 err = CHIP_ERROR_BUFFER_TOO_SMALL);
270 ChipLogFunctError(err);
274 const char * Command::GetStateStr() const
276 #if CHIP_DETAIL_LOGGING
279 case CommandState::Uninitialized:
280 return "Uninitialized";
282 case CommandState::Initialized:
283 return "Initialized";
285 case CommandState::AddCommand:
288 case CommandState::Sending:
291 #endif // CHIP_DETAIL_LOGGING
295 void Command::MoveToState(const CommandState aTargetState)
297 mState = aTargetState;
298 ChipLogDetail(DataManagement, "ICR moving to [%10.10s]", GetStateStr());
301 void Command::ClearState(void)
303 MoveToState(CommandState::Uninitialized);