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