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