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