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 the initiator side of a CHIP Read Interaction.
22 : *
23 : */
24 :
25 : #include <app/AppConfig.h>
26 : #include <app/InteractionModelEngine.h>
27 : #include <app/InteractionModelHelper.h>
28 : #include <app/ReadClient.h>
29 : #include <app/StatusResponse.h>
30 : #include <assert.h>
31 : #include <lib/core/TLVTypes.h>
32 : #include <lib/support/FibonacciUtils.h>
33 : #include <messaging/ReliableMessageMgr.h>
34 : #include <messaging/ReliableMessageProtocolConfig.h>
35 : #include <platform/LockTracker.h>
36 : #include <tracing/metric_event.h>
37 :
38 : #include <app-common/zap-generated/cluster-objects.h>
39 : #include <app-common/zap-generated/ids/Attributes.h>
40 : #include <app-common/zap-generated/ids/Clusters.h>
41 :
42 : namespace chip {
43 : namespace app {
44 :
45 : using Status = Protocols::InteractionModel::Status;
46 :
47 1127 : ReadClient::ReadClient(InteractionModelEngine * apImEngine, Messaging::ExchangeManager * apExchangeMgr, Callback & apCallback,
48 1127 : InteractionType aInteractionType) :
49 1127 : mExchange(*this),
50 1127 : mpCallback(apCallback), mOnConnectedCallback(HandleDeviceConnected, this),
51 2254 : mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this)
52 : {
53 1127 : assertChipStackLockedByCurrentThread();
54 :
55 1127 : mpExchangeMgr = apExchangeMgr;
56 1127 : mInteractionType = aInteractionType;
57 :
58 1127 : mpImEngine = apImEngine;
59 :
60 1127 : if (aInteractionType == InteractionType::Subscribe)
61 : {
62 319 : mpImEngine->AddReadClient(this);
63 : }
64 1127 : }
65 :
66 285 : void ReadClient::ClearActiveSubscriptionState()
67 : {
68 285 : mIsReporting = false;
69 285 : mWaitingForFirstPrimingReport = true;
70 285 : mPendingMoreChunks = false;
71 285 : mMinIntervalFloorSeconds = 0;
72 285 : mMaxInterval = 0;
73 285 : mSubscriptionId = 0;
74 285 : mIsResubscriptionScheduled = false;
75 285 : mSuppressResponse = false;
76 :
77 285 : MoveToState(ClientState::Idle);
78 285 : }
79 :
80 589 : void ReadClient::StopResubscription()
81 : {
82 589 : CancelLivenessCheckTimer();
83 589 : CancelResubscribeTimer();
84 :
85 : // Only deallocate the paths if they are not already deallocated.
86 589 : if (mReadPrepareParams.mpAttributePathParamsList != nullptr || mReadPrepareParams.mpEventPathParamsList != nullptr ||
87 362 : mReadPrepareParams.mpDataVersionFilterList != nullptr)
88 : {
89 227 : mpCallback.OnDeallocatePaths(std::move(mReadPrepareParams));
90 : // Make sure we will never try to free those pointers again.
91 227 : mReadPrepareParams.mpAttributePathParamsList = nullptr;
92 227 : mReadPrepareParams.mAttributePathParamsListSize = 0;
93 227 : mReadPrepareParams.mpEventPathParamsList = nullptr;
94 227 : mReadPrepareParams.mEventPathParamsListSize = 0;
95 227 : mReadPrepareParams.mpDataVersionFilterList = nullptr;
96 227 : mReadPrepareParams.mDataVersionFilterListSize = 0;
97 : }
98 589 : }
99 :
100 1215 : ReadClient::~ReadClient()
101 : {
102 1127 : assertChipStackLockedByCurrentThread();
103 :
104 1127 : if (IsSubscriptionType())
105 : {
106 319 : StopResubscription();
107 :
108 : // Only remove ourselves from the engine's tracker list if we still continue to have a valid pointer to it.
109 : // This won't be the case if the engine shut down before this destructor was called (in which case, mpImEngine
110 : // will point to null)
111 : //
112 319 : if (mpImEngine)
113 : {
114 154 : mpImEngine->RemoveReadClient(this);
115 : }
116 : }
117 1215 : }
118 :
119 14 : uint32_t ReadClient::ComputeTimeTillNextSubscription()
120 : {
121 14 : uint32_t maxWaitTimeInMsec = 0;
122 14 : uint32_t waitTimeInMsec = 0;
123 14 : uint32_t minWaitTimeInMsec = 0;
124 :
125 14 : if (mNumRetries <= CHIP_RESUBSCRIBE_MAX_FIBONACCI_STEP_INDEX)
126 : {
127 14 : maxWaitTimeInMsec = GetFibonacciForIndex(mNumRetries) * CHIP_RESUBSCRIBE_WAIT_TIME_MULTIPLIER_MS;
128 : }
129 : else
130 : {
131 0 : maxWaitTimeInMsec = CHIP_RESUBSCRIBE_MAX_RETRY_WAIT_INTERVAL_MS;
132 : }
133 :
134 14 : if (maxWaitTimeInMsec != 0)
135 : {
136 3 : minWaitTimeInMsec = (CHIP_RESUBSCRIBE_MIN_WAIT_TIME_INTERVAL_PERCENT_PER_STEP * maxWaitTimeInMsec) / 100;
137 3 : waitTimeInMsec = minWaitTimeInMsec + (Crypto::GetRandU32() % (maxWaitTimeInMsec - minWaitTimeInMsec));
138 : }
139 :
140 14 : if (mMinimalResubscribeDelay.count() > waitTimeInMsec)
141 : {
142 0 : waitTimeInMsec = mMinimalResubscribeDelay.count();
143 : }
144 :
145 14 : return waitTimeInMsec;
146 : }
147 :
148 14 : CHIP_ERROR ReadClient::ScheduleResubscription(uint32_t aTimeTillNextResubscriptionMs, Optional<SessionHandle> aNewSessionHandle,
149 : bool aReestablishCASE)
150 : {
151 14 : VerifyOrReturnError(IsIdle(), CHIP_ERROR_INCORRECT_STATE);
152 :
153 : //
154 : // If we're establishing CASE, make sure we are not provided a new SessionHandle as well.
155 : //
156 14 : VerifyOrReturnError(!aReestablishCASE || !aNewSessionHandle.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
157 :
158 14 : if (aNewSessionHandle.HasValue())
159 : {
160 0 : mReadPrepareParams.mSessionHolder.Grab(aNewSessionHandle.Value());
161 : }
162 :
163 14 : mForceCaseOnNextResub = aReestablishCASE;
164 14 : if (mForceCaseOnNextResub && mReadPrepareParams.mSessionHolder)
165 : {
166 : // Mark our existing session defunct, so that we will try to
167 : // re-establish it when the timer fires (unless something re-establishes
168 : // before then).
169 0 : mReadPrepareParams.mSessionHolder->AsSecureSession()->MarkAsDefunct();
170 : }
171 :
172 28 : ReturnErrorOnFailure(
173 : InteractionModelEngine::GetInstance()->GetExchangeManager()->GetSessionManager()->SystemLayer()->StartTimer(
174 : System::Clock::Milliseconds32(aTimeTillNextResubscriptionMs), OnResubscribeTimerCallback, this));
175 14 : mIsResubscriptionScheduled = true;
176 :
177 14 : return CHIP_NO_ERROR;
178 : }
179 :
180 1029 : void ReadClient::Close(CHIP_ERROR aError, bool allowResubscription)
181 : {
182 1029 : if (IsReadType())
183 : {
184 1488 : if (aError != CHIP_NO_ERROR)
185 : {
186 51 : mpCallback.OnError(aError);
187 : }
188 : }
189 : else
190 : {
191 285 : if (IsAwaitingInitialReport() || IsAwaitingSubscribeResponse())
192 : {
193 : MATTER_LOG_METRIC_END(Tracing::kMetricDeviceSubscriptionSetup, aError);
194 : }
195 :
196 285 : ClearActiveSubscriptionState();
197 570 : if (aError != CHIP_NO_ERROR)
198 : {
199 : //
200 : // We infer that re-subscription was requested by virtue of having a non-zero list of event OR attribute paths present
201 : // in mReadPrepareParams. This would only be the case if an application called SendAutoResubscribeRequest which
202 : // populates mReadPrepareParams with the values provided by the application.
203 : //
204 116 : if (allowResubscription &&
205 49 : (mReadPrepareParams.mEventPathParamsListSize != 0 || mReadPrepareParams.mAttributePathParamsListSize != 0))
206 : {
207 16 : CHIP_ERROR originalReason = aError;
208 :
209 16 : aError = mpCallback.OnResubscriptionNeeded(this, aError);
210 32 : if (aError == CHIP_NO_ERROR)
211 : {
212 16 : return;
213 : }
214 4 : if (aError == CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT)
215 : {
216 4 : VerifyOrDie(originalReason == CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT);
217 2 : ChipLogProgress(DataManagement, "ICD device is inactive mark subscription as InactiveICDSubscription");
218 2 : MoveToState(ClientState::InactiveICDSubscription);
219 2 : return;
220 : }
221 : }
222 :
223 : //
224 : // Either something bad happened when requesting resubscription or the application has decided to not
225 : // continue by returning an error. Let's convey the error back up to the application
226 : // and shut everything down.
227 : //
228 100 : mpCallback.OnError(aError);
229 : }
230 :
231 269 : StopResubscription();
232 : }
233 :
234 1013 : mExchange.Release();
235 :
236 1013 : mpCallback.OnDone(this);
237 : }
238 :
239 0 : const char * ReadClient::GetStateStr() const
240 : {
241 : #if CHIP_DETAIL_LOGGING
242 0 : switch (mState)
243 : {
244 0 : case ClientState::Idle:
245 0 : return "Idle";
246 0 : case ClientState::AwaitingInitialReport:
247 0 : return "AwaitingInitialReport";
248 0 : case ClientState::AwaitingSubscribeResponse:
249 0 : return "AwaitingSubscribeResponse";
250 0 : case ClientState::SubscriptionActive:
251 0 : return "SubscriptionActive";
252 0 : case ClientState::InactiveICDSubscription:
253 0 : return "InactiveICDSubscription";
254 : }
255 : #endif // CHIP_DETAIL_LOGGING
256 0 : return "N/A";
257 : }
258 :
259 1980 : void ReadClient::MoveToState(const ClientState aTargetState)
260 : {
261 1980 : mState = aTargetState;
262 1980 : ChipLogDetail(DataManagement, "%s ReadClient[%p]: Moving to [%10.10s]", __func__, this, GetStateStr());
263 1980 : }
264 :
265 874 : CHIP_ERROR ReadClient::SendRequest(ReadPrepareParams & aReadPrepareParams)
266 : {
267 874 : if (mInteractionType == InteractionType::Read)
268 : {
269 796 : return SendReadRequest(aReadPrepareParams);
270 : }
271 :
272 78 : if (mInteractionType == InteractionType::Subscribe)
273 : {
274 78 : return SendSubscribeRequest(aReadPrepareParams);
275 : }
276 :
277 0 : return CHIP_ERROR_INVALID_ARGUMENT;
278 : }
279 :
280 796 : CHIP_ERROR ReadClient::SendReadRequest(ReadPrepareParams & aReadPrepareParams)
281 : {
282 796 : CHIP_ERROR err = CHIP_NO_ERROR;
283 :
284 796 : ChipLogDetail(DataManagement, "%s ReadClient[%p]: Sending Read Request", __func__, this);
285 :
286 796 : VerifyOrReturnError(ClientState::Idle == mState, err = CHIP_ERROR_INCORRECT_STATE);
287 :
288 : Span<AttributePathParams> attributePaths(aReadPrepareParams.mpAttributePathParamsList,
289 796 : aReadPrepareParams.mAttributePathParamsListSize);
290 796 : Span<EventPathParams> eventPaths(aReadPrepareParams.mpEventPathParamsList, aReadPrepareParams.mEventPathParamsListSize);
291 : Span<DataVersionFilter> dataVersionFilters(aReadPrepareParams.mpDataVersionFilterList,
292 796 : aReadPrepareParams.mDataVersionFilterListSize);
293 :
294 796 : System::PacketBufferHandle msgBuf;
295 796 : ReadRequestMessage::Builder request;
296 796 : System::PacketBufferTLVWriter writer;
297 :
298 796 : TEMPORARY_RETURN_IGNORED InitWriterWithSpaceReserved(writer, kReservedSizeForTLVEncodingOverhead);
299 796 : ReturnErrorOnFailure(request.Init(&writer));
300 :
301 796 : if (!attributePaths.empty())
302 : {
303 674 : AttributePathIBs::Builder & attributePathListBuilder = request.CreateAttributeRequests();
304 674 : ReturnErrorOnFailure(err = request.GetError());
305 674 : ReturnErrorOnFailure(GenerateAttributePaths(attributePathListBuilder, attributePaths));
306 : }
307 :
308 796 : if (!eventPaths.empty())
309 : {
310 318 : EventPathIBs::Builder & eventPathListBuilder = request.CreateEventRequests();
311 318 : ReturnErrorOnFailure(err = request.GetError());
312 :
313 318 : ReturnErrorOnFailure(GenerateEventPaths(eventPathListBuilder, eventPaths));
314 :
315 318 : Optional<EventNumber> eventMin;
316 318 : ReturnErrorOnFailure(GetMinEventNumber(aReadPrepareParams, eventMin));
317 318 : if (eventMin.HasValue())
318 : {
319 315 : EventFilterIBs::Builder & eventFilters = request.CreateEventFilters();
320 315 : ReturnErrorOnFailure(err = request.GetError());
321 315 : ReturnErrorOnFailure(eventFilters.GenerateEventFilter(eventMin.Value()));
322 : }
323 : }
324 :
325 796 : ReturnErrorOnFailure(request.IsFabricFiltered(aReadPrepareParams.mIsFabricFiltered).GetError());
326 :
327 796 : bool encodedDataVersionList = false;
328 796 : TLV::TLVWriter backup;
329 796 : request.Checkpoint(backup);
330 796 : DataVersionFilterIBs::Builder & dataVersionFilterListBuilder = request.CreateDataVersionFilters();
331 796 : ReturnErrorOnFailure(request.GetError());
332 796 : if (!attributePaths.empty())
333 : {
334 674 : ReturnErrorOnFailure(GenerateDataVersionFilterList(dataVersionFilterListBuilder, attributePaths, dataVersionFilters,
335 : encodedDataVersionList));
336 : }
337 796 : ReturnErrorOnFailure(dataVersionFilterListBuilder.GetWriter()->UnreserveBuffer(kReservedSizeForTLVEncodingOverhead));
338 796 : if (encodedDataVersionList)
339 : {
340 80 : ReturnErrorOnFailure(dataVersionFilterListBuilder.EndOfDataVersionFilterIBs());
341 : }
342 : else
343 : {
344 716 : request.Rollback(backup);
345 : }
346 :
347 796 : ReturnErrorOnFailure(request.EndOfReadRequestMessage());
348 796 : ReturnErrorOnFailure(writer.Finalize(&msgBuf));
349 :
350 796 : VerifyOrReturnError(aReadPrepareParams.mSessionHolder, CHIP_ERROR_MISSING_SECURE_SESSION);
351 :
352 796 : auto exchange = mpExchangeMgr->NewContext(aReadPrepareParams.mSessionHolder.Get().Value(), this);
353 796 : VerifyOrReturnError(exchange != nullptr, err = CHIP_ERROR_NO_MEMORY);
354 :
355 796 : mExchange.Grab(exchange);
356 :
357 796 : if (aReadPrepareParams.mTimeout == System::Clock::kZero)
358 : {
359 796 : ReturnErrorOnFailure(mExchange->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime));
360 : }
361 : else
362 : {
363 0 : mExchange->SetResponseTimeout(aReadPrepareParams.mTimeout);
364 : }
365 :
366 796 : ReturnErrorOnFailure(mExchange->SendMessage(Protocols::InteractionModel::MsgType::ReadRequest, std::move(msgBuf),
367 : Messaging::SendFlags(Messaging::SendMessageFlags::kExpectResponse)));
368 :
369 796 : mPeer = aReadPrepareParams.mSessionHolder->AsSecureSession()->GetPeer();
370 796 : MoveToState(ClientState::AwaitingInitialReport);
371 :
372 796 : return CHIP_NO_ERROR;
373 796 : }
374 :
375 344 : CHIP_ERROR ReadClient::GenerateEventPaths(EventPathIBs::Builder & aEventPathsBuilder, const Span<EventPathParams> & aEventPaths)
376 : {
377 857 : for (auto & event : aEventPaths)
378 : {
379 513 : VerifyOrReturnError(event.IsValidEventPath(), CHIP_ERROR_IM_MALFORMED_EVENT_PATH_IB);
380 513 : EventPathIB::Builder & path = aEventPathsBuilder.CreatePath();
381 513 : ReturnErrorOnFailure(aEventPathsBuilder.GetError());
382 513 : ReturnErrorOnFailure(path.Encode(event));
383 : }
384 :
385 344 : return aEventPathsBuilder.EndOfEventPaths();
386 : }
387 :
388 986 : CHIP_ERROR ReadClient::GenerateAttributePaths(AttributePathIBs::Builder & aAttributePathIBsBuilder,
389 : const Span<AttributePathParams> & aAttributePaths)
390 : {
391 2986 : for (auto & attribute : aAttributePaths)
392 : {
393 2002 : VerifyOrReturnError(attribute.IsValidAttributePath(), CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH_IB);
394 2000 : AttributePathIB::Builder & path = aAttributePathIBsBuilder.CreatePath();
395 2000 : ReturnErrorOnFailure(aAttributePathIBsBuilder.GetError());
396 2000 : ReturnErrorOnFailure(path.Encode(attribute));
397 : }
398 :
399 984 : return aAttributePathIBsBuilder.EndOfAttributePathIBs();
400 : }
401 :
402 977 : CHIP_ERROR ReadClient::BuildDataVersionFilterList(DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder,
403 : const Span<AttributePathParams> & aAttributePaths,
404 : const Span<DataVersionFilter> & aDataVersionFilters,
405 : bool & aEncodedDataVersionList)
406 : {
407 : #if CHIP_PROGRESS_LOGGING
408 977 : size_t encodedFilterCount = 0;
409 977 : size_t irrelevantFilterCount = 0;
410 977 : size_t skippedFilterCount = 0;
411 : #endif
412 3470 : for (auto & filter : aDataVersionFilters)
413 : {
414 2494 : VerifyOrReturnError(filter.IsValidDataVersionFilter(), CHIP_ERROR_INVALID_ARGUMENT);
415 :
416 : // If data version filter is for some cluster none of whose attributes are included in our paths, discard this filter.
417 2494 : bool intersected = false;
418 2506 : for (auto & path : aAttributePaths)
419 : {
420 2500 : if (path.IncludesAttributesInCluster(filter))
421 : {
422 2488 : intersected = true;
423 2488 : break;
424 : }
425 : }
426 :
427 2494 : if (!intersected)
428 : {
429 : #if CHIP_PROGRESS_LOGGING
430 6 : ++irrelevantFilterCount;
431 : #endif
432 6 : continue;
433 : }
434 :
435 2488 : TLV::TLVWriter backup;
436 2488 : aDataVersionFilterIBsBuilder.Checkpoint(backup);
437 2488 : CHIP_ERROR err = aDataVersionFilterIBsBuilder.EncodeDataVersionFilterIB(filter);
438 4976 : if (err == CHIP_NO_ERROR)
439 : {
440 : #if CHIP_PROGRESS_LOGGING
441 2487 : ++encodedFilterCount;
442 : #endif
443 2487 : aEncodedDataVersionList = true;
444 : }
445 2 : else if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
446 : {
447 : // Packet is full, ignore the rest of the list
448 1 : aDataVersionFilterIBsBuilder.Rollback(backup);
449 : #if CHIP_PROGRESS_LOGGING
450 1 : ssize_t nonSkippedFilterCount = &filter - aDataVersionFilters.data();
451 1 : skippedFilterCount = aDataVersionFilters.size() - static_cast<size_t>(nonSkippedFilterCount);
452 : #endif // CHIP_PROGRESS_LOGGING
453 1 : break;
454 : }
455 : else
456 : {
457 0 : return err;
458 : }
459 : }
460 :
461 977 : ChipLogProgress(DataManagement,
462 : "%lu data version filters provided, %lu not relevant, %lu encoded, %lu skipped due to lack of space",
463 : static_cast<unsigned long>(aDataVersionFilters.size()), static_cast<unsigned long>(irrelevantFilterCount),
464 : static_cast<unsigned long>(encodedFilterCount), static_cast<unsigned long>(skippedFilterCount));
465 977 : return CHIP_NO_ERROR;
466 : }
467 :
468 982 : CHIP_ERROR ReadClient::GenerateDataVersionFilterList(DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder,
469 : const Span<AttributePathParams> & aAttributePaths,
470 : const Span<DataVersionFilter> & aDataVersionFilters,
471 : bool & aEncodedDataVersionList)
472 : {
473 : // Give the callback a chance first, otherwise use the list we have, if any.
474 982 : ReturnErrorOnFailure(
475 : mpCallback.OnUpdateDataVersionFilterList(aDataVersionFilterIBsBuilder, aAttributePaths, aEncodedDataVersionList));
476 :
477 982 : if (!aEncodedDataVersionList)
478 : {
479 977 : ReturnErrorOnFailure(BuildDataVersionFilterList(aDataVersionFilterIBsBuilder, aAttributePaths, aDataVersionFilters,
480 : aEncodedDataVersionList));
481 : }
482 :
483 982 : return CHIP_NO_ERROR;
484 : }
485 :
486 3 : void ReadClient::OnActiveModeNotification()
487 : {
488 3 : VerifyOrDie(mpImEngine->InActiveReadClientList(this));
489 :
490 : // Note: this API only works when issuing subscription via SendAutoResubscribeRequest. When SendAutoResubscribeRequest is
491 : // called, either mEventPathParamsListSize or mAttributePathParamsListSize is not 0.
492 3 : VerifyOrReturn(mReadPrepareParams.mEventPathParamsListSize != 0 || mReadPrepareParams.mAttributePathParamsListSize != 0);
493 :
494 : // When we reach here, the subscription definitely exceeded the liveness timeout. Just continue the unfinished resubscription
495 : // logic in `OnLivenessTimeoutCallback`.
496 3 : if (IsInactiveICDSubscription())
497 : {
498 1 : TriggerResubscriptionForLivenessTimeout(CHIP_ERROR_TIMEOUT);
499 1 : return;
500 : }
501 :
502 : // If this API has been called, that means the subscription for this ReadClient is gone
503 : // on the server side (because otherwise the server would not have checked in with us).
504 : // Even if we think we have a live subscription, we are wrong, and should just forcibly time it
505 : // out and schedule a new one.
506 2 : if (!mIsResubscriptionScheduled)
507 : {
508 : // Closing will ultimately trigger ScheduleResubscription with the aReestablishCASE argument set to true, effectively
509 : // rendering the session defunct.
510 1 : Close(CHIP_ERROR_TIMEOUT);
511 1 : return;
512 : }
513 :
514 : // If we have already detected subscription loss and are waiting to try to re-subscribe,
515 : // now is a really good time to do it, since the server is listening.
516 1 : TriggerResubscribeIfScheduled("check-in message");
517 : }
518 :
519 3 : void ReadClient::OnPeerTypeChange(PeerType aType)
520 : {
521 3 : VerifyOrDie(mpImEngine->InActiveReadClientList(this));
522 :
523 3 : mIsPeerLIT = (aType == PeerType::kLITICD);
524 :
525 3 : ChipLogProgress(DataManagement, "Peer is now %s LIT ICD.", mIsPeerLIT ? "a" : "not a");
526 :
527 : // If the peer is no longer LIT and we were waiting for a check-in to try to resubscribe,
528 : // just try to resubscribe now, because a SIT is not going to send a check-in.
529 3 : if (!mIsPeerLIT && IsInactiveICDSubscription())
530 : {
531 0 : TriggerResubscriptionForLivenessTimeout(CHIP_ERROR_TIMEOUT);
532 : }
533 3 : }
534 :
535 2197 : CHIP_ERROR ReadClient::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
536 : System::PacketBufferHandle && aPayload)
537 : {
538 2197 : CHIP_ERROR err = CHIP_NO_ERROR;
539 2197 : Status status = Status::InvalidAction;
540 : // Based on Matter specification (10.7.3. ReportDataMessage), SuppressResponse in ReportDataMessage is omitted if `false`.
541 : // If this is a single ReportDataMessage for the current transaction, or this is the last ReportDataMessage in the
542 : // transaction (i.e. there is no pending data anymore), then this ReportDataMessage should not have SuppressResponse.
543 : // Otherwise, this ReportDataMessage should have SuppressResponse.
544 : // For each received message, we assume mSuppressResponse to be false. If it is set true, it will be updated in
545 : // ProcessReportData call below.
546 : // For all other message types (e.g. SubscribeResponse, StatusResponse etc), mSuppressResponse is not relevant and always false.
547 2197 : mSuppressResponse = false;
548 2197 : VerifyOrExit(!IsIdle() && !IsInactiveICDSubscription(), err = CHIP_ERROR_INCORRECT_STATE);
549 :
550 2197 : if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::ReportData))
551 : {
552 1841 : err = ProcessReportData(std::move(aPayload), ReportType::kContinuingTransaction);
553 : }
554 356 : else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::SubscribeResponse))
555 : {
556 286 : ChipLogProgress(DataManagement, "SubscribeResponse is received");
557 286 : VerifyOrExit(apExchangeContext == mExchange.Get(), err = CHIP_ERROR_INCORRECT_STATE);
558 286 : err = ProcessSubscribeResponse(std::move(aPayload));
559 : MATTER_LOG_METRIC_END(Tracing::kMetricDeviceSubscriptionSetup, err);
560 : }
561 70 : else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse))
562 : {
563 138 : VerifyOrExit(apExchangeContext == mExchange.Get(), err = CHIP_ERROR_INCORRECT_STATE);
564 70 : CHIP_ERROR statusError = CHIP_NO_ERROR;
565 70 : SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError));
566 70 : SuccessOrExit(err = statusError);
567 2 : err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
568 : }
569 : else
570 : {
571 0 : err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
572 : }
573 :
574 2197 : exit:
575 4394 : if (err != CHIP_NO_ERROR)
576 : {
577 156 : if (err == CHIP_ERROR_INVALID_SUBSCRIPTION)
578 : {
579 4 : status = Status::InvalidSubscription;
580 : }
581 78 : if (!mSuppressResponse)
582 : {
583 76 : TEMPORARY_RETURN_IGNORED StatusResponse::Send(status, apExchangeContext, false /*aExpectResponse*/);
584 : }
585 : }
586 :
587 3655 : if ((!IsSubscriptionType() && !mPendingMoreChunks) || err != CHIP_NO_ERROR)
588 : {
589 771 : Close(err);
590 : }
591 :
592 2197 : return err;
593 : }
594 :
595 88 : void ReadClient::OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload)
596 : {
597 88 : Status status = Status::Success;
598 88 : mExchange.Grab(apExchangeContext);
599 :
600 : //
601 : // Let's update the session we're tracking in our SessionHolder to that associated with the message that was just received.
602 : // This CAN be different from the one we were tracking before, since the server is permitted to send exchanges on any valid
603 : // session to us, of which there could be multiple.
604 : //
605 : // Since receipt of a message is proof of a working session on the peer, it's always best to update to that if possible
606 : // to maximize our chances of success later.
607 : //
608 88 : mReadPrepareParams.mSessionHolder.Grab(mExchange->GetSessionHandle());
609 :
610 88 : mSuppressResponse = false;
611 :
612 88 : CHIP_ERROR err = ProcessReportData(std::move(aPayload), ReportType::kUnsolicited);
613 176 : if (err != CHIP_NO_ERROR)
614 : {
615 0 : if (err == CHIP_ERROR_INVALID_SUBSCRIPTION)
616 : {
617 0 : status = Status::InvalidSubscription;
618 : }
619 : else
620 : {
621 0 : status = Status::InvalidAction;
622 : }
623 :
624 0 : if (!mSuppressResponse)
625 : {
626 0 : TEMPORARY_RETURN_IGNORED StatusResponse::Send(status, mExchange.Get(), false /*aExpectResponse*/);
627 : }
628 0 : Close(err);
629 : }
630 88 : }
631 :
632 1937 : CHIP_ERROR ReadClient::ProcessReportData(System::PacketBufferHandle && aPayload, ReportType aReportType)
633 : {
634 1937 : CHIP_ERROR err = CHIP_NO_ERROR;
635 1937 : ReportDataMessage::Parser report;
636 1937 : SubscriptionId subscriptionId = 0;
637 1937 : EventReportIBs::Parser eventReportIBs;
638 1937 : AttributeReportIBs::Parser attributeReportIBs;
639 1937 : System::PacketBufferTLVReader reader;
640 1937 : reader.Init(std::move(aPayload));
641 1937 : err = report.Init(reader);
642 1937 : SuccessOrExit(err);
643 :
644 : #if CHIP_CONFIG_IM_PRETTY_PRINT
645 1935 : if (aReportType != ReportType::kUnsolicited)
646 : {
647 1847 : TEMPORARY_RETURN_IGNORED report.PrettyPrint();
648 : }
649 : #endif
650 :
651 1935 : err = report.GetSuppressResponse(&mSuppressResponse);
652 1935 : SuccessOrExit(err);
653 :
654 1935 : err = report.GetSubscriptionId(&subscriptionId);
655 3870 : if (CHIP_NO_ERROR == err)
656 : {
657 428 : VerifyOrExit(IsSubscriptionType(), err = CHIP_ERROR_INVALID_ARGUMENT);
658 426 : if (mWaitingForFirstPrimingReport)
659 : {
660 288 : mSubscriptionId = subscriptionId;
661 : }
662 138 : else if (!IsMatchingSubscriptionId(subscriptionId))
663 : {
664 2 : err = CHIP_ERROR_INVALID_SUBSCRIPTION;
665 : }
666 : }
667 3014 : else if (CHIP_END_OF_TLV == err)
668 : {
669 1507 : if (IsSubscriptionType())
670 : {
671 0 : err = CHIP_ERROR_INVALID_ARGUMENT;
672 : }
673 : else
674 : {
675 1507 : err = CHIP_NO_ERROR;
676 : }
677 : }
678 1933 : SuccessOrExit(err);
679 :
680 1931 : err = report.GetMoreChunkedMessages(&mPendingMoreChunks);
681 3862 : if (CHIP_END_OF_TLV == err)
682 : {
683 1063 : mPendingMoreChunks = false;
684 1063 : err = CHIP_NO_ERROR;
685 : }
686 1931 : SuccessOrExit(err);
687 :
688 1931 : err = report.GetEventReports(&eventReportIBs);
689 3862 : if (err == CHIP_END_OF_TLV)
690 : {
691 1290 : err = CHIP_NO_ERROR;
692 : }
693 1282 : else if (err == CHIP_NO_ERROR)
694 : {
695 641 : chip::TLV::TLVReader EventReportsReader;
696 641 : eventReportIBs.GetReader(&EventReportsReader);
697 641 : err = ProcessEventReportIBs(EventReportsReader);
698 : }
699 1931 : SuccessOrExit(err);
700 :
701 1931 : err = report.GetAttributeReportIBs(&attributeReportIBs);
702 3862 : if (err == CHIP_END_OF_TLV)
703 : {
704 634 : err = CHIP_NO_ERROR;
705 : }
706 2594 : else if (err == CHIP_NO_ERROR)
707 : {
708 1297 : TLV::TLVReader attributeReportIBsReader;
709 1297 : attributeReportIBs.GetReader(&attributeReportIBsReader);
710 1297 : err = ProcessAttributeReportIBs(attributeReportIBsReader);
711 : }
712 1931 : SuccessOrExit(err);
713 :
714 1927 : if (mIsReporting && !mPendingMoreChunks)
715 : {
716 1030 : mpCallback.OnReportEnd();
717 1030 : mIsReporting = false;
718 : }
719 :
720 1927 : SuccessOrExit(err = report.ExitContainer());
721 :
722 1927 : exit:
723 1937 : if (IsSubscriptionType())
724 : {
725 428 : if (IsAwaitingInitialReport())
726 : {
727 290 : MoveToState(ClientState::AwaitingSubscribeResponse);
728 : }
729 247 : else if (IsSubscriptionActive() && err == CHIP_NO_ERROR)
730 : {
731 : //
732 : // Only refresh the liveness check timer if we've successfully established
733 : // a subscription and have a valid value for mMaxInterval which the function
734 : // relies on.
735 : //
736 109 : mpCallback.NotifySubscriptionStillActive(*this);
737 109 : err = RefreshLivenessCheckTimer();
738 : }
739 : }
740 :
741 3171 : if (!mSuppressResponse && err == CHIP_NO_ERROR)
742 : {
743 1230 : bool noResponseExpected = IsSubscriptionActive() && !mPendingMoreChunks;
744 1230 : err = StatusResponse::Send(Status::Success, mExchange.Get(), !noResponseExpected);
745 : }
746 :
747 1937 : mWaitingForFirstPrimingReport = false;
748 3874 : return err;
749 1937 : }
750 :
751 12 : void ReadClient::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
752 : {
753 12 : ChipLogError(DataManagement, "Time out! failed to receive report data from Exchange: " ChipLogFormatExchange,
754 : ChipLogValueExchange(apExchangeContext));
755 :
756 12 : Close(CHIP_ERROR_TIMEOUT);
757 12 : }
758 :
759 5 : CHIP_ERROR ReadClient::ReadICDOperatingModeFromAttributeDataIB(TLV::TLVReader && aReader, PeerType & aType)
760 : {
761 : Clusters::IcdManagement::Attributes::OperatingMode::TypeInfo::DecodableType operatingMode;
762 :
763 5 : CHIP_ERROR err = DataModel::Decode(aReader, operatingMode);
764 5 : ReturnErrorOnFailure(err);
765 :
766 5 : switch (operatingMode)
767 : {
768 4 : case Clusters::IcdManagement::OperatingModeEnum::kSit:
769 4 : aType = PeerType::kNormal;
770 4 : break;
771 1 : case Clusters::IcdManagement::OperatingModeEnum::kLit:
772 1 : aType = PeerType::kLITICD;
773 1 : break;
774 0 : default:
775 0 : err = CHIP_ERROR_INVALID_ARGUMENT;
776 0 : break;
777 : }
778 :
779 5 : return err;
780 : }
781 :
782 5226 : CHIP_ERROR ReadClient::ProcessAttributePath(AttributePathIB::Parser & aAttributePathParser,
783 : ConcreteDataAttributePath & aAttributePath)
784 : {
785 : // The ReportData must contain a concrete attribute path. Don't validate ID
786 : // ranges here, so we can tell apart "malformed data" and "out of range
787 : // IDs".
788 5226 : CHIP_ERROR err = CHIP_NO_ERROR;
789 : // The ReportData must contain a concrete attribute path
790 5226 : err = aAttributePathParser.GetConcreteAttributePath(aAttributePath, AttributePathIB::ValidateIdRanges::kNo);
791 10452 : VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH_IB);
792 5222 : return CHIP_NO_ERROR;
793 : }
794 :
795 6802 : void ReadClient::NoteReportingData()
796 : {
797 6802 : if (!mIsReporting)
798 : {
799 1084 : mpCallback.OnReportBegin();
800 1084 : mIsReporting = true;
801 : }
802 6802 : }
803 :
804 1297 : CHIP_ERROR ReadClient::ProcessAttributeReportIBs(TLV::TLVReader & aAttributeReportIBsReader)
805 : {
806 1297 : CHIP_ERROR err = CHIP_NO_ERROR;
807 13038 : while (CHIP_NO_ERROR == (err = aAttributeReportIBsReader.Next()))
808 : {
809 5226 : TLV::TLVReader dataReader;
810 5226 : AttributeReportIB::Parser report;
811 5226 : AttributeDataIB::Parser data;
812 5226 : AttributeStatusIB::Parser status;
813 5226 : AttributePathIB::Parser path;
814 5226 : ConcreteDataAttributePath attributePath;
815 5226 : StatusIB statusIB;
816 :
817 5226 : TLV::TLVReader reader = aAttributeReportIBsReader;
818 5226 : ReturnErrorOnFailure(report.Init(reader));
819 :
820 5226 : err = report.GetAttributeStatus(&status);
821 10452 : if (CHIP_NO_ERROR == err)
822 : {
823 9 : StatusIB::Parser errorStatus;
824 9 : ReturnErrorOnFailure(status.GetPath(&path));
825 9 : ReturnErrorOnFailure(ProcessAttributePath(path, attributePath));
826 9 : if (!attributePath.IsValid())
827 : {
828 : // Don't fail the entire read or subscription when there is an
829 : // out-of-range ID. Just skip that one AttributeReportIB.
830 0 : ChipLogError(DataManagement,
831 : "Skipping AttributeStatusIB with out-of-range IDs: (%d, " ChipLogFormatMEI ", " ChipLogFormatMEI ") ",
832 : attributePath.mEndpointId, ChipLogValueMEI(attributePath.mClusterId),
833 : ChipLogValueMEI(attributePath.mAttributeId));
834 0 : continue;
835 : }
836 :
837 9 : ReturnErrorOnFailure(status.GetErrorStatus(&errorStatus));
838 9 : ReturnErrorOnFailure(errorStatus.DecodeStatusIB(statusIB));
839 9 : NoteReportingData();
840 9 : mpCallback.OnAttributeData(attributePath, nullptr, statusIB);
841 : }
842 10434 : else if (CHIP_END_OF_TLV == err)
843 : {
844 5217 : ReturnErrorOnFailure(report.GetAttributeData(&data));
845 5217 : ReturnErrorOnFailure(data.GetPath(&path));
846 5217 : ReturnErrorOnFailure(ProcessAttributePath(path, attributePath));
847 5213 : if (!attributePath.IsValid())
848 : {
849 : // Don't fail the entire read or subscription when there is an
850 : // out-of-range ID. Just skip that one AttributeReportIB.
851 2 : ChipLogError(DataManagement,
852 : "Skipping AttributeDataIB with out-of-range IDs: (%d, " ChipLogFormatMEI ", " ChipLogFormatMEI ") ",
853 : attributePath.mEndpointId, ChipLogValueMEI(attributePath.mClusterId),
854 : ChipLogValueMEI(attributePath.mAttributeId));
855 2 : continue;
856 : }
857 :
858 5211 : DataVersion version = 0;
859 5211 : ReturnErrorOnFailure(data.GetDataVersion(&version));
860 5211 : attributePath.mDataVersion.SetValue(version);
861 :
862 5211 : if (mReadPrepareParams.mpDataVersionFilterList != nullptr)
863 : {
864 65 : UpdateDataVersionFilters(attributePath);
865 : }
866 :
867 5211 : ReturnErrorOnFailure(data.GetData(&dataReader));
868 :
869 : // The element in an array may be another array -- so we should only set the list operation when we are handling the
870 : // whole list.
871 5211 : if (!attributePath.IsListOperation() && dataReader.GetType() == TLV::kTLVType_Array)
872 : {
873 2975 : attributePath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
874 : }
875 :
876 5211 : if (attributePath.MatchesConcreteAttributePath(ConcreteAttributePath(
877 : kRootEndpointId, Clusters::IcdManagement::Id, Clusters::IcdManagement::Attributes::OperatingMode::Id)))
878 : {
879 : PeerType peerType;
880 5 : TLV::TLVReader operatingModeTlvReader;
881 5 : operatingModeTlvReader.Init(dataReader);
882 10 : if (CHIP_NO_ERROR == ReadICDOperatingModeFromAttributeDataIB(std::move(operatingModeTlvReader), peerType))
883 : {
884 : // It is safe to call `OnPeerTypeChange` since we are in the middle of parsing the attribute data, And
885 : // the subscription should be active so `OnActiveModeNotification` is a no-op in this case.
886 5 : InteractionModelEngine::GetInstance()->OnPeerTypeChange(mPeer, peerType);
887 : }
888 : else
889 : {
890 0 : ChipLogError(DataManagement, "Failed to get ICD state from attribute data with error'%" CHIP_ERROR_FORMAT "'",
891 : err.Format());
892 : }
893 : }
894 :
895 5211 : NoteReportingData();
896 5211 : mpCallback.OnAttributeData(attributePath, &dataReader, statusIB);
897 : }
898 : }
899 :
900 2586 : if (CHIP_END_OF_TLV == err)
901 : {
902 1293 : err = CHIP_NO_ERROR;
903 : }
904 :
905 1293 : return err;
906 : }
907 :
908 641 : CHIP_ERROR ReadClient::ProcessEventReportIBs(TLV::TLVReader & aEventReportIBsReader)
909 : {
910 641 : CHIP_ERROR err = CHIP_NO_ERROR;
911 4446 : while (CHIP_NO_ERROR == (err = aEventReportIBsReader.Next()))
912 : {
913 1582 : TLV::TLVReader dataReader;
914 1582 : EventReportIB::Parser report;
915 1582 : EventDataIB::Parser data;
916 1582 : EventHeader header;
917 1582 : StatusIB statusIB; // Default value for statusIB is success.
918 :
919 1582 : TLV::TLVReader reader = aEventReportIBsReader;
920 1582 : ReturnErrorOnFailure(report.Init(reader));
921 :
922 1582 : err = report.GetEventData(&data);
923 :
924 3164 : if (err == CHIP_NO_ERROR)
925 : {
926 1579 : header.mTimestamp = mEventTimestamp;
927 1579 : ReturnErrorOnFailure(data.DecodeEventHeader(header));
928 1579 : mEventTimestamp = header.mTimestamp;
929 :
930 1579 : ReturnErrorOnFailure(data.GetData(&dataReader));
931 :
932 : //
933 : // Update the event number being tracked in mReadPrepareParams in case
934 : // we want to send it in the next SubscribeRequest message to convey
935 : // the event number for which we have already received an event.
936 : //
937 1579 : mReadPrepareParams.mEventNumber.SetValue(header.mEventNumber + 1);
938 :
939 1579 : NoteReportingData();
940 1579 : mpCallback.OnEventData(header, &dataReader, nullptr);
941 : }
942 6 : else if (err == CHIP_END_OF_TLV)
943 : {
944 3 : EventStatusIB::Parser status;
945 3 : EventPathIB::Parser pathIB;
946 3 : StatusIB::Parser statusIBParser;
947 3 : ReturnErrorOnFailure(report.GetEventStatus(&status));
948 3 : ReturnErrorOnFailure(status.GetPath(&pathIB));
949 3 : ReturnErrorOnFailure(pathIB.GetEventPath(&header.mPath));
950 3 : ReturnErrorOnFailure(status.GetErrorStatus(&statusIBParser));
951 3 : ReturnErrorOnFailure(statusIBParser.DecodeStatusIB(statusIB));
952 :
953 3 : NoteReportingData();
954 3 : mpCallback.OnEventData(header, nullptr, &statusIB);
955 : }
956 : }
957 :
958 1282 : if (CHIP_END_OF_TLV == err)
959 : {
960 641 : err = CHIP_NO_ERROR;
961 : }
962 :
963 641 : return err;
964 : }
965 :
966 0 : void ReadClient::OverrideLivenessTimeout(System::Clock::Timeout aLivenessTimeout)
967 : {
968 0 : mLivenessTimeoutOverride = aLivenessTimeout;
969 0 : auto err = RefreshLivenessCheckTimer();
970 0 : if (err != CHIP_NO_ERROR)
971 : {
972 0 : Close(err);
973 : }
974 0 : }
975 :
976 393 : CHIP_ERROR ReadClient::RefreshLivenessCheckTimer()
977 : {
978 393 : CHIP_ERROR err = CHIP_NO_ERROR;
979 :
980 393 : VerifyOrReturnError(IsSubscriptionActive(), CHIP_ERROR_INCORRECT_STATE);
981 :
982 393 : CancelLivenessCheckTimer();
983 :
984 : System::Clock::Timeout timeout;
985 393 : ReturnErrorOnFailure(ComputeLivenessCheckTimerTimeout(&timeout));
986 :
987 : // EFR32/MBED/INFINION/K32W's chrono count return long unsigned, but other platform returns unsigned
988 393 : ChipLogProgress(
989 : DataManagement,
990 : "Refresh LivenessCheckTime for %lu milliseconds with SubscriptionId = 0x%08" PRIx32 " Peer = %02x:" ChipLogFormatX64,
991 : static_cast<long unsigned>(timeout.count()), mSubscriptionId, GetFabricIndex(), ChipLogValueX64(GetPeerNodeId()));
992 393 : err = InteractionModelEngine::GetInstance()->GetExchangeManager()->GetSessionManager()->SystemLayer()->StartTimer(
993 393 : timeout, OnLivenessTimeoutCallback, this);
994 :
995 393 : return err;
996 : }
997 :
998 393 : CHIP_ERROR ReadClient::ComputeLivenessCheckTimerTimeout(System::Clock::Timeout * aTimeout)
999 : {
1000 393 : if (mLivenessTimeoutOverride != System::Clock::kZero)
1001 : {
1002 0 : *aTimeout = mLivenessTimeoutOverride;
1003 0 : return CHIP_NO_ERROR;
1004 : }
1005 :
1006 393 : VerifyOrReturnError(mReadPrepareParams.mSessionHolder, CHIP_ERROR_INCORRECT_STATE);
1007 :
1008 : //
1009 : // To calculate the duration we're willing to wait for a report to come to us, we take into account the maximum interval of
1010 : // the subscription AND the time it takes for the report to make it to us in the worst case.
1011 : //
1012 : // We have no way to estimate what the network latency will be, but we do know the other side will time out its ReportData
1013 : // after its computed round-trip timeout plus the processing time it gives us (app::kExpectedIMProcessingTime). Once it
1014 : // times out, assuming it sent the report at all, there's no point in us thinking we still have a subscription.
1015 : //
1016 : // We can't use ComputeRoundTripTimeout() on the session for two reasons: we want the roundtrip timeout from the point of
1017 : // view of the peer, not us, and we want to start off with the assumption the peer will likely have, which is that we are
1018 : // idle, whereas ComputeRoundTripTimeout() uses the current activity state of the peer.
1019 : //
1020 : // So recompute the round-trip timeout directly. Assume MRP, since in practice that is likely what is happening.
1021 393 : auto & peerMRPConfig = mReadPrepareParams.mSessionHolder->GetRemoteMRPConfig();
1022 : // Peer will assume we are idle (hence we pass kZero to GetMessageReceiptTimeout()), but will assume we treat it as active
1023 : // for the response, so to match the retransmission timeout computation for the message back to the peer, we should treat
1024 : // it as active and handling non-initial message, isFirstMessageOnExchange needs to be set as false for
1025 : // GetRetransmissionTimeout.
1026 : auto roundTripTimeout =
1027 393 : mReadPrepareParams.mSessionHolder->GetMessageReceiptTimeout(System::Clock::kZero, true /*isFirstMessageOnExchange*/) +
1028 393 : kExpectedIMProcessingTime +
1029 393 : GetRetransmissionTimeout(peerMRPConfig.mActiveRetransTimeout, peerMRPConfig.mIdleRetransTimeout,
1030 393 : System::SystemClock().GetMonotonicTimestamp(), peerMRPConfig.mActiveThresholdTime,
1031 393 : false /*isFirstMessageOnExchange*/);
1032 393 : *aTimeout = System::Clock::Seconds16(mMaxInterval) + roundTripTimeout;
1033 393 : return CHIP_NO_ERROR;
1034 : }
1035 :
1036 982 : void ReadClient::CancelLivenessCheckTimer()
1037 : {
1038 982 : InteractionModelEngine::GetInstance()->GetExchangeManager()->GetSessionManager()->SystemLayer()->CancelTimer(
1039 : OnLivenessTimeoutCallback, this);
1040 982 : }
1041 :
1042 590 : void ReadClient::CancelResubscribeTimer()
1043 : {
1044 590 : InteractionModelEngine::GetInstance()->GetExchangeManager()->GetSessionManager()->SystemLayer()->CancelTimer(
1045 : OnResubscribeTimerCallback, this);
1046 590 : mIsResubscriptionScheduled = false;
1047 590 : }
1048 :
1049 8 : void ReadClient::OnLivenessTimeoutCallback(System::Layer * apSystemLayer, void * apAppState)
1050 : {
1051 8 : ReadClient * const _this = reinterpret_cast<ReadClient *>(apAppState);
1052 :
1053 : // TODO: add a more specific error here for liveness timeout failure to distinguish between other classes of timeouts (i.e
1054 : // response timeouts).
1055 8 : CHIP_ERROR subscriptionTerminationCause = CHIP_ERROR_TIMEOUT;
1056 :
1057 : //
1058 : // Might as well try to see if this instance exists in the tracked list in the IM.
1059 : // This might blow-up if either the client has since been free'ed (use-after-free), or if the engine has since
1060 : // been shutdown at which point the client wouldn't exist in the active read client list.
1061 : //
1062 8 : VerifyOrDie(_this->mpImEngine->InActiveReadClientList(_this));
1063 :
1064 8 : ChipLogError(DataManagement,
1065 : "Subscription Liveness timeout with SubscriptionID = 0x%08" PRIx32 ", Peer = %02x:" ChipLogFormatX64,
1066 : _this->mSubscriptionId, _this->GetFabricIndex(), ChipLogValueX64(_this->GetPeerNodeId()));
1067 :
1068 : // If subscription client is able to handle check-in messages and peer operation mode is LIT,
1069 : // use CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT as subscriptionTerminationCause.
1070 : // This will cause us to wait for a check-in message before trying to re-subscribe, instead of trying
1071 : // (and probably failing, because we are dealing with a LIT ICD) off a timer.
1072 8 : if (_this->mIsPeerLIT && _this->mReadPrepareParams.mRegisteredCheckInToken)
1073 : {
1074 3 : subscriptionTerminationCause = CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT;
1075 : }
1076 :
1077 8 : _this->TriggerResubscriptionForLivenessTimeout(subscriptionTerminationCause);
1078 8 : }
1079 :
1080 9 : void ReadClient::TriggerResubscriptionForLivenessTimeout(CHIP_ERROR aReason)
1081 : {
1082 : // We didn't get a message from the server on time; it's possible that it no
1083 : // longer has a useful CASE session to us. Mark defunct all sessions that
1084 : // have not seen peer activity in at least as long as our session.
1085 9 : const auto & holder = mReadPrepareParams.mSessionHolder;
1086 9 : if (holder)
1087 : {
1088 9 : System::Clock::Timestamp lastPeerActivity = holder->AsSecureSession()->GetLastPeerActivityTime();
1089 9 : mpImEngine->GetExchangeManager()->GetSessionManager()->ForEachMatchingSession(mPeer, [&lastPeerActivity](auto * session) {
1090 9 : if (!session->IsCASESession())
1091 : {
1092 9 : return;
1093 : }
1094 :
1095 0 : if (session->GetLastPeerActivityTime() > lastPeerActivity)
1096 : {
1097 0 : return;
1098 : }
1099 :
1100 0 : session->MarkAsDefunct();
1101 : });
1102 : }
1103 :
1104 9 : Close(aReason);
1105 9 : }
1106 :
1107 286 : CHIP_ERROR ReadClient::ProcessSubscribeResponse(System::PacketBufferHandle && aPayload)
1108 : {
1109 286 : System::PacketBufferTLVReader reader;
1110 286 : reader.Init(std::move(aPayload));
1111 :
1112 286 : SubscribeResponseMessage::Parser subscribeResponse;
1113 286 : ReturnErrorOnFailure(subscribeResponse.Init(reader));
1114 :
1115 : #if CHIP_CONFIG_IM_PRETTY_PRINT
1116 286 : TEMPORARY_RETURN_IGNORED subscribeResponse.PrettyPrint();
1117 : #endif
1118 :
1119 286 : SubscriptionId subscriptionId = 0;
1120 572 : VerifyOrReturnError(subscribeResponse.GetSubscriptionId(&subscriptionId) == CHIP_NO_ERROR, CHIP_ERROR_INVALID_ARGUMENT);
1121 286 : VerifyOrReturnError(IsMatchingSubscriptionId(subscriptionId), CHIP_ERROR_INVALID_SUBSCRIPTION);
1122 284 : ReturnErrorOnFailure(subscribeResponse.GetMaxInterval(&mMaxInterval));
1123 :
1124 : #if CHIP_PROGRESS_LOGGING
1125 284 : auto duration = System::Clock::Milliseconds32(System::SystemClock().GetMonotonicTimestamp() - mSubscribeRequestTime);
1126 : #endif
1127 284 : ChipLogProgress(DataManagement,
1128 : "Subscription established in %" PRIu32 "ms with SubscriptionID = 0x%08" PRIx32 " MinInterval = %u"
1129 : "s MaxInterval = %us Peer = %02x:" ChipLogFormatX64,
1130 : duration.count(), mSubscriptionId, mMinIntervalFloorSeconds, mMaxInterval, GetFabricIndex(),
1131 : ChipLogValueX64(GetPeerNodeId()));
1132 :
1133 284 : ReturnErrorOnFailure(subscribeResponse.ExitContainer());
1134 :
1135 284 : MoveToState(ClientState::SubscriptionActive);
1136 :
1137 284 : mpCallback.OnSubscriptionEstablished(subscriptionId);
1138 :
1139 284 : mNumRetries = 0;
1140 :
1141 284 : ReturnErrorOnFailure(RefreshLivenessCheckTimer());
1142 :
1143 284 : return CHIP_NO_ERROR;
1144 286 : }
1145 :
1146 227 : CHIP_ERROR ReadClient::SendAutoResubscribeRequest(ReadPrepareParams && aReadPrepareParams)
1147 : {
1148 : // Make sure we don't use minimal resubscribe delays from previous attempts
1149 : // for this one.
1150 227 : mMinimalResubscribeDelay = System::Clock::kZero;
1151 :
1152 227 : mReadPrepareParams = std::move(aReadPrepareParams);
1153 227 : CHIP_ERROR err = SendSubscribeRequest(mReadPrepareParams);
1154 454 : if (err != CHIP_NO_ERROR)
1155 : {
1156 1 : StopResubscription();
1157 : }
1158 227 : return err;
1159 : }
1160 :
1161 0 : CHIP_ERROR ReadClient::SendAutoResubscribeRequest(const ScopedNodeId & aPublisherId, ReadPrepareParams && aReadPrepareParams)
1162 : {
1163 0 : mPeer = aPublisherId;
1164 0 : mReadPrepareParams = std::move(aReadPrepareParams);
1165 0 : CHIP_ERROR err = EstablishSessionToPeer();
1166 0 : if (err != CHIP_NO_ERROR)
1167 : {
1168 : // Make sure we call our callback's OnDeallocatePaths.
1169 0 : StopResubscription();
1170 : }
1171 0 : return err;
1172 : }
1173 :
1174 318 : CHIP_ERROR ReadClient::SendSubscribeRequest(const ReadPrepareParams & aReadPrepareParams)
1175 : {
1176 318 : VerifyOrReturnError(aReadPrepareParams.mMinIntervalFloorSeconds <= aReadPrepareParams.mMaxIntervalCeilingSeconds,
1177 : CHIP_ERROR_INVALID_ARGUMENT);
1178 :
1179 315 : auto err = SendSubscribeRequestImpl(aReadPrepareParams);
1180 630 : if (CHIP_NO_ERROR != err)
1181 : {
1182 : MATTER_LOG_METRIC_END(Tracing::kMetricDeviceSubscriptionSetup, err);
1183 : }
1184 315 : return err;
1185 : }
1186 :
1187 315 : CHIP_ERROR ReadClient::SendSubscribeRequestImpl(const ReadPrepareParams & aReadPrepareParams)
1188 : {
1189 : MATTER_LOG_METRIC_BEGIN(Tracing::kMetricDeviceSubscriptionSetup);
1190 :
1191 : #if CHIP_PROGRESS_LOGGING
1192 315 : mSubscribeRequestTime = System::SystemClock().GetMonotonicTimestamp();
1193 : #endif
1194 :
1195 315 : VerifyOrReturnError(ClientState::Idle == mState, CHIP_ERROR_INCORRECT_STATE);
1196 :
1197 315 : if (&aReadPrepareParams != &mReadPrepareParams)
1198 : {
1199 78 : mReadPrepareParams.mSessionHolder = aReadPrepareParams.mSessionHolder;
1200 : }
1201 :
1202 315 : mIsPeerLIT = aReadPrepareParams.mIsPeerLIT;
1203 315 : mReadPrepareParams.mRegisteredCheckInToken = aReadPrepareParams.mRegisteredCheckInToken;
1204 :
1205 315 : if (aReadPrepareParams.mRegisteredCheckInToken)
1206 : {
1207 11 : ChipLogProgress(DataManagement, "ICD Check-In token has been registered in peer device " ChipLogFormatScopedNodeId,
1208 : ChipLogValueScopedNodeId(mPeer));
1209 : }
1210 :
1211 315 : mMinIntervalFloorSeconds = aReadPrepareParams.mMinIntervalFloorSeconds;
1212 :
1213 : // Todo: Remove the below, Update span in ReadPrepareParams
1214 315 : Span<AttributePathParams> attributePaths(aReadPrepareParams.mpAttributePathParamsList,
1215 315 : aReadPrepareParams.mAttributePathParamsListSize);
1216 315 : Span<EventPathParams> eventPaths(aReadPrepareParams.mpEventPathParamsList, aReadPrepareParams.mEventPathParamsListSize);
1217 315 : Span<DataVersionFilter> dataVersionFilters(aReadPrepareParams.mpDataVersionFilterList,
1218 315 : aReadPrepareParams.mDataVersionFilterListSize);
1219 :
1220 315 : System::PacketBufferHandle msgBuf;
1221 315 : System::PacketBufferTLVWriter writer;
1222 315 : SubscribeRequestMessage::Builder request;
1223 315 : TEMPORARY_RETURN_IGNORED InitWriterWithSpaceReserved(writer, kReservedSizeForTLVEncodingOverhead);
1224 :
1225 315 : ReturnErrorOnFailure(request.Init(&writer));
1226 :
1227 315 : request.KeepSubscriptions(aReadPrepareParams.mKeepSubscriptions)
1228 315 : .MinIntervalFloorSeconds(aReadPrepareParams.mMinIntervalFloorSeconds)
1229 315 : .MaxIntervalCeilingSeconds(aReadPrepareParams.mMaxIntervalCeilingSeconds);
1230 :
1231 315 : if (!attributePaths.empty())
1232 : {
1233 308 : AttributePathIBs::Builder & attributePathListBuilder = request.CreateAttributeRequests();
1234 308 : ReturnErrorOnFailure(attributePathListBuilder.GetError());
1235 308 : ReturnErrorOnFailure(GenerateAttributePaths(attributePathListBuilder, attributePaths));
1236 : }
1237 :
1238 315 : if (!eventPaths.empty())
1239 : {
1240 22 : EventPathIBs::Builder & eventPathListBuilder = request.CreateEventRequests();
1241 22 : ReturnErrorOnFailure(eventPathListBuilder.GetError());
1242 22 : ReturnErrorOnFailure(GenerateEventPaths(eventPathListBuilder, eventPaths));
1243 :
1244 22 : Optional<EventNumber> eventMin;
1245 22 : ReturnErrorOnFailure(GetMinEventNumber(aReadPrepareParams, eventMin));
1246 22 : if (eventMin.HasValue())
1247 : {
1248 0 : EventFilterIBs::Builder & eventFilters = request.CreateEventFilters();
1249 0 : ReturnErrorOnFailure(request.GetError());
1250 0 : ReturnErrorOnFailure(eventFilters.GenerateEventFilter(eventMin.Value()));
1251 : }
1252 : }
1253 :
1254 315 : ReturnErrorOnFailure(request.IsFabricFiltered(aReadPrepareParams.mIsFabricFiltered).GetError());
1255 :
1256 315 : bool encodedDataVersionList = false;
1257 315 : TLV::TLVWriter backup;
1258 315 : request.Checkpoint(backup);
1259 315 : DataVersionFilterIBs::Builder & dataVersionFilterListBuilder = request.CreateDataVersionFilters();
1260 315 : ReturnErrorOnFailure(request.GetError());
1261 315 : if (!attributePaths.empty())
1262 : {
1263 308 : ReturnErrorOnFailure(GenerateDataVersionFilterList(dataVersionFilterListBuilder, attributePaths, dataVersionFilters,
1264 : encodedDataVersionList));
1265 : }
1266 315 : ReturnErrorOnFailure(dataVersionFilterListBuilder.GetWriter()->UnreserveBuffer(kReservedSizeForTLVEncodingOverhead));
1267 315 : if (encodedDataVersionList)
1268 : {
1269 65 : ReturnErrorOnFailure(dataVersionFilterListBuilder.EndOfDataVersionFilterIBs());
1270 : }
1271 : else
1272 : {
1273 250 : request.Rollback(backup);
1274 : }
1275 :
1276 315 : ReturnErrorOnFailure(request.EndOfSubscribeRequestMessage());
1277 315 : ReturnErrorOnFailure(writer.Finalize(&msgBuf));
1278 :
1279 315 : VerifyOrReturnError(aReadPrepareParams.mSessionHolder, CHIP_ERROR_MISSING_SECURE_SESSION);
1280 :
1281 315 : auto exchange = mpExchangeMgr->NewContext(aReadPrepareParams.mSessionHolder.Get().Value(), this);
1282 315 : if (exchange == nullptr)
1283 : {
1284 0 : if (aReadPrepareParams.mSessionHolder->AsSecureSession()->IsActiveSession())
1285 : {
1286 0 : return CHIP_ERROR_NO_MEMORY;
1287 : }
1288 :
1289 : // Trying to subscribe with a defunct session somehow.
1290 0 : return CHIP_ERROR_INCORRECT_STATE;
1291 : }
1292 :
1293 315 : mExchange.Grab(exchange);
1294 :
1295 315 : if (aReadPrepareParams.mTimeout == System::Clock::kZero)
1296 : {
1297 315 : ReturnErrorOnFailure(mExchange->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime));
1298 : }
1299 : else
1300 : {
1301 0 : mExchange->SetResponseTimeout(aReadPrepareParams.mTimeout);
1302 : }
1303 :
1304 315 : ReturnErrorOnFailure(mExchange->SendMessage(Protocols::InteractionModel::MsgType::SubscribeRequest, std::move(msgBuf),
1305 : Messaging::SendFlags(Messaging::SendMessageFlags::kExpectResponse)));
1306 :
1307 315 : mPeer = aReadPrepareParams.mSessionHolder->AsSecureSession()->GetPeer();
1308 :
1309 315 : MoveToState(ClientState::AwaitingInitialReport);
1310 :
1311 315 : return CHIP_NO_ERROR;
1312 315 : }
1313 :
1314 6 : CHIP_ERROR ReadClient::DefaultResubscribePolicy(CHIP_ERROR aTerminationCause)
1315 : {
1316 12 : if (aTerminationCause == CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT)
1317 : {
1318 0 : ChipLogProgress(DataManagement, "ICD device is inactive, skipping scheduling resubscribe within DefaultResubscribePolicy");
1319 0 : return CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT;
1320 : }
1321 :
1322 6 : VerifyOrReturnError(IsIdle(), CHIP_ERROR_INCORRECT_STATE);
1323 :
1324 6 : auto timeTillNextResubscription = ComputeTimeTillNextSubscription();
1325 6 : ChipLogProgress(DataManagement,
1326 : "Will try to resubscribe to %02x:" ChipLogFormatX64 " at retry index %" PRIu32 " after %" PRIu32
1327 : "ms due to error %" CHIP_ERROR_FORMAT,
1328 : GetFabricIndex(), ChipLogValueX64(GetPeerNodeId()), mNumRetries, timeTillNextResubscription,
1329 : aTerminationCause.Format());
1330 12 : return ScheduleResubscription(timeTillNextResubscription, NullOptional, aTerminationCause == CHIP_ERROR_TIMEOUT);
1331 : }
1332 :
1333 0 : void ReadClient::HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr,
1334 : const SessionHandle & sessionHandle)
1335 : {
1336 0 : ReadClient * const _this = static_cast<ReadClient *>(context);
1337 0 : VerifyOrDie(_this != nullptr);
1338 :
1339 0 : ChipLogProgress(DataManagement, "HandleDeviceConnected");
1340 0 : _this->mReadPrepareParams.mSessionHolder.Grab(sessionHandle);
1341 0 : _this->mpExchangeMgr = &exchangeMgr;
1342 :
1343 0 : _this->mpCallback.OnCASESessionEstablished(sessionHandle, _this->mReadPrepareParams);
1344 :
1345 0 : auto err = _this->SendSubscribeRequest(_this->mReadPrepareParams);
1346 0 : if (err != CHIP_NO_ERROR)
1347 : {
1348 0 : _this->Close(err);
1349 : }
1350 0 : }
1351 :
1352 0 : void ReadClient::HandleDeviceConnectionFailure(void * context, const OperationalSessionSetup::ConnectionFailureInfo & failureInfo)
1353 : {
1354 0 : ReadClient * const _this = static_cast<ReadClient *>(context);
1355 0 : VerifyOrDie(_this != nullptr);
1356 :
1357 0 : ChipLogError(DataManagement, "Failed to establish CASE for re-subscription with error '%" CHIP_ERROR_FORMAT "'",
1358 : failureInfo.error.Format());
1359 :
1360 : #if CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
1361 : #if CHIP_DETAIL_LOGGING
1362 0 : if (failureInfo.requestedBusyDelay.HasValue())
1363 : {
1364 0 : ChipLogDetail(DataManagement, "Will delay resubscription by %u ms due to BUSY response",
1365 : failureInfo.requestedBusyDelay.Value().count());
1366 : }
1367 : #endif // CHIP_DETAIL_LOGGING
1368 0 : _this->mMinimalResubscribeDelay = failureInfo.requestedBusyDelay.ValueOr(System::Clock::kZero);
1369 : #else
1370 : _this->mMinimalResubscribeDelay = System::Clock::kZero;
1371 : #endif // CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
1372 :
1373 0 : _this->Close(failureInfo.error);
1374 0 : }
1375 :
1376 11 : void ReadClient::OnResubscribeTimerCallback(System::Layer * /* If this starts being used, fix callers that pass nullptr */,
1377 : void * apAppState)
1378 : {
1379 11 : ReadClient * const _this = static_cast<ReadClient *>(apAppState);
1380 11 : VerifyOrDie(_this != nullptr);
1381 :
1382 11 : _this->mIsResubscriptionScheduled = false;
1383 :
1384 : CHIP_ERROR err;
1385 :
1386 11 : ChipLogProgress(DataManagement, "OnResubscribeTimerCallback: ForceCASE = %d", _this->mForceCaseOnNextResub);
1387 11 : _this->mNumRetries++;
1388 :
1389 11 : bool allowResubscribeOnError = true;
1390 22 : if (!_this->mReadPrepareParams.mSessionHolder ||
1391 11 : !_this->mReadPrepareParams.mSessionHolder->AsSecureSession()->IsActiveSession())
1392 : {
1393 : // We don't have an active CASE session. We need to go ahead and set
1394 : // one up, if we can.
1395 0 : if (_this->EstablishSessionToPeer() == CHIP_NO_ERROR)
1396 : {
1397 0 : return;
1398 : }
1399 :
1400 0 : if (_this->mForceCaseOnNextResub)
1401 : {
1402 : // Caller asked us to force CASE but we have no way to do CASE.
1403 : // Just stop trying.
1404 0 : allowResubscribeOnError = false;
1405 : }
1406 :
1407 : // No way to send our subscribe request.
1408 0 : err = CHIP_ERROR_INCORRECT_STATE;
1409 0 : ExitNow();
1410 : }
1411 :
1412 11 : err = _this->SendSubscribeRequest(_this->mReadPrepareParams);
1413 :
1414 11 : exit:
1415 22 : if (err != CHIP_NO_ERROR)
1416 : {
1417 : //
1418 : // Call Close (which should trigger re-subscription again) EXCEPT if we got here because we didn't have a valid
1419 : // CASESessionManager pointer when mForceCaseOnNextResub was true.
1420 : //
1421 : // In that case, don't permit re-subscription to occur.
1422 : //
1423 0 : _this->Close(err, allowResubscribeOnError);
1424 : }
1425 : }
1426 :
1427 65 : void ReadClient::UpdateDataVersionFilters(const ConcreteDataAttributePath & aPath)
1428 : {
1429 130 : for (size_t index = 0; index < mReadPrepareParams.mDataVersionFilterListSize; index++)
1430 : {
1431 65 : if (mReadPrepareParams.mpDataVersionFilterList[index].mEndpointId == aPath.mEndpointId &&
1432 65 : mReadPrepareParams.mpDataVersionFilterList[index].mClusterId == aPath.mClusterId)
1433 : {
1434 : // Now we know the current version for this cluster is aPath.mDataVersion.
1435 65 : mReadPrepareParams.mpDataVersionFilterList[index].mDataVersion = aPath.mDataVersion;
1436 : }
1437 : }
1438 65 : }
1439 :
1440 340 : CHIP_ERROR ReadClient::GetMinEventNumber(const ReadPrepareParams & aReadPrepareParams, Optional<EventNumber> & aEventMin)
1441 : {
1442 340 : if (aReadPrepareParams.mEventNumber.HasValue())
1443 : {
1444 313 : aEventMin = aReadPrepareParams.mEventNumber;
1445 : }
1446 : else
1447 : {
1448 27 : ReturnErrorOnFailure(mpCallback.GetHighestReceivedEventNumber(aEventMin));
1449 27 : if (aEventMin.HasValue())
1450 : {
1451 : // We want to start with the first event _after_ the last one we received.
1452 2 : aEventMin.SetValue(aEventMin.Value() + 1);
1453 : }
1454 : }
1455 340 : return CHIP_NO_ERROR;
1456 : }
1457 :
1458 202 : bool ReadClient::TriggerResubscribeIfScheduled(const char * reason)
1459 : {
1460 202 : if (!mIsResubscriptionScheduled)
1461 : {
1462 201 : return false;
1463 : }
1464 :
1465 1 : ChipLogDetail(DataManagement, "ReadClient[%p] triggering resubscribe, reason: %s", this, reason);
1466 1 : CancelResubscribeTimer();
1467 1 : OnResubscribeTimerCallback(nullptr, this);
1468 :
1469 1 : return true;
1470 : }
1471 :
1472 0 : Optional<System::Clock::Timeout> ReadClient::GetSubscriptionTimeout()
1473 : {
1474 0 : if (!IsSubscriptionType() || !IsSubscriptionActive())
1475 : {
1476 0 : return NullOptional;
1477 : }
1478 :
1479 : System::Clock::Timeout timeout;
1480 0 : CHIP_ERROR err = ComputeLivenessCheckTimerTimeout(&timeout);
1481 0 : if (err != CHIP_NO_ERROR)
1482 : {
1483 0 : return NullOptional;
1484 : }
1485 :
1486 0 : return MakeOptional(timeout);
1487 : }
1488 :
1489 0 : CHIP_ERROR ReadClient::EstablishSessionToPeer()
1490 : {
1491 0 : ChipLogProgress(DataManagement, "Trying to establish a CASE session for subscription");
1492 0 : auto * caseSessionManager = InteractionModelEngine::GetInstance()->GetCASESessionManager();
1493 0 : VerifyOrReturnError(caseSessionManager != nullptr, CHIP_ERROR_INCORRECT_STATE);
1494 0 : caseSessionManager->FindOrEstablishSession(mPeer, &mOnConnectedCallback, &mOnConnectionFailureCallback);
1495 0 : return CHIP_NO_ERROR;
1496 : }
1497 :
1498 : } // namespace app
1499 : } // namespace chip
|