Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / app / Command.cpp
1 /*
2  *
3  *    Copyright (c) 2020-2021 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 /**
20  *    @file
21  *      This file defines Base class for a CHIP IM Command
22  *
23  */
24
25 #include "Command.h"
26 #include "CommandHandler.h"
27 #include "CommandSender.h"
28 #include "InteractionModelEngine.h"
29 #include <core/CHIPTLVDebug.hpp>
30
31 namespace chip {
32 namespace app {
33
34 CHIP_ERROR Command::Init(Messaging::ExchangeManager * apExchangeMgr, InteractionModelDelegate * apDelegate)
35 {
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);
41
42     mpExchangeMgr = apExchangeMgr;
43     mpDelegate    = apDelegate;
44     err           = Reset();
45     SuccessOrExit(err);
46
47 exit:
48     ChipLogFunctError(err);
49     return err;
50 }
51
52 CHIP_ERROR Command::Reset()
53 {
54     CHIP_ERROR err = CHIP_NO_ERROR;
55
56     ClearExistingExchangeContext();
57
58     if (mCommandMessageBuf.IsNull())
59     {
60         // TODO: Calculate the packet buffer size
61         mCommandMessageBuf = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
62         VerifyOrExit(!mCommandMessageBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
63     }
64
65     mCommandMessageWriter.Init(std::move(mCommandMessageBuf));
66     err = mInvokeCommandBuilder.Init(&mCommandMessageWriter);
67     SuccessOrExit(err);
68
69     mInvokeCommandBuilder.CreateCommandListBuilder();
70     MoveToState(CommandState::Initialized);
71
72     mCommandIndex = 0;
73
74 exit:
75     ChipLogFunctError(err);
76
77     return err;
78 }
79
80 CHIP_ERROR Command::ProcessCommandMessage(System::PacketBufferHandle && payload, CommandRoleId aCommandRoleId)
81 {
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;
87
88     reader.Init(std::move(payload));
89     err = reader.Next();
90     SuccessOrExit(err);
91
92     err = invokeCommandParser.Init(reader);
93     SuccessOrExit(err);
94
95     err = invokeCommandParser.CheckSchemaValidity();
96     SuccessOrExit(err);
97
98     err = invokeCommandParser.GetCommandList(&commandListParser);
99     SuccessOrExit(err);
100
101     commandListParser.GetReader(&commandListReader);
102
103     while (CHIP_NO_ERROR == (err = commandListReader.Next()))
104     {
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);
107
108         CommandDataElement::Parser commandElement;
109
110         err = commandElement.Init(commandListReader);
111         SuccessOrExit(err);
112
113         err = commandElement.CheckSchemaValidity();
114         SuccessOrExit(err);
115
116         err = ProcessCommandDataElement(commandElement);
117         SuccessOrExit(err);
118     }
119
120     // if we have exhausted this container
121     if (CHIP_END_OF_TLV == err)
122     {
123         err = CHIP_NO_ERROR;
124     }
125
126 exit:
127     return err;
128 }
129
130 void Command::Shutdown()
131 {
132     VerifyOrExit(mState != CommandState::Uninitialized, );
133     mCommandMessageWriter.Reset();
134     mCommandMessageBuf = nullptr;
135
136     ClearExistingExchangeContext();
137
138     mpExchangeMgr = nullptr;
139     mpDelegate    = nullptr;
140     MoveToState(CommandState::Uninitialized);
141
142     mCommandIndex = 0;
143 exit:
144     return;
145 }
146
147 chip::TLV::TLVWriter & Command::CreateCommandDataElementTLVWriter()
148 {
149     mCommandDataBuf = chip::System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
150     if (mCommandDataBuf.IsNull())
151     {
152         ChipLogDetail(DataManagement, "Unable to allocate packet buffer");
153     }
154
155     mCommandDataWriter.Init(mCommandDataBuf.Retain());
156
157     return mCommandDataWriter;
158 }
159
160 CHIP_ERROR Command::AddCommand(chip::EndpointId aEndpointId, chip::GroupId aGroupId, chip::ClusterId aClusterId,
161                                chip::CommandId aCommandId, BitFlags<CommandPathFlags> aFlags)
162 {
163     CommandParams commandParams(aEndpointId, aGroupId, aClusterId, aCommandId, aFlags);
164
165     return AddCommand(commandParams);
166 }
167
168 CHIP_ERROR Command::AddCommand(CommandParams & aCommandParams)
169 {
170     CHIP_ERROR err              = CHIP_NO_ERROR;
171     const uint8_t * commandData = nullptr;
172     uint32_t commandLen         = 0;
173
174     if (!mCommandDataBuf.IsNull())
175     {
176         commandData = mCommandDataBuf->Start();
177         commandLen  = mCommandDataBuf->DataLength();
178     }
179
180     if (commandLen > 0 && commandData != nullptr)
181     {
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);
185
186         commandData += 1;
187         commandLen -= 1;
188     }
189
190     {
191         CommandDataElement::Builder commandDataElement =
192             mInvokeCommandBuilder.GetCommandListBuilder().CreateCommandDataElementBuilder();
193         CommandPath::Builder commandPath = commandDataElement.CreateCommandPathBuilder();
194         if (aCommandParams.Flags.Has(CommandPathFlags::kEndpointIdValid))
195         {
196             commandPath.EndpointId(aCommandParams.EndpointId);
197         }
198
199         if (aCommandParams.Flags.Has(CommandPathFlags::kGroupIdValid))
200         {
201             commandPath.GroupId(aCommandParams.GroupId);
202         }
203
204         commandPath.ClusterId(aCommandParams.ClusterId).CommandId(aCommandParams.CommandId).EndOfCommandPath();
205
206         err = commandPath.GetError();
207         SuccessOrExit(err);
208
209         if (commandLen > 0 && commandData != nullptr)
210         {
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);
216             SuccessOrExit(err);
217         }
218         commandDataElement.EndOfCommandDataElement();
219
220         err = commandDataElement.GetError();
221         SuccessOrExit(err);
222     }
223     MoveToState(CommandState::AddCommand);
224
225 exit:
226     mCommandDataBuf = nullptr;
227     ChipLogFunctError(err);
228     return err;
229 }
230
231 CHIP_ERROR Command::AddStatusCode(const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, Protocols::Id aProtocolId,
232                                   const uint16_t aProtocolCode)
233 {
234     CHIP_ERROR err = CHIP_NO_ERROR;
235     StatusElement::Builder statusElementBuilder;
236
237     err = statusElementBuilder.Init(mInvokeCommandBuilder.GetWriter());
238     SuccessOrExit(err);
239
240     statusElementBuilder.EncodeStatusElement(aGeneralCode, aProtocolId.ToFullyQualifiedSpecForm(), aProtocolCode)
241         .EndOfStatusElement();
242     err = statusElementBuilder.GetError();
243
244     MoveToState(CommandState::AddCommand);
245
246 exit:
247     ChipLogFunctError(err);
248     return err;
249 }
250
251 CHIP_ERROR Command::ClearExistingExchangeContext()
252 {
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)
256     {
257         mpExchangeCtx->Abort();
258         mpExchangeCtx = nullptr;
259     }
260
261     return CHIP_NO_ERROR;
262 }
263
264 CHIP_ERROR Command::FinalizeCommandsMessage()
265 {
266     CHIP_ERROR err = CHIP_NO_ERROR;
267
268     mInvokeCommandBuilder.EndOfInvokeCommand();
269     err = mInvokeCommandBuilder.GetError();
270     SuccessOrExit(err);
271
272     err = mCommandMessageWriter.Finalize(&mCommandMessageBuf);
273     SuccessOrExit(err);
274
275     VerifyOrExit(mCommandMessageBuf->EnsureReservedSize(System::PacketBuffer::kDefaultHeaderReserve),
276                  err = CHIP_ERROR_BUFFER_TOO_SMALL);
277
278 exit:
279     ChipLogFunctError(err);
280     return err;
281 }
282
283 const char * Command::GetStateStr() const
284 {
285 #if CHIP_DETAIL_LOGGING
286     switch (mState)
287     {
288     case CommandState::Uninitialized:
289         return "Uninitialized";
290
291     case CommandState::Initialized:
292         return "Initialized";
293
294     case CommandState::AddCommand:
295         return "AddCommand";
296
297     case CommandState::Sending:
298         return "Sending";
299     }
300 #endif // CHIP_DETAIL_LOGGING
301     return "N/A";
302 }
303
304 void Command::MoveToState(const CommandState aTargetState)
305 {
306     mState = aTargetState;
307     ChipLogDetail(DataManagement, "ICR moving to [%10.10s]", GetStateStr());
308 }
309
310 void Command::ClearState(void)
311 {
312     MoveToState(CommandState::Uninitialized);
313 }
314
315 } // namespace app
316 } // namespace chip