Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 : #include <app/AppConfig.h>
19 : #include <app/AttributePathExpandIterator.h>
20 : #include <app/InteractionModelEngine.h>
21 : #include <app/MessageDef/EventPathIB.h>
22 : #include <app/MessageDef/StatusResponseMessage.h>
23 : #include <app/MessageDef/SubscribeRequestMessage.h>
24 : #include <app/MessageDef/SubscribeResponseMessage.h>
25 : #include <app/data-model-provider/Provider.h>
26 : #include <app/icd/server/ICDServerConfig.h>
27 : #include <lib/core/TLVUtilities.h>
28 : #include <lib/support/CodeUtils.h>
29 : #include <messaging/ExchangeContext.h>
30 :
31 : #include <app/ReadHandler.h>
32 : #include <app/reporting/Engine.h>
33 :
34 : #if CHIP_CONFIG_ENABLE_ICD_SERVER
35 : #include <app/icd/server/ICDConfigurationData.h> //nogncheck
36 : #endif
37 :
38 : namespace chip {
39 : namespace app {
40 : using Status = Protocols::InteractionModel::Status;
41 :
42 33 : uint16_t ReadHandler::GetPublisherSelectedIntervalLimit()
43 : {
44 : #if CHIP_CONFIG_ENABLE_ICD_SERVER
45 : // We don't need to check for precision loss since the max value of the IdleModeDuration can fit inside a uint16_t
46 : const auto idleModeDuration =
47 : std::chrono::duration_cast<System::Clock::Seconds16>(ICDConfigurationData::GetInstance().GetIdleModeDuration()).count();
48 : return std::max(idleModeDuration, kSubscriptionMaxIntervalPublisherLimit);
49 : #else
50 33 : return kSubscriptionMaxIntervalPublisherLimit;
51 : #endif
52 : }
53 :
54 1138 : ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext,
55 1138 : InteractionType aInteractionType, Observer * observer) :
56 1138 : mExchangeCtx(*this),
57 2276 : mManagementCallback(apCallback)
58 : {
59 1138 : VerifyOrDie(apExchangeContext != nullptr);
60 :
61 1138 : mExchangeCtx.Grab(apExchangeContext);
62 : #if CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
63 : // TODO: this should be replaced by a pointer to the InteractionModelEngine that created the ReadHandler
64 : // once InteractionModelEngine is no longer a singleton (see issue 23625)
65 : mExchangeMgr = apExchangeContext->GetExchangeMgr();
66 : #endif // CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
67 :
68 1138 : mInteractionType = aInteractionType;
69 1138 : mLastWrittenEventsBytes = 0;
70 1138 : mTransactionStartGeneration = mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().GetDirtySetGeneration();
71 1138 : mFlags.ClearAll();
72 1138 : SetStateFlag(ReadHandlerFlags::PrimingReports);
73 :
74 1138 : mSessionHandle.Grab(mExchangeCtx->GetSessionHandle());
75 :
76 1138 : VerifyOrDie(observer != nullptr);
77 1138 : mObserver = observer;
78 1138 : }
79 :
80 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
81 0 : ReadHandler::ReadHandler(ManagementCallback & apCallback, Observer * observer) :
82 0 : mExchangeCtx(*this), mManagementCallback(apCallback)
83 : {
84 0 : mInteractionType = InteractionType::Subscribe;
85 0 : mFlags.ClearAll();
86 :
87 0 : VerifyOrDie(observer != nullptr);
88 0 : mObserver = observer;
89 0 : }
90 :
91 0 : void ReadHandler::OnSubscriptionResumed(const SessionHandle & sessionHandle,
92 : SubscriptionResumptionSessionEstablisher & resumptionSessionEstablisher)
93 : {
94 0 : mSubscriptionId = resumptionSessionEstablisher.mSubscriptionInfo.mSubscriptionId;
95 0 : mMinIntervalFloorSeconds = resumptionSessionEstablisher.mSubscriptionInfo.mMinInterval;
96 0 : mMaxInterval = resumptionSessionEstablisher.mSubscriptionInfo.mMaxInterval;
97 0 : SetStateFlag(ReadHandlerFlags::FabricFiltered, resumptionSessionEstablisher.mSubscriptionInfo.mFabricFiltered);
98 :
99 : // Move dynamically allocated attributes and events from the SubscriptionInfo struct into
100 : // the object pool managed by the IM engine
101 0 : for (size_t i = 0; i < resumptionSessionEstablisher.mSubscriptionInfo.mAttributePaths.AllocatedSize(); i++)
102 : {
103 0 : AttributePathParams params = resumptionSessionEstablisher.mSubscriptionInfo.mAttributePaths[i].GetParams();
104 0 : CHIP_ERROR err = mManagementCallback.GetInteractionModelEngine()->PushFrontAttributePathList(mpAttributePathList, params);
105 0 : if (err != CHIP_NO_ERROR)
106 : {
107 0 : Close();
108 0 : return;
109 : }
110 : }
111 0 : for (size_t i = 0; i < resumptionSessionEstablisher.mSubscriptionInfo.mEventPaths.AllocatedSize(); i++)
112 : {
113 0 : EventPathParams params = resumptionSessionEstablisher.mSubscriptionInfo.mEventPaths[i].GetParams();
114 0 : CHIP_ERROR err = mManagementCallback.GetInteractionModelEngine()->PushFrontEventPathParamsList(mpEventPathList, params);
115 0 : if (err != CHIP_NO_ERROR)
116 : {
117 0 : Close();
118 0 : return;
119 : }
120 : }
121 :
122 0 : mSessionHandle.Grab(sessionHandle);
123 :
124 0 : SetStateFlag(ReadHandlerFlags::ActiveSubscription);
125 :
126 0 : auto * appCallback = mManagementCallback.GetAppCallback();
127 0 : if (appCallback)
128 : {
129 0 : appCallback->OnSubscriptionEstablished(*this);
130 : }
131 : // Notify the observer that a subscription has been resumed
132 0 : mObserver->OnSubscriptionEstablished(this);
133 :
134 0 : MoveToState(HandlerState::CanStartReporting);
135 :
136 0 : SingleLinkedListNode<AttributePathParams> * attributePath = mpAttributePathList;
137 0 : while (attributePath)
138 : {
139 0 : TEMPORARY_RETURN_IGNORED mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().SetDirty(
140 0 : attributePath->mValue);
141 0 : attributePath = attributePath->mpNext;
142 : }
143 : }
144 :
145 : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
146 :
147 1138 : ReadHandler::~ReadHandler()
148 : {
149 1138 : mObserver->OnReadHandlerDestroyed(this);
150 :
151 1138 : auto * appCallback = mManagementCallback.GetAppCallback();
152 1138 : if (mFlags.Has(ReadHandlerFlags::ActiveSubscription) && appCallback)
153 : {
154 240 : mFlags.Clear(ReadHandlerFlags::ActiveSubscription);
155 240 : appCallback->OnSubscriptionTerminated(*this);
156 : }
157 :
158 1138 : if (IsAwaitingReportResponse())
159 : {
160 2 : mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().OnReportConfirm();
161 : }
162 1138 : mManagementCallback.GetInteractionModelEngine()->ReleaseAttributePathList(mpAttributePathList);
163 1138 : mManagementCallback.GetInteractionModelEngine()->ReleaseEventPathList(mpEventPathList);
164 1138 : mManagementCallback.GetInteractionModelEngine()->ReleaseDataVersionFilterList(mpDataVersionFilterList);
165 1138 : }
166 :
167 868 : void ReadHandler::Close(CloseOptions options)
168 : {
169 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
170 868 : if (IsType(InteractionType::Subscribe) && options == CloseOptions::kDropPersistedSubscription)
171 : {
172 102 : auto * subscriptionResumptionStorage = mManagementCallback.GetInteractionModelEngine()->GetSubscriptionResumptionStorage();
173 102 : if (subscriptionResumptionStorage)
174 : {
175 0 : TEMPORARY_RETURN_IGNORED subscriptionResumptionStorage->Delete(GetInitiatorNodeId(), GetAccessingFabricIndex(),
176 : mSubscriptionId);
177 : }
178 : }
179 : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
180 :
181 : #if CHIP_PROGRESS_LOGGING
182 868 : if (IsType(InteractionType::Subscribe))
183 : {
184 116 : const ScopedNodeId & peer = mSessionHandle ? mSessionHandle->GetPeer() : ScopedNodeId();
185 116 : ChipLogProgress(DataManagement, "Subscription id 0x%" PRIx32 " from node " ChipLogFormatScopedNodeId " torn down",
186 : mSubscriptionId, ChipLogValueScopedNodeId(peer));
187 : }
188 : #endif // CHIP_PROGRESS_LOGGING
189 :
190 868 : MoveToState(HandlerState::AwaitingDestruction);
191 868 : mManagementCallback.OnDone(*this);
192 868 : }
193 :
194 1090 : void ReadHandler::OnInitialRequest(System::PacketBufferHandle && aPayload)
195 : {
196 1090 : CHIP_ERROR err = CHIP_NO_ERROR;
197 1090 : System::PacketBufferHandle response;
198 :
199 1090 : if (IsType(InteractionType::Subscribe))
200 : {
201 303 : err = ProcessSubscribeRequest(std::move(aPayload));
202 : }
203 : else
204 : {
205 787 : err = ProcessReadRequest(std::move(aPayload));
206 : }
207 :
208 2180 : if (err != CHIP_NO_ERROR)
209 : {
210 8 : Status status = Status::InvalidAction;
211 8 : if (err.IsIMStatus())
212 : {
213 0 : status = StatusIB(err).mStatus;
214 : }
215 8 : TEMPORARY_RETURN_IGNORED StatusResponse::Send(status, mExchangeCtx.Get(), /* aExpectResponse = */ false);
216 : // At this point we can't have a persisted subscription, since that
217 : // happens only when ProcessSubscribeRequest returns success. And our
218 : // subscription id is almost certainly not actually useful at this
219 : // point, either. So don't try to mess with persisted subscriptions in
220 : // Close().
221 8 : Close(CloseOptions::kKeepPersistedSubscription);
222 : }
223 : else
224 : {
225 : // Force us to be in a dirty state so we get processed by the reporting
226 1082 : SetStateFlag(ReadHandlerFlags::ForceDirty);
227 : }
228 1090 : }
229 :
230 1231 : CHIP_ERROR ReadHandler::OnStatusResponse(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload,
231 : bool & aSendStatusResponse)
232 : {
233 1231 : CHIP_ERROR err = CHIP_NO_ERROR;
234 1231 : aSendStatusResponse = true;
235 1231 : CHIP_ERROR statusError = CHIP_NO_ERROR;
236 1231 : SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError));
237 : // Since this is a valid Status Response message, we don't have to send a Status Response in reply to it.
238 1229 : aSendStatusResponse = false;
239 1229 : SuccessOrExit(err = statusError);
240 1217 : switch (mState)
241 : {
242 1217 : case HandlerState::AwaitingReportResponse:
243 1217 : if (IsChunkedReport())
244 : {
245 856 : mExchangeCtx->WillSendMessage();
246 : }
247 361 : else if (IsType(InteractionType::Subscribe))
248 : {
249 361 : if (IsPriming())
250 : {
251 284 : err = SendSubscribeResponse();
252 :
253 284 : SetStateFlag(ReadHandlerFlags::ActiveSubscription);
254 284 : auto * appCallback = mManagementCallback.GetAppCallback();
255 284 : if (appCallback)
256 : {
257 240 : appCallback->OnSubscriptionEstablished(*this);
258 : }
259 284 : mObserver->OnSubscriptionEstablished(this);
260 : }
261 : }
262 : else
263 : {
264 : //
265 : // We're done processing a read, so let's close out and return.
266 : //
267 0 : Close();
268 0 : return CHIP_NO_ERROR;
269 : }
270 :
271 1217 : MoveToState(HandlerState::CanStartReporting);
272 1217 : break;
273 :
274 0 : case HandlerState::CanStartReporting:
275 : case HandlerState::Idle:
276 : default:
277 0 : err = CHIP_ERROR_INCORRECT_STATE;
278 0 : break;
279 : }
280 :
281 1231 : exit:
282 1231 : return err;
283 : }
284 :
285 1982 : CHIP_ERROR ReadHandler::AcquireExchangeForSend()
286 : {
287 1982 : if (IsPriming() || IsChunkedReport())
288 : {
289 : // We already hold mExchangeCtx from the inbound request. The underlying
290 : // session can be released while a send is pending (e.g. TCP teardown);
291 : // guard before grabbing it to avoid crashing in UseSuggestedResponseTimeout.
292 1896 : VerifyOrReturnLogError(mExchangeCtx->HasSessionHandle(), CHIP_ERROR_MISSING_SECURE_SESSION);
293 1894 : mSessionHandle.Grab(mExchangeCtx->GetSessionHandle());
294 : }
295 : else
296 : {
297 : // For subscription re-reports, the previous exchange was released after
298 : // the last send. mExchangeCtx must be null here; open a fresh exchange
299 : // from the stored session handle.
300 86 : VerifyOrReturnLogError(!mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
301 86 : VerifyOrReturnLogError(mSessionHandle, CHIP_ERROR_INCORRECT_STATE);
302 : #if CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
303 : auto exchange = mExchangeMgr->NewContext(mSessionHandle.Get().Value(), this);
304 : #else // CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
305 : auto exchange =
306 86 : mManagementCallback.GetInteractionModelEngine()->GetExchangeManager()->NewContext(mSessionHandle.Get().Value(), this);
307 : #endif // CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
308 86 : VerifyOrReturnLogError(exchange != nullptr, CHIP_ERROR_INCORRECT_STATE);
309 84 : mExchangeCtx.Grab(exchange);
310 : }
311 1978 : return CHIP_NO_ERROR;
312 : }
313 :
314 32 : CHIP_ERROR ReadHandler::SendStatusReport(Protocols::InteractionModel::Status aStatus)
315 : {
316 32 : VerifyOrReturnLogError(mState == HandlerState::CanStartReporting, CHIP_ERROR_INCORRECT_STATE);
317 32 : ReturnErrorOnFailure(AcquireExchangeForSend());
318 31 : return StatusResponse::Send(aStatus, mExchangeCtx.Get(), /* aExpectResponse = */ false);
319 : }
320 :
321 1954 : CHIP_ERROR ReadHandler::SendReportData(System::PacketBufferHandle && aPayload, bool aMoreChunks)
322 : {
323 1954 : VerifyOrReturnLogError(mState == HandlerState::CanStartReporting, CHIP_ERROR_INCORRECT_STATE);
324 1950 : VerifyOrDie(!IsAwaitingReportResponse()); // Should not be reportable!
325 1950 : ReturnErrorOnFailure(AcquireExchangeForSend());
326 :
327 1947 : if (!IsReporting())
328 : {
329 : mCurrentReportsBeginGeneration =
330 1135 : mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().GetDirtySetGeneration();
331 : }
332 1947 : SetStateFlag(ReadHandlerFlags::ChunkedReport, aMoreChunks);
333 1947 : bool responseExpected = IsType(InteractionType::Subscribe) || aMoreChunks;
334 :
335 1947 : ReturnErrorOnFailure(mExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime));
336 1947 : CHIP_ERROR err = mExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::ReportData, std::move(aPayload),
337 : responseExpected ? Messaging::SendMessageFlags::kExpectResponse
338 : : Messaging::SendMessageFlags::kNone);
339 3894 : if (err == CHIP_NO_ERROR)
340 : {
341 1945 : if (responseExpected)
342 : {
343 1241 : MoveToState(HandlerState::AwaitingReportResponse);
344 : }
345 : else
346 : {
347 : // Make sure we're not treated as an in-flight report waiting for a
348 : // response by the reporting engine.
349 704 : mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().OnReportConfirm();
350 : }
351 :
352 : // If we just finished a non-priming subscription report, notify our observers.
353 : // Priming reports are handled when we send a SubscribeResponse.
354 1945 : if (IsType(InteractionType::Subscribe) && !IsPriming() && !IsChunkedReport())
355 : {
356 82 : mObserver->OnSubscriptionReportSent(this);
357 : }
358 : }
359 1947 : if (!aMoreChunks)
360 : {
361 1081 : mPreviousReportsBeginGeneration = mCurrentReportsBeginGeneration;
362 1081 : ClearForceDirtyFlag();
363 1081 : mManagementCallback.GetInteractionModelEngine()->ReleaseDataVersionFilterList(mpDataVersionFilterList);
364 : }
365 :
366 1947 : return err;
367 : }
368 :
369 1233 : CHIP_ERROR ReadHandler::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
370 : System::PacketBufferHandle && aPayload)
371 : {
372 1233 : CHIP_ERROR err = CHIP_NO_ERROR;
373 1233 : bool sendStatusResponse = true;
374 1233 : if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse))
375 : {
376 1231 : err = OnStatusResponse(apExchangeContext, std::move(aPayload), sendStatusResponse);
377 : }
378 : else
379 : {
380 2 : ChipLogDetail(DataManagement, "ReadHandler:: Msg type %d not supported", aPayloadHeader.GetMessageType());
381 2 : err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
382 : }
383 :
384 1233 : if (sendStatusResponse)
385 : {
386 4 : TEMPORARY_RETURN_IGNORED StatusResponse::Send(Status::InvalidAction, apExchangeContext, false /*aExpectResponse*/);
387 : }
388 :
389 2466 : if (err != CHIP_NO_ERROR)
390 : {
391 16 : Close();
392 : }
393 1233 : return err;
394 : }
395 :
396 10 : bool ReadHandler::IsFromSubscriber(Messaging::ExchangeContext & apExchangeContext) const
397 : {
398 10 : return (IsType(InteractionType::Subscribe) &&
399 30 : GetInitiatorNodeId() == apExchangeContext.GetSessionHandle()->AsSecureSession()->GetPeerNodeId() &&
400 20 : GetAccessingFabricIndex() == apExchangeContext.GetSessionHandle()->GetFabricIndex());
401 : }
402 :
403 8 : void ReadHandler::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
404 : {
405 8 : ChipLogError(DataManagement, "Time out! failed to receive status response from Exchange: " ChipLogFormatExchange,
406 : ChipLogValueExchange(apExchangeContext));
407 : #if CHIP_CONFIG_ENABLE_ICD_SERVER && CHIP_CONFIG_ENABLE_ICD_CIP && CHIP_CONFIG_ENABLE_ICD_CHECK_IN_ON_REPORT_TIMEOUT
408 : switch (mState)
409 : {
410 : case HandlerState::AwaitingReportResponse:
411 : if (IsType(InteractionType::Subscribe) && !IsPriming())
412 : {
413 : // Trigger check-in message when a non-priming subscription report times out.
414 : ChipLogError(DataManagement, "Trigger check-in message when non-priming subscription report times out");
415 : if (mSessionHandle)
416 : {
417 : ICDNotifier::GetInstance().NotifySendCheckIn(MakeOptional(GetSubjectDescriptor()));
418 : }
419 : else
420 : {
421 : ChipLogError(DataManagement, "Failed to get subject descriptor for sending check-in message on report timeout");
422 : }
423 : }
424 : break;
425 : case HandlerState::CanStartReporting:
426 : case HandlerState::Idle:
427 : default:
428 : break;
429 : }
430 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP && CHIP_CONFIG_ENABLE_ICD_SERVER
431 :
432 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
433 8 : Close(CloseOptions::kKeepPersistedSubscription);
434 : #else
435 : Close();
436 : #endif
437 8 : }
438 :
439 791 : CHIP_ERROR ReadHandler::ProcessReadRequest(System::PacketBufferHandle && aPayload)
440 : {
441 791 : CHIP_ERROR err = CHIP_NO_ERROR;
442 791 : System::PacketBufferTLVReader reader;
443 :
444 791 : ReadRequestMessage::Parser readRequestParser;
445 791 : EventPathIBs::Parser eventPathListParser;
446 791 : EventFilterIBs::Parser eventFilterIBsParser;
447 791 : AttributePathIBs::Parser attributePathListParser;
448 :
449 791 : reader.Init(std::move(aPayload));
450 :
451 791 : ReturnErrorOnFailure(readRequestParser.Init(reader));
452 :
453 : // No need to pretty-print here. We pretty-print read requests in the read
454 : // case of InteractionModelEngine::OnReadInitialRequest, so we do it even if
455 : // we reject a read request.
456 :
457 791 : err = readRequestParser.GetAttributeRequests(&attributePathListParser);
458 1582 : if (err == CHIP_END_OF_TLV)
459 : {
460 122 : err = CHIP_NO_ERROR;
461 : }
462 1338 : else if (err == CHIP_NO_ERROR)
463 : {
464 669 : ReturnErrorOnFailure(ProcessAttributePaths(attributePathListParser));
465 669 : DataVersionFilterIBs::Parser dataVersionFilterListParser;
466 669 : err = readRequestParser.GetDataVersionFilters(&dataVersionFilterListParser);
467 1338 : if (err == CHIP_END_OF_TLV)
468 : {
469 589 : err = CHIP_NO_ERROR;
470 : }
471 160 : else if (err == CHIP_NO_ERROR)
472 : {
473 80 : ReturnErrorOnFailure(ProcessDataVersionFilterList(dataVersionFilterListParser));
474 : }
475 : }
476 791 : ReturnErrorOnFailure(err);
477 791 : err = readRequestParser.GetEventRequests(&eventPathListParser);
478 1582 : if (err == CHIP_END_OF_TLV)
479 : {
480 473 : err = CHIP_NO_ERROR;
481 : }
482 636 : else if (err == CHIP_NO_ERROR)
483 : {
484 318 : ReturnErrorOnFailure(err);
485 318 : ReturnErrorOnFailure(ProcessEventPaths(eventPathListParser));
486 318 : err = readRequestParser.GetEventFilters(&eventFilterIBsParser);
487 636 : if (err == CHIP_END_OF_TLV)
488 : {
489 3 : err = CHIP_NO_ERROR;
490 : }
491 630 : else if (err == CHIP_NO_ERROR)
492 : {
493 315 : ReturnErrorOnFailure(ProcessEventFilters(eventFilterIBsParser));
494 : }
495 : }
496 791 : ReturnErrorOnFailure(err);
497 :
498 : bool isFabricFiltered;
499 791 : ReturnErrorOnFailure(readRequestParser.GetIsFabricFiltered(&isFabricFiltered));
500 787 : SetStateFlag(ReadHandlerFlags::FabricFiltered, isFabricFiltered);
501 787 : ReturnErrorOnFailure(readRequestParser.ExitContainer());
502 787 : MoveToState(HandlerState::CanStartReporting);
503 :
504 787 : mExchangeCtx->WillSendMessage();
505 :
506 : // There must be no code after the WillSendMessage() call that can cause
507 : // this method to return a failure.
508 :
509 787 : return CHIP_NO_ERROR;
510 791 : }
511 :
512 970 : CHIP_ERROR ReadHandler::ProcessAttributePaths(AttributePathIBs::Parser & aAttributePathListParser)
513 : {
514 970 : CHIP_ERROR err = CHIP_NO_ERROR;
515 970 : TLV::TLVReader reader;
516 970 : aAttributePathListParser.GetReader(&reader);
517 5022 : while (CHIP_NO_ERROR == (err = reader.Next()))
518 : {
519 1541 : VerifyOrReturnError(TLV::AnonymousTag() == reader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG);
520 1541 : AttributePathParams attribute;
521 1541 : AttributePathIB::Parser path;
522 1541 : ReturnErrorOnFailure(path.Init(reader));
523 1541 : ReturnErrorOnFailure(path.ParsePath(attribute));
524 1541 : ReturnErrorOnFailure(
525 : mManagementCallback.GetInteractionModelEngine()->PushFrontAttributePathList(mpAttributePathList, attribute));
526 : }
527 : // if we have exhausted this container
528 1940 : if (CHIP_END_OF_TLV == err)
529 : {
530 970 : mManagementCallback.GetInteractionModelEngine()->RemoveDuplicateConcreteAttributePath(mpAttributePathList);
531 970 : mAttributePathExpandPosition = AttributePathExpandIterator::Position::StartIterating(mpAttributePathList);
532 970 : err = CHIP_NO_ERROR;
533 : }
534 970 : return err;
535 : }
536 :
537 145 : CHIP_ERROR ReadHandler::ProcessDataVersionFilterList(DataVersionFilterIBs::Parser & aDataVersionFilterListParser)
538 : {
539 145 : CHIP_ERROR err = CHIP_NO_ERROR;
540 145 : TLV::TLVReader reader;
541 :
542 145 : aDataVersionFilterListParser.GetReader(&reader);
543 5274 : while (CHIP_NO_ERROR == (err = reader.Next()))
544 : {
545 2492 : VerifyOrReturnError(TLV::AnonymousTag() == reader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG);
546 2492 : DataVersionFilter versionFilter;
547 2492 : ClusterPathIB::Parser path;
548 2492 : DataVersionFilterIB::Parser filter;
549 2492 : ReturnErrorOnFailure(filter.Init(reader));
550 2492 : DataVersion version = 0;
551 2492 : ReturnErrorOnFailure(filter.GetDataVersion(&version));
552 2492 : versionFilter.mDataVersion.SetValue(version);
553 2492 : ReturnErrorOnFailure(filter.GetPath(&path));
554 2492 : ReturnErrorOnFailure(path.GetEndpoint(&(versionFilter.mEndpointId)));
555 2492 : ReturnErrorOnFailure(path.GetCluster(&(versionFilter.mClusterId)));
556 2492 : VerifyOrReturnError(versionFilter.IsValidDataVersionFilter(), CHIP_ERROR_IM_MALFORMED_DATA_VERSION_FILTER_IB);
557 2492 : ReturnErrorOnFailure(mManagementCallback.GetInteractionModelEngine()->PushFrontDataVersionFilterList(
558 : mpDataVersionFilterList, versionFilter));
559 : }
560 :
561 290 : if (CHIP_END_OF_TLV == err)
562 : {
563 145 : err = CHIP_NO_ERROR;
564 : }
565 145 : return err;
566 : }
567 :
568 336 : CHIP_ERROR ReadHandler::ProcessEventPaths(EventPathIBs::Parser & aEventPathsParser)
569 : {
570 336 : CHIP_ERROR err = CHIP_NO_ERROR;
571 336 : TLV::TLVReader reader;
572 336 : aEventPathsParser.GetReader(&reader);
573 1670 : while (CHIP_NO_ERROR == (err = reader.Next()))
574 : {
575 499 : VerifyOrReturnError(TLV::AnonymousTag() == reader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG);
576 499 : EventPathParams event;
577 499 : EventPathIB::Parser path;
578 499 : ReturnErrorOnFailure(path.Init(reader));
579 499 : ReturnErrorOnFailure(path.ParsePath(event));
580 499 : ReturnErrorOnFailure(mManagementCallback.GetInteractionModelEngine()->PushFrontEventPathParamsList(mpEventPathList, event));
581 : }
582 :
583 : // if we have exhausted this container
584 672 : if (CHIP_END_OF_TLV == err)
585 : {
586 336 : err = CHIP_NO_ERROR;
587 : }
588 336 : return err;
589 : }
590 :
591 315 : CHIP_ERROR ReadHandler::ProcessEventFilters(EventFilterIBs::Parser & aEventFiltersParser)
592 : {
593 315 : CHIP_ERROR err = CHIP_NO_ERROR;
594 315 : TLV::TLVReader reader;
595 315 : aEventFiltersParser.GetReader(&reader);
596 :
597 1260 : while (CHIP_NO_ERROR == (err = reader.Next()))
598 : {
599 315 : VerifyOrReturnError(TLV::AnonymousTag() == reader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG);
600 315 : EventFilterIB::Parser filter;
601 315 : ReturnErrorOnFailure(filter.Init(reader));
602 : // this is for current node, and would have only one event filter.
603 315 : ReturnErrorOnFailure(filter.GetEventMin(&(mEventMin)));
604 : }
605 630 : if (CHIP_END_OF_TLV == err)
606 : {
607 315 : err = CHIP_NO_ERROR;
608 : }
609 315 : return err;
610 : }
611 :
612 0 : const char * ReadHandler::GetStateStr() const
613 : {
614 : #if CHIP_DETAIL_LOGGING
615 0 : switch (mState)
616 : {
617 0 : case HandlerState::Idle:
618 0 : return "Idle";
619 0 : case HandlerState::AwaitingDestruction:
620 0 : return "AwaitingDestruction";
621 0 : case HandlerState::CanStartReporting:
622 0 : return "CanStartReporting";
623 :
624 0 : case HandlerState::AwaitingReportResponse:
625 0 : return "AwaitingReportResponse";
626 : }
627 : #endif // CHIP_DETAIL_LOGGING
628 0 : return "N/A";
629 : }
630 :
631 4428 : void ReadHandler::MoveToState(const HandlerState aTargetState)
632 : {
633 4428 : if (aTargetState == mState)
634 : {
635 0 : return;
636 : }
637 :
638 4428 : if (IsAwaitingReportResponse() && aTargetState != HandlerState::AwaitingReportResponse)
639 : {
640 1239 : mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().OnReportConfirm();
641 : }
642 :
643 4428 : mState = aTargetState;
644 4428 : ChipLogDetail(DataManagement, "IM RH moving to [%s]", GetStateStr());
645 :
646 : //
647 : // If we just unblocked sending reports, let's go ahead and schedule the reporting
648 : // engine to run to kick that off.
649 : //
650 4428 : if (aTargetState == HandlerState::CanStartReporting)
651 : {
652 2315 : if (ShouldReportUnscheduled())
653 : {
654 1921 : TEMPORARY_RETURN_IGNORED mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().ScheduleRun();
655 : }
656 : else
657 : {
658 : // If we became reportable, the scheduler will schedule a run as soon as allowed
659 394 : mObserver->OnBecameReportable(this);
660 : }
661 : }
662 : }
663 :
664 888 : bool ReadHandler::CheckEventClean(EventManagement & aEventManager)
665 : {
666 888 : if (mFlags.Has(ReadHandlerFlags::ChunkedReport))
667 : {
668 525 : if ((mLastScheduledEventNumber != 0) && (mEventMin <= mLastScheduledEventNumber))
669 : {
670 519 : return false;
671 : }
672 : }
673 : else
674 : {
675 363 : EventNumber lastEventNumber = aEventManager.GetLastEventNumber();
676 363 : if ((lastEventNumber != 0) && (mEventMin <= lastEventNumber))
677 : {
678 : // We have more events. snapshot last event number
679 345 : aEventManager.SetScheduledEventInfo(mLastScheduledEventNumber, mLastWrittenEventsBytes);
680 345 : return false;
681 : }
682 : }
683 24 : return true;
684 : }
685 :
686 284 : CHIP_ERROR ReadHandler::SendSubscribeResponse()
687 : {
688 284 : System::PacketBufferHandle packet = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
689 284 : VerifyOrReturnLogError(!packet.IsNull(), CHIP_ERROR_NO_MEMORY);
690 :
691 284 : System::PacketBufferTLVWriter writer;
692 284 : writer.Init(std::move(packet));
693 :
694 284 : SubscribeResponseMessage::Builder response;
695 284 : ReturnErrorOnFailure(response.Init(&writer));
696 284 : ReturnErrorOnFailure(response.SubscriptionId(mSubscriptionId).MaxInterval(mMaxInterval).EndOfSubscribeResponseMessage());
697 :
698 284 : ReturnErrorOnFailure(writer.Finalize(&packet));
699 284 : VerifyOrReturnLogError(mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
700 :
701 284 : ClearStateFlag(ReadHandlerFlags::PrimingReports);
702 284 : return mExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::SubscribeResponse, std::move(packet));
703 284 : }
704 :
705 307 : CHIP_ERROR ReadHandler::ProcessSubscribeRequest(System::PacketBufferHandle && aPayload)
706 : {
707 307 : System::PacketBufferTLVReader reader;
708 307 : reader.Init(std::move(aPayload));
709 :
710 307 : SubscribeRequestMessage::Parser subscribeRequestParser;
711 307 : ReturnErrorOnFailure(subscribeRequestParser.Init(reader));
712 :
713 : // No need to pretty-print here. We pretty-print subscribe requests in the
714 : // subscribe case of InteractionModelEngine::OnReadInitialRequest, so we do
715 : // it even if we reject a subscribe request.
716 :
717 307 : AttributePathIBs::Parser attributePathListParser;
718 307 : CHIP_ERROR err = subscribeRequestParser.GetAttributeRequests(&attributePathListParser);
719 614 : if (err == CHIP_END_OF_TLV)
720 : {
721 6 : err = CHIP_NO_ERROR;
722 : }
723 602 : else if (err == CHIP_NO_ERROR)
724 : {
725 301 : ReturnErrorOnFailure(ProcessAttributePaths(attributePathListParser));
726 301 : DataVersionFilterIBs::Parser dataVersionFilterListParser;
727 301 : err = subscribeRequestParser.GetDataVersionFilters(&dataVersionFilterListParser);
728 602 : if (err == CHIP_END_OF_TLV)
729 : {
730 236 : err = CHIP_NO_ERROR;
731 : }
732 130 : else if (err == CHIP_NO_ERROR)
733 : {
734 65 : ReturnErrorOnFailure(ProcessDataVersionFilterList(dataVersionFilterListParser));
735 : }
736 : }
737 307 : ReturnErrorOnFailure(err);
738 :
739 307 : EventPathIBs::Parser eventPathListParser;
740 307 : err = subscribeRequestParser.GetEventRequests(&eventPathListParser);
741 614 : if (err == CHIP_END_OF_TLV)
742 : {
743 289 : err = CHIP_NO_ERROR;
744 : }
745 36 : else if (err == CHIP_NO_ERROR)
746 : {
747 18 : ReturnErrorOnFailure(ProcessEventPaths(eventPathListParser));
748 18 : EventFilterIBs::Parser eventFilterIBsParser;
749 18 : err = subscribeRequestParser.GetEventFilters(&eventFilterIBsParser);
750 36 : if (err == CHIP_END_OF_TLV)
751 : {
752 18 : err = CHIP_NO_ERROR;
753 : }
754 0 : else if (err == CHIP_NO_ERROR)
755 : {
756 0 : ReturnErrorOnFailure(ProcessEventFilters(eventFilterIBsParser));
757 : }
758 : }
759 307 : ReturnErrorOnFailure(err);
760 :
761 307 : ReturnErrorOnFailure(subscribeRequestParser.GetMinIntervalFloorSeconds(&mMinIntervalFloorSeconds));
762 307 : ReturnErrorOnFailure(subscribeRequestParser.GetMaxIntervalCeilingSeconds(&mSubscriberRequestedMaxInterval));
763 307 : mMaxInterval = mSubscriberRequestedMaxInterval;
764 :
765 307 : VerifyOrReturnError(mMinIntervalFloorSeconds <= mMaxInterval, CHIP_ERROR_INVALID_ARGUMENT);
766 :
767 : #if CHIP_CONFIG_ENABLE_ICD_SERVER
768 :
769 : // To optimize ICD sleep/idle cycles, it is preferred to synchronize the subscription report interval
770 : // with the IdleModeDuration defined in the ICD Management Cluster.
771 : // We aim to use IdleModeDuration, but must respect the Min Interval Floor and Max Interval Ceiling.
772 : // Note: This behavior can be overridden using the OnSubscriptionRequested callback defined
773 : // in the application.
774 :
775 : // Per spec and static checks, GetIdleModeDuration max value fits a uint16_t.
776 : // The final selected interval must be a uint16_t but we use a uint32 here during calculation to prevent overflows.
777 : uint32_t preferredMaxInterval = ICDConfigurationData::GetInstance().GetIdleModeDuration().count();
778 : // Determine the max interval ceiling between GetPublisherSelectedIntervalLimit
779 : // and the subscriber-requested MaxInterval.
780 : uint16_t maxIntervalCeiling = std::max(GetPublisherSelectedIntervalLimit(), mMaxInterval);
781 :
782 : // Spec doesn't allow IdleModeDuration of 0 but make sure here to prevent div0
783 : if (preferredMaxInterval == 0)
784 : {
785 : preferredMaxInterval = maxIntervalCeiling;
786 : }
787 : else
788 : {
789 : // Must respect the minimal interval floor
790 : if (preferredMaxInterval < mMinIntervalFloorSeconds)
791 : {
792 : // Round up to the nearest multiple of IdleModeDuration that is >= mMinIntervalFloorSeconds.
793 : const uint32_t remainder = mMinIntervalFloorSeconds % preferredMaxInterval;
794 : preferredMaxInterval =
795 : remainder == 0 ? mMinIntervalFloorSeconds : (mMinIntervalFloorSeconds + (preferredMaxInterval - remainder));
796 : }
797 :
798 : // Use the smallest interval between preferredMaxInterval and maxIntervalCeiling.
799 : // This also ensure that no overflow occurs when casting to uint16_t below
800 : preferredMaxInterval = std::min<uint32_t>(preferredMaxInterval, maxIntervalCeiling);
801 : }
802 : mMaxInterval = static_cast<uint16_t>(preferredMaxInterval);
803 : #endif // CHIP_CONFIG_ENABLE_ICD_SERVER
804 :
805 : //
806 : // Notify the application (if requested) of the impending subscription and check whether we should still proceed to set it up.
807 : // This also provides the application an opportunity to modify the negotiated min/max intervals set above.
808 : //
809 307 : auto * appCallback = mManagementCallback.GetAppCallback();
810 307 : if (appCallback)
811 : {
812 494 : if (appCallback->OnSubscriptionRequested(*this, *mExchangeCtx->GetSessionHandle()->AsSecureSession()) != CHIP_NO_ERROR)
813 : {
814 6 : return CHIP_ERROR_TRANSACTION_CANCELED;
815 : }
816 : }
817 :
818 301 : ChipLogProgress(DataManagement, "Final negotiated min/max parameters: Min = %ds, Max = %ds", mMinIntervalFloorSeconds,
819 : mMaxInterval);
820 :
821 : bool isFabricFiltered;
822 301 : ReturnErrorOnFailure(subscribeRequestParser.GetIsFabricFiltered(&isFabricFiltered));
823 301 : SetStateFlag(ReadHandlerFlags::FabricFiltered, isFabricFiltered);
824 301 : ReturnErrorOnFailure(Crypto::DRBG_get_bytes(reinterpret_cast<uint8_t *>(&mSubscriptionId), sizeof(mSubscriptionId)));
825 301 : ReturnErrorOnFailure(subscribeRequestParser.ExitContainer());
826 301 : MoveToState(HandlerState::CanStartReporting);
827 :
828 301 : mExchangeCtx->WillSendMessage();
829 :
830 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
831 301 : PersistSubscription();
832 : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
833 :
834 301 : return CHIP_NO_ERROR;
835 307 : }
836 :
837 301 : void ReadHandler::PersistSubscription()
838 : {
839 301 : auto * subscriptionResumptionStorage = mManagementCallback.GetInteractionModelEngine()->GetSubscriptionResumptionStorage();
840 301 : VerifyOrReturn(subscriptionResumptionStorage != nullptr);
841 :
842 : // TODO(#31873): We need to store the CAT information to enable better interactions with ICDs
843 0 : SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = GetInitiatorNodeId(),
844 0 : .mFabricIndex = GetAccessingFabricIndex(),
845 0 : .mSubscriptionId = mSubscriptionId,
846 0 : .mMinInterval = mMinIntervalFloorSeconds,
847 0 : .mMaxInterval = mMaxInterval,
848 0 : .mFabricFiltered = IsFabricFiltered() };
849 0 : VerifyOrReturn(subscriptionInfo.SetAttributePaths(mpAttributePathList) == CHIP_NO_ERROR);
850 0 : VerifyOrReturn(subscriptionInfo.SetEventPaths(mpEventPathList) == CHIP_NO_ERROR);
851 :
852 0 : CHIP_ERROR err = subscriptionResumptionStorage->Save(subscriptionInfo);
853 0 : if (err != CHIP_NO_ERROR)
854 : {
855 0 : ChipLogError(DataManagement, "Failed to save subscription info error: '%" CHIP_ERROR_FORMAT, err.Format());
856 : }
857 0 : }
858 :
859 1168 : void ReadHandler::ResetPathIterator()
860 : {
861 1168 : mAttributePathExpandPosition = AttributePathExpandIterator::Position::StartIterating(mpAttributePathList);
862 1168 : mAttributeEncoderState.Reset();
863 1168 : }
864 :
865 348 : void ReadHandler::AttributePathIsDirty(DataModel::Provider * apDataModel, const AttributePathParams & aAttributeChanged)
866 : {
867 348 : mDirtyGeneration = mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().GetDirtySetGeneration();
868 :
869 : // We want to get the value, but not advance the iterator position.
870 348 : AttributePathExpandIterator::Position tempPosition = mAttributePathExpandPosition;
871 348 : ConcreteAttributePath path;
872 :
873 : // We won't reset the path iterator for every AttributePathIsDirty call to reduce the number of full data reports.
874 : // The iterator will be reset after finishing each report session.
875 : //
876 : // Here we just reset the iterator to the beginning of the current cluster, if the dirty path affects it.
877 : // This will ensure the reports are consistent within a single cluster generated from a single path in the request.
878 :
879 : // TODO (#16699): Currently we can only guarantee the reports generated from a single path in the request are consistent. The
880 : // data might be inconsistent if the user send a request with two paths from the same cluster. We need to clearify the behavior
881 : // or make it consistent.
882 614 : if (AttributePathExpandIterator(apDataModel, tempPosition).Next(path) &&
883 1141 : (aAttributeChanged.HasWildcardEndpointId() || aAttributeChanged.mEndpointId == path.mEndpointId) &&
884 527 : (aAttributeChanged.HasWildcardClusterId() || aAttributeChanged.mClusterId == path.mClusterId))
885 : {
886 264 : ChipLogDetail(DataManagement,
887 : "The dirty path intersects the cluster we are currently reporting; reset the iterator to the beginning of "
888 : "that cluster");
889 : // If we're currently in the middle of generating reports for a given cluster and that in turn is marked dirty, let's reset
890 : // our iterator to point back to the beginning of that cluster. This ensures that the receiver will get a coherent view of
891 : // the state of the cluster as present on the server
892 264 : mAttributePathExpandPosition.IterateFromTheStartOfTheCurrentClusterIfAttributeWildcard();
893 :
894 264 : mAttributeEncoderState.Reset();
895 : }
896 :
897 : // ReportScheduler will take care of verifying the reportability of the handler and schedule the run
898 348 : mObserver->OnBecameReportable(this);
899 348 : }
900 :
901 15109 : Transport::SecureSession * ReadHandler::GetSession() const
902 : {
903 15109 : if (!mSessionHandle)
904 : {
905 0 : return nullptr;
906 : }
907 15109 : return mSessionHandle->AsSecureSession();
908 : }
909 :
910 19 : void ReadHandler::ForceDirtyState()
911 : {
912 19 : SetStateFlag(ReadHandlerFlags::ForceDirty);
913 19 : }
914 :
915 6964 : void ReadHandler::SetStateFlag(ReadHandlerFlags aFlag, bool aValue)
916 : {
917 6964 : bool oldReportable = ShouldStartReporting();
918 6964 : mFlags.Set(aFlag, aValue);
919 :
920 : // If we became reportable, schedule a reporting run.
921 6964 : if (!oldReportable && ShouldStartReporting())
922 : {
923 : // If we became reportable, the scheduler will schedule a run as soon as allowed
924 13 : mObserver->OnBecameReportable(this);
925 : }
926 6964 : }
927 :
928 1382 : void ReadHandler::ClearStateFlag(ReadHandlerFlags aFlag)
929 : {
930 1382 : SetStateFlag(aFlag, false);
931 1382 : }
932 :
933 1980 : size_t ReadHandler::GetReportBufferMaxSize()
934 : {
935 1980 : Transport::SecureSession * session = GetSession();
936 1980 : if (session && session->AllowsLargePayload())
937 : {
938 0 : return kMaxLargeSecureSduLengthBytes;
939 : }
940 1980 : return kMaxSecureSduLengthBytes;
941 : }
942 :
943 : } // namespace app
944 : } // namespace chip
|