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 : #pragma once
26 :
27 : #include <type_traits>
28 :
29 : #include "CommandSenderLegacyCallback.h"
30 :
31 : #include <app/CommandPathParams.h>
32 : #include <app/MessageDef/InvokeRequestMessage.h>
33 : #include <app/MessageDef/InvokeResponseMessage.h>
34 : #include <app/MessageDef/StatusIB.h>
35 : #include <app/PendingResponseTrackerImpl.h>
36 : #include <app/data-model/EncodableToTLV.h>
37 : #include <app/data-model/Encode.h>
38 : #include <lib/core/CHIPCore.h>
39 : #include <lib/core/Optional.h>
40 : #include <lib/core/TLVDebug.h>
41 : #include <lib/support/BitFlags.h>
42 : #include <lib/support/CodeUtils.h>
43 : #include <lib/support/DLLUtil.h>
44 : #include <lib/support/logging/CHIPLogging.h>
45 : #include <messaging/ExchangeHolder.h>
46 : #include <messaging/ExchangeMgr.h>
47 : #include <messaging/Flags.h>
48 : #include <protocols/Protocols.h>
49 : #include <system/SystemPacketBuffer.h>
50 : #include <system/TLVPacketBufferBackingStore.h>
51 :
52 : #define COMMON_STATUS_SUCCESS 0
53 :
54 : namespace chip {
55 : namespace app {
56 :
57 : class CommandSender final : public Messaging::ExchangeDelegate
58 : {
59 : public:
60 : // CommandSender::ExtendableCallback::OnResponse is public SDK API, so we cannot break
61 : // source compatibility for it. To allow for additional values to be added at a future
62 : // time without constantly changing the function's declaration parameter list, we are
63 : // defining the struct ResponseData and adding that to the parameter list to allow for
64 : // future extendability.
65 : struct ResponseData
66 : {
67 : // The command path field in invoke command response.
68 : const ConcreteCommandPath & path;
69 : // The status of the command. It can be any success status, including possibly a cluster-specific one.
70 : // If `data` is not null, statusIB will always be a generic SUCCESS status with no-cluster specific
71 : // information.
72 : const StatusIB & statusIB;
73 : // The command data, will be nullptr if the server returns a StatusIB.
74 : TLV::TLVReader * data;
75 : // Reference for the command. This should be associated with the reference value sent out in the initial
76 : // invoke request.
77 : Optional<uint16_t> commandRef;
78 : };
79 :
80 : // CommandSender::ExtendableCallback::OnNoResponse is public SDK API, so we cannot break
81 : // source compatibility for it. To allow for additional values to be added at a future
82 : // time without constantly changing the function's declaration parameter list, we are
83 : // defining the struct NoResponseData and adding that to the parameter list to allow for
84 : // future extendability.
85 : struct NoResponseData
86 : {
87 : uint16_t commandRef;
88 : };
89 :
90 : // CommandSender::ExtendableCallback::OnError is public SDK API, so we cannot break source
91 : // compatibility for it. To allow for additional values to be added at a future time
92 : // without constantly changing the function's declaration parameter list, we are
93 : // defining the struct ErrorData and adding that to the parameter list
94 : // to allow for future extendability.
95 : struct ErrorData
96 : {
97 : /**
98 : * The following errors will be delivered through `error`
99 : *
100 : * - CHIP_ERROR_TIMEOUT: A response was not received within the expected response timeout.
101 : * - CHIP_ERROR_*TLV*: A malformed, non-compliant response was received from the server.
102 : * - CHIP_ERROR encapsulating a StatusIB: If we got a non-path-specific
103 : * status response from the server. In that case, constructing
104 : * a StatusIB from the error can be used to extract the status.
105 : * - CHIP_ERROR*: All other cases.
106 : */
107 : CHIP_ERROR error;
108 : };
109 :
110 : /**
111 : * @brief Callback that is extendable for future features, starting with batch commands
112 : *
113 : * The two major differences between ExtendableCallback and Callback are:
114 : * 1. Path-specific errors go to OnResponse instead of OnError
115 : * - Note: Non-path-specific errors still go to OnError.
116 : * 2. Instead of having new parameters at the end of the arguments list, with defaults,
117 : * as functionality expands, a parameter whose type is defined in this header is used
118 : * as the argument to the callbacks
119 : *
120 : * To support batch commands client must use ExtendableCallback.
121 : */
122 : class ExtendableCallback
123 : {
124 : public:
125 : virtual ~ExtendableCallback() = default;
126 :
127 : /**
128 : * OnResponse will be called for all path specific responses from the server that have been received
129 : * and processed. Specifically:
130 : * - When a status code is received and it is IM::Success, aData will be nullptr.
131 : * - When a status code is received and it is IM and/or cluster error, aData will be nullptr.
132 : * - These kinds of errors are referred to as path-specific errors.
133 : * - When a data response is received, aData will point to a valid TLVReader initialized to point at the struct container
134 : * that contains the data payload (callee will still need to open and process the container).
135 : *
136 : * The CommandSender object MUST continue to exist after this call is completed. The application shall wait until it
137 : * receives an OnDone call to destroy the object.
138 : *
139 : * @param[in] commandSender The command sender object that initiated the command transaction.
140 : * @param[in] aResponseData Information pertaining to the response.
141 : */
142 : virtual void OnResponse(CommandSender * commandSender, const ResponseData & aResponseData) {}
143 :
144 : /**
145 : * Called for each request that failed to receive a response after the server indicates completion of all requests.
146 : *
147 : * This callback may be omitted if clients have alternative ways to track non-responses.
148 : *
149 : * The CommandSender object MUST continue to exist after this call is completed. The application shall wait until it
150 : * receives an OnDone call to destroy the object.
151 : *
152 : * @param commandSender The CommandSender object that initiated the transaction.
153 : * @param aNoResponseData Details about the request without a response.
154 : */
155 : virtual void OnNoResponse(CommandSender * commandSender, const NoResponseData & aNoResponseData) {}
156 :
157 : /**
158 : * OnError will be called when a non-path-specific error occurs *after* a successful call to SendCommandRequest().
159 : *
160 : * The CommandSender object MUST continue to exist after this call is completed. The application shall wait until it
161 : * receives an OnDone call to destroy and free the object.
162 : *
163 : * NOTE: Path specific errors do NOT come to OnError, but instead go to OnResponse.
164 : *
165 : * @param[in] apCommandSender The command sender object that initiated the command transaction.
166 : * @param[in] aErrorData A error data regarding error that occurred.
167 : */
168 : virtual void OnError(const CommandSender * apCommandSender, const ErrorData & aErrorData) {}
169 :
170 : /**
171 : * OnDone will be called when CommandSender has finished all work and is safe to destroy and free the
172 : * allocated CommandSender object.
173 : *
174 : * This function will:
175 : * - Always be called exactly *once* for a given CommandSender instance.
176 : * - Be called even in error circumstances.
177 : * - Only be called after a successful call to SendCommandRequest returns, if SendCommandRequest is used.
178 : * - Always be called before a successful return from SendGroupCommandRequest, if SendGroupCommandRequest is used.
179 : *
180 : * This function must be implemented to destroy the CommandSender object.
181 : *
182 : * @param[in] apCommandSender The command sender object of the terminated invoke command transaction.
183 : */
184 : virtual void OnDone(CommandSender * apCommandSender) = 0;
185 : };
186 :
187 : // `Callback` exists for legacy purposes. If you are developing a new callback implementation,
188 : // please use `ExtendableCallback`.
189 : using Callback = CommandSenderLegacyCallback;
190 :
191 : // SetCommandSenderConfig is a public SDK API, so we cannot break source compatibility
192 : // for it. By having parameters to that API use this struct instead of individual
193 : // function arguments, we centralize required changes to one file when adding new
194 : // funtionality.
195 : struct ConfigParameters
196 : {
197 : ConfigParameters & SetRemoteMaxPathsPerInvoke(uint16_t aRemoteMaxPathsPerInvoke)
198 : {
199 : remoteMaxPathsPerInvoke = aRemoteMaxPathsPerInvoke;
200 : return *this;
201 : }
202 :
203 : // If remoteMaxPathsPerInvoke is 1, this will allow the CommandSender client to contain only one command and
204 : // doesn't enforce other batch commands requirements.
205 : uint16_t remoteMaxPathsPerInvoke = 1;
206 : };
207 :
208 : // AddRequestData is a public SDK API, so we must maintain source compatibility.
209 : // Using this struct for API parameters instead of individual parameters allows us
210 : // to make necessary changes for new functionality in a single location.
211 : struct AddRequestDataParameters
212 : {
213 : // gcc bug requires us to have the constructor below
214 : // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96645
215 : AddRequestDataParameters() {}
216 :
217 0 : AddRequestDataParameters(const Optional<uint16_t> & aTimedInvokeTimeoutMs) : timedInvokeTimeoutMs(aTimedInvokeTimeoutMs) {}
218 :
219 : AddRequestDataParameters & SetCommandRef(uint16_t aCommandRef)
220 : {
221 : commandRef.SetValue(aCommandRef);
222 : return *this;
223 : }
224 :
225 : // When a value is provided for timedInvokeTimeoutMs, this invoke becomes a timed
226 : // invoke. CommandSender will use the minimum of all provided timeouts for execution.
227 : const Optional<uint16_t> timedInvokeTimeoutMs;
228 : // The command reference is required when sending multiple commands. It allows the caller
229 : // to associate this request with its corresponding response.
230 : Optional<uint16_t> commandRef;
231 : };
232 :
233 : // PrepareCommand is a public SDK API, so we must maintain source compatibility.
234 : // Using this struct for API parameters instead of individual parameters allows us
235 : // to make necessary changes for new functionality in a single location.
236 : struct PrepareCommandParameters
237 : {
238 : // gcc bug requires us to have the constructor below
239 : // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96645
240 : PrepareCommandParameters() {}
241 :
242 17 : PrepareCommandParameters(const AddRequestDataParameters & aAddRequestDataParam) :
243 17 : commandRef(aAddRequestDataParam.commandRef)
244 17 : {}
245 :
246 : PrepareCommandParameters & SetStartDataStruct(bool aStartDataStruct)
247 : {
248 : startDataStruct = aStartDataStruct;
249 : return *this;
250 : }
251 :
252 : PrepareCommandParameters & SetCommandRef(uint16_t aCommandRef)
253 : {
254 : commandRef.SetValue(aCommandRef);
255 : return *this;
256 : }
257 : // The command reference is required when sending multiple commands. It allows the caller
258 : // to associate this request with its corresponding response. We validate the reference
259 : // early in PrepareCommand, even though it's not used until FinishCommand. This proactive
260 : // validation helps prevent unnecessary writing an InvokeRequest into the packet that later
261 : // needs to be undone.
262 : Optional<uint16_t> commandRef;
263 : // If the InvokeRequest needs to be in a state with a started data TLV struct container
264 : bool startDataStruct = false;
265 : };
266 :
267 : // FinishCommand is a public SDK API, so we must maintain source compatibility.
268 : // Using this struct for API parameters instead of individual parameters allows us
269 : // to make necessary changes for new functionality in a single location.
270 : struct FinishCommandParameters
271 : {
272 : // gcc bug requires us to have the constructor below
273 : // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96645
274 : FinishCommandParameters() {}
275 :
276 : FinishCommandParameters(const Optional<uint16_t> & aTimedInvokeTimeoutMs) : timedInvokeTimeoutMs(aTimedInvokeTimeoutMs) {}
277 16 : FinishCommandParameters(const AddRequestDataParameters & aAddRequestDataParam) :
278 16 : timedInvokeTimeoutMs(aAddRequestDataParam.timedInvokeTimeoutMs), commandRef(aAddRequestDataParam.commandRef)
279 16 : {}
280 :
281 : FinishCommandParameters & SetEndDataStruct(bool aEndDataStruct)
282 : {
283 : endDataStruct = aEndDataStruct;
284 : return *this;
285 : }
286 :
287 : FinishCommandParameters & SetCommandRef(uint16_t aCommandRef)
288 : {
289 : commandRef.SetValue(aCommandRef);
290 : return *this;
291 : }
292 :
293 : // When a value is provided for timedInvokeTimeoutMs, this invoke becomes a timed
294 : // invoke. CommandSender will use the minimum of all provided timeouts for execution.
295 : const Optional<uint16_t> timedInvokeTimeoutMs;
296 : // The command reference is required when sending multiple commands. It allows the caller
297 : // to associate this request with its corresponding response. This value must be
298 : // the same as the one provided in PrepareCommandParameters when calling PrepareCommand.
299 : Optional<uint16_t> commandRef;
300 : // If InvokeRequest is in a state where the data TLV struct container is currently open
301 : // and FinishCommand should close it.
302 : bool endDataStruct = false;
303 : };
304 :
305 : class TestOnlyMarker
306 : {
307 : };
308 :
309 : /*
310 : * Constructor.
311 : *
312 : * The callback passed in has to outlive this CommandSender object.
313 : * If used in a groups setting, callbacks do not need to be passed.
314 : * If callbacks are passed the only one that will be called in a group sesttings is the onDone
315 : */
316 : CommandSender(Callback * apCallback, Messaging::ExchangeManager * apExchangeMgr, bool aIsTimedRequest = false,
317 : bool aSuppressResponse = false, bool aAllowLargePayload = false);
318 : CommandSender(std::nullptr_t, Messaging::ExchangeManager * apExchangeMgr, bool aIsTimedRequest = false,
319 : bool aSuppressResponse = false, bool aAllowLargePayload = false) :
320 : CommandSender(static_cast<Callback *>(nullptr), apExchangeMgr, aIsTimedRequest, aSuppressResponse, aAllowLargePayload)
321 : {}
322 : CommandSender(ExtendableCallback * apCallback, Messaging::ExchangeManager * apExchangeMgr, bool aIsTimedRequest = false,
323 : bool aSuppressResponse = false, bool aAllowLargePayload = false);
324 : // TODO(#32138): After there is a macro that is always defined for all unit tests, the constructor with
325 : // TestOnlyMarker should only be compiled if that macro is defined.
326 : CommandSender(TestOnlyMarker aTestMarker, ExtendableCallback * apCallback, Messaging::ExchangeManager * apExchangeMgr,
327 : PendingResponseTracker * apPendingResponseTracker, bool aIsTimedRequest = false, bool aSuppressResponse = false,
328 : bool aAllowLargePayload = false) :
329 : CommandSender(apCallback, apExchangeMgr, aIsTimedRequest, aSuppressResponse, aAllowLargePayload)
330 : {
331 : mpPendingResponseTracker = apPendingResponseTracker;
332 : }
333 : ~CommandSender();
334 :
335 : /**
336 : * Enables additional features of CommandSender, for example sending batch commands.
337 : *
338 : * In the case of enabling batch commands, once set it ensures that commands contain all
339 : * required data elements while building the InvokeRequestMessage. This must be called
340 : * before PrepareCommand.
341 : *
342 : * @param [in] aConfigParams contains information to configure CommandSender behavior,
343 : * such as such as allowing a max number of paths per invoke greater than one,
344 : * based on how many paths the remote peer claims to support.
345 : *
346 : * @return CHIP_ERROR_INCORRECT_STATE
347 : * If device has previously called `PrepareCommand`.
348 : * @return CHIP_ERROR_INVALID_ARGUMENT
349 : * Invalid argument value.
350 : * @return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE
351 : * Device has not enabled batch command support. To enable:
352 : * 1. Enable the CHIP_CONFIG_COMMAND_SENDER_BUILTIN_SUPPORT_FOR_BATCHED_COMMANDS
353 : * configuration option.
354 : * 2. Ensure you provide ExtendableCallback.
355 : */
356 : CHIP_ERROR SetCommandSenderConfig(ConfigParameters & aConfigParams);
357 :
358 : CHIP_ERROR PrepareCommand(const CommandPathParams & aCommandPathParams, PrepareCommandParameters & aPrepareCommandParams);
359 :
360 : [[deprecated("PrepareCommand should migrate to calling PrepareCommand with PrepareCommandParameters")]] CHIP_ERROR
361 : PrepareCommand(const CommandPathParams & aCommandPathParams, bool aStartDataStruct = true)
362 : {
363 : PrepareCommandParameters prepareCommandParams;
364 : prepareCommandParams.SetStartDataStruct(aStartDataStruct);
365 : return PrepareCommand(aCommandPathParams, prepareCommandParams);
366 : }
367 :
368 : CHIP_ERROR FinishCommand(FinishCommandParameters & aFinishCommandParams);
369 :
370 : [[deprecated("FinishCommand should migrate to calling FinishCommand with FinishCommandParameters")]] CHIP_ERROR
371 : FinishCommand(bool aEndDataStruct = true)
372 : {
373 : FinishCommandParameters finishCommandParams;
374 : finishCommandParams.SetEndDataStruct(aEndDataStruct);
375 : return FinishCommand(finishCommandParams);
376 : }
377 : [[deprecated("FinishCommand should migrate to calling FinishCommand with FinishCommandParameters")]] CHIP_ERROR
378 : FinishCommand(const Optional<uint16_t> & aTimedInvokeTimeoutMs)
379 : {
380 : FinishCommandParameters finishCommandParams(aTimedInvokeTimeoutMs);
381 : return FinishCommand(finishCommandParams);
382 : }
383 :
384 : TLV::TLVWriter * GetCommandDataIBTLVWriter();
385 :
386 : /**
387 : * API for adding request data using DataModel::EncodableToTLV.
388 : *
389 : * @param [in] aCommandPath The path of the command being requested.
390 : * @param [in] aEncodable The request data to encode into the
391 : * `CommandFields` member of `CommandDataIB`.
392 : * @param [in] aAddRequestDataParams parameters associated with building the
393 : * InvokeRequestMessage that are associated with this request.
394 : *
395 : * This API will not fail if this is an untimed invoke but the command provided requires a timed
396 : * invoke interaction. If the caller wants that to fail before sending the command, they should call
397 : * the templated version of AddRequestData.
398 : */
399 : CHIP_ERROR AddRequestData(const CommandPathParams & aCommandPath, const DataModel::EncodableToTLV & aEncodable,
400 : AddRequestDataParameters & aAddRequestDataParams);
401 :
402 : /**
403 : * API for adding a data request. The template parameter T is generally
404 : * expected to be a ClusterName::Commands::CommandName::Type struct, but any
405 : * object that can be encoded using the DataModel::Encode machinery and
406 : * exposes the right command id will work.
407 : *
408 : * @param [in] aCommandPath The path of the command being requested.
409 : * @param [in] aData The data for the request.
410 : */
411 : template <typename CommandDataT, typename std::enable_if_t<!CommandDataT::MustUseTimedInvoke(), int> = 0>
412 : CHIP_ERROR AddRequestData(const CommandPathParams & aCommandPath, const CommandDataT & aData)
413 : {
414 : AddRequestDataParameters addRequestDataParams;
415 : return AddRequestData(aCommandPath, aData, addRequestDataParams);
416 : }
417 :
418 : template <typename CommandDataT,
419 : typename std::enable_if_t<!std::is_base_of_v<DataModel::EncodableToTLV, CommandDataT>, int> = 0>
420 0 : CHIP_ERROR AddRequestData(const CommandPathParams & aCommandPath, const CommandDataT & aData,
421 : AddRequestDataParameters & aAddRequestDataParams)
422 : {
423 0 : VerifyOrReturnError(!CommandDataT::MustUseTimedInvoke() || aAddRequestDataParams.timedInvokeTimeoutMs.HasValue(),
424 : CHIP_ERROR_INVALID_ARGUMENT);
425 :
426 0 : DataModel::EncodableType<CommandDataT> encodable(aData);
427 0 : return AddRequestData(aCommandPath, encodable, aAddRequestDataParams);
428 0 : }
429 :
430 : template <typename CommandDataT>
431 0 : CHIP_ERROR AddRequestData(const CommandPathParams & aCommandPath, const CommandDataT & aData,
432 : const Optional<uint16_t> & aTimedInvokeTimeoutMs)
433 : {
434 0 : AddRequestDataParameters addRequestDataParams(aTimedInvokeTimeoutMs);
435 0 : return AddRequestData(aCommandPath, aData, addRequestDataParams);
436 0 : }
437 :
438 : /**
439 : * @brief Returns the number of InvokeResponseMessages received.
440 : *
441 : * Responses to multiple requests might be split across several InvokeResponseMessages.
442 : * This function helps track the total count. Primarily for test validation purposes.
443 : */
444 : size_t GetInvokeResponseMessageCount();
445 :
446 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
447 : /**
448 : * Version of AddRequestData that allows sending a message that is
449 : * guaranteed to fail due to requiring a timed invoke but not providing a
450 : * timeout parameter. For use in tests only.
451 : */
452 : template <typename CommandDataT>
453 : CHIP_ERROR TestOnlyAddRequestDataNoTimedCheck(const CommandPathParams & aCommandPath, const CommandDataT & aData,
454 : AddRequestDataParameters & aAddRequestDataParams)
455 : {
456 : DataModel::EncodableType<CommandDataT> encodable(aData);
457 : return AddRequestData(aCommandPath, encodable, aAddRequestDataParams);
458 : }
459 :
460 : CHIP_ERROR TestOnlyFinishCommand(FinishCommandParameters & aFinishCommandParams)
461 : {
462 : if (mBatchCommandsEnabled)
463 : {
464 : VerifyOrReturnError(aFinishCommandParams.commandRef.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
465 : }
466 : return FinishCommandInternal(aFinishCommandParams);
467 : }
468 :
469 : /**
470 : * Version of SendCommandRequest that sets the TimedRequest flag but does not send the TimedInvoke
471 : * action. For use in tests only.
472 : */
473 : CHIP_ERROR TestOnlyCommandSenderTimedRequestFlagWithNoTimedInvoke(const SessionHandle & session,
474 : Optional<System::Clock::Timeout> timeout = NullOptional);
475 :
476 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
477 :
478 : private:
479 : CHIP_ERROR FinishCommandInternal(FinishCommandParameters & aFinishCommandParams);
480 :
481 : public:
482 : // Sends a queued up command request to the target encapsulated by the secureSession handle.
483 : //
484 : // Upon successful return from this call, all subsequent errors that occur during this interaction
485 : // will be conveyed through the OnError callback above. In addition, upon completion of work regardless of
486 : // whether it was successful or not, the OnDone callback will be invoked to indicate completion of work on this
487 : // object and to indicate to the application that it can destroy and free this object.
488 : //
489 : // Applications can, however, destroy this object at any time after this call, except while handling
490 : // an OnResponse or OnError callback, and it will safely clean-up.
491 : //
492 : // If this call returns failure, the callback's OnDone will never be called; the client is responsible
493 : // for destroying this object on failure.
494 : //
495 : // Client can specify the maximum time to wait for response (in milliseconds) via timeout parameter.
496 : // Default timeout value will be used otherwise.
497 : //
498 : CHIP_ERROR SendCommandRequest(const SessionHandle & session, Optional<System::Clock::Timeout> timeout = NullOptional);
499 :
500 : // Sends a queued up group command request to the target encapsulated by the secureSession handle.
501 : //
502 : // If this function is successful, it will invoke the OnDone callback before returning to indicate
503 : // to the application that it can destroy and free this object.
504 : //
505 : CHIP_ERROR SendGroupCommandRequest(const SessionHandle & session);
506 :
507 : private:
508 : friend class TestCommandInteraction;
509 :
510 : enum class State : uint8_t
511 : {
512 : Idle, ///< Default state that the object starts out in, where no work has commenced
513 : AddingCommand, ///< In the process of adding a command.
514 : AddedCommand, ///< A command has been completely encoded and is awaiting transmission.
515 : AwaitingTimedStatus, ///< Sent a Timed Request and waiting for response.
516 : AwaitingResponse, ///< The command has been sent successfully, and we are awaiting invoke response.
517 : ResponseReceived, ///< Received a response to our invoke and request and processing the response.
518 : AwaitingDestruction, ///< The object has completed its work and is awaiting destruction by the application.
519 : };
520 :
521 : /**
522 : * Class to help backup CommandSender's buffer containing InvokeRequestMessage when adding InvokeRequest
523 : * in case there is a failure to add InvokeRequest. Intended usage is as follows:
524 : * - Allocate RollbackInvokeRequest on the stack.
525 : * - Attempt adding InvokeRequest into InvokeRequestMessage buffer.
526 : * - If modification is added successfully, call DisableAutomaticRollback() to prevent destructor from
527 : * rolling back InvokeReqestMessage.
528 : * - If there is an issue adding InvokeRequest, destructor will take care of rolling back
529 : * InvokeRequestMessage to previously saved state.
530 : */
531 : class RollbackInvokeRequest
532 : {
533 : public:
534 : explicit RollbackInvokeRequest(CommandSender & aCommandSender);
535 : ~RollbackInvokeRequest();
536 :
537 : /**
538 : * Disables rolling back to previously saved state for InvokeRequestMessage.
539 : */
540 : void DisableAutomaticRollback();
541 :
542 : private:
543 : CommandSender & mCommandSender;
544 : TLV::TLVWriter mBackupWriter;
545 : State mBackupState;
546 : bool mRollbackInDestructor = false;
547 : };
548 :
549 : union CallbackHandle
550 : {
551 32 : CallbackHandle(Callback * apCallback) : legacyCallback(apCallback) {}
552 10 : CallbackHandle(ExtendableCallback * apExtendableCallback) : extendableCallback(apExtendableCallback) {}
553 : Callback * legacyCallback;
554 : ExtendableCallback * extendableCallback;
555 : };
556 :
557 : void MoveToState(const State aTargetState);
558 : const char * GetStateStr() const;
559 :
560 : /*
561 : * Allocates a packet buffer used for encoding an invoke request payload.
562 : *
563 : * This can be called multiple times safely, as it will only allocate the buffer once for the lifetime
564 : * of this object.
565 : */
566 : CHIP_ERROR AllocateBuffer();
567 :
568 : // ExchangeDelegate interface implementation. Private so people won't
569 : // accidentally call it on us when we're not being treated as an actual
570 : // ExchangeDelegate.
571 : CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
572 : System::PacketBufferHandle && aPayload) override;
573 : void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override;
574 :
575 : void FlushNoCommandResponse();
576 : //
577 : // Called internally to signal the completion of all work on this object, gracefully close the
578 : // exchange (by calling into the base class) and finally, signal to the application that it's
579 : // safe to release this object.
580 : //
581 : void Close();
582 :
583 : /*
584 : * This forcibly closes the exchange context if a valid one is pointed to. Such a situation does
585 : * not arise during normal message processing flows that all normally call Close() above. This can only
586 : * arise due to application-initiated destruction of the object when this object is handling receiving/sending
587 : * message payloads.
588 : */
589 : void Abort();
590 :
591 : CHIP_ERROR ProcessInvokeResponse(System::PacketBufferHandle && payload, bool & moreChunkedMessages);
592 : CHIP_ERROR ProcessInvokeResponseIB(InvokeResponseIB::Parser & aInvokeResponse);
593 :
594 : void SetTimedInvokeTimeoutMs(const Optional<uint16_t> & aTimedInvokeTimeoutMs);
595 :
596 : // Send our queued-up Invoke Request message. Assumes the exchange is ready
597 : // and mPendingInvokeData is populated.
598 : CHIP_ERROR SendInvokeRequest();
599 :
600 : CHIP_ERROR Finalize(System::PacketBufferHandle & commandPacket);
601 :
602 : CHIP_ERROR SendCommandRequestInternal(const SessionHandle & session, Optional<System::Clock::Timeout> timeout);
603 :
604 20 : void OnResponseCallback(const ResponseData & aResponseData)
605 : {
606 : // mpExtendableCallback and mpCallback are mutually exclusive.
607 20 : if (mUseExtendableCallback && mCallbackHandle.extendableCallback)
608 : {
609 3 : mCallbackHandle.extendableCallback->OnResponse(this, aResponseData);
610 : }
611 17 : else if (mCallbackHandle.legacyCallback)
612 : {
613 17 : mCallbackHandle.legacyCallback->OnResponse(this, aResponseData.path, aResponseData.statusIB, aResponseData.data);
614 : }
615 20 : }
616 :
617 23 : void OnErrorCallback(CHIP_ERROR aError)
618 : {
619 : // mpExtendableCallback and mpCallback are mutually exclusive.
620 23 : if (mUseExtendableCallback && mCallbackHandle.extendableCallback)
621 : {
622 1 : ErrorData errorData = { aError };
623 1 : mCallbackHandle.extendableCallback->OnError(this, errorData);
624 1 : }
625 22 : else if (mCallbackHandle.legacyCallback)
626 : {
627 22 : mCallbackHandle.legacyCallback->OnError(this, aError);
628 : }
629 23 : }
630 :
631 33 : void OnDoneCallback()
632 : {
633 : // mpExtendableCallback and mpCallback are mutually exclusive.
634 33 : if (mUseExtendableCallback && mCallbackHandle.extendableCallback)
635 : {
636 3 : mCallbackHandle.extendableCallback->OnDone(this);
637 : }
638 30 : else if (mCallbackHandle.legacyCallback)
639 : {
640 30 : mCallbackHandle.legacyCallback->OnDone(this);
641 : }
642 33 : }
643 :
644 : Messaging::ExchangeHolder mExchangeCtx;
645 : CallbackHandle mCallbackHandle;
646 : Messaging::ExchangeManager * mpExchangeMgr = nullptr;
647 : InvokeRequestMessage::Builder mInvokeRequestBuilder;
648 : // TODO Maybe we should change PacketBufferTLVWriter so we can finalize it
649 : // but have it hold on to the buffer, and get the buffer from it later.
650 : // Then we could avoid this extra pointer-sized member.
651 : System::PacketBufferHandle mPendingInvokeData;
652 : // If mTimedInvokeTimeoutMs has a value, we are expected to do a timed
653 : // invoke.
654 : Optional<uint16_t> mTimedInvokeTimeoutMs;
655 : TLV::TLVType mDataElementContainerType = TLV::kTLVType_NotSpecified;
656 :
657 : chip::System::PacketBufferTLVWriter mCommandMessageWriter;
658 :
659 : #if CHIP_CONFIG_COMMAND_SENDER_BUILTIN_SUPPORT_FOR_BATCHED_COMMANDS
660 : PendingResponseTrackerImpl mNonTestPendingResponseTracker;
661 : #endif // CHIP_CONFIG_COMMAND_SENDER_BUILTIN_SUPPORT_FOR_BATCHED_COMMANDS
662 : PendingResponseTracker * mpPendingResponseTracker = nullptr;
663 :
664 : uint16_t mInvokeResponseMessageCount = 0;
665 : uint16_t mFinishedCommandCount = 0;
666 : uint16_t mRemoteMaxPathsPerInvoke = 1;
667 :
668 : State mState = State::Idle;
669 : bool mSuppressResponse = false;
670 : bool mTimedRequest = false;
671 : bool mBufferAllocated = false;
672 : bool mBatchCommandsEnabled = false;
673 : bool mUseExtendableCallback = false;
674 : bool mAllowLargePayload = false;
675 : };
676 :
677 : } // namespace app
678 : } // namespace chip
|