forked from project-chip/connectedhomeip
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCommandHandler.h
278 lines (250 loc) · 11.6 KB
/
CommandHandler.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/*
*
* Copyright (c) 2020 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <app/CommandHandlerExchangeInterface.h>
#include <app/ConcreteCommandPath.h>
#include <app/data-model/EncodableToTLV.h>
#include <app/data-model/Encode.h>
#include <lib/core/CHIPCore.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/IntrusiveList.h>
#include <lib/support/logging/CHIPLogging.h>
namespace chip {
namespace app {
/**
* A handler for incoming Invoke interactions.
*
* Allows adding responses to be sent in an InvokeResponse: see the various
* "Add*" methods.
*
* Allows adding the responses asynchronously when using `CommandHandler::Handle`
* (see documentation for `CommandHandler::Handle` for details)
*
* Upgrading notes: this class has moved to an interface from a previous more complex
* implementation. If upgrading code between versions, please see docs/upgrading.md
*/
class CommandHandler
{
public:
virtual ~CommandHandler() = default;
/**
* Class that allows asynchronous command processing before sending a
* response. When such processing is desired:
*
* 1) Create a Handle initialized with the CommandHandler that delivered the
* incoming command.
* 2) Ensure the Handle, or some Handle it's moved into via the move
* constructor or move assignment operator, remains alive during the
* course of the asynchronous processing.
* 3) Ensure that the ConcreteCommandPath involved will be known when
* sending the response.
* 4) When ready to send the response:
* * Ensure that no other Matter tasks are running in parallel (e.g. by
* running on the Matter event loop or holding the Matter stack lock).
* * Call Get() to get the CommandHandler.
* * Check that Get() did not return null.
* * Add the response to the CommandHandler via one of the Add* methods.
* * Let the Handle get destroyed, or manually call Handle::Release() if
* destruction of the Handle is not desirable for some reason.
*
* The Invoke Response will not be sent until all outstanding Handles have
* been destroyed or have had Release called.
*/
class Handle : public IntrusiveListNodeBase<>
{
public:
Handle() {}
Handle(const Handle & handle) = delete;
Handle(Handle && handle)
{
Init(handle.mpHandler);
handle.Release();
}
Handle(decltype(nullptr)) {}
Handle(CommandHandler * handler);
~Handle() { Release(); }
Handle & operator=(Handle && handle)
{
Release();
Init(handle.mpHandler);
handle.Release();
return *this;
}
Handle & operator=(decltype(nullptr))
{
Release();
return *this;
}
/**
* Get the CommandHandler object it holds. Get() may return a nullptr if the CommandHandler object it holds is no longer
* valid.
*/
CommandHandler * Get();
void Release();
void Invalidate() { mpHandler = nullptr; }
private:
void Init(CommandHandler * handler);
CommandHandler * mpHandler = nullptr;
};
/**
* Adds the given command status and returns any failures in adding statuses (e.g. out
* of buffer space) to the caller
*/
virtual CHIP_ERROR FallibleAddStatus(const ConcreteCommandPath & aRequestCommandPath,
const Protocols::InteractionModel::Status aStatus, const char * context = nullptr) = 0;
/**
* Adds a status when the caller is unable to handle any failures. Logging is performed
* and failure to register the status is checked with VerifyOrDie.
*/
virtual void AddStatus(const ConcreteCommandPath & aCommandPath, const Protocols::InteractionModel::Status aStatus,
const char * context = nullptr) = 0;
virtual CHIP_ERROR AddClusterSpecificSuccess(const ConcreteCommandPath & aRequestCommandPath, ClusterStatus aClusterStatus) = 0;
virtual CHIP_ERROR AddClusterSpecificFailure(const ConcreteCommandPath & aRequestCommandPath, ClusterStatus aClusterStatus) = 0;
/**
* GetAccessingFabricIndex() may only be called during synchronous command
* processing. Anything that runs async (while holding a
* CommandHandler::Handle or equivalent) must not call this method, because
* it will not work right if the session we're using was evicted.
*/
virtual FabricIndex GetAccessingFabricIndex() const = 0;
/**
* API for adding a data response. The `aEncodable` is generally expected to encode
* a ClusterName::Commands::CommandName::Type struct, however any object should work.
*
* @param [in] aRequestCommandPath the concrete path of the command we are
* responding to.
* @param [in] aResponseCommandId the command whose content is being encoded.
* @param [in] aEncodable - an encodable that places the command data structure
* for `aResponseCommandId` into a TLV Writer.
*
* If you have no great way of handling the returned CHIP_ERROR, consider
* using `AddResponse` which will automatically reply with `Failure` in
* case AddResponseData fails.
*/
virtual CHIP_ERROR AddResponseData(const ConcreteCommandPath & aRequestCommandPath, CommandId aResponseCommandId,
const DataModel::EncodableToTLV & aEncodable) = 0;
/**
* Attempts to encode a response to a command.
*
* `aRequestCommandPath` represents the request path (endpoint/cluster/commandid) and the reply
* will preserve the same path and switch the command id to aResponseCommandId.
*
* As this command does not return any error codes, it must try its best to encode the reply
* and if it fails, it MUST encode a `Protocols::InteractionModel::Status::Failure` as a
* reply (i.e. a reply is guaranteed to be sent).
*
* Above is the main difference from AddResponseData: AddResponse will auto-reply with failure while
* AddResponseData allows the caller to try to deal with any CHIP_ERRORs.
*/
virtual void AddResponse(const ConcreteCommandPath & aRequestCommandPath, CommandId aResponseCommandId,
const DataModel::EncodableToTLV & aEncodable) = 0;
/**
* Check whether the InvokeRequest we are handling is a timed invoke.
*/
virtual bool IsTimedInvoke() const = 0;
/**
* @brief Flush acks right away for a slow command
*
* Some commands that do heavy lifting of storage/crypto should
* ack right away to improve reliability and reduce needless retries. This
* method can be manually called in commands that are especially slow to
* immediately schedule an acknowledgement (if needed) since the delayed
* stand-alone ack timer may actually not hit soon enough due to blocking command
* execution.
*
*/
virtual void FlushAcksRightAwayOnSlowCommand() = 0;
virtual Access::SubjectDescriptor GetSubjectDescriptor() const = 0;
/**
* Gets the inner exchange context object, without ownership.
*
* WARNING: This is dangerous, since it is directly interacting with the
* exchange being managed automatically by mpResponder and
* if not done carefully, may end up with use-after-free errors.
*
* @return The inner exchange context, might be nullptr if no
* exchange context has been assigned or the context
* has been released.
*/
virtual Messaging::ExchangeContext * GetExchangeContext() const = 0;
/**
* API for adding a data response. The template parameter T is generally
* expected to be a ClusterName::Commands::CommandName::Type struct, but any
* object that can be encoded using the DataModel::Encode machinery and
* exposes the right command id will work.
*
* If you have no great way of handling the returned CHIP_ERROR, consider
* using `AddResponse` which will automatically reply with `Failure` in
* case AddResponseData fails.
*
* @param [in] aRequestCommandPath the concrete path of the command we are
* responding to.
*
* The response path will be the same as the request, except the
* reply command ID used will be `CommandData::GetCommandId()` assumed
* to be a member of the templated type
*
* @param [in] aData the data for the response. It is expected to provide
* `GetCommandData` as a STATIC on its type as well as encode the
* correct data structure for building a reply.
*/
template <typename CommandData>
CHIP_ERROR AddResponseData(const ConcreteCommandPath & aRequestCommandPath, const CommandData & aData)
{
DataModel::EncodableType<CommandData> encoder(aData);
return AddResponseData(aRequestCommandPath, CommandData::GetCommandId(), encoder);
}
/**
* API for adding a response. This will try to encode a data response (response command), and if that fails
* it will encode a Protocols::InteractionModel::Status::Failure status response instead.
*
* Above is the main difference from AddResponseData: AddResponse will auto-reply with failure while
* AddResponseData allows the caller to try to deal with any CHIP_ERRORs.
*
* The template parameter T is generally expected to be a ClusterName::Commands::CommandName::Type struct, but any object that
* can be encoded using the DataModel::Encode machinery and exposes the right command id will work.
*
* Since the function will call AddStatus when it fails to encode the data, it cannot send any response when it fails to encode
* a status code since another AddStatus call will also fail. The error from AddStatus will just be logged.
*
* @param [in] aRequestCommandPath the concrete path of the command we are
* responding to.
* @param [in] aData the data for the response.
*/
template <typename CommandData>
void AddResponse(const ConcreteCommandPath & aRequestCommandPath, const CommandData & aData)
{
DataModel::EncodableType<CommandData> encodable(aData);
AddResponse(aRequestCommandPath, CommandData::GetCommandId(), encodable);
}
protected:
/**
* IncrementHoldOff will increase the inner refcount of the CommandHandler.
*
* Users should use CommandHandler::Handle for management the lifespan of the CommandHandler.
* DefRef should be released in reasonable time, and Close() should only be called when the refcount reached 0.
*/
virtual void IncrementHoldOff(Handle * apHandle) {}
/**
* DecrementHoldOff is used by CommandHandler::Handle for decreasing the refcount of the CommandHandler.
* When refcount reached 0, CommandHandler will send the response to the peer and shutdown.
*/
virtual void DecrementHoldOff(Handle * apHandle) {}
};
} // namespace app
} // namespace chip