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