blob: 8ed201a0d934e6d0c48cc751ce7b115d541fcbfd [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file defines objects for a CHIP Interaction Data model Engine which handle unsolicited IM message, and
* manage different kinds of IM client and handlers.
*
*/
#pragma once
#include <access/AccessControl.h>
#include <app/AppConfig.h>
#include <app/AttributePathParams.h>
#include <app/CommandHandler.h>
#include <app/CommandHandlerInterface.h>
#include <app/CommandSender.h>
#include <app/ConcreteAttributePath.h>
#include <app/ConcreteCommandPath.h>
#include <app/ConcreteEventPath.h>
#include <app/DataVersionFilter.h>
#include <app/EventPathParams.h>
#include <app/MessageDef/AttributeReportIBs.h>
#include <app/MessageDef/ReportDataMessage.h>
#include <app/ObjectList.h>
#include <app/ReadClient.h>
#include <app/ReadHandler.h>
#include <app/StatusResponse.h>
#include <app/SubscriptionResumptionSessionEstablisher.h>
#include <app/SubscriptionsInfoProvider.h>
#include <app/TimedHandler.h>
#include <app/WriteClient.h>
#include <app/WriteHandler.h>
#include <app/reporting/Engine.h>
#include <app/reporting/ReportScheduler.h>
#include <app/util/attribute-metadata.h>
#include <app/util/basic-types.h>
#include <lib/core/CHIPCore.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/Pool.h>
#include <lib/support/logging/CHIPLogging.h>
#include <messaging/ExchangeContext.h>
#include <messaging/ExchangeMgr.h>
#include <messaging/Flags.h>
#include <protocols/Protocols.h>
#include <protocols/interaction_model/Constants.h>
#include <system/SystemPacketBuffer.h>
#include <app/CASESessionManager.h>
namespace chip {
namespace app {
/**
* @class InteractionModelEngine
*
* @brief This is a singleton hosting all CHIP unsolicited message processing and managing interaction model related clients and
* handlers
*
*/
class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
public Messaging::ExchangeDelegate,
public CommandHandler::Callback,
public ReadHandler::ManagementCallback,
public FabricTable::Delegate,
public SubscriptionsInfoProvider
{
public:
/**
* @brief Retrieve the singleton Interaction Model Engine.
*
* @return A pointer to the shared InteractionModel Engine
*
*/
static InteractionModelEngine * GetInstance(void);
/**
* Spec 8.5.1 A publisher SHALL always ensure that every fabric the node is commissioned into can create at least three
* subscriptions to the publisher and that each subscription SHALL support at least 3 attribute/event paths.
*/
static constexpr size_t kMinSupportedSubscriptionsPerFabric = 3;
static constexpr size_t kMinSupportedPathsPerSubscription = 3;
static constexpr size_t kMinSupportedPathsPerReadRequest = 9;
static constexpr size_t kMinSupportedReadRequestsPerFabric = 1;
static constexpr size_t kReadHandlerPoolSize = CHIP_IM_MAX_NUM_SUBSCRIPTIONS + CHIP_IM_MAX_NUM_READS;
// 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
// fix it when we have reduced the memory footprint of ReadHandlers.
InteractionModelEngine(void);
/**
* Initialize the InteractionModel Engine.
*
* @param[in] apExchangeMgr A pointer to the ExchangeManager object.
* @param[in] apFabricTable A pointer to the FabricTable object.
* @param[in] apCASESessionMgr An optional pointer to a CASESessionManager (used for re-subscriptions).
*
* @retval #CHIP_ERROR_INCORRECT_STATE If the state is not equal to
* kState_NotInitialized.
* @retval #CHIP_NO_ERROR On success.
*
*/
CHIP_ERROR Init(Messaging::ExchangeManager * apExchangeMgr, FabricTable * apFabricTable,
reporting::ReportScheduler * reportScheduler, CASESessionManager * apCASESessionMgr = nullptr,
SubscriptionResumptionStorage * subscriptionResumptionStorage = nullptr);
void Shutdown();
Messaging::ExchangeManager * GetExchangeManager(void) const { return mpExchangeMgr; }
/**
* Returns a pointer to the CASESessionManager. This can return nullptr if one wasn't
* provided in the call to Init().
*/
CASESessionManager * GetCASESessionManager() const { return mpCASESessionMgr; }
#if CHIP_CONFIG_ENABLE_READ_CLIENT
/**
* Tears down an active subscription.
*
* @retval #CHIP_ERROR_KEY_NOT_FOUND If the subscription is not found.
* @retval #CHIP_NO_ERROR On success.
*/
CHIP_ERROR ShutdownSubscription(const ScopedNodeId & aPeerNodeId, SubscriptionId aSubscriptionId);
/**
* Tears down active subscriptions for a given peer node ID.
*/
void ShutdownSubscriptions(FabricIndex aFabricIndex, NodeId aPeerNodeId);
/**
* Tears down all active subscriptions for a given fabric.
*/
void ShutdownSubscriptions(FabricIndex aFabricIndex);
/**
* Tears down all active subscriptions.
*/
void ShutdownAllSubscriptions();
#endif // CHIP_CONFIG_ENABLE_READ_CLIENT
uint32_t GetNumActiveReadHandlers() const;
uint32_t GetNumActiveReadHandlers(ReadHandler::InteractionType type) const;
/**
* Returns the number of active readhandlers with a specific type on a specific fabric.
*/
uint32_t GetNumActiveReadHandlers(ReadHandler::InteractionType type, FabricIndex fabricIndex) const;
uint32_t GetNumActiveWriteHandlers() const;
/**
* Returns the handler at a particular index within the active handler list.
*/
ReadHandler * ActiveHandlerAt(unsigned int aIndex);
/**
* Returns the write handler at a particular index within the active handler list.
*/
WriteHandler * ActiveWriteHandlerAt(unsigned int aIndex);
/**
* The Magic number of this InteractionModelEngine, the magic number is set during Init()
*/
uint32_t GetMagicNumber() const { return mMagic; }
reporting::Engine & GetReportingEngine() { return mReportingEngine; }
reporting::ReportScheduler * GetReportScheduler() { return mReportScheduler; }
void ReleaseAttributePathList(ObjectList<AttributePathParams> *& aAttributePathList);
CHIP_ERROR PushFrontAttributePathList(ObjectList<AttributePathParams> *& aAttributePathList,
AttributePathParams & aAttributePath);
// If a concrete path indicates an attribute that is also referenced by a wildcard path in the request,
// the path SHALL be removed from the list.
void RemoveDuplicateConcreteAttributePath(ObjectList<AttributePathParams> *& aAttributePaths);
void ReleaseEventPathList(ObjectList<EventPathParams> *& aEventPathList);
CHIP_ERROR PushFrontEventPathParamsList(ObjectList<EventPathParams> *& aEventPathList, EventPathParams & aEventPath);
void ReleaseDataVersionFilterList(ObjectList<DataVersionFilter> *& aDataVersionFilterList);
CHIP_ERROR PushFrontDataVersionFilterList(ObjectList<DataVersionFilter> *& aDataVersionFilterList,
DataVersionFilter & aDataVersionFilter);
CHIP_ERROR RegisterCommandHandler(CommandHandlerInterface * handler);
CHIP_ERROR UnregisterCommandHandler(CommandHandlerInterface * handler);
CommandHandlerInterface * FindCommandHandler(EndpointId endpointId, ClusterId clusterId);
void UnregisterCommandHandlers(EndpointId endpointId);
/*
* Register an application callback to be notified of notable events when handling reads/subscribes.
*/
void RegisterReadHandlerAppCallback(ReadHandler::ApplicationCallback * mpApplicationCallback)
{
mpReadHandlerApplicationCallback = mpApplicationCallback;
}
void UnregisterReadHandlerAppCallback() { mpReadHandlerApplicationCallback = nullptr; }
/**
* Called when a timed interaction has failed (i.e. the exchange it was
* happening on has closed while the exchange delegate was the timed
* handler).
*/
void OnTimedInteractionFailed(TimedHandler * apTimedHandler);
/**
* Called when a timed invoke is received. This function takes over all
* handling of the exchange, status reporting, and so forth.
*/
void OnTimedInvoke(TimedHandler * apTimedHandler, Messaging::ExchangeContext * apExchangeContext,
const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload);
/**
* Called when a timed write is received. This function takes over all
* handling of the exchange, status reporting, and so forth.
*/
void OnTimedWrite(TimedHandler * apTimedHandler, Messaging::ExchangeContext * apExchangeContext,
const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload);
#if CHIP_CONFIG_ENABLE_READ_CLIENT
/**
* Activate the idle subscriptions.
*
* When subscribing to ICD and liveness timeout reached, the read client will move to `InactiveICDSubscription` state and
* resubscription can be triggered via OnActiveModeNotification().
*/
void OnActiveModeNotification(ScopedNodeId aPeer);
/**
* Used to notify when a peer becomes LIT ICD or vice versa.
*
* ReadClient will call this function when it finds any updates of the OperatingMode attribute from ICD management
* cluster. The application doesn't need to call this function, usually.
*/
void OnPeerTypeChange(ScopedNodeId aPeer, ReadClient::PeerType aType);
/**
* Add a read client to the internally tracked list of weak references. This list is used to
* correctly dispatch unsolicited reports to the right matching handler by subscription ID.
*/
void AddReadClient(ReadClient * apReadClient);
/**
* Remove a read client from the internally tracked list of weak references.
*/
void RemoveReadClient(ReadClient * apReadClient);
/**
* Test to see if a read client is in the actively tracked list.
*/
bool InActiveReadClientList(ReadClient * apReadClient);
/**
* Return the number of active read clients being tracked by the engine.
*/
size_t GetNumActiveReadClients();
#endif // CHIP_CONFIG_ENABLE_READ_CLIENT
/**
* Returns the number of dirty subscriptions. Including the subscriptions that are generating reports.
*/
size_t GetNumDirtySubscriptions() const;
/**
* Returns whether the write operation to the given path is conflict with another write operations. (i.e. another write
* transaction is in the middle of processing the chunked value of the given path.)
*/
bool HasConflictWriteRequests(const WriteHandler * apWriteHandler, const ConcreteAttributePath & aPath);
/**
* Select the oldest (and the one that exceeds the per subscription resource minimum if there are any) read handler on the
* fabric with the given fabric index. Evict it when the fabric uses more resources than the per fabric quota or aForceEvict is
* true.
*
* @retval Whether we have evicted a subscription.
*/
bool TrimFabricForSubscriptions(FabricIndex aFabricIndex, bool aForceEvict);
/**
* Select a read handler and abort the read transaction if the fabric is using more resources (number of paths or number of read
* handlers) then we guaranteed.
*
* - The youngest oversized read handlers will be chosen first.
* - If there are no oversized read handlers, the youngest read handlers will be chosen.
*
* @retval Whether we have evicted a read transaction.
*/
bool TrimFabricForRead(FabricIndex aFabricIndex);
/**
* Returns the minimal value of guaranteed subscriptions per fabic. UINT16_MAX will be returned if current app is configured to
* use heap for the object pools used by interaction model engine.
*
* @retval the minimal value of guaranteed subscriptions per fabic.
*/
uint16_t GetMinGuaranteedSubscriptionsPerFabric() const;
// virtual method from FabricTable::Delegate
void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override;
SubscriptionResumptionStorage * GetSubscriptionResumptionStorage() { return mpSubscriptionResumptionStorage; };
CHIP_ERROR ResumeSubscriptions();
bool SubjectHasActiveSubscription(FabricIndex aFabricIndex, NodeId subjectID) override;
bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subjectID) override;
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
//
// Get direct access to the underlying read handler pool
//
auto & GetReadHandlerPool() { return mReadHandlers; }
//
// Override the maximal capacity of the fabric table only for interaction model engine
//
// If -1 is passed in, no override is instituted and default behavior resumes.
//
void SetConfigMaxFabrics(int32_t sz) { mMaxNumFabricsOverride = sz; }
//
// Override the maximal capacity of the underlying read handler pool to mimic
// out of memory scenarios in unit-tests. You need to SetConfigMaxFabrics to make GetGuaranteedReadRequestsPerFabric
// working correctly.
//
// If -1 is passed in, no override is instituted and default behavior resumes.
//
void SetHandlerCapacityForReads(int32_t sz) { mReadHandlerCapacityForReadsOverride = sz; }
void SetHandlerCapacityForSubscriptions(int32_t sz) { mReadHandlerCapacityForSubscriptionsOverride = sz; }
//
// Override the maximal capacity of the underlying attribute path pool and event path pool to mimic
// out of paths exhausted scenarios in unit-tests.
//
// If -1 is passed in, no override is instituted and default behavior resumes.
//
void SetPathPoolCapacityForReads(int32_t sz) { mPathPoolCapacityForReadsOverride = sz; }
void SetPathPoolCapacityForSubscriptions(int32_t sz) { mPathPoolCapacityForSubscriptionsOverride = sz; }
//
// We won't limit the handler used per fabric on platforms that are using heap for memory pools, so we introduces a flag to
// enforce such check based on the configured size. This flag is used for unit tests only, there is another compare time flag
// CHIP_CONFIG_IM_FORCE_FABRIC_QUOTA_CHECK for stress tests.
//
void SetForceHandlerQuota(bool forceHandlerQuota) { mForceHandlerQuota = forceHandlerQuota; }
//
// When testing subscriptions using the high-level APIs in src/controller/ReadInteraction.h,
// they don't provide for the ability to shut down those subscriptions after they've been established.
//
// So for the purposes of unit tests, add a helper here to shut down and clean-up all active handlers.
//
void ShutdownActiveReads()
{
#if CHIP_CONFIG_ENABLE_READ_CLIENT
for (auto * readClient = mpActiveReadClientList; readClient != nullptr;)
{
readClient->mpImEngine = nullptr;
auto * tmpClient = readClient->GetNextClient();
readClient->SetNextClient(nullptr);
readClient->Close(CHIP_NO_ERROR);
readClient = tmpClient;
}
//
// After that, we just null out our tracker.
//
mpActiveReadClientList = nullptr;
#endif // CHIP_CONFIG_ENABLE_READ_CLIENT
mReadHandlers.ReleaseAll();
}
#endif
private:
friend class reporting::Engine;
friend class TestCommandInteraction;
friend class TestInteractionModelEngine;
friend class SubscriptionResumptionSessionEstablisher;
using Status = Protocols::InteractionModel::Status;
void OnDone(CommandHandler & apCommandObj) override;
void OnDone(ReadHandler & apReadObj) override;
ReadHandler::ApplicationCallback * GetAppCallback() override { return mpReadHandlerApplicationCallback; }
InteractionModelEngine * GetInteractionModelEngine() override { return this; }
CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate) override;
/**
* Called when Interaction Model receives a Command Request message.
*/
Status OnInvokeCommandRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
System::PacketBufferHandle && aPayload, bool aIsTimedInvoke);
CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
System::PacketBufferHandle && aPayload) override;
void OnResponseTimeout(Messaging::ExchangeContext * ec) override;
/**
* 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
* of concrete paths and walk each path to check if it has privileges to read that attribute.
*
* If there is AT LEAST one "existent path" (as the spec calls it) that has sufficient privilege, aHasValidAttributePath
* will be set to true. Otherwise, it will be set to false.
*
* aRequestedAttributePathCount will be updated to reflect the number of attribute paths in the request.
*
*
*/
static CHIP_ERROR ParseAttributePaths(const Access::SubjectDescriptor & aSubjectDescriptor,
AttributePathIBs::Parser & aAttributePathListParser, bool & aHasValidAttributePath,
size_t & aRequestedAttributePathCount);
/**
* 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
* of concrete paths and walk each path to check if it has privileges to read that event.
*
* If there is AT LEAST one "existent path" (as the spec calls it) that has sufficient privilege, aHasValidEventPath
* will be set to true. Otherwise, it will be set to false.
*
* aRequestedEventPathCount will be updated to reflect the number of event paths in the request.
*/
static CHIP_ERROR ParseEventPaths(const Access::SubjectDescriptor & aSubjectDescriptor,
EventPathIBs::Parser & aEventPathListParser, bool & aHasValidEventPath,
size_t & aRequestedEventPathCount);
/**
* Called when Interaction Model receives a Read Request message. Errors processing
* the Read Request are handled entirely within this function. If the
* status returned is not Status::Success, the caller will send a status
* response message with that status.
*/
Status OnReadInitialRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
System::PacketBufferHandle && aPayload, ReadHandler::InteractionType aInteractionType);
/**
* Called when Interaction Model receives a Write Request message. Errors processing
* the Write Request are handled entirely within this function. If the
* status returned is not Status::Success, the caller will send a status
* response message with that status.
*/
Status OnWriteRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
System::PacketBufferHandle && aPayload, bool aIsTimedWrite);
/**
* Called when Interaction Model receives a Timed Request message. Errors processing
* the Timed Request are handled entirely within this function. The caller pre-sets status to failure and the callee is
* expected to set it to success if it does not want an automatic status response message to be sent.
*/
CHIP_ERROR OnTimedRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
System::PacketBufferHandle && aPayload, Protocols::InteractionModel::Status & aStatus);
/**This function handles processing of un-solicited ReportData messages on the client, which can
* only occur post subscription establishment
*/
Status OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
System::PacketBufferHandle && aPayload);
void DispatchCommand(CommandHandler & apCommandObj, const ConcreteCommandPath & aCommandPath,
TLV::TLVReader & apPayload) override;
Protocols::InteractionModel::Status CommandExists(const ConcreteCommandPath & aCommandPath) override;
bool HasActiveRead();
inline size_t GetPathPoolCapacityForReads() const
{
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
return (mPathPoolCapacityForReadsOverride == -1) ? CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS
: static_cast<size_t>(mPathPoolCapacityForReadsOverride);
#else
return CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS;
#endif
}
inline size_t GetReadHandlerPoolCapacityForReads() const
{
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
return (mReadHandlerCapacityForReadsOverride == -1) ? CHIP_IM_MAX_NUM_READS
: static_cast<size_t>(mReadHandlerCapacityForReadsOverride);
#else
return CHIP_IM_MAX_NUM_READS;
#endif
}
inline size_t GetPathPoolCapacityForSubscriptions() const
{
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
return (mPathPoolCapacityForSubscriptionsOverride == -1) ? CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS
: static_cast<size_t>(mPathPoolCapacityForSubscriptionsOverride);
#else
return CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS;
#endif
}
inline size_t GetReadHandlerPoolCapacityForSubscriptions() const
{
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
return (mReadHandlerCapacityForSubscriptionsOverride == -1)
? CHIP_IM_MAX_NUM_SUBSCRIPTIONS
: static_cast<size_t>(mReadHandlerCapacityForSubscriptionsOverride);
#else
return CHIP_IM_MAX_NUM_SUBSCRIPTIONS;
#endif
}
inline uint8_t GetConfigMaxFabrics() const
{
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
return (mMaxNumFabricsOverride == -1) ? CHIP_CONFIG_MAX_FABRICS : static_cast<uint8_t>(mMaxNumFabricsOverride);
#else
return CHIP_CONFIG_MAX_FABRICS;
#endif
}
inline size_t GetGuaranteedReadRequestsPerFabric() const
{
return GetReadHandlerPoolCapacityForReads() / GetConfigMaxFabrics();
}
/**
* Verify and ensure (by killing oldest read handlers that make the resources used by the current fabric exceed the fabric
* quota)
* - If the subscription uses resources within the per subscription limit, this function will always success by evicting
* existing subscriptions.
* - If the subscription uses more than per subscription limit, this function will return PATHS_EXHAUSTED if we are running out
* of paths.
*
* After the checks above, we will try to ensure we have a free Readhandler for processing the subscription.
*
* @retval true when we have enough resources for the incoming subscription, false if not.
*/
bool EnsureResourceForSubscription(FabricIndex aFabricIndex, size_t aRequestedAttributePathCount,
size_t aRequestedEventPathCount);
/**
* Verify and ensure (by killing oldest read handlers that make the resources used by the current fabric exceed the fabric
* quota) the resources for handling a new read transaction with the given resource requirments.
* - PASE sessions will be counted in a virtual fabric (i.e. kInvalidFabricIndex will be consided as a "valid" fabric in this
* function)
* - If the existing resources can serve this read transaction, this function will return Status::Success.
* - or if the resources used by read transactions in the fabric index meets the per fabric resource limit (i.e. 9 paths & 1
* read) after accepting this read request, this function will always return Status::Success by evicting existing read
* transactions from other fabrics which are using more than the guaranteed minimum number of read.
* - or if the resources used by read transactions in the fabric index will exceed the per fabric resource limit (i.e. 9 paths &
* 1 read) after accepting this read request, this function will return a failure status without evicting any existing
* transaction.
* - However, read transactions on PASE sessions won't evict any existing read transactions when we have already commissioned
* CHIP_CONFIG_MAX_FABRICS fabrics on the device.
*
* @retval Status::Success: The read transaction can be accepted.
* @retval Status::Busy: The remaining resource is insufficient to handle this read request, and the accessing fabric for this
* read request will use more resources than we guaranteed, the client is expected to retry later.
* @retval Status::PathsExhausted: The attribute / event path pool is exhausted, and the read request is requesting more
* resources than we guaranteed.
*/
Status EnsureResourceForRead(FabricIndex aFabricIndex, size_t aRequestedAttributePathCount, size_t aRequestedEventPathCount);
/**
* Helper for various ShutdownSubscriptions functions. The subscriptions
* that match all the provided arguments will be shut down.
*/
void ShutdownMatchingSubscriptions(const Optional<FabricIndex> & aFabricIndex = NullOptional,
const Optional<NodeId> & aPeerNodeId = NullOptional);
static void ResumeSubscriptionsTimerCallback(System::Layer * apSystemLayer, void * apAppState);
template <typename T, size_t N>
void ReleasePool(ObjectList<T> *& aObjectList, ObjectPool<ObjectList<T>, N> & aObjectPool);
template <typename T, size_t N>
CHIP_ERROR PushFront(ObjectList<T> *& aObjectList, T & aData, ObjectPool<ObjectList<T>, N> & aObjectPool);
Messaging::ExchangeManager * mpExchangeMgr = nullptr;
CommandHandlerInterface * mCommandHandlerList = nullptr;
ObjectPool<CommandHandler, CHIP_IM_MAX_NUM_COMMAND_HANDLER> mCommandHandlerObjs;
ObjectPool<TimedHandler, CHIP_IM_MAX_NUM_TIMED_HANDLER> mTimedHandlers;
WriteHandler mWriteHandlers[CHIP_IM_MAX_NUM_WRITE_HANDLER];
reporting::Engine mReportingEngine;
reporting::ReportScheduler * mReportScheduler = nullptr;
static constexpr size_t kReservedHandlersForReads = kMinSupportedReadRequestsPerFabric * (CHIP_CONFIG_MAX_FABRICS);
static constexpr size_t kReservedPathsForReads = kMinSupportedPathsPerReadRequest * kReservedHandlersForReads;
#if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
static_assert(CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS >=
CHIP_CONFIG_MAX_FABRICS * (kMinSupportedPathsPerSubscription * kMinSupportedSubscriptionsPerFabric),
"CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS is too small to match the requirements of spec 8.5.1");
static_assert(CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS >=
CHIP_CONFIG_MAX_FABRICS * (kMinSupportedReadRequestsPerFabric * kMinSupportedPathsPerReadRequest),
"CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS is too small to match the requirements of spec 8.5.1");
static_assert(CHIP_IM_MAX_NUM_SUBSCRIPTIONS >= CHIP_CONFIG_MAX_FABRICS * kMinSupportedSubscriptionsPerFabric,
"CHIP_IM_MAX_NUM_SUBSCRIPTIONS is too small to match the requirements of spec 8.5.1");
static_assert(CHIP_IM_MAX_NUM_READS >= CHIP_CONFIG_MAX_FABRICS * kMinSupportedReadRequestsPerFabric,
"CHIP_IM_MAX_NUM_READS is too small to match the requirements of spec 8.5.1");
#endif
ObjectPool<ObjectList<AttributePathParams>,
CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS + CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS>
mAttributePathPool;
ObjectPool<ObjectList<EventPathParams>,
CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS + CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS>
mEventPathPool;
ObjectPool<ObjectList<DataVersionFilter>,
CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS + CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS>
mDataVersionFilterPool;
ObjectPool<ReadHandler, CHIP_IM_MAX_NUM_READS + CHIP_IM_MAX_NUM_SUBSCRIPTIONS> mReadHandlers;
#if CHIP_CONFIG_ENABLE_READ_CLIENT
ReadClient * mpActiveReadClientList = nullptr;
#endif
ReadHandler::ApplicationCallback * mpReadHandlerApplicationCallback = nullptr;
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
int mReadHandlerCapacityForSubscriptionsOverride = -1;
int mPathPoolCapacityForSubscriptionsOverride = -1;
int mReadHandlerCapacityForReadsOverride = -1;
int mPathPoolCapacityForReadsOverride = -1;
int mMaxNumFabricsOverride = -1;
// We won't limit the handler used per fabric on platforms that are using heap for memory pools, so we introduces a flag to
// enforce such check based on the configured size. This flag is used for unit tests only, there is another compare time flag
// CHIP_CONFIG_IM_FORCE_FABRIC_QUOTA_CHECK for stress tests.
bool mForceHandlerQuota = false;
#endif
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
bool HasSubscriptionsToResume();
uint32_t ComputeTimeSecondsTillNextSubscriptionResumption();
uint32_t mNumSubscriptionResumptionRetries = 0;
bool mSubscriptionResumptionScheduled = false;
#endif
FabricTable * mpFabricTable;
CASESessionManager * mpCASESessionMgr = nullptr;
SubscriptionResumptionStorage * mpSubscriptionResumptionStorage = nullptr;
// A magic number for tracking values between stack Shutdown()-s and Init()-s.
// An ObjectHandle is valid iff. its magic equals to this one.
uint32_t mMagic = 0;
};
void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, chip::TLV::TLVReader & aReader,
CommandHandler * apCommandObj);
/**
* Check whether the given cluster exists on the given endpoint and supports
* the given command. If it does, Success will be returned. If it does not,
* one of UnsupportedEndpoint, UnsupportedCluster, or UnsupportedCommand
* will be returned, depending on how the command fails to exist.
*/
Protocols::InteractionModel::Status ServerClusterCommandExists(const ConcreteCommandPath & aCommandPath);
/**
* Fetch attribute value and version info and write to the AttributeReport provided.
* The ReadSingleClusterData will do everything required for encoding an attribute, i.e. it will try to put one or more
* AttributeReportIB to the AttributeReportIBs::Builder.
* When the endpoint / cluster / attribute data specified by aPath does not exist, corresponding interaction
* model error code will be put into aAttributeReports, and CHIP_NO_ERROR will be returned. If the data exists on the server, the
* data (with tag kData) and the data version (with tag kDataVersion) will be put into aAttributeReports. TLVWriter error will be
* returned if any error occurred while encoding these values. This function is implemented by CHIP as a part of cluster data
* storage & management.
*
* @param[in] aSubjectDescriptor The subject descriptor for the read.
* @param[in] aPath The concrete path of the data being read.
* @param[in] aAttributeReports The TLV Builder for Cluter attribute builder.
*
* @retval CHIP_NO_ERROR on success
*/
CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered,
const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports,
AttributeValueEncoder::AttributeEncodeState * apEncoderState);
/**
* Check whether concrete attribute path is an "existent attribute path" in spec terms.
* @param[in] aPath The concrete path of the data being read.
* @retval boolean true if the concrete attribute path indicates an attribute that exists on the node.
*/
bool ConcreteAttributePathExists(const ConcreteAttributePath & aPath);
/**
* Get the registered attribute access override. nullptr when attribute access override is not found.
*
* TODO(#16806): This function and registerAttributeAccessOverride can be member functions of InteractionModelEngine.
*/
AttributeAccessInterface * GetAttributeAccessOverride(EndpointId aEndpointId, ClusterId aClusterId);
/**
* TODO: Document.
*/
CHIP_ERROR WriteSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor,
const ConcreteDataAttributePath & aAttributePath, TLV::TLVReader & aReader,
WriteHandler * apWriteHandler);
/**
* Check if the given cluster has the given DataVersion.
*/
bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath, DataVersion aRequiredVersion);
/**
* Returns true if device type is on endpoint, false otherwise.
*/
bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint);
/**
* Returns the metadata of the attribute for the given path.
*
* @retval The metadata of the attribute, will return null if the given attribute does not exists.
*/
const EmberAfAttributeMetadata * GetAttributeMetadata(const ConcreteAttributePath & aPath);
/**
* Returns the event support status for the given event, as an interaction model status.
*/
Protocols::InteractionModel::Status CheckEventSupportStatus(const ConcreteEventPath & aPath);
} // namespace app
} // namespace chip