Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 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 read client for a CHIP Interaction Data model
22 : *
23 : */
24 :
25 : #pragma once
26 : #include "system/SystemClock.h"
27 : #include <app/AppConfig.h>
28 : #include <app/AttributePathParams.h>
29 : #include <app/ConcreteAttributePath.h>
30 : #include <app/EventHeader.h>
31 : #include <app/EventPathParams.h>
32 : #include <app/MessageDef/ReadRequestMessage.h>
33 : #include <app/MessageDef/StatusIB.h>
34 : #include <app/MessageDef/StatusResponseMessage.h>
35 : #include <app/MessageDef/SubscribeRequestMessage.h>
36 : #include <app/MessageDef/SubscribeResponseMessage.h>
37 : #include <app/OperationalSessionSetup.h>
38 : #include <app/ReadPrepareParams.h>
39 : #include <app/data-model/Decode.h>
40 : #include <lib/core/CHIPCallback.h>
41 : #include <lib/core/CHIPCore.h>
42 : #include <lib/core/TLVDebug.h>
43 : #include <lib/support/CodeUtils.h>
44 : #include <lib/support/DLLUtil.h>
45 : #include <lib/support/logging/CHIPLogging.h>
46 : #include <messaging/ExchangeContext.h>
47 : #include <messaging/ExchangeHolder.h>
48 : #include <messaging/ExchangeMgr.h>
49 : #include <messaging/Flags.h>
50 : #include <protocols/Protocols.h>
51 : #include <system/SystemPacketBuffer.h>
52 :
53 : #if CHIP_CONFIG_ENABLE_READ_CLIENT
54 : namespace chip {
55 : namespace app {
56 :
57 : class InteractionModelEngine;
58 :
59 : /**
60 : * @class ReadClient
61 : *
62 : * @brief The read client represents the initiator side of a Read Or Subscribe Interaction (depending on the APIs invoked).
63 : *
64 : * When used to manage subscriptions, the client provides functionality to automatically re-subscribe as needed,
65 : * including re-establishing CASE under certain conditions (see Callback::OnResubscriptionNeeded for more info).
66 : * This is the default behavior. A consumer can completely opt-out of this behavior by over-riding
67 : * Callback::OnResubscriptionNeeded and providing an alternative implementation.
68 : *
69 : */
70 : class ReadClient : public Messaging::ExchangeDelegate
71 : {
72 : public:
73 : class Callback
74 : {
75 : public:
76 0 : Callback() = default;
77 :
78 : // Callbacks are not expected to be copyable or movable.
79 : Callback(const Callback &) = delete;
80 : Callback(Callback &&) = delete;
81 : Callback & operator=(const Callback &) = delete;
82 : Callback & operator=(Callback &&) = delete;
83 :
84 0 : virtual ~Callback() = default;
85 :
86 : /**
87 : * Used to notify a (maybe empty) report data is received from peer and the subscription and the peer is alive.
88 : *
89 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
90 : *
91 : */
92 0 : virtual void NotifySubscriptionStillActive(const ReadClient & apReadClient) {}
93 :
94 : /**
95 : * Used to signal the commencement of processing of the first attribute or event report received in a given exchange.
96 : *
97 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
98 : *
99 : * Once OnReportBegin has been called, either OnReportEnd or OnError will be called before OnDone.
100 : *
101 : */
102 0 : virtual void OnReportBegin() {}
103 :
104 : /**
105 : * Used to signal the completion of processing of the last attribute or event report in a given exchange.
106 : *
107 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
108 : *
109 : */
110 0 : virtual void OnReportEnd() {}
111 :
112 : /**
113 : * Used to deliver event data received through the Read and Subscribe interactions
114 : *
115 : * Only one of the apData and apStatus can be non-null.
116 : *
117 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
118 : *
119 : * @param[in] aEventHeader The event header in report response.
120 : * @param[in] apData A TLVReader positioned right on the payload of the event.
121 : * @param[in] apStatus Event-specific status, containing an InteractionModel::Status code as well as an optional
122 : * cluster-specific status code.
123 : */
124 0 : virtual void OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData, const StatusIB * apStatus) {}
125 :
126 : /**
127 : * Used to deliver attribute data received through the Read and Subscribe interactions.
128 : *
129 : * This callback will be called when:
130 : * - Receiving attribute data as response of Read interactions
131 : * - Receiving attribute data as reports of subscriptions
132 : * - Receiving attribute data as initial reports of subscriptions
133 : *
134 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
135 : *
136 : * @param[in] aPath The attribute path field in report response.
137 : * @param[in] apData The attribute data of the given path, will be a nullptr if status is not Success.
138 : * @param[in] aStatus Attribute-specific status, containing an InteractionModel::Status code as well as an
139 : * optional cluster-specific status code.
140 : */
141 0 : virtual void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) {}
142 :
143 : /**
144 : * OnSubscriptionEstablished will be called when a subscription is established for the given subscription transaction.
145 : * If using auto resubscription, OnSubscriptionEstablished will be called whenever resubscription is established.
146 : *
147 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
148 : *
149 : * @param[in] aSubscriptionId The identifier of the subscription that was established.
150 : */
151 0 : virtual void OnSubscriptionEstablished(SubscriptionId aSubscriptionId) {}
152 :
153 : /**
154 : * OnResubscriptionNeeded will be called when a subscription that was started with SendAutoResubscribeRequest has terminated
155 : * and re-subscription is needed. The termination cause is provided to help inform subsequent re-subscription logic.
156 : *
157 : * The base implementation automatically re-subscribes at appropriate intervals taking the termination cause into account
158 : * (see ReadClient::DefaultResubscribePolicy for more details). If the default implementation doesn't suffice, the logic of
159 : * ReadClient::DefaultResubscribePolicy is broken down into its constituent methods that are publicly available for
160 : * applications to call and sequence.
161 : *
162 : * If the peer is LIT ICD, and the timeout is reached, `aTerminationCause` will be
163 : * `CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT`. In this case, returning `CHIP_NO_ERROR` will still trigger a resubscribe
164 : * attempt, while returning `CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT` will put the subscription in the
165 : * `InactiveICDSubscription` state. In the latter case, OnResubscriptionNeeded will be called again when
166 : * `OnActiveModeNotification` is called.
167 : *
168 : * If the method is over-ridden, it's the application's responsibility to take the appropriate steps needed to eventually
169 : * call-back into the ReadClient object to schedule a re-subscription (by invoking ReadClient::ScheduleResubscription).
170 : *
171 : * If the application DOES NOT want re-subscription to happen on a particular invocation of this method, returning anything
172 : * other than CHIP_NO_ERROR will terminate the interaction and result in OnError, OnDeallocatePaths and OnDone being called
173 : * in that sequence.
174 : *
175 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
176 : *
177 : * @param[in] aTerminationCause The cause of failure of the subscription that just terminated.
178 : */
179 0 : virtual CHIP_ERROR OnResubscriptionNeeded(ReadClient * apReadClient, CHIP_ERROR aTerminationCause)
180 : {
181 0 : return apReadClient->DefaultResubscribePolicy(aTerminationCause);
182 : }
183 :
184 : /**
185 : * OnError will be called when an error occurs *after* a successful call to SendRequest(). The following
186 : * errors will be delivered through this call in the aError field:
187 : *
188 : * - CHIP_ERROR_TIMEOUT: A response was not received within the expected response timeout.
189 : * - CHIP_ERROR_*TLV*: A malformed, non-compliant response was received from the server.
190 : * - CHIP_ERROR encapsulating a StatusIB: If we got a non-path-specific
191 : * status response from the server. In that case, constructing
192 : * a StatusIB from the error can be used to extract the status.
193 : * - CHIP_ERROR*: All other cases.
194 : *
195 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
196 : *
197 : * @param[in] aError A system error code that conveys the overall error code.
198 : */
199 0 : virtual void OnError(CHIP_ERROR aError) {}
200 :
201 : /**
202 : * OnDone will be called when ReadClient has finished all work and it is
203 : * safe to destroy and free the allocated ReadClient object and any
204 : * other objects associated with the Read or Subscribe interaction.
205 : *
206 : * The ReadClient is allowed to be destroyed during execution of this callback.
207 : *
208 : * This function will:
209 : * - Only be called after a successful call to SendRequest or
210 : * SendAutoResubscribeRequest has been made, when the read completes or
211 : * the subscription is shut down.
212 : * - Not be called if the ReadClient instance is destroyed before
213 : * the OnDone call happens.
214 : * - Always be called exactly *once* for a given ReadClient instance,
215 : * if it's called at all.
216 : * - Be called even in error circumstances.
217 : *
218 : * @param[in] apReadClient the ReadClient for the completed interaction.
219 : */
220 : virtual void OnDone(ReadClient * apReadClient) = 0;
221 :
222 : /**
223 : * This function is invoked when using SendAutoResubscribeRequest, where the ReadClient was configured to auto re-subscribe
224 : * and the ReadPrepareParams was moved into this client for management. This will have to be free'ed appropriately by the
225 : * application. If SendAutoResubscribeRequest fails, this function will be called before it returns the failure. If
226 : * SendAutoResubscribeRequest succeeds, this function will be called immediately before calling OnDone, or
227 : * when the ReadClient is destroyed, if that happens before OnDone. If SendAutoResubscribeRequest is not called,
228 : * this function will not be called.
229 : */
230 0 : virtual void OnDeallocatePaths(ReadPrepareParams && aReadPrepareParams) {}
231 :
232 : /**
233 : * This function is invoked when constructing a read/subscribeRequest that does not have data
234 : * version filters specified, to give the callback a chance to provide some.
235 : *
236 : * This function is expected to encode as many complete data version filters as will fit into
237 : * the buffer, rolling back any partially-encoded filters if it runs out of space, and set the
238 : * aEncodedDataVersionList boolean to true if it has successfully encoded at least one data version filter.
239 : *
240 : * Otherwise aEncodedDataVersionList will be set to false.
241 : *
242 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
243 : */
244 0 : virtual CHIP_ERROR OnUpdateDataVersionFilterList(DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder,
245 : const Span<AttributePathParams> & aAttributePaths,
246 : bool & aEncodedDataVersionList)
247 : {
248 0 : aEncodedDataVersionList = false;
249 0 : return CHIP_NO_ERROR;
250 : }
251 :
252 : /**
253 : * Get highest received event number.
254 : * If the application does not want to filter events by event number, it should call ClearValue() on aEventNumber
255 : * and return CHIP_NO_ERROR. An error return from this function will fail the entire read client interaction.
256 : *
257 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
258 : */
259 0 : virtual CHIP_ERROR GetHighestReceivedEventNumber(Optional<EventNumber> & aEventNumber)
260 : {
261 0 : aEventNumber.ClearValue();
262 0 : return CHIP_NO_ERROR;
263 : }
264 :
265 : /**
266 : * OnUnsolicitedMessageFromPublisher will be called for a subscription
267 : * ReadClient when any incoming message is received from a matching
268 : * node on the fabric.
269 : *
270 : * This callback will be called:
271 : * - When receiving any unsolicited communication from the node
272 : * - Even for disconnected subscriptions.
273 : *
274 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
275 : *
276 : * @param[in] apReadClient the ReadClient for the subscription.
277 : */
278 0 : virtual void OnUnsolicitedMessageFromPublisher(ReadClient * apReadClient) {}
279 :
280 : /**
281 : * OnCASESessionEstablished will be called for a subscription ReadClient when
282 : * it finishes setting up a CASE session, as part of either automatic
283 : * re-subscription or doing an initial subscribe based on ScopedNodeId.
284 : *
285 : * The callee is allowed to modify the ReadPrepareParams (e.g. to change
286 : * things like min/max intervals based on the session parameters).
287 : *
288 : * The ReadClient MUST NOT be destroyed during execution of this callback (i.e. before the callback returns).
289 : */
290 0 : virtual void OnCASESessionEstablished(const SessionHandle & aSession, ReadPrepareParams & aSubscriptionParams) {}
291 : };
292 :
293 : enum class InteractionType : uint8_t
294 : {
295 : Read,
296 : Subscribe,
297 : };
298 :
299 : enum class PeerType : uint8_t
300 : {
301 : kNormal,
302 : kLITICD,
303 : };
304 :
305 : /**
306 : *
307 : * Constructor.
308 : *
309 : * The callback passed in has to outlive this ReadClient object.
310 : *
311 : * This object can outlive the InteractionModelEngine passed in. However, upon shutdown of the engine,
312 : * this object will cease to function correctly since it depends on the engine for a number of critical functions.
313 : *
314 : * @param[in] apImEngine A valid pointer to the IM engine.
315 : * @param[in] apExchangeMgr A pointer to the ExchangeManager object. Allowed to be null
316 : * if the version of SendAutoResubscribeRequest that takes a
317 : * ScopedNodeId is used.
318 : * @param[in] apCallback Callback set by application.
319 : * @param[in] aInteractionType Type of interaction (read or subscribe)
320 : *
321 : * @retval #CHIP_ERROR_INCORRECT_STATE incorrect state if it is already initialized
322 : * @retval #CHIP_NO_ERROR On success.
323 : *
324 : */
325 : ReadClient(InteractionModelEngine * apImEngine, Messaging::ExchangeManager * apExchangeMgr, Callback & apCallback,
326 : InteractionType aInteractionType);
327 :
328 : /**
329 : * Destructor.
330 : *
331 : * The ReadClient object may be destroyed at any time while not in the middle of executing a ReadClient::Callback
332 : * callback, and may also be destroyed from inside the OnDone callback.
333 : *
334 : * Destroying the ReadClient will abort the exchange context if a valid one still exists. It will also cancel any
335 : * liveness timers that may be active.
336 : *
337 : * OnDone() will not be called if the ReadClient is destroyed before that call would have happened.
338 : */
339 : ~ReadClient() override;
340 :
341 : /**
342 : * Send a request. There can be one request outstanding on a given ReadClient.
343 : * If SendRequest returns success, no more SendRequest calls can happen on this ReadClient
344 : * until the corresponding OnDone call has happened.
345 : *
346 : * This will send either a Read Request or a Subscribe Request depending on
347 : * the InteractionType this read client was initialized with.
348 : *
349 : * If the params contain more data version filters than can fit in the request packet
350 : * the list will be truncated as needed, i.e. filter inclusion is on a best effort basis.
351 : *
352 : * @retval #others fail to send read request
353 : * @retval #CHIP_NO_ERROR On success.
354 : */
355 : CHIP_ERROR SendRequest(ReadPrepareParams & aReadPrepareParams);
356 :
357 : /**
358 : * Re-activate an inactive subscription.
359 : *
360 : * This function should be called when the peer is an ICD that is checking in and this ReadClient represents a subscription
361 : * that would cause that ICD to not need to check in anymore.
362 : *
363 : * This API only works when issuing subscription via SendAutoResubscribeRequest.
364 : */
365 : void OnActiveModeNotification();
366 :
367 : void OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload);
368 :
369 195 : void OnUnsolicitedMessageFromPublisher()
370 : {
371 195 : TriggerResubscribeIfScheduled("unsolicited message");
372 :
373 : // Then notify callbacks
374 195 : mpCallback.OnUnsolicitedMessageFromPublisher(this);
375 195 : }
376 :
377 : auto GetSubscriptionId() const
378 : {
379 : using returnType = Optional<decltype(mSubscriptionId)>;
380 : return mInteractionType == InteractionType::Subscribe ? returnType(mSubscriptionId) : returnType::Missing();
381 : }
382 :
383 41 : FabricIndex GetFabricIndex() const { return mPeer.GetFabricIndex(); }
384 71 : NodeId GetPeerNodeId() const { return mPeer.GetNodeId(); }
385 645 : bool IsReadType() { return mInteractionType == InteractionType::Read; }
386 5006 : bool IsSubscriptionType() const { return mInteractionType == InteractionType::Subscribe; };
387 :
388 : /*
389 : * Retrieve the reporting intervals associated with an active subscription. This should only be called if we're of subscription
390 : * interaction type and after a subscription has been established.
391 : */
392 : CHIP_ERROR GetReportingIntervals(uint16_t & aMinIntervalFloorSeconds, uint16_t & aMaxIntervalCeilingSeconds) const
393 : {
394 : VerifyOrReturnError(IsSubscriptionType(), CHIP_ERROR_INCORRECT_STATE);
395 : VerifyOrReturnError(IsSubscriptionActive(), CHIP_ERROR_INCORRECT_STATE);
396 :
397 : aMinIntervalFloorSeconds = mMinIntervalFloorSeconds;
398 : aMaxIntervalCeilingSeconds = mMaxInterval;
399 :
400 : return CHIP_NO_ERROR;
401 : }
402 :
403 161 : ReadClient * GetNextClient() { return mpNext; }
404 154 : void SetNextClient(ReadClient * apClient) { mpNext = apClient; }
405 :
406 : /**
407 : * Like SendSubscribeRequest, but the ReadClient will automatically attempt to re-establish the subscription if
408 : * we decide that the subscription has dropped. The exact behavior of the re-establishment can be controlled
409 : * by overriding Callback::OnResubscriptionNeeded(). If not overridden, a default behavior with exponential
410 : * backoff from will be used.
411 : *
412 : * The application has to know to
413 : * a) allocate a ReadPrepareParams object that will have fields mpEventPathParamsList and mpAttributePathParamsList and
414 : * mpDataVersionFilterList with lifetimes as long as the ReadClient itself and b) free those up later in the call to
415 : * OnDeallocatePaths. Note: At a given time in the system, you can either have a single subscription with re-sub enabled that
416 : * has mKeepSubscriptions = false, OR, multiple subs with re-sub enabled with mKeepSubscriptions = true. You shall not
417 : * have a mix of both simultaneously. If SendAutoResubscribeRequest is called at all, it guarantees that it will call
418 : * OnDeallocatePaths (either before returning error, or when OnDone is called or the ReadClient is destroyed).
419 : * SendAutoResubscribeRequest is the only case that calls OnDeallocatePaths, since that's the only case when the consumer
420 : * moved a ReadParams into the client.
421 : *
422 : */
423 : CHIP_ERROR SendAutoResubscribeRequest(ReadPrepareParams && aReadPrepareParams);
424 :
425 : /**
426 : * Like SendAutoResubscribeRequest above, but without a session being
427 : * available in the ReadPrepareParams. When this is used, the ReadClient is
428 : * responsible for setting up the CASE session itself.
429 : *
430 : * When using this version of SendAutoResubscribeRequest, any session to
431 : * which ReadPrepareParams has a reference will be ignored.
432 : */
433 : CHIP_ERROR SendAutoResubscribeRequest(const ScopedNodeId & aPublisherId, ReadPrepareParams && aReadPrepareParams);
434 :
435 : /**
436 : * This provides a standard re-subscription policy implementation that given a termination cause, does the following:
437 : * - Calculates the time till next subscription with fibonacci back-off (implemented by ComputeTimeTillNextSubscription()).
438 : * - Schedules the next subscription attempt at the computed interval from the previous step. Operational discovery and
439 : * CASE establishment will be attempted if aTerminationCause was CHIP_ERROR_TIMEOUT. In all other cases, it will attempt
440 : * to re-use a previously established session.
441 : */
442 : CHIP_ERROR DefaultResubscribePolicy(CHIP_ERROR aTerminationCause);
443 :
444 : /**
445 : * Computes the time, in milliseconds, until the next re-subscription over
446 : * an ever increasing window following a fibonacci sequence with the current retry count
447 : * used as input to the fibonacci algorithm.
448 : *
449 : * CHIP_RESUBSCRIBE_MAX_FIBONACCI_STEP_INDEX is the maximum value the retry count can tick up to.
450 : *
451 : */
452 : uint32_t ComputeTimeTillNextSubscription();
453 :
454 : /**
455 : * Schedules a re-subscription aTimeTillNextResubscriptionMs into the future.
456 : *
457 : * If an application wants to set up CASE on their own, they should call ComputeTimeTillNextSubscription() to compute the next
458 : * interval at which they should attempt CASE and attempt CASE at that time. On successful CASE establishment, this method
459 : * should be called with the new SessionHandle provided through 'aNewSessionHandle', 'aTimeTillNextResubscriptionMs' set to 0
460 : * (i.e async, but as soon as possible) and 'aReestablishCASE' set to false.
461 : *
462 : * Otherwise, if aReestablishCASE is true, operational discovery and CASE will be attempted at that time before
463 : * the actual IM interaction is initiated.
464 : *
465 : * aReestablishCASE SHALL NOT be set to true if a valid SessionHandle is provided through newSessionHandle.
466 : */
467 : CHIP_ERROR ScheduleResubscription(uint32_t aTimeTillNextResubscriptionMs, Optional<SessionHandle> aNewSessionHandle,
468 : bool aReestablishCASE);
469 :
470 : // Like SendSubscribeRequest, but allows sending certain forms of invalid
471 : // subscribe requests that servers are expected to reject, for testing
472 : // purposes. Should only be called from tests.
473 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
474 : CHIP_ERROR SendSubscribeRequestWithoutValidation(const ReadPrepareParams & aReadPrepareParams)
475 : {
476 : return SendSubscribeRequestImpl(aReadPrepareParams);
477 : }
478 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
479 :
480 : /**
481 : * Override the interval at which liveness of the subscription is assessed.
482 : * By default, this is set set to the max interval of the subscription + ACK timeout of the underlying session.
483 : *
484 : * This can be only be called once a subscription has been established and is active. Once called, this will cancel any existing
485 : * liveness timers and schedule a new one.
486 : *
487 : * This can be called from the Callback::OnSubscriptionEstablished callback.
488 : *
489 : */
490 : void OverrideLivenessTimeout(System::Clock::Timeout aLivenessTimeout);
491 :
492 : /**
493 : * If the ReadClient currently has a resubscription attempt scheduled,
494 : * trigger that attempt right now. This is generally useful when a consumer
495 : * has some sort of indication that the server side is currently up and
496 : * communicating, so right now is a good time to try to resubscribe.
497 : *
498 : * The reason string is used for logging if a resubscribe is triggered.
499 : */
500 : void TriggerResubscribeIfScheduled(const char * reason);
501 :
502 : /**
503 : * Returns the timeout after which we consider the subscription to have
504 : * dropped, if we have received no messages within that amount of time.
505 : *
506 : * Returns NullOptional if a subscription has not yet been established (and
507 : * hence the MaxInterval is not yet known), or if the subscription session
508 : * is gone and hence the relevant MRP parameters can no longer be determined.
509 : */
510 : Optional<System::Clock::Timeout> GetSubscriptionTimeout();
511 :
512 : private:
513 : friend class TestReadInteraction;
514 : friend class InteractionModelEngine;
515 :
516 : enum class ClientState : uint8_t
517 : {
518 : Idle, ///< The client has been initialized and is ready for a SendRequest
519 : AwaitingInitialReport, ///< The client is waiting for initial report
520 : AwaitingSubscribeResponse, ///< The client is waiting for subscribe response
521 : SubscriptionActive, ///< The client is maintaining subscription
522 : InactiveICDSubscription, ///< The client is waiting to resubscribe for LIT device
523 : };
524 :
525 : enum class ReportType
526 : {
527 : // kUnsolicited reports are the first message in an exchange.
528 : kUnsolicited,
529 : // kContinuingTransaction reports are responses to a message we sent.
530 : kContinuingTransaction
531 : };
532 :
533 611 : bool IsMatchingSubscriptionId(SubscriptionId aSubscriptionId)
534 : {
535 611 : return aSubscriptionId == mSubscriptionId && mInteractionType == InteractionType::Subscribe;
536 : }
537 :
538 : CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
539 : System::PacketBufferHandle && aPayload) override;
540 : void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override;
541 :
542 : /**
543 : * Updates the type (LIT ICD or not) of the peer.
544 : *
545 : * When the subscription is active, this function will just set the flag. When the subscription is an InactiveICDSubscription,
546 : * setting the peer type to SIT or normal devices will also trigger a resubscription attempt.
547 : *
548 : */
549 : void OnPeerTypeChange(PeerType aType);
550 :
551 : /**
552 : * Check if current read client is being used
553 : *
554 : */
555 2043 : bool IsIdle() const { return mState == ClientState::Idle; }
556 2189 : bool IsInactiveICDSubscription() const { return mState == ClientState::InactiveICDSubscription; }
557 1153 : bool IsSubscriptionActive() const { return mState == ClientState::SubscriptionActive; }
558 700 : bool IsAwaitingInitialReport() const { return mState == ClientState::AwaitingInitialReport; }
559 253 : bool IsAwaitingSubscribeResponse() const { return mState == ClientState::AwaitingSubscribeResponse; }
560 :
561 : CHIP_ERROR GenerateEventPaths(EventPathIBs::Builder & aEventPathsBuilder, const Span<EventPathParams> & aEventPaths);
562 : CHIP_ERROR GenerateAttributePaths(AttributePathIBs::Builder & aAttributePathIBsBuilder,
563 : const Span<AttributePathParams> & aAttributePaths);
564 :
565 : CHIP_ERROR GenerateDataVersionFilterList(DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder,
566 : const Span<AttributePathParams> & aAttributePaths,
567 : const Span<DataVersionFilter> & aDataVersionFilters, bool & aEncodedDataVersionList);
568 : CHIP_ERROR BuildDataVersionFilterList(DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder,
569 : const Span<AttributePathParams> & aAttributePaths,
570 : const Span<DataVersionFilter> & aDataVersionFilters, bool & aEncodedDataVersionList);
571 : CHIP_ERROR ReadICDOperatingModeFromAttributeDataIB(TLV::TLVReader && aReader, PeerType & aType);
572 : CHIP_ERROR ProcessAttributeReportIBs(TLV::TLVReader & aAttributeDataIBsReader);
573 : CHIP_ERROR ProcessEventReportIBs(TLV::TLVReader & aEventReportIBsReader);
574 :
575 : static void OnLivenessTimeoutCallback(System::Layer * apSystemLayer, void * apAppState);
576 : CHIP_ERROR ProcessSubscribeResponse(System::PacketBufferHandle && aPayload);
577 : CHIP_ERROR RefreshLivenessCheckTimer();
578 : CHIP_ERROR ComputeLivenessCheckTimerTimeout(System::Clock::Timeout * aTimeout);
579 : void CancelLivenessCheckTimer();
580 : void CancelResubscribeTimer();
581 : void TriggerResubscriptionForLivenessTimeout(CHIP_ERROR aReason);
582 : void MoveToState(const ClientState aTargetState);
583 : CHIP_ERROR ProcessAttributePath(AttributePathIB::Parser & aAttributePath, ConcreteDataAttributePath & aClusterInfo);
584 : CHIP_ERROR ProcessReportData(System::PacketBufferHandle && aPayload, ReportType aReportType);
585 : const char * GetStateStr() const;
586 :
587 : /*
588 : * Checks if we should re-subscribe based on the specified re-subscription policy. If we should, re-subscription is scheduled
589 : * aNextResubscribeIntervalMsec is updated accordingly, and true is returned.
590 : *
591 : * If we should not resubscribe, false is returned.
592 : *
593 : * @param[out] aNextResubscribeIntervalMsec How long we will wait before trying to auto-resubscribe.
594 : */
595 : bool ResubscribeIfNeeded(uint32_t & aNextResubscribeIntervalMsec);
596 :
597 : // Specialized request-sending functions.
598 : CHIP_ERROR SendReadRequest(ReadPrepareParams & aReadPrepareParams);
599 : // SendSubscribeRequest performs som validation on aSubscribePrepareParams
600 : // and then calls SendSubscribeRequestImpl.
601 : CHIP_ERROR SendSubscribeRequest(const ReadPrepareParams & aSubscribePrepareParams);
602 : CHIP_ERROR SendSubscribeRequestImpl(const ReadPrepareParams & aSubscribePrepareParams);
603 : void UpdateDataVersionFilters(const ConcreteDataAttributePath & aPath);
604 : static void OnResubscribeTimerCallback(System::Layer * apSystemLayer, void * apAppState);
605 : // Called to ensure OnReportBegin is called before calling OnEventData or OnAttributeData
606 : void NoteReportingData();
607 :
608 : /*
609 : * Called internally to signal the completion of all work on this object, gracefully close the
610 : * exchange and finally, signal to the application that it's
611 : * safe to release this object.
612 : *
613 : * If aError != CHIP_NO_ERROR, this will trigger re-subscriptions if allowResubscription is true
614 : * AND if this ReadClient instance is tracking a subscription AND the applications decides to do so
615 : * in their implementation of Callback::OnResubscriptionNeeded().
616 : */
617 : void Close(CHIP_ERROR aError, bool allowResubscription = true);
618 :
619 : void StopResubscription();
620 : void ClearActiveSubscriptionState();
621 :
622 : static void HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr,
623 : const SessionHandle & sessionHandle);
624 : static void HandleDeviceConnectionFailure(void * context, const OperationalSessionSetup::ConnectionFailureInfo & failureInfo);
625 :
626 : CHIP_ERROR GetMinEventNumber(const ReadPrepareParams & aReadPrepareParams, Optional<EventNumber> & aEventMin);
627 :
628 : /**
629 : * Start setting up a CASE session to our peer, if we can locate a
630 : * CASESessionManager. Returns error if we did not even manage to kick off
631 : * a CASE attempt.
632 : */
633 : CHIP_ERROR EstablishSessionToPeer();
634 :
635 : Messaging::ExchangeManager * mpExchangeMgr = nullptr;
636 : Messaging::ExchangeHolder mExchange;
637 : Callback & mpCallback;
638 : ClientState mState = ClientState::Idle;
639 : bool mIsReporting = false;
640 : bool mIsInitialReport = true;
641 : // boolean to check if client is waiting for the first priming report
642 : bool mWaitingForFirstPrimingReport = true;
643 : bool mPendingMoreChunks = false;
644 : uint16_t mMinIntervalFloorSeconds = 0;
645 : uint16_t mMaxInterval = 0;
646 : SubscriptionId mSubscriptionId = 0;
647 : ScopedNodeId mPeer;
648 : InteractionType mInteractionType = InteractionType::Read;
649 : Timestamp mEventTimestamp;
650 :
651 : bool mForceCaseOnNextResub = true;
652 : bool mIsResubscriptionScheduled = false;
653 :
654 : // mMinimalResubscribeDelay is used to store the delay returned with a BUSY
655 : // response to a Sigma1 message.
656 : System::Clock::Milliseconds16 mMinimalResubscribeDelay = System::Clock::kZero;
657 :
658 : chip::Callback::Callback<OnDeviceConnected> mOnConnectedCallback;
659 : chip::Callback::Callback<OperationalSessionSetup::OnSetupFailure> mOnConnectionFailureCallback;
660 :
661 : ReadClient * mpNext = nullptr;
662 : InteractionModelEngine * mpImEngine = nullptr;
663 :
664 : //
665 : // This stores the params associated with the interaction in a specific set of cases:
666 : // 1. Stores all parameters when used with subscriptions initiated using SendAutoResubscribeRequest.
667 : // 2. Stores just the SessionHolder when used with any subscriptions.
668 : //
669 : ReadPrepareParams mReadPrepareParams;
670 : uint32_t mNumRetries = 0;
671 :
672 : System::Clock::Timeout mLivenessTimeoutOverride = System::Clock::kZero;
673 :
674 : bool mIsPeerLIT = false;
675 :
676 : // End Of Container (0x18) uses one byte.
677 : static constexpr uint16_t kReservedSizeForEndOfContainer = 1;
678 : // Reserved size for the uint8_t InteractionModelRevision flag, which takes up 1 byte for the control tag and 1 byte for the
679 : // context tag, 1 byte for value
680 : static constexpr uint16_t kReservedSizeForIMRevision = 1 + 1 + 1;
681 : // Reserved buffer for TLV level overhead (the overhead for data version filter IBs EndOfContainer, IM reversion end
682 : // of RequestMessage (another end of container)).
683 : static constexpr uint16_t kReservedSizeForTLVEncodingOverhead =
684 : kReservedSizeForEndOfContainer + kReservedSizeForIMRevision + kReservedSizeForEndOfContainer;
685 :
686 : #if CHIP_PROGRESS_LOGGING
687 : // Tracks the time when a subscribe request is successfully sent.
688 : // This timestamp allows for logging the duration taken to established the subscription.
689 : System::Clock::Timestamp mSubscribeRequestTime = System::Clock::kZero;
690 : #endif
691 : };
692 :
693 : }; // namespace app
694 : }; // namespace chip
695 : #endif // CHIP_CONFIG_ENABLE_READ_CLIENT
|