Apply Upstream code (2021-03-15)
[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)
35 {
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);
40
41     mpExchangeMgr = apExchangeMgr;
42
43     err = Reset();
44     SuccessOrExit(err);
45
46 exit:
47     ChipLogFunctError(err);
48     return err;
49 }
50
51 CHIP_ERROR Command::Reset()
52 {
53     CHIP_ERROR err = CHIP_NO_ERROR;
54
55     ClearExistingExchangeContext();
56
57     if (mCommandMessageBuf.IsNull())
58     {
59         // TODO: Calculate the packet buffer size
60         mCommandMessageBuf = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLength);
61         VerifyOrExit(!mCommandMessageBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
62     }
63
64     mCommandMessageWriter.Init(std::move(mCommandMessageBuf));
65     err = mInvokeCommandBuilder.Init(&mCommandMessageWriter);
66     SuccessOrExit(err);
67
68     mInvokeCommandBuilder.CreateCommandListBuilder();
69     MoveToState(CommandState::Initialized);
70
71 exit:
72     ChipLogFunctError(err);
73
74     return err;
75 }
76
77 CHIP_ERROR Command::ProcessCommandMessage(System::PacketBufferHandle && payload, CommandRoleId aCommandRoleId)
78 {
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;
84
85     reader.Init(std::move(payload));
86     err = reader.Next();
87     SuccessOrExit(err);
88
89     err = invokeCommandParser.Init(reader);
90     SuccessOrExit(err);
91
92     err = invokeCommandParser.CheckSchemaValidity();
93     SuccessOrExit(err);
94
95     err = invokeCommandParser.GetCommandList(&commandListParser);
96     SuccessOrExit(err);
97
98     commandListParser.GetReader(&commandListReader);
99
100     while (CHIP_NO_ERROR == (err = commandListReader.Next()))
101     {
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);
104
105         CommandDataElement::Parser commandElement;
106
107         err = commandElement.Init(commandListReader);
108         SuccessOrExit(err);
109
110         err = commandElement.CheckSchemaValidity();
111         SuccessOrExit(err);
112
113         err = ProcessCommandDataElement(commandElement);
114         SuccessOrExit(err);
115     }
116
117     // if we have exhausted this container
118     if (CHIP_END_OF_TLV == err)
119     {
120         err = CHIP_NO_ERROR;
121     }
122
123 exit:
124     return err;
125 }
126
127 void Command::Shutdown()
128 {
129     VerifyOrExit(mState != CommandState::Uninitialized, );
130     mCommandMessageWriter.Reset();
131     mCommandMessageBuf = nullptr;
132
133     ClearExistingExchangeContext();
134
135     mpExchangeMgr = nullptr;
136     MoveToState(CommandState::Uninitialized);
137
138 exit:
139     return;
140 }
141
142 chip::TLV::TLVWriter & Command::CreateCommandDataElementTLVWriter()
143 {
144     mCommandDataBuf = chip::System::PacketBufferHandle::New(chip::app::kMaxSecureSduLength);
145     if (mCommandDataBuf.IsNull())
146     {
147         ChipLogDetail(DataManagement, "Unable to allocate packet buffer");
148     }
149
150     mCommandDataWriter.Init(mCommandDataBuf.Retain());
151
152     return mCommandDataWriter;
153 }
154
155 CHIP_ERROR Command::AddCommand(chip::EndpointId aEndpointId, chip::GroupId aGroupId, chip::ClusterId aClusterId,
156                                chip::CommandId aCommandId, BitFlags<CommandPathFlags> aFlags)
157 {
158     CommandParams commandParams(aEndpointId, aGroupId, aClusterId, aCommandId, aFlags);
159
160     return AddCommand(commandParams);
161 }
162
163 CHIP_ERROR Command::AddCommand(CommandParams & aCommandParams)
164 {
165     CHIP_ERROR err = CHIP_NO_ERROR;
166     const uint8_t * apCommandData;
167     uint32_t apCommandLen;
168
169     apCommandData = mCommandDataBuf->Start();
170     apCommandLen  = mCommandDataBuf->DataLength();
171
172     if (apCommandLen > 0)
173     {
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);
177
178         apCommandData += 1;
179         apCommandLen -= 1;
180     }
181
182     {
183         CommandDataElement::Builder commandDataElement =
184             mInvokeCommandBuilder.GetCommandListBuilder().CreateCommandDataElementBuilder();
185         CommandPath::Builder commandPath = commandDataElement.CreateCommandPathBuilder();
186         if (aCommandParams.Flags.Has(CommandPathFlags::kEndpointIdValid))
187         {
188             commandPath.EndpointId(aCommandParams.EndpointId);
189         }
190
191         if (aCommandParams.Flags.Has(CommandPathFlags::kGroupIdValid))
192         {
193             commandPath.GroupId(aCommandParams.GroupId);
194         }
195
196         commandPath.ClusterId(aCommandParams.ClusterId).CommandId(aCommandParams.CommandId).EndOfCommandPath();
197
198         err = commandPath.GetError();
199         SuccessOrExit(err);
200
201         if (apCommandLen > 0)
202         {
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);
208             SuccessOrExit(err);
209         }
210         commandDataElement.EndOfCommandDataElement();
211
212         err = commandDataElement.GetError();
213         SuccessOrExit(err);
214     }
215     MoveToState(CommandState::AddCommand);
216
217 exit:
218     mCommandDataBuf = nullptr;
219     ChipLogFunctError(err);
220     return err;
221 }
222
223 CHIP_ERROR Command::AddStatusCode(const uint16_t aGeneralCode, const uint32_t aProtocolId, const uint16_t aProtocolCode,
224                                   const chip::ClusterId aClusterId)
225 {
226     CHIP_ERROR err = CHIP_NO_ERROR;
227     StatusElement::Builder statusElementBuilder;
228
229     err = statusElementBuilder.Init(mInvokeCommandBuilder.GetWriter());
230     SuccessOrExit(err);
231
232     statusElementBuilder.EncodeStatusElement(aGeneralCode, aProtocolId, aProtocolCode, aProtocolCode).EndOfStatusElement();
233     err = statusElementBuilder.GetError();
234
235     MoveToState(CommandState::AddCommand);
236
237 exit:
238     ChipLogFunctError(err);
239     return err;
240 }
241
242 CHIP_ERROR Command::ClearExistingExchangeContext()
243 {
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)
247     {
248         mpExchangeCtx->Abort();
249         mpExchangeCtx = nullptr;
250     }
251
252     return CHIP_NO_ERROR;
253 }
254
255 CHIP_ERROR Command::FinalizeCommandsMessage()
256 {
257     CHIP_ERROR err = CHIP_NO_ERROR;
258
259     mInvokeCommandBuilder.EndOfInvokeCommand();
260     err = mInvokeCommandBuilder.GetError();
261     SuccessOrExit(err);
262
263     err = mCommandMessageWriter.Finalize(&mCommandMessageBuf);
264     SuccessOrExit(err);
265
266     VerifyOrExit(mCommandMessageBuf->EnsureReservedSize(System::PacketBuffer::kDefaultHeaderReserve),
267                  err = CHIP_ERROR_BUFFER_TOO_SMALL);
268
269 exit:
270     ChipLogFunctError(err);
271     return err;
272 }
273
274 const char * Command::GetStateStr() const
275 {
276 #if CHIP_DETAIL_LOGGING
277     switch (mState)
278     {
279     case CommandState::Uninitialized:
280         return "Uninitialized";
281
282     case CommandState::Initialized:
283         return "Initialized";
284
285     case CommandState::AddCommand:
286         return "AddCommand";
287
288     case CommandState::Sending:
289         return "Sending";
290     }
291 #endif // CHIP_DETAIL_LOGGING
292     return "N/A";
293 }
294
295 void Command::MoveToState(const CommandState aTargetState)
296 {
297     mState = aTargetState;
298     ChipLogDetail(DataManagement, "ICR moving to [%10.10s]", GetStateStr());
299 }
300
301 void Command::ClearState(void)
302 {
303     MoveToState(CommandState::Uninitialized);
304 }
305
306 } // namespace app
307 } // namespace chip