Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2021 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : /**
20 : * @file
21 : * This file defines objects for a CHIP Interaction Data model Engine which handle unsolicited IM message, and
22 : * manage different kinds of IM client and handlers.
23 : *
24 : */
25 :
26 : #pragma once
27 :
28 : #include <access/AccessControl.h>
29 : #include <app/AppConfig.h>
30 : #include <app/AttributePathParams.h>
31 : #include <app/CASESessionManager.h>
32 : #include <app/CommandHandlerImpl.h>
33 : #include <app/CommandResponseSender.h>
34 : #include <app/CommandSender.h>
35 : #include <app/ConcreteAttributePath.h>
36 : #include <app/ConcreteCommandPath.h>
37 : #include <app/ConcreteEventPath.h>
38 : #include <app/DataVersionFilter.h>
39 : #include <app/DeviceLoadStatusProvider.h>
40 : #include <app/EventPathParams.h>
41 : #include <app/MessageDef/AttributeReportIBs.h>
42 : #include <app/MessageDef/ReportDataMessage.h>
43 : #include <app/ReadClient.h>
44 : #include <app/ReadHandler.h>
45 : #include <app/StatusResponse.h>
46 : #include <app/SubscriptionResumptionSessionEstablisher.h>
47 : #include <app/SubscriptionStats.h>
48 : #include <app/SubscriptionsInfoProvider.h>
49 : #include <app/TimedHandler.h>
50 : #include <app/WriteClient.h>
51 : #include <app/WriteHandler.h>
52 : #include <app/data-model-provider/MetadataTypes.h>
53 : #include <app/data-model-provider/OperationTypes.h>
54 : #include <app/data-model-provider/Provider.h>
55 : #include <app/icd/server/ICDServerConfig.h>
56 : #include <app/reporting/Engine.h>
57 : #include <app/reporting/ReportScheduler.h>
58 : #include <app/util/attribute-metadata.h>
59 : #include <app/util/basic-types.h>
60 : #include <lib/core/CHIPCore.h>
61 : #include <lib/support/CodeUtils.h>
62 : #include <lib/support/DLLUtil.h>
63 : #include <lib/support/LinkedList.h>
64 : #include <lib/support/Pool.h>
65 : #include <lib/support/logging/CHIPLogging.h>
66 : #include <messaging/ExchangeContext.h>
67 : #include <messaging/ExchangeMgr.h>
68 : #include <messaging/Flags.h>
69 : #include <protocols/Protocols.h>
70 : #include <protocols/interaction_model/Constants.h>
71 : #include <system/SystemPacketBuffer.h>
72 : #include <transport/MessageStats.h>
73 :
74 : #if CHIP_CONFIG_ENABLE_ICD_SERVER
75 : #include <app/icd/server/ICDManager.h> // nogncheck
76 : #endif // CHIP_CONFIG_ENABLE_ICD_SERVER
77 :
78 : namespace chip {
79 : namespace app {
80 :
81 : /**
82 : * @class InteractionModelEngine
83 : *
84 : * @brief This is a singleton hosting all CHIP unsolicited message processing and managing interaction model related clients and
85 : * handlers
86 : *
87 : */
88 : class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
89 : public Messaging::ExchangeDelegate,
90 : private DataModel::ActionContext,
91 : public CommandResponseSender::Callback,
92 : public CommandHandlerImpl::Callback,
93 : public ReadHandler::ManagementCallback,
94 : public FabricTable::Delegate,
95 : public SubscriptionsInfoProvider,
96 : public TimedHandlerDelegate,
97 : public WriteHandlerDelegate,
98 : public DeviceLoadStatusProvider
99 : {
100 : public:
101 : /**
102 : * @brief Retrieve the singleton Interaction Model Engine.
103 : *
104 : * @return A pointer to the shared InteractionModel Engine
105 : *
106 : */
107 : static InteractionModelEngine * GetInstance(void);
108 :
109 : /**
110 : * Spec 8.5.1 A publisher SHALL always ensure that every fabric the node is commissioned into can create at least three
111 : * subscriptions to the publisher and that each subscription SHALL support at least 3 attribute/event paths.
112 : */
113 : static constexpr size_t kMinSupportedSubscriptionsPerFabric = 3;
114 : static constexpr size_t kMinSupportedPathsPerSubscription = 3;
115 : static constexpr size_t kMinSupportedPathsPerReadRequest = 9;
116 : static constexpr size_t kMinSupportedReadRequestsPerFabric = 1;
117 : static constexpr size_t kReadHandlerPoolSize = CHIP_IM_MAX_NUM_SUBSCRIPTIONS + CHIP_IM_MAX_NUM_READS;
118 :
119 : // TODO: Per spec, the above numbers should be 3, 3, 9, 1, however, we use a lower limit to reduce the memory usage and should
120 : // fix it when we have reduced the memory footprint of ReadHandlers.
121 :
122 : InteractionModelEngine(void);
123 :
124 : /**
125 : * Initialize the InteractionModel Engine.
126 : *
127 : * @param[in] apExchangeMgr A pointer to the ExchangeManager object.
128 : * @param[in] apFabricTable A pointer to the FabricTable object.
129 : * @param[in] apCASESessionMgr An optional pointer to a CASESessionManager (used for re-subscriptions).
130 : * @parma[in] eventManagement An optional pointer to a EventManagement. If null, the global instance will be used.
131 : *
132 : */
133 : CHIP_ERROR Init(Messaging::ExchangeManager * apExchangeMgr, FabricTable * apFabricTable,
134 : reporting::ReportScheduler * reportScheduler, CASESessionManager * apCASESessionMgr = nullptr,
135 : SubscriptionResumptionStorage * subscriptionResumptionStorage = nullptr,
136 : EventManagement * eventManagement = nullptr);
137 :
138 : void Shutdown();
139 :
140 : #if CHIP_CONFIG_ENABLE_ICD_SERVER
141 : void SetICDManager(ICDManager * manager) { mICDManager = manager; };
142 : #endif // CHIP_CONFIG_ENABLE_ICD_SERVER
143 :
144 3915 : Messaging::ExchangeManager * GetExchangeManager(void) const { return mpExchangeMgr; }
145 :
146 : /**
147 : * Returns a pointer to the CASESessionManager. This can return nullptr if one wasn't
148 : * provided in the call to Init().
149 : */
150 1 : CASESessionManager * GetCASESessionManager() const { return mpCASESessionMgr; }
151 :
152 : #if CHIP_CONFIG_ENABLE_READ_CLIENT
153 : /**
154 : * Tears down an active subscription.
155 : *
156 : * @retval #CHIP_ERROR_KEY_NOT_FOUND If the subscription is not found.
157 : * @retval #CHIP_NO_ERROR On success.
158 : */
159 : CHIP_ERROR ShutdownSubscription(const ScopedNodeId & aPeerNodeId, SubscriptionId aSubscriptionId);
160 :
161 : /**
162 : * Tears down active subscriptions for a given peer node ID.
163 : */
164 : void ShutdownSubscriptions(FabricIndex aFabricIndex, NodeId aPeerNodeId);
165 :
166 : /**
167 : * Tears down all active subscriptions for a given fabric.
168 : */
169 : void ShutdownSubscriptions(FabricIndex aFabricIndex);
170 :
171 : /**
172 : * Tears down all active subscriptions.
173 : */
174 : void ShutdownAllSubscriptions();
175 :
176 : #endif // CHIP_CONFIG_ENABLE_READ_CLIENT
177 :
178 : /**
179 : * Tears down all subscription handlers.
180 : */
181 : void ShutdownAllSubscriptionHandlers();
182 :
183 : uint32_t GetNumActiveReadHandlers() const;
184 : uint32_t GetNumActiveReadHandlers(ReadHandler::InteractionType type) const;
185 :
186 : /**
187 : * Returns the number of active readhandlers with a specific type on a specific fabric.
188 : */
189 : uint32_t GetNumActiveReadHandlers(ReadHandler::InteractionType type, FabricIndex fabricIndex) const;
190 :
191 : uint32_t GetNumActiveWriteHandlers() const;
192 :
193 : /**
194 : * Returns the handler at a particular index within the active handler list.
195 : */
196 : ReadHandler * ActiveHandlerAt(unsigned int aIndex);
197 :
198 : /**
199 : * Returns the write handler at a particular index within the active handler list.
200 : */
201 : WriteHandler * ActiveWriteHandlerAt(unsigned int aIndex);
202 :
203 7766 : reporting::Engine & GetReportingEngine() { return mReportingEngine; }
204 :
205 345 : reporting::ReportScheduler * GetReportScheduler() { return mReportScheduler; }
206 :
207 : void ReleaseAttributePathList(SingleLinkedListNode<AttributePathParams> *& aAttributePathList);
208 :
209 : CHIP_ERROR PushFrontAttributePathList(SingleLinkedListNode<AttributePathParams> *& aAttributePathList,
210 : AttributePathParams & aAttributePath);
211 :
212 : // If a concrete path indicates an attribute that is also referenced by a wildcard path in the request,
213 : // the path SHALL be removed from the list.
214 : void RemoveDuplicateConcreteAttributePath(SingleLinkedListNode<AttributePathParams> *& aAttributePaths);
215 :
216 : void ReleaseEventPathList(SingleLinkedListNode<EventPathParams> *& aEventPathList);
217 :
218 : CHIP_ERROR PushFrontEventPathParamsList(SingleLinkedListNode<EventPathParams> *& aEventPathList, EventPathParams & aEventPath);
219 :
220 : void ReleaseDataVersionFilterList(SingleLinkedListNode<DataVersionFilter> *& aDataVersionFilterList);
221 :
222 : CHIP_ERROR PushFrontDataVersionFilterList(SingleLinkedListNode<DataVersionFilter> *& aDataVersionFilterList,
223 : DataVersionFilter & aDataVersionFilter);
224 :
225 : /*
226 : * Register an application callback to be notified of notable events when handling reads/subscribes.
227 : */
228 46 : void RegisterReadHandlerAppCallback(ReadHandler::ApplicationCallback * mpApplicationCallback)
229 : {
230 46 : mpReadHandlerApplicationCallback = mpApplicationCallback;
231 46 : }
232 46 : void UnregisterReadHandlerAppCallback() { mpReadHandlerApplicationCallback = nullptr; }
233 :
234 : // TimedHandlerDelegate implementation
235 : void OnTimedInteractionFailed(TimedHandler * apTimedHandler) override;
236 : void OnTimedInvoke(TimedHandler * apTimedHandler, Messaging::ExchangeContext * apExchangeContext,
237 : const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) override;
238 : void OnTimedWrite(TimedHandler * apTimedHandler, Messaging::ExchangeContext * apExchangeContext,
239 : const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) override;
240 :
241 : // WriteHandlerDelegate implementation
242 : bool HasConflictWriteRequests(const WriteHandler * apWriteHandler, const ConcreteAttributePath & apath) override;
243 :
244 : #if CHIP_CONFIG_ENABLE_READ_CLIENT
245 : /**
246 : *
247 : * Notification that aPeer has sent a check-in message because aMonitoredSubject does
248 : * not have a subscription to it.
249 : *
250 : */
251 : void OnActiveModeNotification(ScopedNodeId aPeer, uint64_t aMonitoredSubject);
252 :
253 : /**
254 : * Used to notify when a peer becomes LIT ICD or vice versa.
255 : *
256 : * ReadClient will call this function when it finds any updates of the OperatingMode attribute from ICD management
257 : * cluster. The application doesn't need to call this function, usually.
258 : */
259 : void OnPeerTypeChange(ScopedNodeId aPeer, ReadClient::PeerType aType);
260 :
261 : /**
262 : * Add a read client to the internally tracked list of weak references. This list is used to
263 : * correctly dispatch unsolicited reports to the right matching handler by subscription ID.
264 : */
265 : void AddReadClient(ReadClient * apReadClient);
266 :
267 : /**
268 : * Remove a read client from the internally tracked list of weak references.
269 : */
270 : void RemoveReadClient(ReadClient * apReadClient);
271 :
272 : /**
273 : * Test to see if a read client is in the actively tracked list.
274 : */
275 : bool InActiveReadClientList(ReadClient * apReadClient);
276 :
277 : /**
278 : * Return the number of active read clients being tracked by the engine.
279 : */
280 : size_t GetNumActiveReadClients();
281 : #endif // CHIP_CONFIG_ENABLE_READ_CLIENT
282 :
283 : /**
284 : * Returns the number of dirty subscriptions. Including the subscriptions that are generating reports.
285 : */
286 : size_t GetNumDirtySubscriptions() const;
287 :
288 : /**
289 : * Select the oldest (and the one that exceeds the per subscription resource minimum if there are any) read handler on the
290 : * fabric with the given fabric index. Evict it when the fabric uses more resources than the per fabric quota or aForceEvict is
291 : * true.
292 : *
293 : * @retval Whether we have evicted a subscription.
294 : */
295 : bool TrimFabricForSubscriptions(FabricIndex aFabricIndex, bool aForceEvict);
296 :
297 : /**
298 : * Select a read handler and abort the read transaction if the fabric is using more resources (number of paths or number of read
299 : * handlers) then we guaranteed.
300 : *
301 : * - The youngest oversized read handlers will be chosen first.
302 : * - If there are no oversized read handlers, the youngest read handlers will be chosen.
303 : *
304 : * @retval Whether we have evicted a read transaction.
305 : */
306 : bool TrimFabricForRead(FabricIndex aFabricIndex);
307 :
308 : /**
309 : * Returns the minimal value of guaranteed subscriptions per fabic. UINT16_MAX will be returned if current app is configured to
310 : * use heap for the object pools used by interaction model engine.
311 : *
312 : * @retval the minimal value of guaranteed subscriptions per fabic.
313 : */
314 : uint16_t GetMinGuaranteedSubscriptionsPerFabric() const;
315 :
316 : // virtual method from FabricTable::Delegate
317 : void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override;
318 :
319 403 : SubscriptionResumptionStorage * GetSubscriptionResumptionStorage() { return mpSubscriptionResumptionStorage; };
320 :
321 : CHIP_ERROR ResumeSubscriptions();
322 :
323 : bool SubjectHasActiveSubscription(FabricIndex aFabricIndex, NodeId subjectID) override;
324 :
325 : bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subjectID) override;
326 :
327 : bool FabricHasAtLeastOneActiveSubscription(FabricIndex aFabricIndex) override;
328 :
329 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
330 : /**
331 : * @brief Function decrements the number of subscriptions to resume counter - mNumOfSubscriptionsToResume.
332 : * This should be called after we have completed a re-subscribe attempt on a persisted subscription wether the attempt
333 : * was successful or not.
334 : */
335 : void DecrementNumSubscriptionsToResume();
336 : #if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
337 : /**
338 : * @brief Function resets the number of retries of subscriptions resumption - mNumSubscriptionResumptionRetries.
339 : * This should be called after we have completed a re-subscribe attempt successfully on a persisted subscription,
340 : * or when the subscription resumption gets terminated.
341 : */
342 : void ResetNumSubscriptionsRetries();
343 : #endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
344 : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
345 :
346 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
347 : //
348 : // Get direct access to the underlying read handler pool
349 : //
350 22 : auto & GetReadHandlerPool() { return mReadHandlers; }
351 :
352 : //
353 : // Override the maximal capacity of the fabric table only for interaction model engine
354 : //
355 : // If -1 is passed in, no override is instituted and default behavior resumes.
356 : //
357 37 : void SetConfigMaxFabrics(int32_t sz) { mMaxNumFabricsOverride = sz; }
358 :
359 : //
360 : // Override the maximal capacity of the underlying read handler pool to mimic
361 : // out of memory scenarios in unit-tests. You need to SetConfigMaxFabrics to make GetGuaranteedReadRequestsPerFabric
362 : // working correctly.
363 : //
364 : // If -1 is passed in, no override is instituted and default behavior resumes.
365 : //
366 39 : void SetHandlerCapacityForReads(int32_t sz) { mReadHandlerCapacityForReadsOverride = sz; }
367 4 : void SetHandlerCapacityForSubscriptions(int32_t sz) { mReadHandlerCapacityForSubscriptionsOverride = sz; }
368 :
369 : //
370 : // Override the maximal capacity of the underlying attribute path pool and event path pool to mimic
371 : // out of paths exhausted scenarios in unit-tests.
372 : //
373 : // If -1 is passed in, no override is instituted and default behavior resumes.
374 : //
375 37 : void SetPathPoolCapacityForReads(int32_t sz) { mPathPoolCapacityForReadsOverride = sz; }
376 4 : void SetPathPoolCapacityForSubscriptions(int32_t sz) { mPathPoolCapacityForSubscriptionsOverride = sz; }
377 :
378 : //
379 : // We won't limit the handler used per fabric on platforms that are using heap for memory pools, so we introduces a flag to
380 : // enforce such check based on the configured size. This flag is used for unit tests only, there is another compare time flag
381 : // CHIP_CONFIG_IM_FORCE_FABRIC_QUOTA_CHECK for stress tests.
382 : //
383 48 : void SetForceHandlerQuota(bool forceHandlerQuota) { mForceHandlerQuota = forceHandlerQuota; }
384 :
385 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
386 : //
387 : // Override the subscription timeout resumption retry interval seconds. The default retry interval will be
388 : // 300s + GetFibonacciForIndex(retry_times) * 300s, which is too long for unit-tests.
389 : //
390 : // If -1 is passed in, no override is instituted and default behavior resumes.
391 : //
392 : void SetSubscriptionTimeoutResumptionRetryIntervalSeconds(int32_t seconds)
393 : {
394 : mSubscriptionResumptionRetrySecondsOverride = seconds;
395 : }
396 : #endif
397 :
398 : //
399 : // When testing subscriptions using the high-level APIs in src/controller/ReadInteraction.h,
400 : // they don't provide for the ability to shut down those subscriptions after they've been established.
401 : //
402 : // So for the purposes of unit tests, add a helper here to shut down and clean-up all active handlers.
403 : //
404 49 : void ShutdownActiveReads()
405 : {
406 : #if CHIP_CONFIG_ENABLE_READ_CLIENT
407 210 : for (auto * readClient = mpActiveReadClientList; readClient != nullptr;)
408 : {
409 161 : readClient->mpImEngine = nullptr;
410 161 : auto * tmpClient = readClient->GetNextClient();
411 161 : readClient->SetNextClient(nullptr);
412 161 : readClient->Close(CHIP_NO_ERROR);
413 161 : readClient = tmpClient;
414 : }
415 :
416 : //
417 : // After that, we just null out our tracker.
418 : //
419 49 : mpActiveReadClientList = nullptr;
420 : #endif // CHIP_CONFIG_ENABLE_READ_CLIENT
421 :
422 49 : mReadHandlers.ReleaseAll();
423 49 : }
424 : #endif
425 :
426 : DataModel::Provider * GetDataModelProvider() const;
427 :
428 : // MUST NOT be used while the interaction model engine is running as interaction
429 : // model functionality (e.g. active reads/writes/subscriptions) rely on data model
430 : // state
431 : //
432 : // Returns the old data model provider value.
433 : DataModel::Provider * SetDataModelProvider(DataModel::Provider * model);
434 :
435 : // DeviceLoadStatusProvider functions implementation
436 : MessageStats GetMessageStats() override;
437 :
438 : SubscriptionStats GetSubscriptionStats(FabricIndex fabric) override;
439 :
440 : private:
441 : /* DataModel::ActionContext implementation */
442 0 : Messaging::ExchangeContext * CurrentExchange() override { return mCurrentExchange; }
443 :
444 : friend class reporting::Engine;
445 : friend class TestCommandInteraction;
446 : friend class TestInteractionModelEngine;
447 : friend class SubscriptionResumptionSessionEstablisher;
448 : using Status = Protocols::InteractionModel::Status;
449 :
450 : void OnDone(CommandResponseSender & apResponderObj) override;
451 : void OnDone(CommandHandlerImpl & apCommandObj) override;
452 : void OnDone(ReadHandler & apReadObj) override;
453 :
454 : void TryToResumeSubscriptions();
455 :
456 1684 : ReadHandler::ApplicationCallback * GetAppCallback() override { return mpReadHandlerApplicationCallback; }
457 :
458 16773 : InteractionModelEngine * GetInteractionModelEngine() override { return this; }
459 :
460 : CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate) override;
461 :
462 : /**
463 : * Called when Interaction Model receives a Command Request message.
464 : */
465 : Status OnInvokeCommandRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
466 : System::PacketBufferHandle && aPayload, bool aIsTimedInvoke);
467 : CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
468 : System::PacketBufferHandle && aPayload) override;
469 : void OnResponseTimeout(Messaging::ExchangeContext * ec) override;
470 :
471 : /**
472 : * This parses the attribute path list to ensure it is well formed. If so, for each path in the list, it will expand to a list
473 : * of concrete paths and walk each path to check if it has privileges to read that attribute.
474 : *
475 : * If there is AT LEAST one "existent path" (as the spec calls it) that has sufficient privilege, aHasValidAttributePath
476 : * will be set to true. Otherwise, it will be set to false.
477 : *
478 : * aRequestedAttributePathCount will be updated to reflect the number of attribute paths in the request.
479 : *
480 : *
481 : */
482 : CHIP_ERROR ParseAttributePaths(const Access::SubjectDescriptor & aSubjectDescriptor,
483 : AttributePathIBs::Parser & aAttributePathListParser, bool & aHasValidAttributePath,
484 : size_t & aRequestedAttributePathCount);
485 :
486 : /**
487 : * This parses the event path list to ensure it is well formed. If so, for each path in the list, it will expand to a list
488 : * of concrete paths and walk each path to check if it has privileges to read that event.
489 : *
490 : * If there is AT LEAST one "existent path" (as the spec calls it) that has sufficient privilege, aHasValidEventPath
491 : * will be set to true. Otherwise, it will be set to false.
492 : *
493 : * aRequestedEventPathCount will be updated to reflect the number of event paths in the request.
494 : */
495 : CHIP_ERROR ParseEventPaths(const Access::SubjectDescriptor & aSubjectDescriptor, EventPathIBs::Parser & aEventPathListParser,
496 : bool & aHasValidEventPath, size_t & aRequestedEventPathCount);
497 :
498 : /**
499 : * Called when Interaction Model receives a Read Request message. Errors processing
500 : * the Read Request are handled entirely within this function. If the
501 : * status returned is not Status::Success, the caller will send a status
502 : * response message with that status.
503 : */
504 : Status OnReadInitialRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
505 : System::PacketBufferHandle && aPayload, ReadHandler::InteractionType aInteractionType);
506 :
507 : /**
508 : * Called when Interaction Model receives a Write Request message. Errors processing
509 : * the Write Request are handled entirely within this function. If the
510 : * status returned is not Status::Success, the caller will send a status
511 : * response message with that status.
512 : */
513 : Status OnWriteRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
514 : System::PacketBufferHandle && aPayload, bool aIsTimedWrite);
515 :
516 : /**
517 : * Called when Interaction Model receives a Timed Request message. Errors processing
518 : * the Timed Request are handled entirely within this function. The caller pre-sets status to failure and the callee is
519 : * expected to set it to success if it does not want an automatic status response message to be sent.
520 : */
521 : CHIP_ERROR OnTimedRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
522 : System::PacketBufferHandle && aPayload, Protocols::InteractionModel::Status & aStatus);
523 :
524 : /**This function handles processing of un-solicited ReportData messages on the client, which can
525 : * only occur post subscription establishment
526 : */
527 : Status OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
528 : System::PacketBufferHandle && aPayload);
529 :
530 : void DispatchCommand(CommandHandlerImpl & apCommandObj, const ConcreteCommandPath & aCommandPath,
531 : TLV::TLVReader & apPayload) override;
532 :
533 : Protocols::InteractionModel::Status ValidateCommandCanBeDispatched(const DataModel::InvokeRequest & request) override;
534 :
535 : bool HasActiveRead();
536 :
537 136 : inline size_t GetPathPoolCapacityForReads() const
538 : {
539 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
540 136 : return (mPathPoolCapacityForReadsOverride == -1) ? CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS
541 136 : : static_cast<size_t>(mPathPoolCapacityForReadsOverride);
542 : #else
543 : return CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS;
544 : #endif
545 : }
546 :
547 892 : inline size_t GetReadHandlerPoolCapacityForReads() const
548 : {
549 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
550 892 : return (mReadHandlerCapacityForReadsOverride == -1) ? CHIP_IM_MAX_NUM_READS
551 892 : : static_cast<size_t>(mReadHandlerCapacityForReadsOverride);
552 : #else
553 : return CHIP_IM_MAX_NUM_READS;
554 : #endif
555 : }
556 :
557 311 : inline size_t GetPathPoolCapacityForSubscriptions() const
558 : {
559 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
560 311 : return (mPathPoolCapacityForSubscriptionsOverride == -1) ? CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS
561 311 : : static_cast<size_t>(mPathPoolCapacityForSubscriptionsOverride);
562 : #else
563 : return CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS;
564 : #endif
565 : }
566 :
567 311 : inline size_t GetReadHandlerPoolCapacityForSubscriptions() const
568 : {
569 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
570 311 : return (mReadHandlerCapacityForSubscriptionsOverride == -1)
571 311 : ? CHIP_IM_MAX_NUM_SUBSCRIPTIONS
572 311 : : static_cast<size_t>(mReadHandlerCapacityForSubscriptionsOverride);
573 : #else
574 : return CHIP_IM_MAX_NUM_SUBSCRIPTIONS;
575 : #endif
576 : }
577 :
578 857 : inline uint8_t GetConfigMaxFabrics() const
579 : {
580 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
581 857 : return (mMaxNumFabricsOverride == -1) ? CHIP_CONFIG_MAX_FABRICS : static_cast<uint8_t>(mMaxNumFabricsOverride);
582 : #else
583 : return CHIP_CONFIG_MAX_FABRICS;
584 : #endif
585 : }
586 :
587 824 : inline size_t GetGuaranteedReadRequestsPerFabric() const
588 : {
589 824 : return GetReadHandlerPoolCapacityForReads() / GetConfigMaxFabrics();
590 : }
591 :
592 : /**
593 : * Verify and ensure (by killing oldest read handlers that make the resources used by the current fabric exceed the fabric
594 : * quota)
595 : * - If the subscription uses resources within the per subscription limit, this function will always success by evicting
596 : * existing subscriptions.
597 : * - If the subscription uses more than per subscription limit, this function will return PATHS_EXHAUSTED if we are running out
598 : * of paths.
599 : *
600 : * After the checks above, we will try to ensure we have a free Readhandler for processing the subscription.
601 : *
602 : * @retval true when we have enough resources for the incoming subscription, false if not.
603 : */
604 : bool EnsureResourceForSubscription(FabricIndex aFabricIndex, size_t aRequestedAttributePathCount,
605 : size_t aRequestedEventPathCount);
606 :
607 : /**
608 : * Verify and ensure (by killing oldest read handlers that make the resources used by the current fabric exceed the fabric
609 : * quota) the resources for handling a new read transaction with the given resource requirments.
610 : * - PASE sessions will be counted in a virtual fabric (i.e. kInvalidFabricIndex will be consided as a "valid" fabric in this
611 : * function)
612 : * - If the existing resources can serve this read transaction, this function will return Status::Success.
613 : * - or if the resources used by read transactions in the fabric index meets the per fabric resource limit (i.e. 9 paths & 1
614 : * read) after accepting this read request, this function will always return Status::Success by evicting existing read
615 : * transactions from other fabrics which are using more than the guaranteed minimum number of read.
616 : * - or if the resources used by read transactions in the fabric index will exceed the per fabric resource limit (i.e. 9 paths &
617 : * 1 read) after accepting this read request, this function will return a failure status without evicting any existing
618 : * transaction.
619 : * - However, read transactions on PASE sessions won't evict any existing read transactions when we have already commissioned
620 : * CHIP_CONFIG_MAX_FABRICS fabrics on the device.
621 : *
622 : * @retval Status::Success: The read transaction can be accepted.
623 : * @retval Status::Busy: The remaining resource is insufficient to handle this read request, and the accessing fabric for this
624 : * read request will use more resources than we guaranteed, the client is expected to retry later.
625 : * @retval Status::PathsExhausted: The attribute / event path pool is exhausted, and the read request is requesting more
626 : * resources than we guaranteed.
627 : */
628 : Status EnsureResourceForRead(FabricIndex aFabricIndex, size_t aRequestedAttributePathCount, size_t aRequestedEventPathCount);
629 :
630 : /**
631 : * Helper for various ShutdownSubscriptions functions. The subscriptions
632 : * that match all the provided arguments will be shut down.
633 : */
634 : void ShutdownMatchingSubscriptions(const Optional<FabricIndex> & aFabricIndex = NullOptional,
635 : const Optional<NodeId> & aPeerNodeId = NullOptional);
636 :
637 : /**
638 : * Validates that the command exists and on success returns the data for the command in `entry`.
639 : */
640 : Status CheckCommandExistence(const ConcreteCommandPath & aCommandPath, DataModel::AcceptedCommandEntry & entry);
641 : Status CheckCommandAccess(const DataModel::InvokeRequest & aRequest, const Access::Privilege aRequiredPrivilege);
642 : Status CheckCommandFlags(const DataModel::InvokeRequest & aRequest, const DataModel::AcceptedCommandEntry & entry);
643 :
644 : /**
645 : * Find the AttributeEntry that corresponds to the given attribute, if there is one.
646 : */
647 : std::optional<DataModel::AttributeEntry> FindAttributeEntry(const ConcreteAttributePath & path);
648 :
649 : static void ResumeSubscriptionsTimerCallback(System::Layer * apSystemLayer, void * apAppState);
650 :
651 : template <typename T, size_t N>
652 : void ReleasePool(SingleLinkedListNode<T> *& aObjectList, ObjectPool<SingleLinkedListNode<T>, N> & aObjectPool);
653 : template <typename T, size_t N>
654 : CHIP_ERROR PushFront(SingleLinkedListNode<T> *& aObjectList, T & aData, ObjectPool<SingleLinkedListNode<T>, N> & aObjectPool);
655 :
656 : Messaging::ExchangeManager * mpExchangeMgr = nullptr;
657 :
658 : #if CHIP_CONFIG_ENABLE_ICD_SERVER
659 : ICDManager * mICDManager = nullptr;
660 : #endif // CHIP_CONFIG_ENABLE_ICD_SERVER
661 :
662 : ObjectPool<CommandResponseSender, CHIP_IM_MAX_NUM_COMMAND_HANDLER> mCommandResponderObjs;
663 : ObjectPool<TimedHandler, CHIP_IM_MAX_NUM_TIMED_HANDLER> mTimedHandlers;
664 : WriteHandler mWriteHandlers[CHIP_IM_MAX_NUM_WRITE_HANDLER];
665 : reporting::Engine mReportingEngine;
666 : reporting::ReportScheduler * mReportScheduler = nullptr;
667 :
668 : static constexpr size_t kReservedHandlersForReads = kMinSupportedReadRequestsPerFabric * (CHIP_CONFIG_MAX_FABRICS);
669 : static constexpr size_t kReservedPathsForReads = kMinSupportedPathsPerReadRequest * kReservedHandlersForReads;
670 :
671 : #if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
672 : static_assert(CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS >=
673 : CHIP_CONFIG_MAX_FABRICS * (kMinSupportedPathsPerSubscription * kMinSupportedSubscriptionsPerFabric),
674 : "CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS is too small to match the requirements of spec 8.5.1");
675 : static_assert(CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS >=
676 : CHIP_CONFIG_MAX_FABRICS * (kMinSupportedReadRequestsPerFabric * kMinSupportedPathsPerReadRequest),
677 : "CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS is too small to match the requirements of spec 8.5.1");
678 : static_assert(CHIP_IM_MAX_NUM_SUBSCRIPTIONS >= CHIP_CONFIG_MAX_FABRICS * kMinSupportedSubscriptionsPerFabric,
679 : "CHIP_IM_MAX_NUM_SUBSCRIPTIONS is too small to match the requirements of spec 8.5.1");
680 : static_assert(CHIP_IM_MAX_NUM_READS >= CHIP_CONFIG_MAX_FABRICS * kMinSupportedReadRequestsPerFabric,
681 : "CHIP_IM_MAX_NUM_READS is too small to match the requirements of spec 8.5.1");
682 : #endif
683 :
684 : ObjectPool<SingleLinkedListNode<AttributePathParams>,
685 : CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS + CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS>
686 : mAttributePathPool;
687 : ObjectPool<SingleLinkedListNode<EventPathParams>,
688 : CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS + CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS>
689 : mEventPathPool;
690 : ObjectPool<SingleLinkedListNode<DataVersionFilter>,
691 : CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS + CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS>
692 : mDataVersionFilterPool;
693 :
694 : ObjectPool<ReadHandler, CHIP_IM_MAX_NUM_READS + CHIP_IM_MAX_NUM_SUBSCRIPTIONS> mReadHandlers;
695 :
696 : #if CHIP_CONFIG_ENABLE_READ_CLIENT
697 : ReadClient * mpActiveReadClientList = nullptr;
698 : #endif
699 :
700 : ReadHandler::ApplicationCallback * mpReadHandlerApplicationCallback = nullptr;
701 :
702 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
703 : int mReadHandlerCapacityForSubscriptionsOverride = -1;
704 : int mPathPoolCapacityForSubscriptionsOverride = -1;
705 :
706 : int mReadHandlerCapacityForReadsOverride = -1;
707 : int mPathPoolCapacityForReadsOverride = -1;
708 :
709 : int mMaxNumFabricsOverride = -1;
710 :
711 : // We won't limit the handler used per fabric on platforms that are using heap for memory pools, so we introduces a flag to
712 : // enforce such check based on the configured size. This flag is used for unit tests only, there is another compare time flag
713 : // CHIP_CONFIG_IM_FORCE_FABRIC_QUOTA_CHECK for stress tests.
714 : bool mForceHandlerQuota = false;
715 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
716 : int mSubscriptionResumptionRetrySecondsOverride = -1;
717 : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
718 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
719 :
720 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
721 : /**
722 : * mNumOfSubscriptionsToResume tracks the number of subscriptions that the device will try to resume at its next resumption
723 : * attempt. At boot up, the attempt will be at the highest min interval of all the subscriptions to resume.
724 : * When the subscription timeout resumption feature is present, after the boot up attempt, the next attempt will be determined
725 : * by ComputeTimeSecondsTillNextSubscriptionResumption.
726 : */
727 : int8_t mNumOfSubscriptionsToResume = 0;
728 : #if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
729 : bool HasSubscriptionsToResume();
730 : uint32_t ComputeTimeSecondsTillNextSubscriptionResumption();
731 : uint32_t mNumSubscriptionResumptionRetries = 0;
732 : bool mSubscriptionResumptionScheduled = false;
733 : #endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
734 : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
735 :
736 : FabricTable * mpFabricTable = nullptr;
737 :
738 : CASESessionManager * mpCASESessionMgr = nullptr;
739 :
740 : SubscriptionResumptionStorage * mpSubscriptionResumptionStorage = nullptr;
741 :
742 : DataModel::Provider * mDataModelProvider = nullptr;
743 : Messaging::ExchangeContext * mCurrentExchange = nullptr;
744 :
745 : enum class State : uint8_t
746 : {
747 : kUninitialized, // The object has not been initialized.
748 : kInitializing, // Initial setup is in progress (e.g. setting up mpExchangeMgr).
749 : kInitialized // The object has been fully initialized and is ready for use.
750 : };
751 : State mState = State::kUninitialized;
752 :
753 : // Changes the current exchange context of a InteractionModelEngine to a given context
754 : class CurrentExchangeValueScope
755 : {
756 : public:
757 2222 : CurrentExchangeValueScope(InteractionModelEngine & engine, Messaging::ExchangeContext * context) : mEngine(engine)
758 : {
759 2222 : mEngine.mCurrentExchange = context;
760 2222 : }
761 :
762 2222 : ~CurrentExchangeValueScope() { mEngine.mCurrentExchange = nullptr; }
763 :
764 : private:
765 : InteractionModelEngine & mEngine;
766 : };
767 : };
768 :
769 : } // namespace app
770 : } // namespace chip
|