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