Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 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 objects for a CHIP IM Invoke Command Sender
22 : *
23 : */
24 :
25 : #include "CommandSender.h"
26 : #include "InteractionModelEngine.h"
27 : #include "StatusResponse.h"
28 : #include <app/TimedRequest.h>
29 : #include <platform/LockTracker.h>
30 : #include <protocols/Protocols.h>
31 : #include <protocols/interaction_model/Constants.h>
32 :
33 : namespace chip {
34 : namespace app {
35 : namespace {
36 :
37 : // Gets the CommandRef if available. Error returned if we expected CommandRef and it wasn't
38 : // provided in the response.
39 : template <typename ParserT>
40 31 : CHIP_ERROR GetRef(ParserT aParser, Optional<uint16_t> & aRef, bool commandRefExpected)
41 : {
42 31 : CHIP_ERROR err = CHIP_NO_ERROR;
43 : uint16_t ref;
44 31 : err = aParser.GetRef(&ref);
45 :
46 31 : VerifyOrReturnError(err == CHIP_NO_ERROR || err == CHIP_END_OF_TLV, err);
47 31 : if (err == CHIP_END_OF_TLV)
48 : {
49 31 : if (commandRefExpected)
50 : {
51 0 : return CHIP_ERROR_INVALID_ARGUMENT;
52 : }
53 31 : aRef = NullOptional;
54 31 : return CHIP_NO_ERROR;
55 : }
56 :
57 0 : aRef = MakeOptional(ref);
58 0 : return CHIP_NO_ERROR;
59 : }
60 :
61 : } // namespace
62 :
63 35 : CommandSender::CommandSender(Callback * apCallback, Messaging::ExchangeManager * apExchangeMgr, bool aIsTimedRequest,
64 35 : bool aSuppressResponse) :
65 35 : mExchangeCtx(*this),
66 35 : mCallbackHandle(apCallback), mpExchangeMgr(apExchangeMgr), mSuppressResponse(aSuppressResponse), mTimedRequest(aIsTimedRequest)
67 : {
68 35 : assertChipStackLockedByCurrentThread();
69 35 : }
70 :
71 2 : CommandSender::CommandSender(ExtendableCallback * apExtendableCallback, Messaging::ExchangeManager * apExchangeMgr,
72 2 : bool aIsTimedRequest, bool aSuppressResponse) :
73 2 : mExchangeCtx(*this),
74 2 : mCallbackHandle(apExtendableCallback), mpExchangeMgr(apExchangeMgr), mSuppressResponse(aSuppressResponse),
75 4 : mTimedRequest(aIsTimedRequest), mUseExtendableCallback(true)
76 : {
77 2 : assertChipStackLockedByCurrentThread();
78 2 : }
79 :
80 37 : CommandSender::~CommandSender()
81 : {
82 37 : assertChipStackLockedByCurrentThread();
83 37 : }
84 :
85 37 : CHIP_ERROR CommandSender::AllocateBuffer()
86 : {
87 37 : if (!mBufferAllocated)
88 : {
89 35 : mCommandMessageWriter.Reset();
90 :
91 35 : System::PacketBufferHandle commandPacket = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
92 35 : VerifyOrReturnError(!commandPacket.IsNull(), CHIP_ERROR_NO_MEMORY);
93 :
94 35 : mCommandMessageWriter.Init(std::move(commandPacket));
95 35 : ReturnErrorOnFailure(mInvokeRequestBuilder.InitWithEndBufferReserved(&mCommandMessageWriter));
96 :
97 35 : mInvokeRequestBuilder.SuppressResponse(mSuppressResponse).TimedRequest(mTimedRequest);
98 35 : ReturnErrorOnFailure(mInvokeRequestBuilder.GetError());
99 :
100 35 : mInvokeRequestBuilder.CreateInvokeRequests(/* aReserveEndBuffer = */ true);
101 35 : ReturnErrorOnFailure(mInvokeRequestBuilder.GetError());
102 :
103 35 : mBufferAllocated = true;
104 35 : }
105 :
106 37 : return CHIP_NO_ERROR;
107 : }
108 :
109 31 : CHIP_ERROR CommandSender::SendCommandRequestInternal(const SessionHandle & session, Optional<System::Clock::Timeout> timeout)
110 : {
111 31 : VerifyOrReturnError(mState == State::AddedCommand, CHIP_ERROR_INCORRECT_STATE);
112 :
113 30 : ReturnErrorOnFailure(Finalize(mPendingInvokeData));
114 :
115 : // Create a new exchange context.
116 30 : auto exchange = mpExchangeMgr->NewContext(session, this);
117 30 : VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_NO_MEMORY);
118 :
119 30 : mExchangeCtx.Grab(exchange);
120 30 : VerifyOrReturnError(!mExchangeCtx->IsGroupExchangeContext(), CHIP_ERROR_INVALID_MESSAGE_TYPE);
121 :
122 30 : mExchangeCtx->SetResponseTimeout(timeout.ValueOr(session->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime)));
123 :
124 30 : if (mTimedInvokeTimeoutMs.HasValue())
125 : {
126 0 : ReturnErrorOnFailure(TimedRequest::Send(mExchangeCtx.Get(), mTimedInvokeTimeoutMs.Value()));
127 0 : MoveToState(State::AwaitingTimedStatus);
128 0 : return CHIP_NO_ERROR;
129 : }
130 :
131 30 : return SendInvokeRequest();
132 : }
133 :
134 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
135 0 : CHIP_ERROR CommandSender::TestOnlyCommandSenderTimedRequestFlagWithNoTimedInvoke(const SessionHandle & session,
136 : Optional<System::Clock::Timeout> timeout)
137 : {
138 0 : VerifyOrReturnError(mTimedRequest, CHIP_ERROR_INCORRECT_STATE);
139 0 : return SendCommandRequestInternal(session, timeout);
140 : }
141 : #endif
142 :
143 31 : CHIP_ERROR CommandSender::SendCommandRequest(const SessionHandle & session, Optional<System::Clock::Timeout> timeout)
144 : {
145 :
146 31 : if (mTimedRequest != mTimedInvokeTimeoutMs.HasValue())
147 : {
148 0 : ChipLogError(
149 : DataManagement,
150 : "Inconsistent timed request state in CommandSender: mTimedRequest (%d) != mTimedInvokeTimeoutMs.HasValue() (%d)",
151 : mTimedRequest, mTimedInvokeTimeoutMs.HasValue());
152 0 : return CHIP_ERROR_INCORRECT_STATE;
153 : }
154 31 : return SendCommandRequestInternal(session, timeout);
155 : }
156 :
157 0 : CHIP_ERROR CommandSender::SendGroupCommandRequest(const SessionHandle & session)
158 : {
159 0 : VerifyOrReturnError(mState == State::AddedCommand, CHIP_ERROR_INCORRECT_STATE);
160 :
161 0 : ReturnErrorOnFailure(Finalize(mPendingInvokeData));
162 :
163 : // Create a new exchange context.
164 0 : auto exchange = mpExchangeMgr->NewContext(session, this);
165 0 : VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_NO_MEMORY);
166 :
167 0 : mExchangeCtx.Grab(exchange);
168 0 : VerifyOrReturnError(mExchangeCtx->IsGroupExchangeContext(), CHIP_ERROR_INVALID_MESSAGE_TYPE);
169 :
170 0 : ReturnErrorOnFailure(SendInvokeRequest());
171 :
172 0 : Close();
173 0 : return CHIP_NO_ERROR;
174 : }
175 :
176 30 : CHIP_ERROR CommandSender::SendInvokeRequest()
177 : {
178 : using namespace Protocols::InteractionModel;
179 : using namespace Messaging;
180 :
181 30 : ReturnErrorOnFailure(
182 : mExchangeCtx->SendMessage(MsgType::InvokeCommandRequest, std::move(mPendingInvokeData), SendMessageFlags::kExpectResponse));
183 30 : MoveToState(State::AwaitingResponse);
184 :
185 30 : return CHIP_NO_ERROR;
186 : }
187 :
188 28 : CHIP_ERROR CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
189 : System::PacketBufferHandle && aPayload)
190 : {
191 : using namespace Protocols::InteractionModel;
192 :
193 28 : if (mState == State::AwaitingResponse)
194 : {
195 28 : MoveToState(State::ResponseReceived);
196 : }
197 :
198 28 : CHIP_ERROR err = CHIP_NO_ERROR;
199 28 : bool sendStatusResponse = false;
200 28 : bool moreChunkedMessages = false;
201 28 : VerifyOrExit(apExchangeContext == mExchangeCtx.Get(), err = CHIP_ERROR_INCORRECT_STATE);
202 28 : sendStatusResponse = true;
203 :
204 28 : if (mState == State::AwaitingTimedStatus)
205 : {
206 0 : if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse))
207 : {
208 0 : CHIP_ERROR statusError = CHIP_NO_ERROR;
209 0 : SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError));
210 0 : sendStatusResponse = false;
211 0 : SuccessOrExit(err = statusError);
212 0 : err = SendInvokeRequest();
213 : }
214 : else
215 : {
216 0 : err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
217 : }
218 : // Skip all other processing here (which is for the response to the
219 : // invoke request), no matter whether err is success or not.
220 0 : goto exit;
221 : }
222 :
223 28 : if (aPayloadHeader.HasMessageType(MsgType::InvokeCommandResponse))
224 : {
225 23 : err = ProcessInvokeResponse(std::move(aPayload), moreChunkedMessages);
226 23 : SuccessOrExit(err);
227 22 : mInvokeResponseMessageCount++;
228 22 : if (moreChunkedMessages)
229 : {
230 0 : StatusResponse::Send(Status::Success, apExchangeContext, /*aExpectResponse = */ true);
231 0 : MoveToState(State::AwaitingResponse);
232 0 : return CHIP_NO_ERROR;
233 : }
234 22 : sendStatusResponse = false;
235 : }
236 5 : else if (aPayloadHeader.HasMessageType(MsgType::StatusResponse))
237 : {
238 4 : CHIP_ERROR statusError = CHIP_NO_ERROR;
239 7 : SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError));
240 3 : SuccessOrExit(err = statusError);
241 0 : err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
242 : }
243 : else
244 : {
245 1 : err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
246 : }
247 :
248 28 : exit:
249 28 : if (err != CHIP_NO_ERROR)
250 : {
251 6 : OnErrorCallback(err);
252 : }
253 :
254 28 : if (sendStatusResponse)
255 : {
256 6 : StatusResponse::Send(Status::InvalidAction, apExchangeContext, /*aExpectResponse = */ false);
257 : }
258 :
259 28 : if (mState != State::AwaitingResponse)
260 : {
261 28 : Close();
262 : }
263 : // Else we got a response to a Timed Request and just sent the invoke.
264 :
265 28 : return err;
266 : }
267 :
268 25 : CHIP_ERROR CommandSender::ProcessInvokeResponse(System::PacketBufferHandle && payload, bool & moreChunkedMessages)
269 : {
270 25 : CHIP_ERROR err = CHIP_NO_ERROR;
271 25 : System::PacketBufferTLVReader reader;
272 : TLV::TLVReader invokeResponsesReader;
273 25 : InvokeResponseMessage::Parser invokeResponseMessage;
274 25 : InvokeResponseIBs::Parser invokeResponses;
275 25 : bool suppressResponse = false;
276 :
277 25 : reader.Init(std::move(payload));
278 25 : ReturnErrorOnFailure(invokeResponseMessage.Init(reader));
279 :
280 : #if CHIP_CONFIG_IM_PRETTY_PRINT
281 24 : invokeResponseMessage.PrettyPrint();
282 : #endif
283 :
284 24 : ReturnErrorOnFailure(invokeResponseMessage.GetSuppressResponse(&suppressResponse));
285 24 : ReturnErrorOnFailure(invokeResponseMessage.GetInvokeResponses(&invokeResponses));
286 24 : invokeResponses.GetReader(&invokeResponsesReader);
287 :
288 55 : while (CHIP_NO_ERROR == (err = invokeResponsesReader.Next()))
289 : {
290 31 : VerifyOrReturnError(TLV::AnonymousTag() == invokeResponsesReader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG);
291 31 : InvokeResponseIB::Parser invokeResponse;
292 31 : ReturnErrorOnFailure(invokeResponse.Init(invokeResponsesReader));
293 31 : ReturnErrorOnFailure(ProcessInvokeResponseIB(invokeResponse));
294 : }
295 :
296 24 : err = invokeResponseMessage.GetMoreChunkedMessages(&moreChunkedMessages);
297 : // If the MoreChunkedMessages element is absent, we receive CHIP_END_OF_TLV. In this
298 : // case, per the specification, a default value of false is used.
299 24 : if (CHIP_END_OF_TLV == err)
300 : {
301 24 : moreChunkedMessages = false;
302 24 : err = CHIP_NO_ERROR;
303 : }
304 24 : ReturnErrorOnFailure(err);
305 :
306 24 : if (suppressResponse && moreChunkedMessages)
307 : {
308 0 : ChipLogError(DataManagement, "Spec violation! InvokeResponse has suppressResponse=true, and moreChunkedMessages=true");
309 : // TODO Is there a better error to return here?
310 0 : return CHIP_ERROR_INVALID_TLV_ELEMENT;
311 : }
312 :
313 : // if we have exhausted this container
314 24 : if (CHIP_END_OF_TLV == err)
315 : {
316 0 : err = CHIP_NO_ERROR;
317 : }
318 24 : ReturnErrorOnFailure(err);
319 24 : return invokeResponseMessage.ExitContainer();
320 25 : }
321 :
322 4 : void CommandSender::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
323 : {
324 4 : ChipLogProgress(DataManagement, "Time out! failed to receive invoke command response from Exchange: " ChipLogFormatExchange,
325 : ChipLogValueExchange(apExchangeContext));
326 :
327 : // TODO(#30453) When timeout occurs for batch commands what should be done? Should all individual
328 : // commands have a path specific error of timeout, or do we give or NoCommandResponse.
329 4 : OnErrorCallback(CHIP_ERROR_TIMEOUT);
330 :
331 4 : Close();
332 4 : }
333 :
334 32 : void CommandSender::Close()
335 : {
336 32 : mSuppressResponse = false;
337 32 : mTimedRequest = false;
338 32 : MoveToState(State::AwaitingDestruction);
339 :
340 32 : OnDoneCallback();
341 32 : }
342 :
343 31 : CHIP_ERROR CommandSender::ProcessInvokeResponseIB(InvokeResponseIB::Parser & aInvokeResponse)
344 : {
345 31 : CHIP_ERROR err = CHIP_NO_ERROR;
346 : ClusterId clusterId;
347 : CommandId commandId;
348 : EndpointId endpointId;
349 : // Default to success when an invoke response is received.
350 31 : StatusIB statusIB;
351 :
352 : {
353 31 : bool commandRefExpected = (mFinishedCommandCount > 1);
354 31 : bool hasDataResponse = false;
355 : TLV::TLVReader commandDataReader;
356 31 : Optional<uint16_t> commandRef;
357 :
358 31 : CommandStatusIB::Parser commandStatus;
359 31 : err = aInvokeResponse.GetStatus(&commandStatus);
360 31 : if (CHIP_NO_ERROR == err)
361 : {
362 24 : CommandPathIB::Parser commandPath;
363 24 : ReturnErrorOnFailure(commandStatus.GetPath(&commandPath));
364 24 : ReturnErrorOnFailure(commandPath.GetClusterId(&clusterId));
365 24 : ReturnErrorOnFailure(commandPath.GetCommandId(&commandId));
366 24 : ReturnErrorOnFailure(commandPath.GetEndpointId(&endpointId));
367 :
368 24 : StatusIB::Parser status;
369 24 : commandStatus.GetErrorStatus(&status);
370 24 : ReturnErrorOnFailure(status.DecodeStatusIB(statusIB));
371 24 : ReturnErrorOnFailure(GetRef(commandStatus, commandRef, commandRefExpected));
372 : }
373 7 : else if (CHIP_END_OF_TLV == err)
374 : {
375 7 : CommandDataIB::Parser commandData;
376 7 : CommandPathIB::Parser commandPath;
377 7 : ReturnErrorOnFailure(aInvokeResponse.GetCommand(&commandData));
378 7 : ReturnErrorOnFailure(commandData.GetPath(&commandPath));
379 7 : ReturnErrorOnFailure(commandPath.GetEndpointId(&endpointId));
380 7 : ReturnErrorOnFailure(commandPath.GetClusterId(&clusterId));
381 7 : ReturnErrorOnFailure(commandPath.GetCommandId(&commandId));
382 7 : commandData.GetFields(&commandDataReader);
383 7 : ReturnErrorOnFailure(GetRef(commandData, commandRef, commandRefExpected));
384 7 : err = CHIP_NO_ERROR;
385 7 : hasDataResponse = true;
386 : }
387 :
388 31 : if (err != CHIP_NO_ERROR)
389 : {
390 0 : ChipLogError(DataManagement, "Received malformed Command Response, err=%" CHIP_ERROR_FORMAT, err.Format());
391 : }
392 : else
393 : {
394 31 : if (hasDataResponse)
395 : {
396 7 : ChipLogProgress(DataManagement,
397 : "Received Command Response Data, Endpoint=%u Cluster=" ChipLogFormatMEI
398 : " Command=" ChipLogFormatMEI,
399 : endpointId, ChipLogValueMEI(clusterId), ChipLogValueMEI(commandId));
400 : }
401 : else
402 : {
403 24 : ChipLogProgress(DataManagement,
404 : "Received Command Response Status for Endpoint=%u Cluster=" ChipLogFormatMEI
405 : " Command=" ChipLogFormatMEI " Status=0x%x",
406 : endpointId, ChipLogValueMEI(clusterId), ChipLogValueMEI(commandId),
407 : to_underlying(statusIB.mStatus));
408 : }
409 : }
410 31 : ReturnErrorOnFailure(err);
411 :
412 : // When using ExtendableCallbacks, we are adhering to a different API contract where path
413 : // specific errors are sent to the OnResponse callback. For more information on the history
414 : // of this issue please see https://github.com/project-chip/connectedhomeip/issues/30991
415 31 : if (statusIB.IsSuccess() || mUseExtendableCallback)
416 : {
417 18 : const ConcreteCommandPath concretePath = ConcreteCommandPath(endpointId, clusterId, commandId);
418 18 : ResponseData responseData = { concretePath, statusIB };
419 18 : responseData.data = hasDataResponse ? &commandDataReader : nullptr;
420 18 : responseData.commandRef = commandRef;
421 18 : OnResponseCallback(responseData);
422 18 : }
423 : else
424 : {
425 13 : OnErrorCallback(statusIB.ToChipError());
426 : }
427 31 : }
428 31 : return CHIP_NO_ERROR;
429 31 : }
430 :
431 0 : CHIP_ERROR CommandSender::SetCommandSenderConfig(CommandSender::ConfigParameters & aConfigParams)
432 : {
433 : #if CHIP_CONFIG_SENDING_BATCH_COMMANDS_ENABLED
434 0 : VerifyOrReturnError(mState == State::Idle, CHIP_ERROR_INCORRECT_STATE);
435 0 : VerifyOrReturnError(aConfigParams.remoteMaxPathsPerInvoke > 0, CHIP_ERROR_INVALID_ARGUMENT);
436 :
437 0 : mRemoteMaxPathsPerInvoke = aConfigParams.remoteMaxPathsPerInvoke;
438 0 : mBatchCommandsEnabled = (aConfigParams.remoteMaxPathsPerInvoke > 1);
439 0 : return CHIP_NO_ERROR;
440 : #else
441 : VerifyOrReturnError(aConfigParams.remoteMaxPathsPerInvoke == 1, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
442 : return CHIP_NO_ERROR;
443 : #endif
444 : }
445 :
446 33 : CHIP_ERROR CommandSender::PrepareCommand(const CommandPathParams & aCommandPathParams,
447 : PrepareCommandParameters & aPrepareCommandParams)
448 : {
449 33 : ReturnErrorOnFailure(AllocateBuffer());
450 :
451 : //
452 : // We must not be in the middle of preparing a command, and must not have already sent InvokeRequestMessage.
453 : //
454 33 : bool canAddAnotherCommand = (mState == State::AddedCommand && mBatchCommandsEnabled && mUseExtendableCallback);
455 33 : VerifyOrReturnError(mState == State::Idle || canAddAnotherCommand, CHIP_ERROR_INCORRECT_STATE);
456 32 : VerifyOrReturnError(mFinishedCommandCount < mRemoteMaxPathsPerInvoke, CHIP_ERROR_MAXIMUM_PATHS_PER_INVOKE_EXCEEDED);
457 :
458 32 : if (mBatchCommandsEnabled)
459 : {
460 3 : VerifyOrReturnError(aPrepareCommandParams.commandRef.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
461 3 : VerifyOrReturnError(aPrepareCommandParams.commandRef.Value() == mFinishedCommandCount, CHIP_ERROR_INVALID_ARGUMENT);
462 : }
463 :
464 32 : InvokeRequests::Builder & invokeRequests = mInvokeRequestBuilder.GetInvokeRequests();
465 32 : CommandDataIB::Builder & invokeRequest = invokeRequests.CreateCommandData();
466 32 : ReturnErrorOnFailure(invokeRequests.GetError());
467 32 : CommandPathIB::Builder & path = invokeRequest.CreatePath();
468 32 : ReturnErrorOnFailure(invokeRequest.GetError());
469 32 : ReturnErrorOnFailure(path.Encode(aCommandPathParams));
470 :
471 32 : if (aPrepareCommandParams.startDataStruct)
472 : {
473 17 : ReturnErrorOnFailure(invokeRequest.GetWriter()->StartContainer(TLV::ContextTag(CommandDataIB::Tag::kFields),
474 : TLV::kTLVType_Structure, mDataElementContainerType));
475 : }
476 :
477 32 : MoveToState(State::AddingCommand);
478 32 : return CHIP_NO_ERROR;
479 : }
480 :
481 31 : CHIP_ERROR CommandSender::FinishCommand(FinishCommandParameters & aFinishCommandParams)
482 : {
483 31 : if (mBatchCommandsEnabled)
484 : {
485 3 : VerifyOrReturnError(aFinishCommandParams.commandRef.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
486 3 : VerifyOrReturnError(aFinishCommandParams.commandRef.Value() == mFinishedCommandCount, CHIP_ERROR_INVALID_ARGUMENT);
487 : }
488 :
489 31 : return FinishCommandInternal(aFinishCommandParams);
490 : }
491 :
492 31 : CHIP_ERROR CommandSender::FinishCommandInternal(FinishCommandParameters & aFinishCommandParams)
493 : {
494 31 : CHIP_ERROR err = CHIP_NO_ERROR;
495 :
496 31 : VerifyOrReturnError(mState == State::AddingCommand, err = CHIP_ERROR_INCORRECT_STATE);
497 :
498 31 : CommandDataIB::Builder & commandData = mInvokeRequestBuilder.GetInvokeRequests().GetCommandData();
499 :
500 31 : if (aFinishCommandParams.endDataStruct)
501 : {
502 16 : ReturnErrorOnFailure(commandData.GetWriter()->EndContainer(mDataElementContainerType));
503 : }
504 :
505 31 : if (aFinishCommandParams.commandRef.HasValue())
506 : {
507 3 : ReturnErrorOnFailure(commandData.Ref(aFinishCommandParams.commandRef.Value()));
508 : }
509 :
510 31 : ReturnErrorOnFailure(commandData.EndOfCommandDataIB());
511 :
512 31 : MoveToState(State::AddedCommand);
513 :
514 31 : mFinishedCommandCount++;
515 :
516 31 : if (aFinishCommandParams.timedInvokeTimeoutMs.HasValue())
517 : {
518 0 : SetTimedInvokeTimeoutMs(aFinishCommandParams.timedInvokeTimeoutMs);
519 : }
520 :
521 31 : return CHIP_NO_ERROR;
522 : }
523 :
524 32 : TLV::TLVWriter * CommandSender::GetCommandDataIBTLVWriter()
525 : {
526 32 : if (mState != State::AddingCommand)
527 : {
528 0 : return nullptr;
529 : }
530 :
531 32 : return mInvokeRequestBuilder.GetInvokeRequests().GetCommandData().GetWriter();
532 : }
533 :
534 0 : void CommandSender::SetTimedInvokeTimeoutMs(const Optional<uint16_t> & aTimedInvokeTimeoutMs)
535 : {
536 0 : if (!mTimedInvokeTimeoutMs.HasValue())
537 : {
538 0 : mTimedInvokeTimeoutMs = aTimedInvokeTimeoutMs;
539 : }
540 0 : else if (aTimedInvokeTimeoutMs.HasValue())
541 : {
542 0 : uint16_t newValue = std::min(mTimedInvokeTimeoutMs.Value(), aTimedInvokeTimeoutMs.Value());
543 0 : mTimedInvokeTimeoutMs.SetValue(newValue);
544 : }
545 0 : }
546 :
547 3 : size_t CommandSender::GetInvokeResponseMessageCount()
548 : {
549 3 : return static_cast<size_t>(mInvokeResponseMessageCount);
550 : }
551 :
552 33 : CHIP_ERROR CommandSender::Finalize(System::PacketBufferHandle & commandPacket)
553 : {
554 33 : VerifyOrReturnError(mState == State::AddedCommand, CHIP_ERROR_INCORRECT_STATE);
555 33 : ReturnErrorOnFailure(mInvokeRequestBuilder.GetInvokeRequests().EndOfInvokeRequests());
556 33 : ReturnErrorOnFailure(mInvokeRequestBuilder.EndOfInvokeRequestMessage());
557 33 : return mCommandMessageWriter.Finalize(&commandPacket);
558 : }
559 :
560 158 : const char * CommandSender::GetStateStr() const
561 : {
562 : #if CHIP_DETAIL_LOGGING
563 158 : switch (mState)
564 : {
565 0 : case State::Idle:
566 0 : return "Idle";
567 :
568 32 : case State::AddingCommand:
569 32 : return "AddingCommand";
570 :
571 36 : case State::AddedCommand:
572 36 : return "AddedCommand";
573 :
574 0 : case State::AwaitingTimedStatus:
575 0 : return "AwaitingTimedStatus";
576 :
577 30 : case State::AwaitingResponse:
578 30 : return "AwaitingResponse";
579 :
580 28 : case State::ResponseReceived:
581 28 : return "ResponseReceived";
582 :
583 32 : case State::AwaitingDestruction:
584 32 : return "AwaitingDestruction";
585 : }
586 : #endif // CHIP_DETAIL_LOGGING
587 0 : return "N/A";
588 : }
589 :
590 158 : void CommandSender::MoveToState(const State aTargetState)
591 : {
592 158 : mState = aTargetState;
593 158 : ChipLogDetail(DataManagement, "ICR moving to [%10.10s]", GetStateStr());
594 158 : }
595 :
596 : } // namespace app
597 : } // namespace chip
|