From 7f8db2f33e75ac9b854539e67194e255a56ffc1a Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Tue, 10 Jan 2023 23:39:07 -0800 Subject: [PATCH 01/41] [ICD] Server side subscription persistence and resumption --- src/app/BUILD.gn | 3 + src/app/InteractionModelEngine.cpp | 53 +- src/app/InteractionModelEngine.h | 8 +- src/app/ReadHandler.cpp | 115 ++++- src/app/ReadHandler.h | 18 + .../SimpleSubscriptionResumptionStorage.cpp | 456 ++++++++++++++++++ src/app/SimpleSubscriptionResumptionStorage.h | 144 ++++++ src/app/SubscriptionResumptionStorage.h | 115 +++++ src/app/server/Server.cpp | 16 +- src/app/server/Server.h | 30 ++ src/darwin/Framework/CHIP/MTRDevice.mm | 2 +- src/lib/core/CHIPConfig.h | 34 +- src/lib/support/DefaultStorageKeyAllocator.h | 8 + 13 files changed, 988 insertions(+), 14 deletions(-) create mode 100644 src/app/SimpleSubscriptionResumptionStorage.cpp create mode 100644 src/app/SimpleSubscriptionResumptionStorage.h create mode 100644 src/app/SubscriptionResumptionStorage.h diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 83d6c7d354a713..cef97a1328f93e 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -173,8 +173,11 @@ static_library("app") { "ReadHandler.cpp", "RequiredPrivilege.cpp", "RequiredPrivilege.h", + "SimpleSubscriptionResumptionStorage.cpp", + "SimpleSubscriptionResumptionStorage.h", "StatusResponse.cpp", "StatusResponse.h", + "SubscriptionResumptionStorage.h", "TimedHandler.cpp", "TimedHandler.h", "TimedRequest.cpp", diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index bba9cd763c27da..4fb9adb6242793 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -50,14 +50,16 @@ InteractionModelEngine * InteractionModelEngine::GetInstance() } CHIP_ERROR InteractionModelEngine::Init(Messaging::ExchangeManager * apExchangeMgr, FabricTable * apFabricTable, - CASESessionManager * apCASESessionMgr) + CASESessionManager * apCASESessionMgr, + SubscriptionResumptionStorage * subscriptionResumptionStorage) { VerifyOrReturnError(apFabricTable != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(apExchangeMgr != nullptr, CHIP_ERROR_INVALID_ARGUMENT); - mpExchangeMgr = apExchangeMgr; - mpFabricTable = apFabricTable; - mpCASESessionMgr = apCASESessionMgr; + mpExchangeMgr = apExchangeMgr; + mpFabricTable = apFabricTable; + mpCASESessionMgr = apCASESessionMgr; + mpSubscriptionResumptionStorage = subscriptionResumptionStorage; ReturnErrorOnFailure(mpFabricTable->AddFabricDelegate(this)); ReturnErrorOnFailure(mpExchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::InteractionModel::Id, this)); @@ -664,6 +666,8 @@ Status InteractionModelEngine::OnUnsolicitedReportData(Messaging::ExchangeContex return Status::Success; } + ChipLogDetail(InteractionModel, "Received report with invalid subscriptionId %lu", (unsigned long) subscriptionId); + return Status::InvalidSubscription; } @@ -1578,5 +1582,46 @@ void InteractionModelEngine::OnFabricRemoved(const FabricTable & fabricTable, Fa // the fabric removal, though, so they will fail when they try to actually send their command response // and will close at that point. } + +CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() +{ +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + SubscriptionResumptionStorage::SubscriptionIndex subscriberIndex; + CHIP_ERROR err = mpSubscriptionResumptionStorage->LoadIndex(subscriberIndex); + ChipLogProgress(AppServer, "%zu subscriber nodes to resume.. (error %" CHIP_ERROR_FORMAT ")", subscriberIndex.mSize, + err.Format()); + for (size_t i = 0; i < subscriberIndex.mSize; i++) + { + std::vector subscriptions; + err = mpSubscriptionResumptionStorage->FindByScopedNodeId(subscriberIndex.mNodes[i], subscriptions); + + ChipLogProgress(AppServer, + "\tNode " ChipLogFormatScopedNodeId ": Loaded %zu subscriptions.. (error %" CHIP_ERROR_FORMAT ")", + ChipLogValueScopedNodeId(subscriberIndex.mNodes[i]), subscriptions.size(), err.Format()); + + for (auto & subscriptionInfo : subscriptions) + { + auto requestedAttributePathCount = subscriptionInfo.mAttributePaths.size(); + auto requestedEventPathCount = subscriptionInfo.mEventPaths.size(); + if (!EnsureResourceForSubscription(subscriptionInfo.mNode.GetFabricIndex(), requestedAttributePathCount, + requestedEventPathCount)) + { + ChipLogProgress(InteractionModel, "no resource for Subscription resumption"); + return CHIP_ERROR_NO_MEMORY; + } + + ReadHandler * handler = mReadHandlers.CreateObject(*this, subscriptionInfo); + if (handler == nullptr) + { + ChipLogProgress(InteractionModel, "no resource for ReadHandler creation"); + return CHIP_ERROR_NO_MEMORY; + } + } + } +#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + + return CHIP_NO_ERROR; +} + } // namespace app } // namespace chip diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index 6475f8bceda547..e1351d91ea0e60 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -114,7 +114,7 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, * */ CHIP_ERROR Init(Messaging::ExchangeManager * apExchangeMgr, FabricTable * apFabricTable, - CASESessionManager * apCASESessionMgr = nullptr); + CASESessionManager * apCASESessionMgr = nullptr, SubscriptionResumptionStorage * subscriptionResumptionStorage = nullptr); void Shutdown(); @@ -292,6 +292,10 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, // virtual method from FabricTable::Delegate void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override; + SubscriptionResumptionStorage * GetSubscriptionResumptionStorage() { return mpSubscriptionResumptionStorage; }; + + CHIP_ERROR ResumeSubscriptions(); + #if CONFIG_BUILD_FOR_HOST_UNIT_TEST // // Get direct access to the underlying read handler pool @@ -596,6 +600,8 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, 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; diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 14b79dda3066a9..0dab06f0993daf 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -41,7 +41,8 @@ using Status = Protocols::InteractionModel::Status; ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, InteractionType aInteractionType) : mExchangeCtx(*this), - mManagementCallback(apCallback) + mManagementCallback(apCallback), mOnConnectedCallback(HandleDeviceConnected, this), + mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) { VerifyOrDie(apExchangeContext != nullptr); @@ -61,6 +62,51 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeCon mSessionHandle.Grab(mExchangeCtx->GetSessionHandle()); } +ReadHandler::ReadHandler(ManagementCallback & apCallback, SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo) : + mExchangeCtx(*this), mManagementCallback(apCallback), mOnConnectedCallback(HandleDeviceConnected, this), + mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) +{ +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + mSubscriptionId = subscriptionInfo.mSubscriptionId; + mMinIntervalFloorSeconds = subscriptionInfo.mMinInterval; + mMaxInterval = subscriptionInfo.mMaxInterval; + SetStateFlag(ReadHandlerFlags::FabricFiltered, subscriptionInfo.mFabricFiltered); + + CHIP_ERROR err; + for (auto & attributePathParams : subscriptionInfo.mAttributePaths) + { + err = InteractionModelEngine::GetInstance()->PushFrontAttributePathList(mpAttributePathList, attributePathParams); + if (err != CHIP_NO_ERROR) + { + Close(); + return; + } + } + for (auto & eventPathParams : subscriptionInfo.mEventPaths) + { + err = InteractionModelEngine::GetInstance()->PushFrontEventPathParamsList(mpEventPathList, eventPathParams); + if (err != CHIP_NO_ERROR) + { + Close(); + return; + } + } + + // Ask IM engine to start case with + auto * caseSessionManager = InteractionModelEngine::GetInstance()->GetCASESessionManager(); + if (caseSessionManager) + { + caseSessionManager->FindOrEstablishSession(subscriptionInfo.mNode, &mOnConnectedCallback, &mOnConnectionFailureCallback); + } + else + { + // TODO: Investigate if need to consider if caseSessionManager does not exist + Close(); + } + +#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS +} + ReadHandler::~ReadHandler() { auto * appCallback = mManagementCallback.GetAppCallback(); @@ -89,6 +135,16 @@ ReadHandler::~ReadHandler() void ReadHandler::Close() { +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage(); + if (subscriptionResumptionStorage) + { + SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { + .mNode = ScopedNodeId(GetInitiatorNodeId(), GetAccessingFabricIndex()), .mSubscriptionId = mSubscriptionId + }; + subscriptionResumptionStorage->Delete(subscriptionInfo); + } +#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS MoveToState(HandlerState::AwaitingDestruction); mManagementCallback.OnDone(*this); } @@ -306,7 +362,11 @@ void ReadHandler::OnResponseTimeout(Messaging::ExchangeContext * apExchangeConte { ChipLogError(DataManagement, "Time out! failed to receive status response from Exchange: " ChipLogFormatExchange, ChipLogValueExchange(apExchangeContext)); +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + // TODO: Have a retry mechanism tied to wake interval for IC devices +#else Close(); +#endif } CHIP_ERROR ReadHandler::ProcessReadRequest(System::PacketBufferHandle && aPayload) @@ -652,6 +712,37 @@ CHIP_ERROR ReadHandler::ProcessSubscribeRequest(System::PacketBufferHandle && aP mExchangeCtx->WillSendMessage(); +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage(); + if (subscriptionResumptionStorage) + { + // Persist Subscription + // mpEventPathList + // mpAttributePathList + + SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNode = ScopedNodeId(GetInitiatorNodeId(), + GetAccessingFabricIndex()), + .mSubscriptionId = mSubscriptionId, + .mMinInterval = mMinIntervalFloorSeconds, + .mMaxInterval = mMaxInterval, + .mFabricFiltered = IsFabricFiltered() }; + ObjectList * attributePath = mpAttributePathList; + while (attributePath) + { + subscriptionInfo.mAttributePaths.push_back(attributePath->mValue); + attributePath = attributePath->mpNext; + } + ObjectList * eventPath = mpEventPathList; + while (eventPath) + { + subscriptionInfo.mEventPaths.push_back(eventPath->mValue); + eventPath = eventPath->mpNext; + } + + err = subscriptionResumptionStorage->Save(subscriptionInfo); + } +#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + return CHIP_NO_ERROR; } @@ -767,5 +858,27 @@ void ReadHandler::ClearStateFlag(ReadHandlerFlags aFlag) SetStateFlag(aFlag, false); } +void ReadHandler::HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle) +{ + ReadHandler * const _this = static_cast(context); + + auto exchange = exchangeMgr.NewContext(sessionHandle, _this); + _this->mExchangeCtx.Grab(exchange); + _this->mSessionHandle.Grab(sessionHandle); + + _this->MoveToState(HandlerState::GeneratingReports); +} + +void ReadHandler::HandleDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR err) +{ + ReadHandler * const _this = static_cast(context); + VerifyOrDie(_this != nullptr); + + // TODO: Have a retry mechanism tied to wake interval for IC devices + ChipLogError(DataManagement, "Failed to establish CASE for subscription-resumption with error '%" CHIP_ERROR_FORMAT "'", + err.Format()); + _this->Close(); +} + } // namespace app } // namespace chip diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 1e4f55066e9f3b..80f1077655da26 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -165,6 +166,15 @@ class ReadHandler : public Messaging::ExchangeDelegate */ ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, InteractionType aInteractionType); + /** + * + * Constructor for resuming a persisted subscription + * + * The callback passed in has to outlive this handler object. + * + */ + ReadHandler(ManagementCallback & apCallback, SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo); + const ObjectList * GetAttributePathList() const { return mpAttributePathList; } const ObjectList * GetEventPathList() const { return mpEventPathList; } const ObjectList * GetDataVersionFilterList() const { return mpDataVersionFilterList; } @@ -383,6 +393,10 @@ class ReadHandler : public Messaging::ExchangeDelegate void SetStateFlag(ReadHandlerFlags aFlag, bool aValue = true); void ClearStateFlag(ReadHandlerFlags aFlag); + // Helpers for continuing the subscription resumption + static void HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle); + static void HandleDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR error); + AttributePathExpandIterator mAttributePathExpandIterator = AttributePathExpandIterator(nullptr); // The current generation of the reporting engine dirty set the last time we were notified that a path we're interested in was @@ -461,6 +475,10 @@ class ReadHandler : public Messaging::ExchangeDelegate PriorityLevel mCurrentPriority = PriorityLevel::Invalid; BitFlags mFlags; InteractionType mInteractionType = InteractionType::Read; + + // Callbacks to handle server-initiated session success/failure + chip::Callback::Callback mOnConnectedCallback; + chip::Callback::Callback mOnConnectionFailureCallback; }; } // namespace app } // namespace chip diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp new file mode 100644 index 00000000000000..f1203af74b10a1 --- /dev/null +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2023 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 the CHIP CASE Session object that provides + * APIs for constructing a secure session using a certificate from the device's + * operational credentials. + */ + +#include + +#include +#include +#include + +namespace chip { +namespace app { + +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kFabricIndexTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kPeerNodeIdTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kSubscriptionIdTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kMinIntervalTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kMaxIntervalTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kFabricFilteredTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kPathCountTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kPathTypeTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEndpointIdTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kClusterIdTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kAttributeIdTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventIdTag; + +CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveIndex(const SubscriptionIndex & index) +{ + std::array buf; + TLV::TLVWriter writer; + writer.Init(buf); + + TLV::TLVType arrayType; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, arrayType)); + + for (size_t i = 0; i < index.mSize; ++i) + { + TLV::TLVType innerType; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, innerType)); + ReturnErrorOnFailure(writer.Put(kFabricIndexTag, index.mNodes[i].GetFabricIndex())); + ReturnErrorOnFailure(writer.Put(kPeerNodeIdTag, index.mNodes[i].GetNodeId())); + ReturnErrorOnFailure(writer.EndContainer(innerType)); + } + + ReturnErrorOnFailure(writer.EndContainer(arrayType)); + + const auto len = writer.GetLengthWritten(); + VerifyOrReturnError(CanCastTo(len), CHIP_ERROR_BUFFER_TOO_SMALL); + + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), buf.data(), + static_cast(len))); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & index) +{ + std::array buf; + uint16_t len = static_cast(buf.size()); + + if (mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), buf.data(), len) != + CHIP_NO_ERROR) + { + index.mSize = 0; + return CHIP_NO_ERROR; + } + + TLV::ContiguousBufferTLVReader reader; + reader.Init(buf.data(), len); + + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag())); + TLV::TLVType arrayType; + ReturnErrorOnFailure(reader.EnterContainer(arrayType)); + + size_t count = 0; + CHIP_ERROR err; + while ((err = reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())) == CHIP_NO_ERROR) + { + if (count >= ArraySize(index.mNodes)) + { + return CHIP_ERROR_NO_MEMORY; + } + + TLV::TLVType containerType; + ReturnErrorOnFailure(reader.EnterContainer(containerType)); + + FabricIndex fabricIndex; + ReturnErrorOnFailure(reader.Next(kFabricIndexTag)); + ReturnErrorOnFailure(reader.Get(fabricIndex)); + + NodeId peerNodeId; + ReturnErrorOnFailure(reader.Next(kPeerNodeIdTag)); + ReturnErrorOnFailure(reader.Get(peerNodeId)); + + index.mNodes[count++] = ScopedNodeId(peerNodeId, fabricIndex); + + ReturnErrorOnFailure(reader.ExitContainer(containerType)); + } + + if (err != CHIP_END_OF_TLV) + { + return err; + } + + ReturnErrorOnFailure(reader.ExitContainer(arrayType)); + ReturnErrorOnFailure(reader.VerifyEndOfContainer()); + + index.mSize = count; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId node, std::vector & subscriptions) +{ + std::array buf; + uint16_t len = static_cast(buf.size()); + + if (mStorage->SyncGetKeyValue(GetStorageKey(node).KeyName(), buf.data(), len) != CHIP_NO_ERROR) + { + return CHIP_NO_ERROR; + } + + TLV::ContiguousBufferTLVReader reader; + reader.Init(buf.data(), len); + + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag())); + TLV::TLVType arrayType; + ReturnErrorOnFailure(reader.EnterContainer(arrayType)); + + size_t count = 0; + CHIP_ERROR err; + while ((err = reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())) == CHIP_NO_ERROR) + { + // Not using CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC per explanation + // in MaxStateSize() header comment block + if (count >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) + { + return CHIP_ERROR_NO_MEMORY; + } + + TLV::TLVType subscriptionContainerType; + ReturnErrorOnFailure(reader.EnterContainer(subscriptionContainerType)); + + SubscriptionInfo subscriptionInfo = { .mNode = node }; + + // Subscription ID + ReturnErrorOnFailure(reader.Next(kSubscriptionIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mSubscriptionId)); + + // Min interval + ReturnErrorOnFailure(reader.Next(kMinIntervalTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mMinInterval)); + + // Max interval + ReturnErrorOnFailure(reader.Next(kMaxIntervalTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mMaxInterval)); + + // Fabric filtered boolean + ReturnErrorOnFailure(reader.Next(kFabricFilteredTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mFabricFiltered)); + + // Paths + uint8_t pathCount = 0; + ReturnErrorOnFailure(reader.Next(kPathCountTag)); + ReturnErrorOnFailure(reader.Get(pathCount)); + + for (uint8_t i = 0; i < pathCount; i++) + { + SubscriptionPathType pathType; + ReturnErrorOnFailure(reader.Next(kPathTypeTag)); + ReturnErrorOnFailure(reader.Get(pathType)); + + EndpointId endpointId; + ReturnErrorOnFailure(reader.Next(kEndpointIdTag)); + ReturnErrorOnFailure(reader.Get(endpointId)); + + ClusterId clusterId; + ReturnErrorOnFailure(reader.Next(kClusterIdTag)); + ReturnErrorOnFailure(reader.Get(clusterId)); + + switch (pathType) + { + case SubscriptionPathType::kAttributePath: { + AttributeId attributeId; + ReturnErrorOnFailure(reader.Next(kAttributeIdTag)); + ReturnErrorOnFailure(reader.Get(attributeId)); + + subscriptionInfo.mAttributePaths.push_back(AttributePathParams(endpointId, clusterId, attributeId)); + break; + } + case SubscriptionPathType::kUrgentEventPath: + case SubscriptionPathType::kNonUrgentEventPath: { + bool isUrgent = (pathType == SubscriptionPathType::kUrgentEventPath); + EventId eventId; + ReturnErrorOnFailure(reader.Next(kEventIdTag)); + ReturnErrorOnFailure(reader.Get(eventId)); + + subscriptionInfo.mEventPaths.push_back(EventPathParams(endpointId, clusterId, eventId, isUrgent)); + break; + } + } + } + subscriptions.push_back(subscriptionInfo); + count++; + ReturnErrorOnFailure(reader.ExitContainer(subscriptionContainerType)); + } + + if (err != CHIP_END_OF_TLV) + { + return err; + } + + ReturnErrorOnFailure(reader.ExitContainer(arrayType)); + ReturnErrorOnFailure(reader.VerifyEndOfContainer()); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNodeId & node, + const std::vector & subscriptions) +{ + // Generate new state + std::array buf; + TLV::TLVWriter writer; + writer.Init(buf); + + TLV::TLVType arrayType; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, arrayType)); + + for (auto & subscriptionInfo : subscriptions) + { + TLV::TLVType subscriptionContainerType; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, subscriptionContainerType)); + ReturnErrorOnFailure(writer.Put(kSubscriptionIdTag, subscriptionInfo.mSubscriptionId)); + ReturnErrorOnFailure(writer.Put(kMinIntervalTag, subscriptionInfo.mMinInterval)); + ReturnErrorOnFailure(writer.Put(kMaxIntervalTag, subscriptionInfo.mMaxInterval)); + ReturnErrorOnFailure(writer.Put(kFabricFilteredTag, subscriptionInfo.mFabricFiltered)); + + uint8_t pathCount = static_cast(subscriptionInfo.mAttributePaths.size() + subscriptionInfo.mEventPaths.size()); + ReturnErrorOnFailure(writer.Put(kPathCountTag, pathCount)); + + for (auto & attributePathParams : subscriptionInfo.mAttributePaths) + { + ReturnErrorOnFailure(writer.Put(kPathTypeTag, SubscriptionPathType::kAttributePath)); + ReturnErrorOnFailure(writer.Put(kEndpointIdTag, attributePathParams.mEndpointId)); + ReturnErrorOnFailure(writer.Put(kClusterIdTag, attributePathParams.mClusterId)); + ReturnErrorOnFailure(writer.Put(kAttributeIdTag, attributePathParams.mAttributeId)); + } + + for (auto & eventPathParams : subscriptionInfo.mEventPaths) + { + if (eventPathParams.mIsUrgentEvent) + { + ReturnErrorOnFailure(writer.Put(kPathTypeTag, SubscriptionPathType::kUrgentEventPath)); + } + else + { + ReturnErrorOnFailure(writer.Put(kPathTypeTag, SubscriptionPathType::kNonUrgentEventPath)); + } + ReturnErrorOnFailure(writer.Put(kEndpointIdTag, eventPathParams.mEndpointId)); + ReturnErrorOnFailure(writer.Put(kClusterIdTag, eventPathParams.mClusterId)); + ReturnErrorOnFailure(writer.Put(kAttributeIdTag, eventPathParams.mEventId)); + } + + ReturnErrorOnFailure(writer.EndContainer(subscriptionContainerType)); + } + + ReturnErrorOnFailure(writer.EndContainer(arrayType)); + + const auto len = writer.GetLengthWritten(); + VerifyOrReturnError(CanCastTo(len), CHIP_ERROR_BUFFER_TOO_SMALL); + + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(GetStorageKey(node).KeyName(), buf.data(), static_cast(len))); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(const SubscriptionInfo & subscriptionInfo) +{ + // Load index and update if fabric/node is new + SubscriptionIndex subscriptionIndex; + LoadIndex(subscriptionIndex); + bool nodeIsNew = true; + for (size_t i = 0; i < subscriptionIndex.mSize; i++) + { + if (subscriptionInfo.mNode == subscriptionIndex.mNodes[i]) + { + nodeIsNew = false; + break; + } + } + if (nodeIsNew) + { + if (subscriptionIndex.mSize == CHIP_IM_MAX_NUM_SUBSCRIPTIONS) + { + return CHIP_ERROR_NO_MEMORY; + } + subscriptionIndex.mNodes[subscriptionIndex.mSize++] = subscriptionInfo.mNode; + SaveIndex(subscriptionIndex); + } + + // Load existing subscriptions for node, then combine and save state + std::vector subscriptions; + CHIP_ERROR err = FindByScopedNodeId(subscriptionInfo.mNode, subscriptions); + if (err != CHIP_NO_ERROR) + { + return err; + } + + // Sanity check for duplicate subscription and remove + for (auto iter = subscriptions.begin(); iter != subscriptions.end();) + { + if ((*iter).mSubscriptionId == subscriptionInfo.mSubscriptionId) + { + iter = subscriptions.erase(iter); + } + else + { + ++iter; + } + } + + // Sanity check this will not go over fabric limit - count + size_t totalSubscriptions = subscriptions.size(); + for (size_t i = 0; i < subscriptionIndex.mSize; i++) + { + // This node has already been loaded and counted + if (subscriptionIndex.mNodes[i] == subscriptionInfo.mNode) + { + continue; + } + + std::vector otherSubscriptions; + err = FindByScopedNodeId(subscriptionIndex.mNodes[i], otherSubscriptions); + if (err == CHIP_NO_ERROR) + { + totalSubscriptions += otherSubscriptions.size(); + } + } + // Not using CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC per explanation + // in MaxStateSize() header comment block + if (totalSubscriptions == CHIP_IM_MAX_NUM_SUBSCRIPTIONS) + { + return CHIP_ERROR_NO_MEMORY; + } + + // Merge new subscription in and save + subscriptions.push_back(subscriptionInfo); + return SaveSubscriptions(subscriptionInfo.mNode, subscriptions); +} + +CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(const SubscriptionInfo & subscriptionInfo) +{ + // load existing subscriptions, then search for subscription + std::vector subscriptions; + CHIP_ERROR err = FindByScopedNodeId(subscriptionInfo.mNode, subscriptions); + + if (err != CHIP_NO_ERROR) + { + return err; + } + + bool subscriptionsChanged = false; + for (auto iter = subscriptions.begin(); iter != subscriptions.end();) + { + if ((*iter).mSubscriptionId == subscriptionInfo.mSubscriptionId) + { + iter = subscriptions.erase(iter); + subscriptionsChanged = true; + } + else + { + ++iter; + } + } + + if (subscriptionsChanged) + { + if (subscriptions.size()) + { + return SaveSubscriptions(subscriptionInfo.mNode, subscriptions); + } + else + { + return mStorage->SyncDeleteKeyValue(GetStorageKey(subscriptionInfo.mNode).KeyName()); + } + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteAll(FabricIndex fabricIndex) +{ + SubscriptionIndex subscriptionIndex; + LoadIndex(subscriptionIndex); + + size_t i = 0; + bool indexChanged = false; + while (i < subscriptionIndex.mSize) + { + if (subscriptionIndex.mNodes[i].GetFabricIndex() == fabricIndex) + { + mStorage->SyncDeleteKeyValue(GetStorageKey(subscriptionIndex.mNodes[i]).KeyName()); + + // Update count and exit if exhausted all nodes + --subscriptionIndex.mSize; + if (i == subscriptionIndex.mSize) + { + break; + } + + // Move the last element into this hole and keep looping + subscriptionIndex.mNodes[i] = subscriptionIndex.mNodes[subscriptionIndex.mSize]; + indexChanged = true; + } + else + { + i++; + } + } + + if (indexChanged) + { + SaveIndex(subscriptionIndex); + } + + return CHIP_NO_ERROR; +} + +StorageKeyName SimpleSubscriptionResumptionStorage::GetStorageKey(const ScopedNodeId & node) +{ + return DefaultStorageKeyAllocator::FabricSubscription(node.GetFabricIndex(), node.GetNodeId()); +} + +} // namespace app +} // namespace chip diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h new file mode 100644 index 00000000000000..9ab692f3e8c81a --- /dev/null +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2023 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 the CHIP CASE Session object that provides + * APIs for constructing a secure session using a certificate from the device's + * operational credentials. + */ + +#pragma once + +#include +#include +#include + +namespace chip { +namespace app { + +/** + * An example SubscriptionResumptionStorage using PersistentStorageDelegate as it backend. + */ +class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage +{ +public: + CHIP_ERROR Init(PersistentStorageDelegate * storage) + { + VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + mStorage = storage; + return CHIP_NO_ERROR; + } + + static StorageKeyName GetStorageKey(const ScopedNodeId & node); + + CHIP_ERROR LoadIndex(SubscriptionIndex & index) override; + + CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, std::vector & subscriptions) override; + + CHIP_ERROR Save(const SubscriptionInfo & subscriptionInfo) override; + + CHIP_ERROR Delete(const SubscriptionInfo & subscriptionInfo) override; + + CHIP_ERROR DeleteAll(FabricIndex fabricIndex) override; + +private: + CHIP_ERROR SaveIndex(const SubscriptionIndex & index); + CHIP_ERROR SaveSubscriptions(const ScopedNodeId & node, const std::vector & subscriptions); + + static constexpr size_t MaxScopedNodeIdSize() { return TLV::EstimateStructOverhead(sizeof(NodeId), sizeof(FabricIndex)); } + + static constexpr size_t MaxIndexSize() + { + // The max size of the list is (1 byte control + bytes for actual value) times max number of list items + return TLV::EstimateStructOverhead((1 + MaxScopedNodeIdSize()) * CHIP_IM_MAX_NUM_SUBSCRIPTIONS); + } + + static constexpr size_t MaxSubscriptionPathsSize() + { + // Not using CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION for the same + // reason MaxStateSize() uses CHIP_IM_MAX_NUM_SUBSCRIPTIONS instead + // of CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC + return TLV::EstimateStructOverhead( + TLV::EstimateStructOverhead(sizeof(uint8_t), sizeof(EndpointId), sizeof(ClusterId), sizeof(AttributeId)) * + CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS); + } + + static constexpr size_t MaxSubscriptionSize() + { + // All the fields added together + return TLV::EstimateStructOverhead(MaxScopedNodeIdSize(), sizeof(SubscriptionId), sizeof(uint16_t), sizeof(uint16_t), + sizeof(bool), MaxSubscriptionPathsSize()); + } + + static constexpr size_t MaxStateSize() + { + // Due to IM engine subscription eviction logic, effective allowed maximum + // subscriptions per fabric is higher than CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC + // when number of fabrics is fewer than maximum. And so max state size should use + // CHIP_IM_MAX_NUM_SUBSCRIPTIONS to better estimate, and allow IM engine eviction + // logic to trigger the right clean up when needed. + + // The max size of the list is (1 byte control + bytes for actual value) times max number of list items + return TLV::EstimateStructOverhead(1 + MaxSubscriptionSize() * CHIP_IM_MAX_NUM_SUBSCRIPTIONS); + } + + enum class SubscriptionPathType : uint8_t + { + kAttributePath = 0x1, + kUrgentEventPath = 0x2, + kNonUrgentEventPath = 0x3, + }; + + // TODO: consider alternate storage scheme to optimize space requirement + + // Nodes TLV structure: + // Array of: + // Scoped Node ID struct of: + // Node ID + // Fabric index + + // Subscription TLV structure: + // Array of: + // Struct of: (Subscription info) + // Node ID + // Subscription ID + // Min interval + // Max interval + // Fabric filtered boolean + // Array of: (Paths) + // Type (attribute, urgent event, non-urgent event) + // Endpoint ID + // Cluster ID + // Attribute/event ID + static constexpr TLV::Tag kFabricIndexTag = TLV::ContextTag(1); + static constexpr TLV::Tag kPeerNodeIdTag = TLV::ContextTag(2); + static constexpr TLV::Tag kSubscriptionIdTag = TLV::ContextTag(3); + static constexpr TLV::Tag kMinIntervalTag = TLV::ContextTag(4); + static constexpr TLV::Tag kMaxIntervalTag = TLV::ContextTag(5); + static constexpr TLV::Tag kFabricFilteredTag = TLV::ContextTag(6); + static constexpr TLV::Tag kPathCountTag = TLV::ContextTag(7); + static constexpr TLV::Tag kPathTypeTag = TLV::ContextTag(8); + static constexpr TLV::Tag kEndpointIdTag = TLV::ContextTag(9); + static constexpr TLV::Tag kClusterIdTag = TLV::ContextTag(10); + static constexpr TLV::Tag kAttributeIdTag = TLV::ContextTag(11); + static constexpr TLV::Tag kEventIdTag = TLV::ContextTag(12); + + PersistentStorageDelegate * mStorage; +}; +} // namespace app +} // namespace chip diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h new file mode 100644 index 00000000000000..6d57ddc8a76120 --- /dev/null +++ b/src/app/SubscriptionResumptionStorage.h @@ -0,0 +1,115 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * + * 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 the classes corresponding to CHIP Interaction Model Event Generatorr Delegate. + * + */ + +#pragma once + +#include +#include +#include + +namespace chip { +namespace app { + +/** + * A SubscriptionPersistenceDelegate is used to persist subscriptions when they are established. + * + * Allows application to append any type of TLV data as part of an event log entry. Events + * have a standard header applicable to all events and this class provides the + * ability to add additional data past such standard header. + */ +class SubscriptionResumptionStorage +{ +public: + /** + * Struct to hold information about subscriptions + */ + struct SubscriptionInfo + { + ScopedNodeId mNode; + SubscriptionId mSubscriptionId; + uint16_t mMinInterval; + uint16_t mMaxInterval; + bool mFabricFiltered; + std::vector mAttributePaths; + std::vector mEventPaths; + }; + + /** + * Struct to hold index of all nodes that have persisted subscriptions + */ + struct SubscriptionIndex + { + size_t mSize; + ScopedNodeId mNodes[CHIP_IM_MAX_NUM_SUBSCRIPTIONS]; + }; + + virtual ~SubscriptionResumptionStorage(){}; + + /** + * Recover fabric-scoped node identities of persisted subscribers. + * + * @param subscriberIndex the nodes for previously persisted subscribers + * @return CHIP_NO_ERROR on success, CHIP_ERROR_KEY_NOT_FOUND if no subscription resumption information can be found, else an + * appropriate CHIP error on failure + */ + virtual CHIP_ERROR LoadIndex(SubscriptionIndex & subscriberIndex) = 0; + + /** + * Recover subscription resumption info for a given fabric-scoped node identity. + * + * @param node the node for which to recover subscription resumption information + * @param subscriptions (out) recovered subscriptions info + * @return CHIP_NO_ERROR on success, CHIP_ERROR_KEY_NOT_FOUND if no subscription resumption information can be found, else an + * appropriate CHIP error on failure + */ + virtual CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, std::vector & subscriptions) = 0; + + /** + * Save subscription resumption information to storage. + * + * @param subscriptionInfo the subscription information to save + * @return CHIP_NO_ERROR on success, else an appropriate CHIP error on failure + */ + virtual CHIP_ERROR Save(const SubscriptionInfo & subscriptionInfo) = 0; + + /** + * Save subscription resumption information to storage. + * + * @param subscriptionInfo the subscription information to delete - only node and subscriptionId will be used to find the + * subscription + * @return CHIP_NO_ERROR on success, else an appropriate CHIP error on failure + */ + virtual CHIP_ERROR Delete(const SubscriptionInfo & subscriptionInfo) = 0; + + /** + * Remove all subscription resumption information associated with the specified + * fabric index. If no entries for the fabric index exist, this is a no-op + * and is considered successful. + * + * @param fabricIndex the index of the fabric for which to remove subscription resumption information + * @return CHIP_NO_ERROR on success, else an appropriate CHIP error on failure + */ + virtual CHIP_ERROR DeleteAll(FabricIndex fabricIndex) = 0; +}; +} // namespace app +} // namespace chip diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index b6c0d571d9e727..7701b8f5e6d8ff 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -126,10 +126,11 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) chip::Platform::MemoryInit(); // Initialize PersistentStorageDelegate-based storage - mDeviceStorage = initParams.persistentStorageDelegate; - mSessionResumptionStorage = initParams.sessionResumptionStorage; - mOperationalKeystore = initParams.operationalKeystore; - mOpCertStore = initParams.opCertStore; + mDeviceStorage = initParams.persistentStorageDelegate; + mSessionResumptionStorage = initParams.sessionResumptionStorage; + mSubscriptionResumptionStorage = initParams.subscriptionResumptionStorage; + mOperationalKeystore = initParams.operationalKeystore; + mOpCertStore = initParams.opCertStore; mCertificateValidityPolicy = initParams.certificateValidityPolicy; @@ -304,7 +305,8 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) mCertificateValidityPolicy, mGroupsProvider); SuccessOrExit(err); - err = chip::app::InteractionModelEngine::GetInstance()->Init(&mExchangeMgr, &GetFabricTable(), &mCASESessionManager); + err = chip::app::InteractionModelEngine::GetInstance()->Init(&mExchangeMgr, &GetFabricTable(), &mCASESessionManager, + mSubscriptionResumptionStorage); SuccessOrExit(err); // This code is necessary to restart listening to existing groups after a reboot @@ -351,6 +353,10 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) } } +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + err = chip::app::InteractionModelEngine::GetInstance()->ResumeSubscriptions(); +#endif + PlatformMgr().HandleServerStarted(); exit: diff --git a/src/app/server/Server.h b/src/app/server/Server.h index 74a5aaedb29f4c..6cfd21a4d99a4f 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -105,6 +106,9 @@ struct ServerInitParams // Session resumption storage: Optional. Support session resumption when provided. // Must be initialized before being provided. SessionResumptionStorage * sessionResumptionStorage = nullptr; + // Session resumption storage: Optional. Support session resumption when provided. + // Must be initialized before being provided. + app::SubscriptionResumptionStorage * subscriptionResumptionStorage = nullptr; // Certificate validity policy: Optional. If none is injected, CHIPCert // enforces a default policy. Credentials::CertificateValidityPolicy * certificateValidityPolicy = nullptr; @@ -220,6 +224,9 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams #if CHIP_CONFIG_ENABLE_SESSION_RESUMPTION static chip::SimpleSessionResumptionStorage sSessionResumptionStorage; +#endif +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + static chip::app::SimpleSubscriptionResumptionStorage sSubscriptionResumptionStorage; #endif static chip::app::DefaultAclStorage sAclStorage; @@ -273,6 +280,14 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams // embedded systems. this->certificateValidityPolicy = &sDefaultCertValidityPolicy; +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + ChipLogProgress(AppServer, "JEFFTEST: initializing subscription resumption storage..."); + ReturnErrorOnFailure(sSubscriptionResumptionStorage.Init(this->persistentStorageDelegate)); + this->subscriptionResumptionStorage = &sSubscriptionResumptionStorage; +#else + ChipLogProgress(AppServer, "JEFFTEST: subscription persistence not supported"); +#endif + return CHIP_NO_ERROR; } }; @@ -320,6 +335,8 @@ class Server SessionResumptionStorage * GetSessionResumptionStorage() { return mSessionResumptionStorage; } + app::SubscriptionResumptionStorage * GetSubscriptionResumptionStorage() { return mSubscriptionResumptionStorage; } + TransportMgrBase & GetTransportManager() { return mTransports; } Credentials::GroupDataProvider * GetGroupDataProvider() { return mGroupsProvider; } @@ -361,6 +378,8 @@ class Server void InitFailSafe(); + void ResumeSubscriptions(); + class GroupDataProviderListener final : public Credentials::GroupDataProvider::GroupListener { public: @@ -477,6 +496,16 @@ class Server "Warning, failed to delete session resumption state for fabric index 0x%x: %" CHIP_ERROR_FORMAT, static_cast(fabricIndex), err.Format()); } + + auto * subscriptionResumptionStorage = mServer->GetSubscriptionResumptionStorage(); + VerifyOrReturn(subscriptionResumptionStorage != nullptr); + err = subscriptionResumptionStorage->DeleteAll(fabricIndex); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, + "Warning, failed to delete subscription resumption state for fabric index 0x%x: %" CHIP_ERROR_FORMAT, + static_cast(fabricIndex), err.Format()); + } } Server * mServer = nullptr; @@ -505,6 +534,7 @@ class Server PersistentStorageDelegate * mDeviceStorage; SessionResumptionStorage * mSessionResumptionStorage; + app::SubscriptionResumptionStorage * mSubscriptionResumptionStorage; Credentials::CertificateValidityPolicy * mCertificateValidityPolicy; Credentials::GroupDataProvider * mGroupsProvider; app::DefaultAttributePersistenceProvider mAttributePersister; diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index f4455ce222993f..3583698a511f6c 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -312,7 +312,7 @@ - (void)_handleUnsolicitedMessageFromPublisher }); } - // in case this is called dyring exponential back off of subscription + // in case this is called during exponential back off of subscription // reestablishment, this starts the attempt right away [self _setupSubscription]; diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index 399df9452263c4..816b2ffd4c176d 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -813,6 +813,25 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; #define CHIP_IM_MAX_NUM_COMMAND_HANDLER 4 #endif +/** + * @def CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION + * + * @brief The maximum number of path per subscription. + */ +#ifndef CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION +#define CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION 3 +#endif + +/** + * @def CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC + * + * @brief Defines the maximum number of ReadHandler for subscriptions per fabric. + * + */ +#ifndef CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC +#define CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC 3 +#endif + /** * @def CHIP_IM_MAX_NUM_SUBSCRIPTIONS * @@ -823,7 +842,7 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; * */ #ifndef CHIP_IM_MAX_NUM_SUBSCRIPTIONS -#define CHIP_IM_MAX_NUM_SUBSCRIPTIONS (CHIP_CONFIG_MAX_FABRICS * 3) +#define CHIP_IM_MAX_NUM_SUBSCRIPTIONS (CHIP_CONFIG_MAX_FABRICS * CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC) #endif /** @@ -853,7 +872,7 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; * @brief The maximum number of path objects for subscriptions, limits the number of attributes being subscribed at the same time. */ #ifndef CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS -#define CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS (CHIP_IM_MAX_NUM_SUBSCRIPTIONS * 3) +#define CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS (CHIP_IM_MAX_NUM_SUBSCRIPTIONS * CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION) #endif /** @@ -1339,3 +1358,14 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; /** * @} */ + +/** + * @def CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + * + * @brief + * If asserted (1), suppress definition of the standard error formatting function + * + */ +#ifndef CHIP_CONFIG_PERSIST_SUBSCRIPTIONS +#define CHIP_CONFIG_PERSIST_SUBSCRIPTIONS 1 +#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS diff --git a/src/lib/support/DefaultStorageKeyAllocator.h b/src/lib/support/DefaultStorageKeyAllocator.h index d74a0e75bcbba5..025eb489b331f9 100644 --- a/src/lib/support/DefaultStorageKeyAllocator.h +++ b/src/lib/support/DefaultStorageKeyAllocator.h @@ -190,6 +190,14 @@ class DefaultStorageKeyAllocator // Event number counter. static StorageKeyName IMEventNumber() { return StorageKeyName::FromConst("g/im/ec"); } + + // Subscription resumption + static StorageKeyName FabricSubscription(FabricIndex fabric, NodeId nodeId) + { + return StorageKeyName::Formatted("f/%x/su/%08" PRIX32 "%08" PRIX32, fabric, static_cast(nodeId >> 32), + static_cast(nodeId)); + } + static StorageKeyName SubscriptionResumptionIndex() { return StorageKeyName::FromConst("g/sui"); } }; } // namespace chip From 0e003bae97fb0bb3e3d2ad2ef555332c6e2dbeb2 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Tue, 10 Jan 2023 23:46:31 -0800 Subject: [PATCH 02/41] restyled change --- src/app/InteractionModelEngine.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index e1351d91ea0e60..c1bb9bd3b85260 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -114,7 +114,8 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, * */ CHIP_ERROR Init(Messaging::ExchangeManager * apExchangeMgr, FabricTable * apFabricTable, - CASESessionManager * apCASESessionMgr = nullptr, SubscriptionResumptionStorage * subscriptionResumptionStorage = nullptr); + CASESessionManager * apCASESessionMgr = nullptr, + SubscriptionResumptionStorage * subscriptionResumptionStorage = nullptr); void Shutdown(); From a3b8add5c10b2fd7f4b3ffbf38f538fba517c518 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Wed, 11 Jan 2023 10:52:03 -0800 Subject: [PATCH 03/41] Correct SimpleSubscriptionResumptionStorage TLV format documentation --- src/app/SimpleSubscriptionResumptionStorage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index 9ab692f3e8c81a..cec12786815cdb 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -120,7 +120,7 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage // Min interval // Max interval // Fabric filtered boolean - // Array of: (Paths) + // Path Count x, with these fields repeating x times // Type (attribute, urgent event, non-urgent event) // Endpoint ID // Cluster ID From c68cf85b6f28d339ed7bc624787b0c1f7a8da815 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Wed, 11 Jan 2023 11:43:26 -0800 Subject: [PATCH 04/41] Fix ReadHandler resumption --- src/app/InteractionModelEngine.cpp | 4 ++-- src/app/ReadHandler.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 4fb9adb6242793..403d42f6429e8a 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1588,14 +1588,14 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS SubscriptionResumptionStorage::SubscriptionIndex subscriberIndex; CHIP_ERROR err = mpSubscriptionResumptionStorage->LoadIndex(subscriberIndex); - ChipLogProgress(AppServer, "%zu subscriber nodes to resume.. (error %" CHIP_ERROR_FORMAT ")", subscriberIndex.mSize, + ChipLogProgress(InteractionModel, "%zu subscriber nodes to resume.. (error %" CHIP_ERROR_FORMAT ")", subscriberIndex.mSize, err.Format()); for (size_t i = 0; i < subscriberIndex.mSize; i++) { std::vector subscriptions; err = mpSubscriptionResumptionStorage->FindByScopedNodeId(subscriberIndex.mNodes[i], subscriptions); - ChipLogProgress(AppServer, + ChipLogProgress(InteractionModel, "\tNode " ChipLogFormatScopedNodeId ": Loaded %zu subscriptions.. (error %" CHIP_ERROR_FORMAT ")", ChipLogValueScopedNodeId(subscriberIndex.mNodes[i]), subscriptions.size(), err.Format()); diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 0dab06f0993daf..a79afc2c4e2a96 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -67,6 +67,7 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback, SubscriptionResumption mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) { #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + mInteractionType = InteractionType::Subscribe; mSubscriptionId = subscriptionInfo.mSubscriptionId; mMinIntervalFloorSeconds = subscriptionInfo.mMinInterval; mMaxInterval = subscriptionInfo.mMaxInterval; @@ -862,8 +863,6 @@ void ReadHandler::HandleDeviceConnected(void * context, Messaging::ExchangeManag { ReadHandler * const _this = static_cast(context); - auto exchange = exchangeMgr.NewContext(sessionHandle, _this); - _this->mExchangeCtx.Grab(exchange); _this->mSessionHandle.Grab(sessionHandle); _this->MoveToState(HandlerState::GeneratingReports); From c6fb866706f017b7fa5f93ee1be0e67adee85bb4 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Wed, 11 Jan 2023 12:14:32 -0800 Subject: [PATCH 05/41] Correct state size estimate --- src/app/SimpleSubscriptionResumptionStorage.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index cec12786815cdb..21dfdac09ea601 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -70,12 +70,9 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage static constexpr size_t MaxSubscriptionPathsSize() { - // Not using CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION for the same - // reason MaxStateSize() uses CHIP_IM_MAX_NUM_SUBSCRIPTIONS instead - // of CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC return TLV::EstimateStructOverhead( TLV::EstimateStructOverhead(sizeof(uint8_t), sizeof(EndpointId), sizeof(ClusterId), sizeof(AttributeId)) * - CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS); + CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION); } static constexpr size_t MaxSubscriptionSize() From a82a9d05fc13e5ce841db17ebd0712a4b4607d28 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Wed, 11 Jan 2023 13:02:03 -0800 Subject: [PATCH 06/41] Replace %zu in log format --- src/app/InteractionModelEngine.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 403d42f6429e8a..df762f0ab22290 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1588,16 +1588,16 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS SubscriptionResumptionStorage::SubscriptionIndex subscriberIndex; CHIP_ERROR err = mpSubscriptionResumptionStorage->LoadIndex(subscriberIndex); - ChipLogProgress(InteractionModel, "%zu subscriber nodes to resume.. (error %" CHIP_ERROR_FORMAT ")", subscriberIndex.mSize, - err.Format()); + ChipLogProgress(InteractionModel, "%u subscriber nodes to resume.. (error %" CHIP_ERROR_FORMAT ")", + static_cast(subscriberIndex.mSize), err.Format()); for (size_t i = 0; i < subscriberIndex.mSize; i++) { std::vector subscriptions; err = mpSubscriptionResumptionStorage->FindByScopedNodeId(subscriberIndex.mNodes[i], subscriptions); - ChipLogProgress(InteractionModel, - "\tNode " ChipLogFormatScopedNodeId ": Loaded %zu subscriptions.. (error %" CHIP_ERROR_FORMAT ")", - ChipLogValueScopedNodeId(subscriberIndex.mNodes[i]), subscriptions.size(), err.Format()); + ChipLogProgress( + InteractionModel, "\tNode " ChipLogFormatScopedNodeId ": Loaded %u subscriptions.. (error %" CHIP_ERROR_FORMAT ")", + ChipLogValueScopedNodeId(subscriberIndex.mNodes[i]), static_cast(subscriptions.size()), err.Format()); for (auto & subscriptionInfo : subscriptions) { From 57cf32e97dc7bd5cc0d8e489be850c0ac7ea1ddf Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Wed, 11 Jan 2023 13:33:52 -0800 Subject: [PATCH 07/41] Move TLV buffer off stack --- .../SimpleSubscriptionResumptionStorage.cpp | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index f1203af74b10a1..3e3279e2c066a8 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -132,16 +132,18 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & in CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId node, std::vector & subscriptions) { - std::array buf; - uint16_t len = static_cast(buf.size()); + Platform::ScopedMemoryBuffer backingBuffer; + backingBuffer.Calloc(MaxIndexSize()); + VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY); + + uint16_t len = static_cast(MaxIndexSize()); - if (mStorage->SyncGetKeyValue(GetStorageKey(node).KeyName(), buf.data(), len) != CHIP_NO_ERROR) + if (mStorage->SyncGetKeyValue(GetStorageKey(node).KeyName(), backingBuffer.Get(), len) != CHIP_NO_ERROR) { return CHIP_NO_ERROR; } - TLV::ContiguousBufferTLVReader reader; - reader.Init(buf.data(), len); + TLV::ScopedBufferTLVReader reader(std::move(backingBuffer), len); ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag())); TLV::TLVType arrayType; @@ -239,10 +241,11 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNodeId & node, const std::vector & subscriptions) { - // Generate new state - std::array buf; - TLV::TLVWriter writer; - writer.Init(buf); + Platform::ScopedMemoryBuffer backingBuffer; + backingBuffer.Calloc(MaxIndexSize()); + VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY); + + TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), MaxIndexSize()); TLV::TLVType arrayType; ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, arrayType)); @@ -286,11 +289,13 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNo } ReturnErrorOnFailure(writer.EndContainer(arrayType)); - + const auto len = writer.GetLengthWritten(); VerifyOrReturnError(CanCastTo(len), CHIP_ERROR_BUFFER_TOO_SMALL); - ReturnErrorOnFailure(mStorage->SyncSetKeyValue(GetStorageKey(node).KeyName(), buf.data(), static_cast(len))); + writer.Finalize(backingBuffer); + + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(GetStorageKey(node).KeyName(), backingBuffer.Get(), static_cast(len))); return CHIP_NO_ERROR; } From 05006c3b206759b996e508b14042e2850c0e0129 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Wed, 11 Jan 2023 13:52:23 -0800 Subject: [PATCH 08/41] restyled --- src/app/SimpleSubscriptionResumptionStorage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 3e3279e2c066a8..e84e17b227b2de 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -289,7 +289,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNo } ReturnErrorOnFailure(writer.EndContainer(arrayType)); - + const auto len = writer.GetLengthWritten(); VerifyOrReturnError(CanCastTo(len), CHIP_ERROR_BUFFER_TOO_SMALL); From 9f56e48698699d852cefa29b0d24a67685b823ad Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 12 Jan 2023 11:12:45 -0800 Subject: [PATCH 09/41] Replaced vector with ScopedMemoryBufferWithSize and shim structs --- src/app/InteractionModelEngine.cpp | 16 +- src/app/ReadHandler.cpp | 56 +++-- src/app/ReadHandler.h | 4 +- .../SimpleSubscriptionResumptionStorage.cpp | 236 ++++++++++-------- src/app/SimpleSubscriptionResumptionStorage.h | 12 +- src/app/SubscriptionResumptionStorage.h | 55 +++- src/app/server/Server.h | 4 +- src/lib/core/CHIPConfig.h | 2 +- src/lib/support/ScopedBuffer.h | 29 ++- 9 files changed, 263 insertions(+), 151 deletions(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index df762f0ab22290..9073f9b83fad41 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1592,19 +1592,21 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() static_cast(subscriberIndex.mSize), err.Format()); for (size_t i = 0; i < subscriberIndex.mSize; i++) { - std::vector subscriptions; + SubscriptionResumptionStorage::SubscriptionList subscriptions; + + // std::vector subscriptions; err = mpSubscriptionResumptionStorage->FindByScopedNodeId(subscriberIndex.mNodes[i], subscriptions); ChipLogProgress( InteractionModel, "\tNode " ChipLogFormatScopedNodeId ": Loaded %u subscriptions.. (error %" CHIP_ERROR_FORMAT ")", - ChipLogValueScopedNodeId(subscriberIndex.mNodes[i]), static_cast(subscriptions.size()), err.Format()); + ChipLogValueScopedNodeId(subscriberIndex.mNodes[i]), static_cast(subscriptions.mSize), err.Format()); - for (auto & subscriptionInfo : subscriptions) + for (size_t j = 0; j < subscriptions.mSize; j++) { - auto requestedAttributePathCount = subscriptionInfo.mAttributePaths.size(); - auto requestedEventPathCount = subscriptionInfo.mEventPaths.size(); - if (!EnsureResourceForSubscription(subscriptionInfo.mNode.GetFabricIndex(), requestedAttributePathCount, - requestedEventPathCount)) + SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = subscriptions.mSubscriptions[j]; + auto requestedAttributePathCount = subscriptionInfo.mAttributePaths.AllocatedCount(); + auto requestedEventPathCount = subscriptionInfo.mEventPaths.AllocatedCount(); + if (!EnsureResourceForSubscription(subscriptionInfo.mFabricIndex, requestedAttributePathCount, requestedEventPathCount)) { ChipLogProgress(InteractionModel, "no resource for Subscription resumption"); return CHIP_ERROR_NO_MEMORY; diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index a79afc2c4e2a96..10cca368b442f1 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -74,8 +74,9 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback, SubscriptionResumption SetStateFlag(ReadHandlerFlags::FabricFiltered, subscriptionInfo.mFabricFiltered); CHIP_ERROR err; - for (auto & attributePathParams : subscriptionInfo.mAttributePaths) + for (size_t i = 0; i < subscriptionInfo.mAttributePaths.AllocatedCount(); i++) { + AttributePathParams attributePathParams = subscriptionInfo.mAttributePaths[i].GetParams(); err = InteractionModelEngine::GetInstance()->PushFrontAttributePathList(mpAttributePathList, attributePathParams); if (err != CHIP_NO_ERROR) { @@ -83,8 +84,9 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback, SubscriptionResumption return; } } - for (auto & eventPathParams : subscriptionInfo.mEventPaths) + for (size_t i = 0; i < subscriptionInfo.mEventPaths.AllocatedCount(); i++) { + EventPathParams eventPathParams = subscriptionInfo.mEventPaths[i].GetParams(); err = InteractionModelEngine::GetInstance()->PushFrontEventPathParamsList(mpEventPathList, eventPathParams); if (err != CHIP_NO_ERROR) { @@ -97,7 +99,8 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback, SubscriptionResumption auto * caseSessionManager = InteractionModelEngine::GetInstance()->GetCASESessionManager(); if (caseSessionManager) { - caseSessionManager->FindOrEstablishSession(subscriptionInfo.mNode, &mOnConnectedCallback, &mOnConnectionFailureCallback); + ScopedNodeId peerNode = ScopedNodeId(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex); + caseSessionManager->FindOrEstablishSession(peerNode, &mOnConnectedCallback, &mOnConnectionFailureCallback); } else { @@ -134,16 +137,19 @@ ReadHandler::~ReadHandler() InteractionModelEngine::GetInstance()->ReleaseDataVersionFilterList(mpDataVersionFilterList); } -void ReadHandler::Close() +void ReadHandler::Close(bool keepPersisted) { #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage(); - if (subscriptionResumptionStorage) + if (!keepPersisted) { - SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { - .mNode = ScopedNodeId(GetInitiatorNodeId(), GetAccessingFabricIndex()), .mSubscriptionId = mSubscriptionId - }; - subscriptionResumptionStorage->Delete(subscriptionInfo); + auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage(); + if (subscriptionResumptionStorage) + { + SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = GetInitiatorNodeId(), + .mFabricIndex = GetAccessingFabricIndex(), + .mSubscriptionId = mSubscriptionId }; + subscriptionResumptionStorage->Delete(subscriptionInfo); + } } #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS MoveToState(HandlerState::AwaitingDestruction); @@ -365,6 +371,7 @@ void ReadHandler::OnResponseTimeout(Messaging::ExchangeContext * apExchangeConte ChipLogValueExchange(apExchangeContext)); #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS // TODO: Have a retry mechanism tied to wake interval for IC devices + Close(true); #else Close(); #endif @@ -717,26 +724,39 @@ CHIP_ERROR ReadHandler::ProcessSubscribeRequest(System::PacketBufferHandle && aP auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage(); if (subscriptionResumptionStorage) { - // Persist Subscription - // mpEventPathList - // mpAttributePathList - - SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNode = ScopedNodeId(GetInitiatorNodeId(), - GetAccessingFabricIndex()), + SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = GetInitiatorNodeId(), + .mFabricIndex = GetAccessingFabricIndex(), .mSubscriptionId = mSubscriptionId, .mMinInterval = mMinIntervalFloorSeconds, .mMaxInterval = mMaxInterval, .mFabricFiltered = IsFabricFiltered() }; ObjectList * attributePath = mpAttributePathList; + size_t attributePathCount = 0; while (attributePath) { - subscriptionInfo.mAttributePaths.push_back(attributePath->mValue); + attributePathCount++; + attributePath = attributePath->mpNext; + } + attributePath = mpAttributePathList; + subscriptionInfo.mAttributePaths.Calloc(attributePathCount); + for (size_t i = 0; i < attributePathCount; i++) + { + subscriptionInfo.mAttributePaths[i].SetValues(attributePath->mValue); attributePath = attributePath->mpNext; } + ObjectList * eventPath = mpEventPathList; + size_t eventPathCount = 0; while (eventPath) { - subscriptionInfo.mEventPaths.push_back(eventPath->mValue); + eventPathCount++; + eventPath = eventPath->mpNext; + } + eventPath = mpEventPathList; + subscriptionInfo.mEventPaths.Calloc(eventPathCount); + for (size_t i = 0; i < eventPathCount; i++) + { + subscriptionInfo.mEventPaths[i].SetValues(eventPath->mValue); eventPath = eventPath->mpNext; } diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 80f1077655da26..02d9e6c726d5ad 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -368,8 +368,10 @@ class ReadHandler : public Messaging::ExchangeDelegate /** * Called internally to signal the completion of all work on this objecta and signal to a registered callback that it's * safe to release this object. + * + * @param keepPersisted Keep the subscription persisted in storage for later resumption */ - void Close(); + void Close(bool keepPersisted = false); static void OnUnblockHoldReportCallback(System::Layer * apSystemLayer, void * apAppState); static void OnRefreshSubscribeTimerSyncCallback(System::Layer * apSystemLayer, void * apAppState); diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index e84e17b227b2de..d39ca04eebd4d1 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -46,6 +46,11 @@ constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventIdTag; CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveIndex(const SubscriptionIndex & index) { + if (index.mSize == 0) + { + return mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); + } + std::array buf; TLV::TLVWriter writer; writer.Init(buf); @@ -130,7 +135,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & in return CHIP_NO_ERROR; } -CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId node, std::vector & subscriptions) +CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions) { Platform::ScopedMemoryBuffer backingBuffer; backingBuffer.Calloc(MaxIndexSize()); @@ -140,6 +145,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId if (mStorage->SyncGetKeyValue(GetStorageKey(node).KeyName(), backingBuffer.Get(), len) != CHIP_NO_ERROR) { + subscriptions.mSize = 0; return CHIP_NO_ERROR; } @@ -149,81 +155,91 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId TLV::TLVType arrayType; ReturnErrorOnFailure(reader.EnterContainer(arrayType)); - size_t count = 0; + size_t count; + ReturnErrorOnFailure(reader.CountRemainingInContainer(&count)); + if (count >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) + { + return CHIP_ERROR_NO_MEMORY; + } + + subscriptions.mSize = count; + CHIP_ERROR err; - while ((err = reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())) == CHIP_NO_ERROR) + for (size_t i = 0; i < count; i++) { - // Not using CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC per explanation - // in MaxStateSize() header comment block - if (count >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) - { - return CHIP_ERROR_NO_MEMORY; - } + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); TLV::TLVType subscriptionContainerType; ReturnErrorOnFailure(reader.EnterContainer(subscriptionContainerType)); - SubscriptionInfo subscriptionInfo = { .mNode = node }; + subscriptions.mSubscriptions[i] = { .mFabricIndex = node.GetFabricIndex(), .mNodeId = node.GetNodeId() }; // Subscription ID ReturnErrorOnFailure(reader.Next(kSubscriptionIdTag)); - ReturnErrorOnFailure(reader.Get(subscriptionInfo.mSubscriptionId)); + ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mSubscriptionId)); // Min interval ReturnErrorOnFailure(reader.Next(kMinIntervalTag)); - ReturnErrorOnFailure(reader.Get(subscriptionInfo.mMinInterval)); + ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mMinInterval)); // Max interval ReturnErrorOnFailure(reader.Next(kMaxIntervalTag)); - ReturnErrorOnFailure(reader.Get(subscriptionInfo.mMaxInterval)); + ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mMaxInterval)); // Fabric filtered boolean ReturnErrorOnFailure(reader.Next(kFabricFilteredTag)); - ReturnErrorOnFailure(reader.Get(subscriptionInfo.mFabricFiltered)); + ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mFabricFiltered)); - // Paths - uint8_t pathCount = 0; + // Attribute Paths + uint16_t pathCount = 0; ReturnErrorOnFailure(reader.Next(kPathCountTag)); ReturnErrorOnFailure(reader.Get(pathCount)); - for (uint8_t i = 0; i < pathCount; i++) + subscriptions.mSubscriptions[i].mAttributePaths = Platform::ScopedMemoryBufferWithSize(); + if (pathCount) { - SubscriptionPathType pathType; - ReturnErrorOnFailure(reader.Next(kPathTypeTag)); - ReturnErrorOnFailure(reader.Get(pathType)); + subscriptions.mSubscriptions[i].mAttributePaths.Calloc(pathCount); + for (uint8_t j = 0; j < pathCount; j++) + { + ReturnErrorOnFailure(reader.Next(kEndpointIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mAttributePaths[j].mEndpointId)); + + ReturnErrorOnFailure(reader.Next(kClusterIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mAttributePaths[j].mClusterId)); - EndpointId endpointId; - ReturnErrorOnFailure(reader.Next(kEndpointIdTag)); - ReturnErrorOnFailure(reader.Get(endpointId)); + ReturnErrorOnFailure(reader.Next(kAttributeIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mAttributePaths[j].mAttributeId)); + } + } - ClusterId clusterId; - ReturnErrorOnFailure(reader.Next(kClusterIdTag)); - ReturnErrorOnFailure(reader.Get(clusterId)); + // Event Paths + pathCount = 0; + ReturnErrorOnFailure(reader.Next(kPathCountTag)); + ReturnErrorOnFailure(reader.Get(pathCount)); - switch (pathType) + subscriptions.mSubscriptions[i].mEventPaths = Platform::ScopedMemoryBufferWithSize(); + if (pathCount) + { + subscriptions.mSubscriptions[i].mEventPaths.Calloc(pathCount); + for (uint8_t j = 0; j < pathCount; j++) { - case SubscriptionPathType::kAttributePath: { - AttributeId attributeId; - ReturnErrorOnFailure(reader.Next(kAttributeIdTag)); - ReturnErrorOnFailure(reader.Get(attributeId)); + EventPathType eventPathType; + ReturnErrorOnFailure(reader.Next(kPathTypeTag)); + ReturnErrorOnFailure(reader.Get(eventPathType)); - subscriptionInfo.mAttributePaths.push_back(AttributePathParams(endpointId, clusterId, attributeId)); - break; - } - case SubscriptionPathType::kUrgentEventPath: - case SubscriptionPathType::kNonUrgentEventPath: { - bool isUrgent = (pathType == SubscriptionPathType::kUrgentEventPath); - EventId eventId; - ReturnErrorOnFailure(reader.Next(kEventIdTag)); - ReturnErrorOnFailure(reader.Get(eventId)); + subscriptions.mSubscriptions[i].mEventPaths[j].mIsUrgentEvent = (eventPathType == EventPathType::kUrgent); - subscriptionInfo.mEventPaths.push_back(EventPathParams(endpointId, clusterId, eventId, isUrgent)); - break; - } + ReturnErrorOnFailure(reader.Next(kEndpointIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mEventPaths[j].mEndpointId)); + + ReturnErrorOnFailure(reader.Next(kClusterIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mEventPaths[j].mClusterId)); + + ReturnErrorOnFailure(reader.Next(kEventIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mEventPaths[j].mEventId)); } } - subscriptions.push_back(subscriptionInfo); - count++; + ReturnErrorOnFailure(reader.ExitContainer(subscriptionContainerType)); } @@ -238,8 +254,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId return CHIP_NO_ERROR; } -CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNodeId & node, - const std::vector & subscriptions) +CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNodeId & node, const SubscriptionList & subscriptions) { Platform::ScopedMemoryBuffer backingBuffer; backingBuffer.Calloc(MaxIndexSize()); @@ -250,39 +265,39 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNo TLV::TLVType arrayType; ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, arrayType)); - for (auto & subscriptionInfo : subscriptions) + for (size_t i = 0; i < subscriptions.mSize; i++) { TLV::TLVType subscriptionContainerType; ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, subscriptionContainerType)); - ReturnErrorOnFailure(writer.Put(kSubscriptionIdTag, subscriptionInfo.mSubscriptionId)); - ReturnErrorOnFailure(writer.Put(kMinIntervalTag, subscriptionInfo.mMinInterval)); - ReturnErrorOnFailure(writer.Put(kMaxIntervalTag, subscriptionInfo.mMaxInterval)); - ReturnErrorOnFailure(writer.Put(kFabricFilteredTag, subscriptionInfo.mFabricFiltered)); - - uint8_t pathCount = static_cast(subscriptionInfo.mAttributePaths.size() + subscriptionInfo.mEventPaths.size()); - ReturnErrorOnFailure(writer.Put(kPathCountTag, pathCount)); - - for (auto & attributePathParams : subscriptionInfo.mAttributePaths) + ReturnErrorOnFailure(writer.Put(kSubscriptionIdTag, subscriptions.mSubscriptions[i].mSubscriptionId)); + ReturnErrorOnFailure(writer.Put(kMinIntervalTag, subscriptions.mSubscriptions[i].mMinInterval)); + ReturnErrorOnFailure(writer.Put(kMaxIntervalTag, subscriptions.mSubscriptions[i].mMaxInterval)); + ReturnErrorOnFailure(writer.Put(kFabricFilteredTag, subscriptions.mSubscriptions[i].mFabricFiltered)); + + ReturnErrorOnFailure( + writer.Put(kPathCountTag, static_cast(subscriptions.mSubscriptions[i].mAttributePaths.AllocatedCount()))); + for (size_t j = 0; j < subscriptions.mSubscriptions[i].mAttributePaths.AllocatedCount(); j++) { - ReturnErrorOnFailure(writer.Put(kPathTypeTag, SubscriptionPathType::kAttributePath)); - ReturnErrorOnFailure(writer.Put(kEndpointIdTag, attributePathParams.mEndpointId)); - ReturnErrorOnFailure(writer.Put(kClusterIdTag, attributePathParams.mClusterId)); - ReturnErrorOnFailure(writer.Put(kAttributeIdTag, attributePathParams.mAttributeId)); + ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptions.mSubscriptions[i].mAttributePaths[j].mEndpointId)); + ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptions.mSubscriptions[i].mAttributePaths[j].mClusterId)); + ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptions.mSubscriptions[i].mAttributePaths[j].mAttributeId)); } - for (auto & eventPathParams : subscriptionInfo.mEventPaths) + ReturnErrorOnFailure( + writer.Put(kPathCountTag, static_cast(subscriptions.mSubscriptions[i].mEventPaths.AllocatedCount()))); + for (size_t j = 0; j < subscriptions.mSubscriptions[i].mAttributePaths.AllocatedCount(); j++) { - if (eventPathParams.mIsUrgentEvent) + if (subscriptions.mSubscriptions[i].mEventPaths[j].mIsUrgentEvent) { - ReturnErrorOnFailure(writer.Put(kPathTypeTag, SubscriptionPathType::kUrgentEventPath)); + ReturnErrorOnFailure(writer.Put(kPathTypeTag, EventPathType::kUrgent)); } else { - ReturnErrorOnFailure(writer.Put(kPathTypeTag, SubscriptionPathType::kNonUrgentEventPath)); + ReturnErrorOnFailure(writer.Put(kPathTypeTag, EventPathType::kNonUrgent)); } - ReturnErrorOnFailure(writer.Put(kEndpointIdTag, eventPathParams.mEndpointId)); - ReturnErrorOnFailure(writer.Put(kClusterIdTag, eventPathParams.mClusterId)); - ReturnErrorOnFailure(writer.Put(kAttributeIdTag, eventPathParams.mEventId)); + ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptions.mSubscriptions[i].mEventPaths[j].mEndpointId)); + ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptions.mSubscriptions[i].mEventPaths[j].mClusterId)); + ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptions.mSubscriptions[i].mEventPaths[j].mEventId)); } ReturnErrorOnFailure(writer.EndContainer(subscriptionContainerType)); @@ -300,15 +315,16 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNo return CHIP_NO_ERROR; } -CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(const SubscriptionInfo & subscriptionInfo) +CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscriptionInfo) { + ScopedNodeId subscriptionNode = ScopedNodeId(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex); // Load index and update if fabric/node is new SubscriptionIndex subscriptionIndex; LoadIndex(subscriptionIndex); bool nodeIsNew = true; for (size_t i = 0; i < subscriptionIndex.mSize; i++) { - if (subscriptionInfo.mNode == subscriptionIndex.mNodes[i]) + if (subscriptionNode == subscriptionIndex.mNodes[i]) { nodeIsNew = false; break; @@ -320,46 +336,48 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(const SubscriptionInfo & su { return CHIP_ERROR_NO_MEMORY; } - subscriptionIndex.mNodes[subscriptionIndex.mSize++] = subscriptionInfo.mNode; + subscriptionIndex.mNodes[subscriptionIndex.mSize++] = subscriptionNode; SaveIndex(subscriptionIndex); } // Load existing subscriptions for node, then combine and save state - std::vector subscriptions; - CHIP_ERROR err = FindByScopedNodeId(subscriptionInfo.mNode, subscriptions); + SubscriptionList subscriptions; + CHIP_ERROR err = FindByScopedNodeId(subscriptionNode, subscriptions); if (err != CHIP_NO_ERROR) { return err; } // Sanity check for duplicate subscription and remove - for (auto iter = subscriptions.begin(); iter != subscriptions.end();) + for (size_t i = 0; i < subscriptions.mSize; i++) { - if ((*iter).mSubscriptionId == subscriptionInfo.mSubscriptionId) - { - iter = subscriptions.erase(iter); - } - else + if (subscriptionInfo.mSubscriptionId == subscriptions.mSubscriptions[i].mSubscriptionId) { - ++iter; + subscriptions.mSize--; + // if not last element, move last element here, essentially deleting this + if (i < subscriptions.mSize) + { + subscriptions.mSubscriptions[i] = std::move(subscriptions.mSubscriptions[subscriptions.mSize]); + } + break; } } // Sanity check this will not go over fabric limit - count - size_t totalSubscriptions = subscriptions.size(); + size_t totalSubscriptions = subscriptions.mSize; for (size_t i = 0; i < subscriptionIndex.mSize; i++) { // This node has already been loaded and counted - if (subscriptionIndex.mNodes[i] == subscriptionInfo.mNode) + if (subscriptionNode == subscriptionIndex.mNodes[i]) { continue; } - std::vector otherSubscriptions; + SubscriptionList otherSubscriptions; err = FindByScopedNodeId(subscriptionIndex.mNodes[i], otherSubscriptions); if (err == CHIP_NO_ERROR) { - totalSubscriptions += otherSubscriptions.size(); + totalSubscriptions += otherSubscriptions.mSize; } } // Not using CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC per explanation @@ -370,15 +388,16 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(const SubscriptionInfo & su } // Merge new subscription in and save - subscriptions.push_back(subscriptionInfo); - return SaveSubscriptions(subscriptionInfo.mNode, subscriptions); + subscriptions.mSubscriptions[subscriptions.mSize++] = std::move(subscriptionInfo); + return SaveSubscriptions(subscriptionNode, subscriptions); } CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(const SubscriptionInfo & subscriptionInfo) { + ScopedNodeId subscriptionNode = ScopedNodeId(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex); // load existing subscriptions, then search for subscription - std::vector subscriptions; - CHIP_ERROR err = FindByScopedNodeId(subscriptionInfo.mNode, subscriptions); + SubscriptionList subscriptions; + CHIP_ERROR err = FindByScopedNodeId(subscriptionNode, subscriptions); if (err != CHIP_NO_ERROR) { @@ -386,28 +405,47 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(const SubscriptionInfo & } bool subscriptionsChanged = false; - for (auto iter = subscriptions.begin(); iter != subscriptions.end();) + for (size_t i = 0; i < subscriptions.mSize; i++) { - if ((*iter).mSubscriptionId == subscriptionInfo.mSubscriptionId) - { - iter = subscriptions.erase(iter); - subscriptionsChanged = true; - } - else + if (subscriptions.mSubscriptions[i].mSubscriptionId == subscriptionInfo.mSubscriptionId) { - ++iter; + subscriptions.mSize--; + // if not last element, move last element here, essentially deleting this + if (i < subscriptions.mSize) + { + subscriptions.mSubscriptions[i] = std::move(subscriptions.mSubscriptions[subscriptions.mSize]); + } + break; } } if (subscriptionsChanged) { - if (subscriptions.size()) + if (subscriptions.mSize) { - return SaveSubscriptions(subscriptionInfo.mNode, subscriptions); + return SaveSubscriptions(subscriptionNode, subscriptions); } else { - return mStorage->SyncDeleteKeyValue(GetStorageKey(subscriptionInfo.mNode).KeyName()); + // Remove node from index + SubscriptionIndex subscriptionIndex; + LoadIndex(subscriptionIndex); + for (size_t i = 0; i < subscriptionIndex.mSize; i++) + { + if (subscriptionNode == subscriptionIndex.mNodes[i]) + { + subscriptionIndex.mSize--; + // if not last element, move last element here, essentially deleting this + if (i < subscriptionIndex.mSize) + { + subscriptionIndex.mNodes[i] = std::move(subscriptionIndex.mNodes[subscriptionIndex.mSize]); + } + break; + } + } + SaveIndex(subscriptionIndex); + + return mStorage->SyncDeleteKeyValue(GetStorageKey(subscriptionNode).KeyName()); } } diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index 21dfdac09ea601..e1fa22387e50b2 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -48,9 +48,9 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage CHIP_ERROR LoadIndex(SubscriptionIndex & index) override; - CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, std::vector & subscriptions) override; + CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions) override; - CHIP_ERROR Save(const SubscriptionInfo & subscriptionInfo) override; + CHIP_ERROR Save(SubscriptionInfo & subscriptionInfo) override; CHIP_ERROR Delete(const SubscriptionInfo & subscriptionInfo) override; @@ -58,7 +58,7 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage private: CHIP_ERROR SaveIndex(const SubscriptionIndex & index); - CHIP_ERROR SaveSubscriptions(const ScopedNodeId & node, const std::vector & subscriptions); + CHIP_ERROR SaveSubscriptions(const ScopedNodeId & node, const SubscriptionList & subscriptions); static constexpr size_t MaxScopedNodeIdSize() { return TLV::EstimateStructOverhead(sizeof(NodeId), sizeof(FabricIndex)); } @@ -101,6 +101,12 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage kNonUrgentEventPath = 0x3, }; + enum class EventPathType : uint8_t + { + kUrgent = 0x1, + kNonUrgent = 0x2, + }; + // TODO: consider alternate storage scheme to optimize space requirement // Nodes TLV structure: diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index 6d57ddc8a76120..e68eed294761d0 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -25,7 +25,6 @@ #include #include -#include namespace chip { namespace app { @@ -40,18 +39,60 @@ namespace app { class SubscriptionResumptionStorage { public: + // TODO: Create RAII ScopedMemoryBuffer replacement container that does not require is_trivial elements + + // Structs to hold + struct AttributePathParamsValues + { + EndpointId mEndpointId; // uint16 + ClusterId mClusterId; // uint32 + AttributeId mAttributeId; // uint32 + void SetValues(AttributePathParams & params) + { + mEndpointId = params.mEndpointId; + mClusterId = params.mClusterId; + mAttributeId = params.mAttributeId; + } + AttributePathParams GetParams() { return AttributePathParams(mEndpointId, mClusterId, mAttributeId); } + }; + struct EventPathParamsValues + { + EndpointId mEndpointId; // uint16 + ClusterId mClusterId; // uint32 + EventId mEventId; // uint32 + bool mIsUrgentEvent; // uint8 + void SetValues(EventPathParams & params) + { + mEndpointId = params.mEndpointId; + mClusterId = params.mClusterId; + mEventId = params.mEventId; + mIsUrgentEvent = params.mIsUrgentEvent; + } + EventPathParams GetParams() { return EventPathParams(mEndpointId, mClusterId, mEventId, mIsUrgentEvent); } + }; + /** * Struct to hold information about subscriptions */ struct SubscriptionInfo { - ScopedNodeId mNode; + FabricIndex mFabricIndex; + NodeId mNodeId; SubscriptionId mSubscriptionId; uint16_t mMinInterval; uint16_t mMaxInterval; bool mFabricFiltered; - std::vector mAttributePaths; - std::vector mEventPaths; + Platform::ScopedMemoryBufferWithSize mAttributePaths; + Platform::ScopedMemoryBufferWithSize mEventPaths; + }; + + /** + * Struct to hold list of subscriptions + */ + struct SubscriptionList + { + size_t mSize; + SubscriptionInfo mSubscriptions[CHIP_IM_MAX_NUM_SUBSCRIPTIONS]; }; /** @@ -82,15 +123,15 @@ class SubscriptionResumptionStorage * @return CHIP_NO_ERROR on success, CHIP_ERROR_KEY_NOT_FOUND if no subscription resumption information can be found, else an * appropriate CHIP error on failure */ - virtual CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, std::vector & subscriptions) = 0; + virtual CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions) = 0; /** * Save subscription resumption information to storage. * - * @param subscriptionInfo the subscription information to save + * @param subscriptionInfo the subscription information to save - caller should expect the passed in value is consumed * @return CHIP_NO_ERROR on success, else an appropriate CHIP error on failure */ - virtual CHIP_ERROR Save(const SubscriptionInfo & subscriptionInfo) = 0; + virtual CHIP_ERROR Save(SubscriptionInfo & subscriptionInfo) = 0; /** * Save subscription resumption information to storage. diff --git a/src/app/server/Server.h b/src/app/server/Server.h index 6cfd21a4d99a4f..48d578ff9ba8f2 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -281,11 +281,11 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams this->certificateValidityPolicy = &sDefaultCertValidityPolicy; #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - ChipLogProgress(AppServer, "JEFFTEST: initializing subscription resumption storage..."); + ChipLogProgress(AppServer, "Initializing subscription resumption storage..."); ReturnErrorOnFailure(sSubscriptionResumptionStorage.Init(this->persistentStorageDelegate)); this->subscriptionResumptionStorage = &sSubscriptionResumptionStorage; #else - ChipLogProgress(AppServer, "JEFFTEST: subscription persistence not supported"); + ChipLogProgress(AppServer, "Subscription persistence not supported"); #endif return CHIP_NO_ERROR; diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index 816b2ffd4c176d..d759aa2e1c06af 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1363,7 +1363,7 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; * @def CHIP_CONFIG_PERSIST_SUBSCRIPTIONS * * @brief - * If asserted (1), suppress definition of the standard error formatting function + * Enable persistence and resumption of subscriptions on servers. * */ #ifndef CHIP_CONFIG_PERSIST_SUBSCRIPTIONS diff --git a/src/lib/support/ScopedBuffer.h b/src/lib/support/ScopedBuffer.h index f01816de1cde50..7493c0f4a715f4 100644 --- a/src/lib/support/ScopedBuffer.h +++ b/src/lib/support/ScopedBuffer.h @@ -153,9 +153,9 @@ class ScopedMemoryBuffer : public Impl::ScopedMemoryBufferBase return *this; } - ScopedMemoryBuffer & Alloc(size_t size) + ScopedMemoryBuffer & Alloc(size_t elementCount) { - Base::Alloc(size * sizeof(T)); + Base::Alloc(elementCount * sizeof(T)); return *this; } }; @@ -177,28 +177,31 @@ class ScopedMemoryBufferWithSize : public ScopedMemoryBuffer { if (this != &other) { - mSize = other.mSize; - other.mSize = 0; + mCount = other.mCount; + other.mCount = 0; } ScopedMemoryBuffer::operator=(std::move(other)); return *this; } - ~ScopedMemoryBufferWithSize() { mSize = 0; } + ~ScopedMemoryBufferWithSize() { mCount = 0; } // return the size in bytes - inline size_t AllocatedSize() const { return mSize; } + inline size_t AllocatedSize() const { return mCount * sizeof(T); } + + // return the count + inline size_t AllocatedCount() const { return mCount; } void Free() { - mSize = 0; + mCount = 0; ScopedMemoryBuffer::Free(); } T * Release() { T * buffer = ScopedMemoryBuffer::Release(); - mSize = 0; + mCount = 0; return buffer; } @@ -207,23 +210,23 @@ class ScopedMemoryBufferWithSize : public ScopedMemoryBuffer ScopedMemoryBuffer::Calloc(elementCount); if (this->Get() != nullptr) { - mSize = elementCount * sizeof(T); + mCount = elementCount; } return *this; } - ScopedMemoryBufferWithSize & Alloc(size_t size) + ScopedMemoryBufferWithSize & Alloc(size_t elementCount) { - ScopedMemoryBuffer::Alloc(size); + ScopedMemoryBuffer::Alloc(elementCount); if (this->Get() != nullptr) { - mSize = size * sizeof(T); + mCount = elementCount; } return *this; } private: - size_t mSize = 0; + size_t mCount = 0; }; } // namespace Platform From 02ebc9c1ec18574d0faf73480ebb6315ee5185f7 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 12 Jan 2023 11:42:33 -0800 Subject: [PATCH 10/41] Fix struct member order --- src/app/InteractionModelEngine.cpp | 1 - src/app/SubscriptionResumptionStorage.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 9073f9b83fad41..1aa2438715a136 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1594,7 +1594,6 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() { SubscriptionResumptionStorage::SubscriptionList subscriptions; - // std::vector subscriptions; err = mpSubscriptionResumptionStorage->FindByScopedNodeId(subscriberIndex.mNodes[i], subscriptions); ChipLogProgress( diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index e68eed294761d0..c67de3259eca2c 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -76,8 +76,8 @@ class SubscriptionResumptionStorage */ struct SubscriptionInfo { - FabricIndex mFabricIndex; NodeId mNodeId; + FabricIndex mFabricIndex; SubscriptionId mSubscriptionId; uint16_t mMinInterval; uint16_t mMaxInterval; From 65b17aff6b295cb0b4017b045260728a0b1331e4 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 12 Jan 2023 12:19:24 -0800 Subject: [PATCH 11/41] Fix one more struct member order --- src/app/SimpleSubscriptionResumptionStorage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index d39ca04eebd4d1..c1fcfd91ecc5a6 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -172,7 +172,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId TLV::TLVType subscriptionContainerType; ReturnErrorOnFailure(reader.EnterContainer(subscriptionContainerType)); - subscriptions.mSubscriptions[i] = { .mFabricIndex = node.GetFabricIndex(), .mNodeId = node.GetNodeId() }; + subscriptions.mSubscriptions[i] = { .mNodeId = node.GetNodeId(), .mFabricIndex = node.GetFabricIndex() }; // Subscription ID ReturnErrorOnFailure(reader.Next(kSubscriptionIdTag)); From 6bacbbd241414b91f61807c32c7d166bc91cd97b Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 12 Jan 2023 14:35:00 -0800 Subject: [PATCH 12/41] Fixed more stack buffer --- .../SimpleSubscriptionResumptionStorage.cpp | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index c1fcfd91ecc5a6..bccbb624defb13 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -51,9 +51,10 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveIndex(const SubscriptionInde return mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); } - std::array buf; - TLV::TLVWriter writer; - writer.Init(buf); + Platform::ScopedMemoryBuffer backingBuffer; + backingBuffer.Calloc(MaxIndexSize()); + + TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), MaxIndexSize()); TLV::TLVType arrayType; ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, arrayType)); @@ -72,7 +73,9 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveIndex(const SubscriptionInde const auto len = writer.GetLengthWritten(); VerifyOrReturnError(CanCastTo(len), CHIP_ERROR_BUFFER_TOO_SMALL); - ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), buf.data(), + writer.Finalize(backingBuffer); + + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), backingBuffer.Get(), static_cast(len))); return CHIP_NO_ERROR; @@ -80,18 +83,19 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveIndex(const SubscriptionInde CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & index) { - std::array buf; - uint16_t len = static_cast(buf.size()); + Platform::ScopedMemoryBuffer backingBuffer; + backingBuffer.Calloc(MaxIndexSize()); + + uint16_t len = static_cast(MaxIndexSize()); - if (mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), buf.data(), len) != + if (mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), backingBuffer.Get(), len) != CHIP_NO_ERROR) { index.mSize = 0; return CHIP_NO_ERROR; } - TLV::ContiguousBufferTLVReader reader; - reader.Init(buf.data(), len); + TLV::ScopedBufferTLVReader reader(std::move(backingBuffer), len); ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag())); TLV::TLVType arrayType; @@ -138,10 +142,10 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & in CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions) { Platform::ScopedMemoryBuffer backingBuffer; - backingBuffer.Calloc(MaxIndexSize()); + backingBuffer.Calloc(MaxStateSize()); VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY); - uint16_t len = static_cast(MaxIndexSize()); + uint16_t len = static_cast(MaxStateSize()); if (mStorage->SyncGetKeyValue(GetStorageKey(node).KeyName(), backingBuffer.Get(), len) != CHIP_NO_ERROR) { @@ -164,7 +168,6 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId subscriptions.mSize = count; - CHIP_ERROR err; for (size_t i = 0; i < count; i++) { ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); @@ -243,11 +246,6 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId ReturnErrorOnFailure(reader.ExitContainer(subscriptionContainerType)); } - if (err != CHIP_END_OF_TLV) - { - return err; - } - ReturnErrorOnFailure(reader.ExitContainer(arrayType)); ReturnErrorOnFailure(reader.VerifyEndOfContainer()); @@ -415,6 +413,8 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(const SubscriptionInfo & { subscriptions.mSubscriptions[i] = std::move(subscriptions.mSubscriptions[subscriptions.mSize]); } + + subscriptionsChanged = true; break; } } From 865a16942bbde2873be5a2f61b7fa094e0e33971 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 12 Jan 2023 14:56:12 -0800 Subject: [PATCH 13/41] Fix copy/paste bug --- src/app/SimpleSubscriptionResumptionStorage.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index bccbb624defb13..f48fcbc8e9f2e4 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -75,8 +75,8 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveIndex(const SubscriptionInde writer.Finalize(backingBuffer); - ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), backingBuffer.Get(), - static_cast(len))); + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), + backingBuffer.Get(), static_cast(len))); return CHIP_NO_ERROR; } @@ -283,7 +283,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNo ReturnErrorOnFailure( writer.Put(kPathCountTag, static_cast(subscriptions.mSubscriptions[i].mEventPaths.AllocatedCount()))); - for (size_t j = 0; j < subscriptions.mSubscriptions[i].mAttributePaths.AllocatedCount(); j++) + for (size_t j = 0; j < subscriptions.mSubscriptions[i].mEventPaths.AllocatedCount(); j++) { if (subscriptions.mSubscriptions[i].mEventPaths[j].mIsUrgentEvent) { From 3d35c8dc4847b33b736bbe8d6a94490a5817708f Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 12 Jan 2023 16:07:47 -0800 Subject: [PATCH 14/41] Moved SubscriptionList array to unique_ptr and dynamically allocated / off stack --- src/app/SubscriptionResumptionStorage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index c67de3259eca2c..27d00dcbe2e95d 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -92,7 +92,7 @@ class SubscriptionResumptionStorage struct SubscriptionList { size_t mSize; - SubscriptionInfo mSubscriptions[CHIP_IM_MAX_NUM_SUBSCRIPTIONS]; + std::unique_ptr mSubscriptions = std::make_unique(CHIP_IM_MAX_NUM_SUBSCRIPTIONS); }; /** From 1d5bed2e064cedf0483782d749c1089a5aae8568 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 12 Jan 2023 16:48:16 -0800 Subject: [PATCH 15/41] Moved SubscriptionIndex array to unique_ptr and dynamically allocated / off stack --- src/app/SubscriptionResumptionStorage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index 27d00dcbe2e95d..f75df4ee269829 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -101,7 +101,7 @@ class SubscriptionResumptionStorage struct SubscriptionIndex { size_t mSize; - ScopedNodeId mNodes[CHIP_IM_MAX_NUM_SUBSCRIPTIONS]; + std::unique_ptr mNodes = std::make_unique(CHIP_IM_MAX_NUM_SUBSCRIPTIONS); }; virtual ~SubscriptionResumptionStorage(){}; From c050d5b9e484e1db88ac2be16b7807f995ad91a3 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 12 Jan 2023 16:53:50 -0800 Subject: [PATCH 16/41] Fixed error condition checks --- src/app/InteractionModelEngine.cpp | 4 ++++ src/app/SimpleSubscriptionResumptionStorage.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 1aa2438715a136..2ba48a5a11d369 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1590,6 +1590,8 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() CHIP_ERROR err = mpSubscriptionResumptionStorage->LoadIndex(subscriberIndex); ChipLogProgress(InteractionModel, "%u subscriber nodes to resume.. (error %" CHIP_ERROR_FORMAT ")", static_cast(subscriberIndex.mSize), err.Format()); + ReturnErrorOnFailure(err); + for (size_t i = 0; i < subscriberIndex.mSize; i++) { SubscriptionResumptionStorage::SubscriptionList subscriptions; @@ -1600,6 +1602,8 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() InteractionModel, "\tNode " ChipLogFormatScopedNodeId ": Loaded %u subscriptions.. (error %" CHIP_ERROR_FORMAT ")", ChipLogValueScopedNodeId(subscriberIndex.mNodes[i]), static_cast(subscriptions.mSize), err.Format()); + ReturnErrorOnFailure(err); + for (size_t j = 0; j < subscriptions.mSize; j++) { SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = subscriptions.mSubscriptions[j]; diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index f48fcbc8e9f2e4..6c2fdea6da1f0f 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -88,10 +88,10 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & in uint16_t len = static_cast(MaxIndexSize()); + index.mSize = 0; if (mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), backingBuffer.Get(), len) != CHIP_NO_ERROR) { - index.mSize = 0; return CHIP_NO_ERROR; } From 843dfb96fe99167126b04fa8fd19f10d291556e3 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 12 Jan 2023 17:01:24 -0800 Subject: [PATCH 17/41] Fixed array size check --- src/app/SimpleSubscriptionResumptionStorage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 6c2fdea6da1f0f..11e5220b1b2359 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -105,7 +105,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & in CHIP_ERROR err; while ((err = reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())) == CHIP_NO_ERROR) { - if (count >= ArraySize(index.mNodes)) + if (count >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) { return CHIP_ERROR_NO_MEMORY; } From e442d5a9d26acb7a172f2587545c5be824e7e17e Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 12 Jan 2023 19:43:05 -0800 Subject: [PATCH 18/41] Addressed CI issues, and disabled subscription persistence and resumption for cc13x2_26x2 and CYW30739 --- .../SimpleSubscriptionResumptionStorage.cpp | 35 +++++++++---------- .../Infineon/CYW30739/CHIPPlatformConfig.h | 3 ++ src/platform/cc13x2_26x2/CHIPPlatformConfig.h | 3 ++ 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 11e5220b1b2359..b85395b17cd61f 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -202,7 +202,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId if (pathCount) { subscriptions.mSubscriptions[i].mAttributePaths.Calloc(pathCount); - for (uint8_t j = 0; j < pathCount; j++) + for (uint16_t j = 0; j < pathCount; j++) { ReturnErrorOnFailure(reader.Next(kEndpointIdTag)); ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mAttributePaths[j].mEndpointId)); @@ -224,7 +224,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId if (pathCount) { subscriptions.mSubscriptions[i].mEventPaths.Calloc(pathCount); - for (uint8_t j = 0; j < pathCount; j++) + for (uint16_t j = 0; j < pathCount; j++) { EventPathType eventPathType; ReturnErrorOnFailure(reader.Next(kPathTypeTag)); @@ -423,30 +423,29 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(const SubscriptionInfo & { if (subscriptions.mSize) { + ChipLogProgress(DataManagement, "JEFFTEST: - save smaller set"); return SaveSubscriptions(subscriptionNode, subscriptions); } - else + + // Remove node from index + SubscriptionIndex subscriptionIndex; + LoadIndex(subscriptionIndex); + for (size_t i = 0; i < subscriptionIndex.mSize; i++) { - // Remove node from index - SubscriptionIndex subscriptionIndex; - LoadIndex(subscriptionIndex); - for (size_t i = 0; i < subscriptionIndex.mSize; i++) + if (subscriptionNode == subscriptionIndex.mNodes[i]) { - if (subscriptionNode == subscriptionIndex.mNodes[i]) + subscriptionIndex.mSize--; + // if not last element, move last element here, essentially deleting this + if (i < subscriptionIndex.mSize) { - subscriptionIndex.mSize--; - // if not last element, move last element here, essentially deleting this - if (i < subscriptionIndex.mSize) - { - subscriptionIndex.mNodes[i] = std::move(subscriptionIndex.mNodes[subscriptionIndex.mSize]); - } - break; + subscriptionIndex.mNodes[i] = std::move(subscriptionIndex.mNodes[subscriptionIndex.mSize]); } + break; } - SaveIndex(subscriptionIndex); - - return mStorage->SyncDeleteKeyValue(GetStorageKey(subscriptionNode).KeyName()); } + SaveIndex(subscriptionIndex); + + return mStorage->SyncDeleteKeyValue(GetStorageKey(subscriptionNode).KeyName()); } return CHIP_NO_ERROR; diff --git a/src/platform/Infineon/CYW30739/CHIPPlatformConfig.h b/src/platform/Infineon/CYW30739/CHIPPlatformConfig.h index 12ee433af8c27d..c55fad0d87c408 100644 --- a/src/platform/Infineon/CYW30739/CHIPPlatformConfig.h +++ b/src/platform/Infineon/CYW30739/CHIPPlatformConfig.h @@ -27,3 +27,6 @@ #define CHIP_CONFIG_UNAUTHENTICATED_CONNECTION_POOL_SIZE 10 #define CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING 1 #define CHIP_DEVICE_CONFIG_ENABLE_THREAD_COMMISSIONABLE_DISCOVERY 1 + +// Disable subscription persistence and resumption until memory issues get sorted out +#define CHIP_CONFIG_PERSIST_SUBSCRIPTIONS 0 diff --git a/src/platform/cc13x2_26x2/CHIPPlatformConfig.h b/src/platform/cc13x2_26x2/CHIPPlatformConfig.h index 2a5981d4bbdea6..ca100ca9c498a9 100644 --- a/src/platform/cc13x2_26x2/CHIPPlatformConfig.h +++ b/src/platform/cc13x2_26x2/CHIPPlatformConfig.h @@ -68,3 +68,6 @@ #ifndef CHIP_CONFIG_MAX_FABRICS #define CHIP_CONFIG_MAX_FABRICS 5 #endif + +// Disable subscription persistence and resumption until memory issues get sorted out +#define CHIP_CONFIG_PERSIST_SUBSCRIPTIONS 0 From 2316be41d9775f0569b2b7f617f657aa869b3131 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Sat, 14 Jan 2023 21:20:40 -0800 Subject: [PATCH 19/41] Addressed PR review comments, including: - ReadHandler constructor side effect moved to separate function - SubscriptionList and SubscriptionIndex member initialization moved to runtome - Improved error handling - remove stored info on error - Changed for loop indices to more descriptive names - Disabled feature by default except Linux and Darwin for CI testing - Added operator[] getter with index to access SubscriptionList and SubscriptionIndex elements --- src/app/InteractionModelEngine.cpp | 27 +- src/app/ReadHandler.cpp | 29 +- src/app/ReadHandler.h | 17 +- .../SimpleSubscriptionResumptionStorage.cpp | 288 +++++++++++------- src/app/SimpleSubscriptionResumptionStorage.h | 2 + src/app/SubscriptionResumptionStorage.h | 8 +- src/app/server/Server.cpp | 14 +- src/app/server/Server.h | 9 +- src/lib/core/CHIPConfig.h | 2 +- src/platform/Darwin/CHIPPlatformConfig.h | 3 + .../Infineon/CYW30739/CHIPPlatformConfig.h | 3 - src/platform/Linux/CHIPPlatformConfig.h | 3 + src/platform/cc13x2_26x2/CHIPPlatformConfig.h | 3 - 13 files changed, 259 insertions(+), 149 deletions(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 2ba48a5a11d369..06f3014e21ccf8 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1588,39 +1588,40 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS SubscriptionResumptionStorage::SubscriptionIndex subscriberIndex; CHIP_ERROR err = mpSubscriptionResumptionStorage->LoadIndex(subscriberIndex); - ChipLogProgress(InteractionModel, "%u subscriber nodes to resume.. (error %" CHIP_ERROR_FORMAT ")", - static_cast(subscriberIndex.mSize), err.Format()); - ReturnErrorOnFailure(err); + ReturnLogErrorOnFailure(err); + ChipLogProgress(InteractionModel, "%u subscriber nodes to resume..", static_cast(subscriberIndex.mSize)); - for (size_t i = 0; i < subscriberIndex.mSize; i++) + for (size_t currentNodeIndex = 0; currentNodeIndex < subscriberIndex.mSize; currentNodeIndex++) { SubscriptionResumptionStorage::SubscriptionList subscriptions; - err = mpSubscriptionResumptionStorage->FindByScopedNodeId(subscriberIndex.mNodes[i], subscriptions); + err = mpSubscriptionResumptionStorage->FindByScopedNodeId(subscriberIndex.mNodes[currentNodeIndex], subscriptions); + ReturnLogErrorOnFailure(err); ChipLogProgress( - InteractionModel, "\tNode " ChipLogFormatScopedNodeId ": Loaded %u subscriptions.. (error %" CHIP_ERROR_FORMAT ")", - ChipLogValueScopedNodeId(subscriberIndex.mNodes[i]), static_cast(subscriptions.mSize), err.Format()); - - ReturnErrorOnFailure(err); - - for (size_t j = 0; j < subscriptions.mSize; j++) + InteractionModel, "\tNode " ChipLogFormatScopedNodeId ": Loaded %u subscriptions..", + ChipLogValueScopedNodeId(subscriberIndex.mNodes[currentNodeIndex]), static_cast(subscriptions.mSize)); + for (size_t currentSubscriptionIndex = 0; currentSubscriptionIndex < subscriptions.mSize; currentSubscriptionIndex++) { - SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = subscriptions.mSubscriptions[j]; + SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = subscriptions.mSubscriptions[currentSubscriptionIndex]; auto requestedAttributePathCount = subscriptionInfo.mAttributePaths.AllocatedCount(); auto requestedEventPathCount = subscriptionInfo.mEventPaths.AllocatedCount(); if (!EnsureResourceForSubscription(subscriptionInfo.mFabricIndex, requestedAttributePathCount, requestedEventPathCount)) { ChipLogProgress(InteractionModel, "no resource for Subscription resumption"); + mpSubscriptionResumptionStorage->Delete(subscriptionInfo); return CHIP_ERROR_NO_MEMORY; } - ReadHandler * handler = mReadHandlers.CreateObject(*this, subscriptionInfo); + ReadHandler * handler = mReadHandlers.CreateObject(*this); if (handler == nullptr) { ChipLogProgress(InteractionModel, "no resource for ReadHandler creation"); + mpSubscriptionResumptionStorage->Delete(subscriptionInfo); return CHIP_ERROR_NO_MEMORY; } + + handler->ResumeSubscription(mpCASESessionMgr, subscriptionInfo); } } #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 10cca368b442f1..aae5446c389ddc 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -62,17 +62,23 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeCon mSessionHandle.Grab(mExchangeCtx->GetSessionHandle()); } -ReadHandler::ReadHandler(ManagementCallback & apCallback, SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo) : +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS +ReadHandler::ReadHandler(ManagementCallback & apCallback) : mExchangeCtx(*this), mManagementCallback(apCallback), mOnConnectedCallback(HandleDeviceConnected, this), mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) { -#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS mInteractionType = InteractionType::Subscribe; + mFlags.ClearAll(); +} + +void ReadHandler::ResumeSubscription(CASESessionManager *caseSessionManager, SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo) +{ mSubscriptionId = subscriptionInfo.mSubscriptionId; mMinIntervalFloorSeconds = subscriptionInfo.mMinInterval; mMaxInterval = subscriptionInfo.mMaxInterval; SetStateFlag(ReadHandlerFlags::FabricFiltered, subscriptionInfo.mFabricFiltered); + // Move dynamically allocated attributes and events from the SubscriptionInfo struct into the object pool managed by the IM engine CHIP_ERROR err; for (size_t i = 0; i < subscriptionInfo.mAttributePaths.AllocatedCount(); i++) { @@ -95,21 +101,12 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback, SubscriptionResumption } } - // Ask IM engine to start case with - auto * caseSessionManager = InteractionModelEngine::GetInstance()->GetCASESessionManager(); - if (caseSessionManager) - { - ScopedNodeId peerNode = ScopedNodeId(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex); - caseSessionManager->FindOrEstablishSession(peerNode, &mOnConnectedCallback, &mOnConnectionFailureCallback); - } - else - { - // TODO: Investigate if need to consider if caseSessionManager does not exist - Close(); - } + // Ask IM engine to start CASE session with subscriber + ScopedNodeId peerNode = ScopedNodeId(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex); + caseSessionManager->FindOrEstablishSession(peerNode, &mOnConnectedCallback, &mOnConnectionFailureCallback); +} #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS -} ReadHandler::~ReadHandler() { @@ -371,7 +368,7 @@ void ReadHandler::OnResponseTimeout(Messaging::ExchangeContext * apExchangeConte ChipLogValueExchange(apExchangeContext)); #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS // TODO: Have a retry mechanism tied to wake interval for IC devices - Close(true); + CloseButKeepPersisted(); #else Close(); #endif diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 02d9e6c726d5ad..fb3281e766baee 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -166,14 +167,16 @@ class ReadHandler : public Messaging::ExchangeDelegate */ ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, InteractionType aInteractionType); +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS /** * - * Constructor for resuming a persisted subscription + * Constructor in preparation for resuming a persisted subscription * * The callback passed in has to outlive this handler object. * */ - ReadHandler(ManagementCallback & apCallback, SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo); + ReadHandler(ManagementCallback & apCallback); +#endif const ObjectList * GetAttributePathList() const { return mpAttributePathList; } const ObjectList * GetEventPathList() const { return mpEventPathList; } @@ -253,6 +256,15 @@ class ReadHandler : public Messaging::ExchangeDelegate */ void OnInitialRequest(System::PacketBufferHandle && aPayload); +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + /** + * + * Resume a persisted subscription + * + */ + void ResumeSubscription(CASESessionManager *caseSessionManager, SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo); +#endif + /** * Send ReportData to initiator * @@ -372,6 +384,7 @@ class ReadHandler : public Messaging::ExchangeDelegate * @param keepPersisted Keep the subscription persisted in storage for later resumption */ void Close(bool keepPersisted = false); + void CloseButKeepPersisted() { Close(true); } static void OnUnblockHoldReportCallback(System::Layer * apSystemLayer, void * apAppState); static void OnRefreshSubscribeTimerSyncCallback(System::Layer * apSystemLayer, void * apAppState); diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index b85395b17cd61f..f22efc628d8f4e 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -63,8 +63,8 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveIndex(const SubscriptionInde { TLV::TLVType innerType; ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, innerType)); - ReturnErrorOnFailure(writer.Put(kFabricIndexTag, index.mNodes[i].GetFabricIndex())); - ReturnErrorOnFailure(writer.Put(kPeerNodeIdTag, index.mNodes[i].GetNodeId())); + ReturnErrorOnFailure(writer.Put(kFabricIndexTag, index[i].GetFabricIndex())); + ReturnErrorOnFailure(writer.Put(kPeerNodeIdTag, index[i].GetNodeId())); ReturnErrorOnFailure(writer.EndContainer(innerType)); } @@ -81,10 +81,32 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveIndex(const SubscriptionInde return CHIP_NO_ERROR; } +#define DeleteIndexAndReturnLogErrorOnFailure(expr) \ + do \ + { \ + auto __err = (expr); \ + if (!::chip::ChipError::IsSuccess(__err)) \ + { \ +mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); \ +ChipLogError(DataManagement, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \ + return __err; \ + } \ + } while (false) + CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & index) +{ + return LoadIndex(index, 0); +} + +CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & index, size_t allocateExtraSpace) { Platform::ScopedMemoryBuffer backingBuffer; backingBuffer.Calloc(MaxIndexSize()); + if (backingBuffer.Get() == nullptr) + { + mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); + return CHIP_ERROR_NO_MEMORY; + } uint16_t len = static_cast(MaxIndexSize()); @@ -92,162 +114,196 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & in if (mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), backingBuffer.Get(), len) != CHIP_NO_ERROR) { + mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); return CHIP_NO_ERROR; } TLV::ScopedBufferTLVReader reader(std::move(backingBuffer), len); - ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag())); + DeleteIndexAndReturnLogErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag())); TLV::TLVType arrayType; - ReturnErrorOnFailure(reader.EnterContainer(arrayType)); + DeleteIndexAndReturnLogErrorOnFailure(reader.EnterContainer(arrayType)); - size_t count = 0; - CHIP_ERROR err; - while ((err = reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())) == CHIP_NO_ERROR) + size_t nodeCount; + DeleteIndexAndReturnLogErrorOnFailure(reader.CountRemainingInContainer(&nodeCount)); + if (nodeCount >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) { - if (count >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) - { - return CHIP_ERROR_NO_MEMORY; - } + mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); + return CHIP_ERROR_NO_MEMORY; + } + index.mNodes = std::unique_ptr(new(std::nothrow) ScopedNodeId[nodeCount + allocateExtraSpace]); + if (!index.mNodes) + { + mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); + return CHIP_ERROR_NO_MEMORY; + } + + index.mSize = nodeCount; + + for (size_t currentNodeIndex = 0; currentNodeIndex < nodeCount; currentNodeIndex++) + { + DeleteIndexAndReturnLogErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); TLV::TLVType containerType; - ReturnErrorOnFailure(reader.EnterContainer(containerType)); + DeleteIndexAndReturnLogErrorOnFailure(reader.EnterContainer(containerType)); FabricIndex fabricIndex; - ReturnErrorOnFailure(reader.Next(kFabricIndexTag)); - ReturnErrorOnFailure(reader.Get(fabricIndex)); + DeleteIndexAndReturnLogErrorOnFailure(reader.Next(kFabricIndexTag)); + DeleteIndexAndReturnLogErrorOnFailure(reader.Get(fabricIndex)); NodeId peerNodeId; - ReturnErrorOnFailure(reader.Next(kPeerNodeIdTag)); - ReturnErrorOnFailure(reader.Get(peerNodeId)); + DeleteIndexAndReturnLogErrorOnFailure(reader.Next(kPeerNodeIdTag)); + DeleteIndexAndReturnLogErrorOnFailure(reader.Get(peerNodeId)); - index.mNodes[count++] = ScopedNodeId(peerNodeId, fabricIndex); + index[currentNodeIndex] = ScopedNodeId(peerNodeId, fabricIndex); - ReturnErrorOnFailure(reader.ExitContainer(containerType)); + DeleteIndexAndReturnLogErrorOnFailure(reader.ExitContainer(containerType)); } - if (err != CHIP_END_OF_TLV) - { - return err; - } - - ReturnErrorOnFailure(reader.ExitContainer(arrayType)); - ReturnErrorOnFailure(reader.VerifyEndOfContainer()); - - index.mSize = count; + DeleteIndexAndReturnLogErrorOnFailure(reader.ExitContainer(arrayType)); + DeleteIndexAndReturnLogErrorOnFailure(reader.VerifyEndOfContainer()); return CHIP_NO_ERROR; } +#define DeleteSubscriptionsAndReturnLogErrorOnFailure(expr) \ + do \ + { \ + auto __err = (expr); \ + if (!::chip::ChipError::IsSuccess(__err)) \ + { \ +mStorage->SyncDeleteKeyValue(GetStorageKey(node).KeyName()); \ +ChipLogError(DataManagement, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \ + return __err; \ + } \ + } while (false) + CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions) +{ + return FindByScopedNodeId(node, subscriptions, 0); +} +CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions, size_t allocateExtraSpace) { Platform::ScopedMemoryBuffer backingBuffer; backingBuffer.Calloc(MaxStateSize()); - VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY); + if (backingBuffer.Get() == nullptr) + { + mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); + return CHIP_ERROR_NO_MEMORY; + } uint16_t len = static_cast(MaxStateSize()); if (mStorage->SyncGetKeyValue(GetStorageKey(node).KeyName(), backingBuffer.Get(), len) != CHIP_NO_ERROR) { subscriptions.mSize = 0; + mStorage->SyncDeleteKeyValue(GetStorageKey(node).KeyName()); return CHIP_NO_ERROR; } TLV::ScopedBufferTLVReader reader(std::move(backingBuffer), len); - ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag())); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag())); TLV::TLVType arrayType; - ReturnErrorOnFailure(reader.EnterContainer(arrayType)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.EnterContainer(arrayType)); - size_t count; - ReturnErrorOnFailure(reader.CountRemainingInContainer(&count)); - if (count >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) + size_t subscriptionCount; + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.CountRemainingInContainer(&subscriptionCount)); + if (subscriptionCount >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) { + mStorage->SyncDeleteKeyValue(GetStorageKey(node).KeyName()); return CHIP_ERROR_NO_MEMORY; } - subscriptions.mSize = count; + subscriptions.mSubscriptions = std::unique_ptr(new(std::nothrow) SubscriptionInfo[subscriptionCount + allocateExtraSpace]); + if (!subscriptions.mSubscriptions) + { + mStorage->SyncDeleteKeyValue(GetStorageKey(node).KeyName()); + return CHIP_ERROR_NO_MEMORY; + } - for (size_t i = 0; i < count; i++) + subscriptions.mSize = subscriptionCount; + + for (size_t currentSubscriptionIndex = 0; currentSubscriptionIndex < subscriptionCount; currentSubscriptionIndex++) { - ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); TLV::TLVType subscriptionContainerType; - ReturnErrorOnFailure(reader.EnterContainer(subscriptionContainerType)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.EnterContainer(subscriptionContainerType)); - subscriptions.mSubscriptions[i] = { .mNodeId = node.GetNodeId(), .mFabricIndex = node.GetFabricIndex() }; + subscriptions[currentSubscriptionIndex] = { .mNodeId = node.GetNodeId(), .mFabricIndex = node.GetFabricIndex() }; // Subscription ID - ReturnErrorOnFailure(reader.Next(kSubscriptionIdTag)); - ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mSubscriptionId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kSubscriptionIdTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mSubscriptionId)); // Min interval - ReturnErrorOnFailure(reader.Next(kMinIntervalTag)); - ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mMinInterval)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kMinIntervalTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mMinInterval)); // Max interval - ReturnErrorOnFailure(reader.Next(kMaxIntervalTag)); - ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mMaxInterval)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kMaxIntervalTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mMaxInterval)); // Fabric filtered boolean - ReturnErrorOnFailure(reader.Next(kFabricFilteredTag)); - ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mFabricFiltered)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kFabricFilteredTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mFabricFiltered)); // Attribute Paths uint16_t pathCount = 0; - ReturnErrorOnFailure(reader.Next(kPathCountTag)); - ReturnErrorOnFailure(reader.Get(pathCount)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kPathCountTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(pathCount)); - subscriptions.mSubscriptions[i].mAttributePaths = Platform::ScopedMemoryBufferWithSize(); + subscriptions[currentSubscriptionIndex].mAttributePaths = Platform::ScopedMemoryBufferWithSize(); if (pathCount) { - subscriptions.mSubscriptions[i].mAttributePaths.Calloc(pathCount); - for (uint16_t j = 0; j < pathCount; j++) + subscriptions[currentSubscriptionIndex].mAttributePaths.Calloc(pathCount); + for (uint16_t currentPathIndex = 0; currentPathIndex < pathCount; currentPathIndex++) { - ReturnErrorOnFailure(reader.Next(kEndpointIdTag)); - ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mAttributePaths[j].mEndpointId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kEndpointIdTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mEndpointId)); - ReturnErrorOnFailure(reader.Next(kClusterIdTag)); - ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mAttributePaths[j].mClusterId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kClusterIdTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mClusterId)); - ReturnErrorOnFailure(reader.Next(kAttributeIdTag)); - ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mAttributePaths[j].mAttributeId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kAttributeIdTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mAttributeId)); } } // Event Paths pathCount = 0; - ReturnErrorOnFailure(reader.Next(kPathCountTag)); - ReturnErrorOnFailure(reader.Get(pathCount)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kPathCountTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(pathCount)); - subscriptions.mSubscriptions[i].mEventPaths = Platform::ScopedMemoryBufferWithSize(); + subscriptions[currentSubscriptionIndex].mEventPaths = Platform::ScopedMemoryBufferWithSize(); if (pathCount) { - subscriptions.mSubscriptions[i].mEventPaths.Calloc(pathCount); - for (uint16_t j = 0; j < pathCount; j++) + subscriptions[currentSubscriptionIndex].mEventPaths.Calloc(pathCount); + for (uint16_t currentPathIndex = 0; currentPathIndex < pathCount; currentPathIndex++) { EventPathType eventPathType; - ReturnErrorOnFailure(reader.Next(kPathTypeTag)); - ReturnErrorOnFailure(reader.Get(eventPathType)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kPathTypeTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(eventPathType)); - subscriptions.mSubscriptions[i].mEventPaths[j].mIsUrgentEvent = (eventPathType == EventPathType::kUrgent); + subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mIsUrgentEvent = (eventPathType == EventPathType::kUrgent); - ReturnErrorOnFailure(reader.Next(kEndpointIdTag)); - ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mEventPaths[j].mEndpointId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kEndpointIdTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEndpointId)); - ReturnErrorOnFailure(reader.Next(kClusterIdTag)); - ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mEventPaths[j].mClusterId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kClusterIdTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mClusterId)); - ReturnErrorOnFailure(reader.Next(kEventIdTag)); - ReturnErrorOnFailure(reader.Get(subscriptions.mSubscriptions[i].mEventPaths[j].mEventId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kEventIdTag)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEventId)); } } - ReturnErrorOnFailure(reader.ExitContainer(subscriptionContainerType)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.ExitContainer(subscriptionContainerType)); } - ReturnErrorOnFailure(reader.ExitContainer(arrayType)); - ReturnErrorOnFailure(reader.VerifyEndOfContainer()); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.ExitContainer(arrayType)); + DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.VerifyEndOfContainer()); return CHIP_NO_ERROR; } @@ -263,29 +319,29 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNo TLV::TLVType arrayType; ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, arrayType)); - for (size_t i = 0; i < subscriptions.mSize; i++) + for (size_t currentSubscriptionIndex = 0; currentSubscriptionIndex < subscriptions.mSize; currentSubscriptionIndex++) { TLV::TLVType subscriptionContainerType; ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, subscriptionContainerType)); - ReturnErrorOnFailure(writer.Put(kSubscriptionIdTag, subscriptions.mSubscriptions[i].mSubscriptionId)); - ReturnErrorOnFailure(writer.Put(kMinIntervalTag, subscriptions.mSubscriptions[i].mMinInterval)); - ReturnErrorOnFailure(writer.Put(kMaxIntervalTag, subscriptions.mSubscriptions[i].mMaxInterval)); - ReturnErrorOnFailure(writer.Put(kFabricFilteredTag, subscriptions.mSubscriptions[i].mFabricFiltered)); + ReturnErrorOnFailure(writer.Put(kSubscriptionIdTag, subscriptions[currentSubscriptionIndex].mSubscriptionId)); + ReturnErrorOnFailure(writer.Put(kMinIntervalTag, subscriptions[currentSubscriptionIndex].mMinInterval)); + ReturnErrorOnFailure(writer.Put(kMaxIntervalTag, subscriptions[currentSubscriptionIndex].mMaxInterval)); + ReturnErrorOnFailure(writer.Put(kFabricFilteredTag, subscriptions[currentSubscriptionIndex].mFabricFiltered)); ReturnErrorOnFailure( - writer.Put(kPathCountTag, static_cast(subscriptions.mSubscriptions[i].mAttributePaths.AllocatedCount()))); - for (size_t j = 0; j < subscriptions.mSubscriptions[i].mAttributePaths.AllocatedCount(); j++) + writer.Put(kPathCountTag, static_cast(subscriptions[currentSubscriptionIndex].mAttributePaths.AllocatedCount()))); + for (size_t currentPathIndex = 0; currentPathIndex < subscriptions[currentSubscriptionIndex].mAttributePaths.AllocatedCount(); currentPathIndex++) { - ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptions.mSubscriptions[i].mAttributePaths[j].mEndpointId)); - ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptions.mSubscriptions[i].mAttributePaths[j].mClusterId)); - ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptions.mSubscriptions[i].mAttributePaths[j].mAttributeId)); + ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mEndpointId)); + ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mClusterId)); + ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mAttributeId)); } ReturnErrorOnFailure( - writer.Put(kPathCountTag, static_cast(subscriptions.mSubscriptions[i].mEventPaths.AllocatedCount()))); - for (size_t j = 0; j < subscriptions.mSubscriptions[i].mEventPaths.AllocatedCount(); j++) + writer.Put(kPathCountTag, static_cast(subscriptions[currentSubscriptionIndex].mEventPaths.AllocatedCount()))); + for (size_t currentPathIndex = 0; currentPathIndex < subscriptions[currentSubscriptionIndex].mEventPaths.AllocatedCount(); currentPathIndex++) { - if (subscriptions.mSubscriptions[i].mEventPaths[j].mIsUrgentEvent) + if (subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mIsUrgentEvent) { ReturnErrorOnFailure(writer.Put(kPathTypeTag, EventPathType::kUrgent)); } @@ -293,9 +349,9 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNo { ReturnErrorOnFailure(writer.Put(kPathTypeTag, EventPathType::kNonUrgent)); } - ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptions.mSubscriptions[i].mEventPaths[j].mEndpointId)); - ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptions.mSubscriptions[i].mEventPaths[j].mClusterId)); - ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptions.mSubscriptions[i].mEventPaths[j].mEventId)); + ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEndpointId)); + ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mClusterId)); + ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEventId)); } ReturnErrorOnFailure(writer.EndContainer(subscriptionContainerType)); @@ -318,11 +374,11 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip ScopedNodeId subscriptionNode = ScopedNodeId(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex); // Load index and update if fabric/node is new SubscriptionIndex subscriptionIndex; - LoadIndex(subscriptionIndex); + LoadIndex(subscriptionIndex, 1); bool nodeIsNew = true; for (size_t i = 0; i < subscriptionIndex.mSize; i++) { - if (subscriptionNode == subscriptionIndex.mNodes[i]) + if (subscriptionNode == subscriptionIndex[i]) { nodeIsNew = false; break; @@ -330,32 +386,51 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip } if (nodeIsNew) { + if (!subscriptionIndex.mSize) + { + subscriptionIndex.mNodes = std::unique_ptr(new(std::nothrow) ScopedNodeId[1]); + if (!subscriptionIndex.mNodes) + { + return CHIP_ERROR_NO_MEMORY; + } + } + if (subscriptionIndex.mSize == CHIP_IM_MAX_NUM_SUBSCRIPTIONS) { return CHIP_ERROR_NO_MEMORY; } - subscriptionIndex.mNodes[subscriptionIndex.mSize++] = subscriptionNode; + subscriptionIndex[subscriptionIndex.mSize++] = subscriptionNode; SaveIndex(subscriptionIndex); } // Load existing subscriptions for node, then combine and save state SubscriptionList subscriptions; - CHIP_ERROR err = FindByScopedNodeId(subscriptionNode, subscriptions); + CHIP_ERROR err = FindByScopedNodeId(subscriptionNode, subscriptions, 1); // ask to allocate 1 extra space for new subscription if (err != CHIP_NO_ERROR) { return err; } + // if this is the first subscription, allocate space for 1 + if (!subscriptions.mSize) + { + subscriptions.mSubscriptions = std::unique_ptr(new(std::nothrow) SubscriptionInfo[1]); + if (!subscriptions.mSubscriptions) + { + return CHIP_ERROR_NO_MEMORY; + } + } + // Sanity check for duplicate subscription and remove for (size_t i = 0; i < subscriptions.mSize; i++) { - if (subscriptionInfo.mSubscriptionId == subscriptions.mSubscriptions[i].mSubscriptionId) + if (subscriptionInfo.mSubscriptionId == subscriptions[i].mSubscriptionId) { subscriptions.mSize--; // if not last element, move last element here, essentially deleting this if (i < subscriptions.mSize) { - subscriptions.mSubscriptions[i] = std::move(subscriptions.mSubscriptions[subscriptions.mSize]); + subscriptions[i] = std::move(subscriptions[subscriptions.mSize]); } break; } @@ -366,13 +441,13 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip for (size_t i = 0; i < subscriptionIndex.mSize; i++) { // This node has already been loaded and counted - if (subscriptionNode == subscriptionIndex.mNodes[i]) + if (subscriptionNode == subscriptionIndex[i]) { continue; } SubscriptionList otherSubscriptions; - err = FindByScopedNodeId(subscriptionIndex.mNodes[i], otherSubscriptions); + err = FindByScopedNodeId(subscriptionIndex[i], otherSubscriptions); if (err == CHIP_NO_ERROR) { totalSubscriptions += otherSubscriptions.mSize; @@ -386,7 +461,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip } // Merge new subscription in and save - subscriptions.mSubscriptions[subscriptions.mSize++] = std::move(subscriptionInfo); + subscriptions[subscriptions.mSize++] = std::move(subscriptionInfo); return SaveSubscriptions(subscriptionNode, subscriptions); } @@ -405,13 +480,13 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(const SubscriptionInfo & bool subscriptionsChanged = false; for (size_t i = 0; i < subscriptions.mSize; i++) { - if (subscriptions.mSubscriptions[i].mSubscriptionId == subscriptionInfo.mSubscriptionId) + if (subscriptionInfo.mSubscriptionId == subscriptions[i].mSubscriptionId) { subscriptions.mSize--; // if not last element, move last element here, essentially deleting this if (i < subscriptions.mSize) { - subscriptions.mSubscriptions[i] = std::move(subscriptions.mSubscriptions[subscriptions.mSize]); + subscriptions[i] = std::move(subscriptions[subscriptions.mSize]); } subscriptionsChanged = true; @@ -423,7 +498,6 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(const SubscriptionInfo & { if (subscriptions.mSize) { - ChipLogProgress(DataManagement, "JEFFTEST: - save smaller set"); return SaveSubscriptions(subscriptionNode, subscriptions); } @@ -432,13 +506,13 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(const SubscriptionInfo & LoadIndex(subscriptionIndex); for (size_t i = 0; i < subscriptionIndex.mSize; i++) { - if (subscriptionNode == subscriptionIndex.mNodes[i]) + if (subscriptionNode == subscriptionIndex[i]) { subscriptionIndex.mSize--; // if not last element, move last element here, essentially deleting this if (i < subscriptionIndex.mSize) { - subscriptionIndex.mNodes[i] = std::move(subscriptionIndex.mNodes[subscriptionIndex.mSize]); + subscriptionIndex[i] = std::move(subscriptionIndex[subscriptionIndex.mSize]); } break; } @@ -460,9 +534,9 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteAll(FabricIndex fabricInde bool indexChanged = false; while (i < subscriptionIndex.mSize) { - if (subscriptionIndex.mNodes[i].GetFabricIndex() == fabricIndex) + if (subscriptionIndex[i].GetFabricIndex() == fabricIndex) { - mStorage->SyncDeleteKeyValue(GetStorageKey(subscriptionIndex.mNodes[i]).KeyName()); + mStorage->SyncDeleteKeyValue(GetStorageKey(subscriptionIndex[i]).KeyName()); // Update count and exit if exhausted all nodes --subscriptionIndex.mSize; @@ -472,7 +546,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteAll(FabricIndex fabricInde } // Move the last element into this hole and keep looping - subscriptionIndex.mNodes[i] = subscriptionIndex.mNodes[subscriptionIndex.mSize]; + subscriptionIndex[i] = subscriptionIndex[subscriptionIndex.mSize]; indexChanged = true; } else diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index e1fa22387e50b2..46df17c3d32b3e 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -59,6 +59,8 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage private: CHIP_ERROR SaveIndex(const SubscriptionIndex & index); CHIP_ERROR SaveSubscriptions(const ScopedNodeId & node, const SubscriptionList & subscriptions); + CHIP_ERROR LoadIndex(SubscriptionIndex & index, size_t allocateExtraSpace); + CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions, size_t allocateExtraSpace); static constexpr size_t MaxScopedNodeIdSize() { return TLV::EstimateStructOverhead(sizeof(NodeId), sizeof(FabricIndex)); } diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index f75df4ee269829..8d857f64d3fb3e 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -92,7 +92,9 @@ class SubscriptionResumptionStorage struct SubscriptionList { size_t mSize; - std::unique_ptr mSubscriptions = std::make_unique(CHIP_IM_MAX_NUM_SUBSCRIPTIONS); + std::unique_ptr mSubscriptions; + SubscriptionInfo &operator[](size_t index) { return mSubscriptions[index]; } + const SubscriptionInfo &operator[](size_t index) const { return mSubscriptions[index]; } }; /** @@ -101,7 +103,9 @@ class SubscriptionResumptionStorage struct SubscriptionIndex { size_t mSize; - std::unique_ptr mNodes = std::make_unique(CHIP_IM_MAX_NUM_SUBSCRIPTIONS); + std::unique_ptr mNodes; + ScopedNodeId &operator[](size_t index) { return mNodes[index]; } + const ScopedNodeId &operator[](size_t index) const { return mNodes[index]; } }; virtual ~SubscriptionResumptionStorage(){}; diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index 7701b8f5e6d8ff..449a987fef1363 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -354,7 +354,7 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) } #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - err = chip::app::InteractionModelEngine::GetInstance()->ResumeSubscriptions(); + ResumeSubscriptions(); #endif PlatformMgr().HandleServerStarted(); @@ -481,4 +481,16 @@ CHIP_ERROR Server::SendUserDirectedCommissioningRequest(chip::Transport::PeerAdd } #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT +void Server::ResumeSubscriptions() +{ +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + CHIP_ERROR err = chip::app::InteractionModelEngine::GetInstance()->ResumeSubscriptions(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Error when trying to resume subscriptions : %" CHIP_ERROR_FORMAT, + err.Format()); + } +#endif +} + } // namespace chip diff --git a/src/app/server/Server.h b/src/app/server/Server.h index 48d578ff9ba8f2..22046cad3ccc2a 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -378,6 +378,9 @@ class Server void InitFailSafe(); + /** + * @brief Called at Server::Init time to resume persisted subscriptions if the feature flag is enabled + */ void ResumeSubscriptions(); class GroupDataProviderListener final : public Credentials::GroupDataProvider::GroupListener @@ -443,6 +446,7 @@ class Server { (void) fabricTable; ClearCASEResumptionStateOnFabricChange(fabricIndex); + ClearSubscriptinoResumptionStateOnFabricChange(fabricIndex); Credentials::GroupDataProvider * groupDataProvider = mServer->GetGroupDataProvider(); if (groupDataProvider != nullptr) @@ -496,10 +500,13 @@ class Server "Warning, failed to delete session resumption state for fabric index 0x%x: %" CHIP_ERROR_FORMAT, static_cast(fabricIndex), err.Format()); } + } + void ClearSubscriptinoResumptionStateOnFabricChange(chip::FabricIndex fabricIndex) + { auto * subscriptionResumptionStorage = mServer->GetSubscriptionResumptionStorage(); VerifyOrReturn(subscriptionResumptionStorage != nullptr); - err = subscriptionResumptionStorage->DeleteAll(fabricIndex); + CHIP_ERROR err = subscriptionResumptionStorage->DeleteAll(fabricIndex); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index d759aa2e1c06af..3d2897f9cad190 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1367,5 +1367,5 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; * */ #ifndef CHIP_CONFIG_PERSIST_SUBSCRIPTIONS -#define CHIP_CONFIG_PERSIST_SUBSCRIPTIONS 1 +#define CHIP_CONFIG_PERSIST_SUBSCRIPTIONS 0 #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS diff --git a/src/platform/Darwin/CHIPPlatformConfig.h b/src/platform/Darwin/CHIPPlatformConfig.h index e3e1a99aebe5a1..892812acd89fd6 100644 --- a/src/platform/Darwin/CHIPPlatformConfig.h +++ b/src/platform/Darwin/CHIPPlatformConfig.h @@ -62,3 +62,6 @@ #ifndef CHIP_CONFIG_KVS_PATH #define CHIP_CONFIG_KVS_PATH "/tmp/chip_kvs" #endif // CHIP_CONFIG_KVS_PATH + +// Enable subscription persistence and resumption for CI +#define CHIP_CONFIG_PERSIST_SUBSCRIPTIONS 1 diff --git a/src/platform/Infineon/CYW30739/CHIPPlatformConfig.h b/src/platform/Infineon/CYW30739/CHIPPlatformConfig.h index c55fad0d87c408..12ee433af8c27d 100644 --- a/src/platform/Infineon/CYW30739/CHIPPlatformConfig.h +++ b/src/platform/Infineon/CYW30739/CHIPPlatformConfig.h @@ -27,6 +27,3 @@ #define CHIP_CONFIG_UNAUTHENTICATED_CONNECTION_POOL_SIZE 10 #define CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING 1 #define CHIP_DEVICE_CONFIG_ENABLE_THREAD_COMMISSIONABLE_DISCOVERY 1 - -// Disable subscription persistence and resumption until memory issues get sorted out -#define CHIP_CONFIG_PERSIST_SUBSCRIPTIONS 0 diff --git a/src/platform/Linux/CHIPPlatformConfig.h b/src/platform/Linux/CHIPPlatformConfig.h index 788fe9b80c75d9..0e36ed76144cd2 100644 --- a/src/platform/Linux/CHIPPlatformConfig.h +++ b/src/platform/Linux/CHIPPlatformConfig.h @@ -64,6 +64,9 @@ using CHIP_CONFIG_PERSISTED_STORAGE_KEY_TYPE = const char *; #define CHIP_CONFIG_BDX_MAX_NUM_TRANSFERS 1 #endif // CHIP_CONFIG_BDX_MAX_NUM_TRANSFERS +// Enable subscription persistence and resumption for CI +#define CHIP_CONFIG_PERSIST_SUBSCRIPTIONS 1 + // ==================== Security Configuration Overrides ==================== #ifndef CHIP_CONFIG_KVS_PATH diff --git a/src/platform/cc13x2_26x2/CHIPPlatformConfig.h b/src/platform/cc13x2_26x2/CHIPPlatformConfig.h index ca100ca9c498a9..2a5981d4bbdea6 100644 --- a/src/platform/cc13x2_26x2/CHIPPlatformConfig.h +++ b/src/platform/cc13x2_26x2/CHIPPlatformConfig.h @@ -68,6 +68,3 @@ #ifndef CHIP_CONFIG_MAX_FABRICS #define CHIP_CONFIG_MAX_FABRICS 5 #endif - -// Disable subscription persistence and resumption until memory issues get sorted out -#define CHIP_CONFIG_PERSIST_SUBSCRIPTIONS 0 From cd757e10e18862f7a2ef8e00369c557f95b16a52 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Sun, 15 Jan 2023 12:26:59 -0800 Subject: [PATCH 20/41] Restyled and ReadHandler include --- src/app/InteractionModelEngine.cpp | 13 ++-- src/app/ReadHandler.cpp | 8 +- src/app/ReadHandler.h | 4 +- .../SimpleSubscriptionResumptionStorage.cpp | 77 +++++++++++-------- src/app/SubscriptionResumptionStorage.h | 8 +- src/app/server/Server.cpp | 3 +- 6 files changed, 67 insertions(+), 46 deletions(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 06f3014e21ccf8..d526784770219e 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1598,14 +1598,15 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() err = mpSubscriptionResumptionStorage->FindByScopedNodeId(subscriberIndex.mNodes[currentNodeIndex], subscriptions); ReturnLogErrorOnFailure(err); - ChipLogProgress( - InteractionModel, "\tNode " ChipLogFormatScopedNodeId ": Loaded %u subscriptions..", - ChipLogValueScopedNodeId(subscriberIndex.mNodes[currentNodeIndex]), static_cast(subscriptions.mSize)); + ChipLogProgress(InteractionModel, "\tNode " ChipLogFormatScopedNodeId ": Loaded %u subscriptions..", + ChipLogValueScopedNodeId(subscriberIndex.mNodes[currentNodeIndex]), + static_cast(subscriptions.mSize)); for (size_t currentSubscriptionIndex = 0; currentSubscriptionIndex < subscriptions.mSize; currentSubscriptionIndex++) { - SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = subscriptions.mSubscriptions[currentSubscriptionIndex]; - auto requestedAttributePathCount = subscriptionInfo.mAttributePaths.AllocatedCount(); - auto requestedEventPathCount = subscriptionInfo.mEventPaths.AllocatedCount(); + SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = + subscriptions.mSubscriptions[currentSubscriptionIndex]; + auto requestedAttributePathCount = subscriptionInfo.mAttributePaths.AllocatedCount(); + auto requestedEventPathCount = subscriptionInfo.mEventPaths.AllocatedCount(); if (!EnsureResourceForSubscription(subscriptionInfo.mFabricIndex, requestedAttributePathCount, requestedEventPathCount)) { ChipLogProgress(InteractionModel, "no resource for Subscription resumption"); diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index aae5446c389ddc..babeca9e6f0635 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -67,18 +67,20 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback) : mExchangeCtx(*this), mManagementCallback(apCallback), mOnConnectedCallback(HandleDeviceConnected, this), mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) { - mInteractionType = InteractionType::Subscribe; + mInteractionType = InteractionType::Subscribe; mFlags.ClearAll(); } -void ReadHandler::ResumeSubscription(CASESessionManager *caseSessionManager, SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo) +void ReadHandler::ResumeSubscription(CASESessionManager * caseSessionManager, + SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo) { mSubscriptionId = subscriptionInfo.mSubscriptionId; mMinIntervalFloorSeconds = subscriptionInfo.mMinInterval; mMaxInterval = subscriptionInfo.mMaxInterval; SetStateFlag(ReadHandlerFlags::FabricFiltered, subscriptionInfo.mFabricFiltered); - // Move dynamically allocated attributes and events from the SubscriptionInfo struct into the object pool managed by the IM engine + // Move dynamically allocated attributes and events from the SubscriptionInfo struct into the object pool managed by the IM + // engine CHIP_ERROR err; for (size_t i = 0; i < subscriptionInfo.mAttributePaths.AllocatedCount(); i++) { diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index fb3281e766baee..04d2f58da7e79f 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -262,7 +263,8 @@ class ReadHandler : public Messaging::ExchangeDelegate * Resume a persisted subscription * */ - void ResumeSubscription(CASESessionManager *caseSessionManager, SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo); + void ResumeSubscription(CASESessionManager * caseSessionManager, + SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo); #endif /** diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index f22efc628d8f4e..2fbccaeedb2e25 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -81,14 +81,14 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveIndex(const SubscriptionInde return CHIP_NO_ERROR; } -#define DeleteIndexAndReturnLogErrorOnFailure(expr) \ +#define DeleteIndexAndReturnLogErrorOnFailure(expr) \ do \ { \ auto __err = (expr); \ if (!::chip::ChipError::IsSuccess(__err)) \ { \ -mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); \ -ChipLogError(DataManagement, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \ + mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); \ + ChipLogError(DataManagement, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \ return __err; \ } \ } while (false) @@ -132,7 +132,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & in return CHIP_ERROR_NO_MEMORY; } - index.mNodes = std::unique_ptr(new(std::nothrow) ScopedNodeId[nodeCount + allocateExtraSpace]); + index.mNodes = std::unique_ptr(new (std::nothrow) ScopedNodeId[nodeCount + allocateExtraSpace]); if (!index.mNodes) { mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); @@ -166,14 +166,14 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & in return CHIP_NO_ERROR; } -#define DeleteSubscriptionsAndReturnLogErrorOnFailure(expr) \ +#define DeleteSubscriptionsAndReturnLogErrorOnFailure(expr) \ do \ { \ auto __err = (expr); \ if (!::chip::ChipError::IsSuccess(__err)) \ { \ -mStorage->SyncDeleteKeyValue(GetStorageKey(node).KeyName()); \ -ChipLogError(DataManagement, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \ + mStorage->SyncDeleteKeyValue(GetStorageKey(node).KeyName()); \ + ChipLogError(DataManagement, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \ return __err; \ } \ } while (false) @@ -182,7 +182,8 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId { return FindByScopedNodeId(node, subscriptions, 0); } -CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions, size_t allocateExtraSpace) +CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions, + size_t allocateExtraSpace) { Platform::ScopedMemoryBuffer backingBuffer; backingBuffer.Calloc(MaxStateSize()); @@ -215,7 +216,8 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId return CHIP_ERROR_NO_MEMORY; } - subscriptions.mSubscriptions = std::unique_ptr(new(std::nothrow) SubscriptionInfo[subscriptionCount + allocateExtraSpace]); + subscriptions.mSubscriptions = + std::unique_ptr(new (std::nothrow) SubscriptionInfo[subscriptionCount + allocateExtraSpace]); if (!subscriptions.mSubscriptions) { mStorage->SyncDeleteKeyValue(GetStorageKey(node).KeyName()); @@ -261,13 +263,16 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId for (uint16_t currentPathIndex = 0; currentPathIndex < pathCount; currentPathIndex++) { DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kEndpointIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mEndpointId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure( + reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mEndpointId)); DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kClusterIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mClusterId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure( + reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mClusterId)); DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kAttributeIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mAttributeId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure( + reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mAttributeId)); } } @@ -286,16 +291,20 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kPathTypeTag)); DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(eventPathType)); - subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mIsUrgentEvent = (eventPathType == EventPathType::kUrgent); + subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mIsUrgentEvent = + (eventPathType == EventPathType::kUrgent); DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kEndpointIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEndpointId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure( + reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEndpointId)); DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kClusterIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mClusterId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure( + reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mClusterId)); DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kEventIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEventId)); + DeleteSubscriptionsAndReturnLogErrorOnFailure( + reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEventId)); } } @@ -328,18 +337,23 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNo ReturnErrorOnFailure(writer.Put(kMaxIntervalTag, subscriptions[currentSubscriptionIndex].mMaxInterval)); ReturnErrorOnFailure(writer.Put(kFabricFilteredTag, subscriptions[currentSubscriptionIndex].mFabricFiltered)); - ReturnErrorOnFailure( - writer.Put(kPathCountTag, static_cast(subscriptions[currentSubscriptionIndex].mAttributePaths.AllocatedCount()))); - for (size_t currentPathIndex = 0; currentPathIndex < subscriptions[currentSubscriptionIndex].mAttributePaths.AllocatedCount(); currentPathIndex++) + ReturnErrorOnFailure(writer.Put( + kPathCountTag, static_cast(subscriptions[currentSubscriptionIndex].mAttributePaths.AllocatedCount()))); + for (size_t currentPathIndex = 0; + currentPathIndex < subscriptions[currentSubscriptionIndex].mAttributePaths.AllocatedCount(); currentPathIndex++) { - ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mEndpointId)); - ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mClusterId)); - ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mAttributeId)); + ReturnErrorOnFailure( + writer.Put(kEndpointIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mEndpointId)); + ReturnErrorOnFailure( + writer.Put(kClusterIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mClusterId)); + ReturnErrorOnFailure(writer.Put( + kAttributeIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mAttributeId)); } ReturnErrorOnFailure( writer.Put(kPathCountTag, static_cast(subscriptions[currentSubscriptionIndex].mEventPaths.AllocatedCount()))); - for (size_t currentPathIndex = 0; currentPathIndex < subscriptions[currentSubscriptionIndex].mEventPaths.AllocatedCount(); currentPathIndex++) + for (size_t currentPathIndex = 0; currentPathIndex < subscriptions[currentSubscriptionIndex].mEventPaths.AllocatedCount(); + currentPathIndex++) { if (subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mIsUrgentEvent) { @@ -349,9 +363,12 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNo { ReturnErrorOnFailure(writer.Put(kPathTypeTag, EventPathType::kNonUrgent)); } - ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEndpointId)); - ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mClusterId)); - ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEventId)); + ReturnErrorOnFailure( + writer.Put(kEndpointIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEndpointId)); + ReturnErrorOnFailure( + writer.Put(kClusterIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mClusterId)); + ReturnErrorOnFailure( + writer.Put(kAttributeIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEventId)); } ReturnErrorOnFailure(writer.EndContainer(subscriptionContainerType)); @@ -388,7 +405,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip { if (!subscriptionIndex.mSize) { - subscriptionIndex.mNodes = std::unique_ptr(new(std::nothrow) ScopedNodeId[1]); + subscriptionIndex.mNodes = std::unique_ptr(new (std::nothrow) ScopedNodeId[1]); if (!subscriptionIndex.mNodes) { return CHIP_ERROR_NO_MEMORY; @@ -405,7 +422,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip // Load existing subscriptions for node, then combine and save state SubscriptionList subscriptions; - CHIP_ERROR err = FindByScopedNodeId(subscriptionNode, subscriptions, 1); // ask to allocate 1 extra space for new subscription + CHIP_ERROR err = FindByScopedNodeId(subscriptionNode, subscriptions, 1); // ask to allocate 1 extra space for new subscription if (err != CHIP_NO_ERROR) { return err; @@ -414,7 +431,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip // if this is the first subscription, allocate space for 1 if (!subscriptions.mSize) { - subscriptions.mSubscriptions = std::unique_ptr(new(std::nothrow) SubscriptionInfo[1]); + subscriptions.mSubscriptions = std::unique_ptr(new (std::nothrow) SubscriptionInfo[1]); if (!subscriptions.mSubscriptions) { return CHIP_ERROR_NO_MEMORY; @@ -547,7 +564,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteAll(FabricIndex fabricInde // Move the last element into this hole and keep looping subscriptionIndex[i] = subscriptionIndex[subscriptionIndex.mSize]; - indexChanged = true; + indexChanged = true; } else { diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index 8d857f64d3fb3e..378571585a924e 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -93,8 +93,8 @@ class SubscriptionResumptionStorage { size_t mSize; std::unique_ptr mSubscriptions; - SubscriptionInfo &operator[](size_t index) { return mSubscriptions[index]; } - const SubscriptionInfo &operator[](size_t index) const { return mSubscriptions[index]; } + SubscriptionInfo & operator[](size_t index) { return mSubscriptions[index]; } + const SubscriptionInfo & operator[](size_t index) const { return mSubscriptions[index]; } }; /** @@ -104,8 +104,8 @@ class SubscriptionResumptionStorage { size_t mSize; std::unique_ptr mNodes; - ScopedNodeId &operator[](size_t index) { return mNodes[index]; } - const ScopedNodeId &operator[](size_t index) const { return mNodes[index]; } + ScopedNodeId & operator[](size_t index) { return mNodes[index]; } + const ScopedNodeId & operator[](size_t index) const { return mNodes[index]; } }; virtual ~SubscriptionResumptionStorage(){}; diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index 449a987fef1363..fc6665deb552e5 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -487,8 +487,7 @@ void Server::ResumeSubscriptions() CHIP_ERROR err = chip::app::InteractionModelEngine::GetInstance()->ResumeSubscriptions(); if (err != CHIP_NO_ERROR) { - ChipLogError(AppServer, "Error when trying to resume subscriptions : %" CHIP_ERROR_FORMAT, - err.Format()); + ChipLogError(AppServer, "Error when trying to resume subscriptions : %" CHIP_ERROR_FORMAT, err.Format()); } #endif } From bda0ca9cbd1568772fb0b6be648841f421bdb142 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Sun, 15 Jan 2023 19:24:16 -0800 Subject: [PATCH 21/41] ReadHandler Callback fix --- src/app/ReadHandler.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 04d2f58da7e79f..a48b101082832e 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include From 3660392deda882058826d36ca504c4e3b6dea06b Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Mon, 16 Jan 2023 08:09:56 -0800 Subject: [PATCH 22/41] Fix ReadHandler Callback const argument --- src/app/ReadHandler.cpp | 3 ++- src/app/ReadHandler.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index babeca9e6f0635..1e95ec69c1152d 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -878,7 +878,8 @@ void ReadHandler::ClearStateFlag(ReadHandlerFlags aFlag) SetStateFlag(aFlag, false); } -void ReadHandler::HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle) +void ReadHandler::HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr, + const SessionHandle & sessionHandle) { ReadHandler * const _this = static_cast(context); diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index a48b101082832e..f2c604593e3df3 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -412,7 +412,8 @@ class ReadHandler : public Messaging::ExchangeDelegate void ClearStateFlag(ReadHandlerFlags aFlag); // Helpers for continuing the subscription resumption - static void HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle); + static void HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr, + const SessionHandle & sessionHandle); static void HandleDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR error); AttributePathExpandIterator mAttributePathExpandIterator = AttributePathExpandIterator(nullptr); From 8b6fe6afc9fb5f984e510be48ab9850ce302a923 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Tue, 17 Jan 2023 06:45:45 -0800 Subject: [PATCH 23/41] Explicitly disable subscription persistence and address review comments --- src/app/InteractionModelEngine.cpp | 2 +- src/app/ReadHandler.cpp | 4 ++-- src/app/ReadHandler.h | 9 ++++++--- src/app/server/Server.h | 4 ++-- src/platform/cc13x2_26x2/CHIPPlatformConfig.h | 2 ++ 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index d526784770219e..fabff710855bad 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1622,7 +1622,7 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() return CHIP_ERROR_NO_MEMORY; } - handler->ResumeSubscription(mpCASESessionMgr, subscriptionInfo); + handler->ResumeSubscription(*mpCASESessionMgr, subscriptionInfo); } } #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 1e95ec69c1152d..eeec00318f18a1 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -71,7 +71,7 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback) : mFlags.ClearAll(); } -void ReadHandler::ResumeSubscription(CASESessionManager * caseSessionManager, +void ReadHandler::ResumeSubscription(CASESessionManager & caseSessionManager, SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo) { mSubscriptionId = subscriptionInfo.mSubscriptionId; @@ -105,7 +105,7 @@ void ReadHandler::ResumeSubscription(CASESessionManager * caseSessionManager, // Ask IM engine to start CASE session with subscriber ScopedNodeId peerNode = ScopedNodeId(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex); - caseSessionManager->FindOrEstablishSession(peerNode, &mOnConnectedCallback, &mOnConnectionFailureCallback); + caseSessionManager.FindOrEstablishSession(peerNode, &mOnConnectedCallback, &mOnConnectionFailureCallback); } #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index f2c604593e3df3..f7a85cf686e4ab 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -261,10 +261,11 @@ class ReadHandler : public Messaging::ExchangeDelegate #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS /** * - * Resume a persisted subscription + * @brief Resume a persisted subscription * + * Used after ReadHandler(ManagementCallback & apCallback). This */ - void ResumeSubscription(CASESessionManager * caseSessionManager, + void ResumeSubscription(CASESessionManager & caseSessionManager, SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo); #endif @@ -384,7 +385,9 @@ class ReadHandler : public Messaging::ExchangeDelegate * Called internally to signal the completion of all work on this objecta and signal to a registered callback that it's * safe to release this object. * - * @param keepPersisted Keep the subscription persisted in storage for later resumption + * @param keepPersisted Keep the subscription persisted in storage for later resumption. Ignored if + * CHIP_CONFIG_PERSIST_SUBSCRIPTIONS not enabled + * */ void Close(bool keepPersisted = false); void CloseButKeepPersisted() { Close(true); } diff --git a/src/app/server/Server.h b/src/app/server/Server.h index 22046cad3ccc2a..61ab16a232c359 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -446,7 +446,7 @@ class Server { (void) fabricTable; ClearCASEResumptionStateOnFabricChange(fabricIndex); - ClearSubscriptinoResumptionStateOnFabricChange(fabricIndex); + ClearSubscriptionResumptionStateOnFabricChange(fabricIndex); Credentials::GroupDataProvider * groupDataProvider = mServer->GetGroupDataProvider(); if (groupDataProvider != nullptr) @@ -502,7 +502,7 @@ class Server } } - void ClearSubscriptinoResumptionStateOnFabricChange(chip::FabricIndex fabricIndex) + void ClearSubscriptionResumptionStateOnFabricChange(chip::FabricIndex fabricIndex) { auto * subscriptionResumptionStorage = mServer->GetSubscriptionResumptionStorage(); VerifyOrReturn(subscriptionResumptionStorage != nullptr); diff --git a/src/platform/cc13x2_26x2/CHIPPlatformConfig.h b/src/platform/cc13x2_26x2/CHIPPlatformConfig.h index 2a5981d4bbdea6..b60ebd2db042e9 100644 --- a/src/platform/cc13x2_26x2/CHIPPlatformConfig.h +++ b/src/platform/cc13x2_26x2/CHIPPlatformConfig.h @@ -68,3 +68,5 @@ #ifndef CHIP_CONFIG_MAX_FABRICS #define CHIP_CONFIG_MAX_FABRICS 5 #endif + +#define CHIP_CONFIG_PERSIST_SUBSCRIPTIONS 0 From dd6903d3b5d56c2d05e031bc57e08aaef6eb83b8 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Wed, 18 Jan 2023 09:03:40 -0800 Subject: [PATCH 24/41] Fixed priming reports on resumption --- src/app/ReadHandler.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index eeec00318f18a1..ca14e670ecdbbe 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -886,6 +886,13 @@ void ReadHandler::HandleDeviceConnected(void * context, Messaging::ExchangeManag _this->mSessionHandle.Grab(sessionHandle); _this->MoveToState(HandlerState::GeneratingReports); + + ObjectList * attributePath = _this->mpAttributePathList; + while (attributePath) + { + InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(attributePath->mValue); + attributePath = attributePath->mpNext; + } } void ReadHandler::HandleDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR err) From cd695189de3f37a87b0265146cd58e43882c50ab Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Wed, 18 Jan 2023 23:50:42 -0800 Subject: [PATCH 25/41] Revamp subscription storage into flat structure and add unit test --- src/app/InteractionModelEngine.cpp | 55 +- src/app/ReadHandler.cpp | 8 +- .../SimpleSubscriptionResumptionStorage.cpp | 674 +++++++----------- src/app/SimpleSubscriptionResumptionStorage.h | 115 ++- src/app/SubscriptionResumptionStorage.h | 64 +- src/app/tests/BUILD.gn | 1 + ...estSimpleSubscriptionResumptionStorage.cpp | 500 +++++++++++++ src/lib/core/CHIPConfig.h | 34 +- src/lib/support/DefaultStorageKeyAllocator.h | 7 +- 9 files changed, 898 insertions(+), 560 deletions(-) create mode 100644 src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index fabff710855bad..d42990e33a21ec 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1586,45 +1586,32 @@ void InteractionModelEngine::OnFabricRemoved(const FabricTable & fabricTable, Fa CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() { #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - SubscriptionResumptionStorage::SubscriptionIndex subscriberIndex; - CHIP_ERROR err = mpSubscriptionResumptionStorage->LoadIndex(subscriberIndex); - ReturnLogErrorOnFailure(err); - ChipLogProgress(InteractionModel, "%u subscriber nodes to resume..", static_cast(subscriberIndex.mSize)); - - for (size_t currentNodeIndex = 0; currentNodeIndex < subscriberIndex.mSize; currentNodeIndex++) + SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo; + auto * iterator = mpSubscriptionResumptionStorage->IterateSubscriptions(); + while (iterator->Next(subscriptionInfo)) { - SubscriptionResumptionStorage::SubscriptionList subscriptions; - - err = mpSubscriptionResumptionStorage->FindByScopedNodeId(subscriberIndex.mNodes[currentNodeIndex], subscriptions); - ReturnLogErrorOnFailure(err); - - ChipLogProgress(InteractionModel, "\tNode " ChipLogFormatScopedNodeId ": Loaded %u subscriptions..", - ChipLogValueScopedNodeId(subscriberIndex.mNodes[currentNodeIndex]), - static_cast(subscriptions.mSize)); - for (size_t currentSubscriptionIndex = 0; currentSubscriptionIndex < subscriptions.mSize; currentSubscriptionIndex++) + auto requestedAttributePathCount = subscriptionInfo.mAttributePaths.AllocatedCount(); + auto requestedEventPathCount = subscriptionInfo.mEventPaths.AllocatedCount(); + if (!EnsureResourceForSubscription(subscriptionInfo.mFabricIndex, requestedAttributePathCount, requestedEventPathCount)) { - SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = - subscriptions.mSubscriptions[currentSubscriptionIndex]; - auto requestedAttributePathCount = subscriptionInfo.mAttributePaths.AllocatedCount(); - auto requestedEventPathCount = subscriptionInfo.mEventPaths.AllocatedCount(); - if (!EnsureResourceForSubscription(subscriptionInfo.mFabricIndex, requestedAttributePathCount, requestedEventPathCount)) - { - ChipLogProgress(InteractionModel, "no resource for Subscription resumption"); - mpSubscriptionResumptionStorage->Delete(subscriptionInfo); - return CHIP_ERROR_NO_MEMORY; - } - - ReadHandler * handler = mReadHandlers.CreateObject(*this); - if (handler == nullptr) - { - ChipLogProgress(InteractionModel, "no resource for ReadHandler creation"); - mpSubscriptionResumptionStorage->Delete(subscriptionInfo); - return CHIP_ERROR_NO_MEMORY; - } + ChipLogProgress(InteractionModel, "no resource for Subscription resumption"); + mpSubscriptionResumptionStorage->Delete(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex, + subscriptionInfo.mSubscriptionId); + return CHIP_ERROR_NO_MEMORY; + } - handler->ResumeSubscription(*mpCASESessionMgr, subscriptionInfo); + ReadHandler * handler = mReadHandlers.CreateObject(*this); + if (handler == nullptr) + { + ChipLogProgress(InteractionModel, "no resource for ReadHandler creation"); + mpSubscriptionResumptionStorage->Delete(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex, + subscriptionInfo.mSubscriptionId); + return CHIP_ERROR_NO_MEMORY; } + + handler->ResumeSubscription(*mpCASESessionMgr, subscriptionInfo); } + iterator->Release(); #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS return CHIP_NO_ERROR; diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index ca14e670ecdbbe..f132bcf465f78c 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -144,12 +144,12 @@ void ReadHandler::Close(bool keepPersisted) auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage(); if (subscriptionResumptionStorage) { - SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = GetInitiatorNodeId(), - .mFabricIndex = GetAccessingFabricIndex(), - .mSubscriptionId = mSubscriptionId }; - subscriptionResumptionStorage->Delete(subscriptionInfo); + subscriptionResumptionStorage->Delete(GetInitiatorNodeId(), GetAccessingFabricIndex(), mSubscriptionId); } } +#else + // We disregard the keepPersisted API that is only used if the subscription persistence feature is enabled. + (void) keepPersisted; #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS MoveToState(HandlerState::AwaitingDestruction); mManagementCallback.OnDone(*this); diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 2fbccaeedb2e25..9a7e53bf5620e3 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -25,564 +25,414 @@ #include #include +#include #include #include namespace chip { namespace app { -constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kFabricIndexTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kPeerNodeIdTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kFabricIndexTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kSubscriptionIdTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kMinIntervalTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kMaxIntervalTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kFabricFilteredTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kPathCountTag; -constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kPathTypeTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventPathTypeTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEndpointIdTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kClusterIdTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kAttributeIdTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventIdTag; -CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveIndex(const SubscriptionIndex & index) +SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::SimpleSubscriptionInfoIterator( + SimpleSubscriptionResumptionStorage & storage) : + mStorage(storage) { - if (index.mSize == 0) - { - return mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); - } - - Platform::ScopedMemoryBuffer backingBuffer; - backingBuffer.Calloc(MaxIndexSize()); - - TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), MaxIndexSize()); - - TLV::TLVType arrayType; - ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, arrayType)); - - for (size_t i = 0; i < index.mSize; ++i) - { - TLV::TLVType innerType; - ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, innerType)); - ReturnErrorOnFailure(writer.Put(kFabricIndexTag, index[i].GetFabricIndex())); - ReturnErrorOnFailure(writer.Put(kPeerNodeIdTag, index[i].GetNodeId())); - ReturnErrorOnFailure(writer.EndContainer(innerType)); - } - - ReturnErrorOnFailure(writer.EndContainer(arrayType)); - - const auto len = writer.GetLengthWritten(); - VerifyOrReturnError(CanCastTo(len), CHIP_ERROR_BUFFER_TOO_SMALL); - - writer.Finalize(backingBuffer); - - ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), - backingBuffer.Get(), static_cast(len))); - - return CHIP_NO_ERROR; + mNextIndex = 0; + mMaxCount = mStorage.MaxCount(); } -#define DeleteIndexAndReturnLogErrorOnFailure(expr) \ - do \ - { \ - auto __err = (expr); \ - if (!::chip::ChipError::IsSuccess(__err)) \ - { \ - mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); \ - ChipLogError(DataManagement, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \ - return __err; \ - } \ - } while (false) - -CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & index) +size_t SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Count() { - return LoadIndex(index, 0); + return mStorage.Count(); } -CHIP_ERROR SimpleSubscriptionResumptionStorage::LoadIndex(SubscriptionIndex & index, size_t allocateExtraSpace) +bool SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Next(SubscriptionInfo & output) { - Platform::ScopedMemoryBuffer backingBuffer; - backingBuffer.Calloc(MaxIndexSize()); - if (backingBuffer.Get() == nullptr) + for (; mNextIndex < mMaxCount; mNextIndex++) { - mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); - return CHIP_ERROR_NO_MEMORY; - } - - uint16_t len = static_cast(MaxIndexSize()); + CHIP_ERROR err = mStorage.Load(mNextIndex, output); + if (err == CHIP_NO_ERROR) + { + // increment index for the next call + mNextIndex++; + return true; + } - index.mSize = 0; - if (mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName(), backingBuffer.Get(), len) != - CHIP_NO_ERROR) - { - mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); - return CHIP_NO_ERROR; + if (err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) + { + ChipLogError(DataManagement, "Failed to load subscription at index %u error %" CHIP_ERROR_FORMAT, + static_cast(mNextIndex), err.Format()); + mStorage.Delete(mNextIndex); + } } - TLV::ScopedBufferTLVReader reader(std::move(backingBuffer), len); + return false; +} - DeleteIndexAndReturnLogErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag())); - TLV::TLVType arrayType; - DeleteIndexAndReturnLogErrorOnFailure(reader.EnterContainer(arrayType)); +void SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Release() +{ + mStorage.mSubscriptionInfoIterators.ReleaseObject(this); +} - size_t nodeCount; - DeleteIndexAndReturnLogErrorOnFailure(reader.CountRemainingInContainer(&nodeCount)); - if (nodeCount >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) - { - mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); - return CHIP_ERROR_NO_MEMORY; - } +SubscriptionResumptionStorage::SubscriptionInfoIterator * SimpleSubscriptionResumptionStorage::IterateSubscriptions() +{ + return mSubscriptionInfoIterators.CreateObject(*this); +} - index.mNodes = std::unique_ptr(new (std::nothrow) ScopedNodeId[nodeCount + allocateExtraSpace]); - if (!index.mNodes) +size_t SimpleSubscriptionResumptionStorage::MaxCount() +{ + // Storage should try to hold the last known CHIP_IM_MAX_NUM_SUBSCRIPTIONS and + // use the larger of that value and the current CHIP_IM_MAX_NUM_SUBSCRIPTIONS + // value to make sure a reduced CHIP_IM_MAX_NUM_SUBSCRIPTIONS value would not + // cause storage leaks / orphaned persisted subscriptions + + uint32_t countMax = 0; + uint16_t len = sizeof(countMax); + CHIP_ERROR err = + mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMax, len); + if (err != CHIP_NO_ERROR) { - mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); - return CHIP_ERROR_NO_MEMORY; + return CHIP_IM_MAX_NUM_SUBSCRIPTIONS; } - index.mSize = nodeCount; + return max(static_cast(countMax), CHIP_IM_MAX_NUM_SUBSCRIPTIONS); +} - for (size_t currentNodeIndex = 0; currentNodeIndex < nodeCount; currentNodeIndex++) +size_t SimpleSubscriptionResumptionStorage::Count() +{ + size_t countMax = MaxCount(); + size_t subscriptionCount = 0; + for (size_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) { - DeleteIndexAndReturnLogErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); - TLV::TLVType containerType; - DeleteIndexAndReturnLogErrorOnFailure(reader.EnterContainer(containerType)); - - FabricIndex fabricIndex; - DeleteIndexAndReturnLogErrorOnFailure(reader.Next(kFabricIndexTag)); - DeleteIndexAndReturnLogErrorOnFailure(reader.Get(fabricIndex)); - - NodeId peerNodeId; - DeleteIndexAndReturnLogErrorOnFailure(reader.Next(kPeerNodeIdTag)); - DeleteIndexAndReturnLogErrorOnFailure(reader.Get(peerNodeId)); - - index[currentNodeIndex] = ScopedNodeId(peerNodeId, fabricIndex); - - DeleteIndexAndReturnLogErrorOnFailure(reader.ExitContainer(containerType)); + if (mStorage->SyncDoesKeyExist(DefaultStorageKeyAllocator::SubscriptionResumption(subscriptionIndex).KeyName())) + { + subscriptionCount++; + } } - DeleteIndexAndReturnLogErrorOnFailure(reader.ExitContainer(arrayType)); - DeleteIndexAndReturnLogErrorOnFailure(reader.VerifyEndOfContainer()); - - return CHIP_NO_ERROR; + return subscriptionCount; } -#define DeleteSubscriptionsAndReturnLogErrorOnFailure(expr) \ - do \ - { \ - auto __err = (expr); \ - if (!::chip::ChipError::IsSuccess(__err)) \ - { \ - mStorage->SyncDeleteKeyValue(GetStorageKey(node).KeyName()); \ - ChipLogError(DataManagement, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \ - return __err; \ - } \ - } while (false) - -CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions) +CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(size_t subscriptionIndex) { - return FindByScopedNodeId(node, subscriptions, 0); + return mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumption(subscriptionIndex).KeyName()); } -CHIP_ERROR SimpleSubscriptionResumptionStorage::FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions, - size_t allocateExtraSpace) + +CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(size_t subscriptionIndex, SubscriptionInfo & subscriptionInfo) { Platform::ScopedMemoryBuffer backingBuffer; - backingBuffer.Calloc(MaxStateSize()); + backingBuffer.Calloc(MaxSubscriptionSize()); if (backingBuffer.Get() == nullptr) { - mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionIndex().KeyName()); return CHIP_ERROR_NO_MEMORY; } - uint16_t len = static_cast(MaxStateSize()); - - if (mStorage->SyncGetKeyValue(GetStorageKey(node).KeyName(), backingBuffer.Get(), len) != CHIP_NO_ERROR) - { - subscriptions.mSize = 0; - mStorage->SyncDeleteKeyValue(GetStorageKey(node).KeyName()); - return CHIP_NO_ERROR; - } + uint16_t len = static_cast(MaxSubscriptionSize()); + ReturnErrorOnFailure(mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumption(subscriptionIndex).KeyName(), + backingBuffer.Get(), len)); TLV::ScopedBufferTLVReader reader(std::move(backingBuffer), len); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag())); - TLV::TLVType arrayType; - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.EnterContainer(arrayType)); - - size_t subscriptionCount; - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.CountRemainingInContainer(&subscriptionCount)); - if (subscriptionCount >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) - { - mStorage->SyncDeleteKeyValue(GetStorageKey(node).KeyName()); - return CHIP_ERROR_NO_MEMORY; - } - - subscriptions.mSubscriptions = - std::unique_ptr(new (std::nothrow) SubscriptionInfo[subscriptionCount + allocateExtraSpace]); - if (!subscriptions.mSubscriptions) - { - mStorage->SyncDeleteKeyValue(GetStorageKey(node).KeyName()); - return CHIP_ERROR_NO_MEMORY; - } - - subscriptions.mSize = subscriptionCount; + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); - for (size_t currentSubscriptionIndex = 0; currentSubscriptionIndex < subscriptionCount; currentSubscriptionIndex++) - { - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); + TLV::TLVType subscriptionContainerType; + ReturnErrorOnFailure(reader.EnterContainer(subscriptionContainerType)); - TLV::TLVType subscriptionContainerType; - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.EnterContainer(subscriptionContainerType)); + // Node ID + ReturnErrorOnFailure(reader.Next(kPeerNodeIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mNodeId)); - subscriptions[currentSubscriptionIndex] = { .mNodeId = node.GetNodeId(), .mFabricIndex = node.GetFabricIndex() }; + // Fabric index + ReturnErrorOnFailure(reader.Next(kFabricIndexTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mFabricIndex)); - // Subscription ID - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kSubscriptionIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mSubscriptionId)); + // Subscription ID + ReturnErrorOnFailure(reader.Next(kSubscriptionIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mSubscriptionId)); - // Min interval - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kMinIntervalTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mMinInterval)); + // Min interval + ReturnErrorOnFailure(reader.Next(kMinIntervalTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mMinInterval)); - // Max interval - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kMaxIntervalTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mMaxInterval)); + // Max interval + ReturnErrorOnFailure(reader.Next(kMaxIntervalTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mMaxInterval)); - // Fabric filtered boolean - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kFabricFilteredTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(subscriptions[currentSubscriptionIndex].mFabricFiltered)); + // Fabric filtered boolean + ReturnErrorOnFailure(reader.Next(kFabricFilteredTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mFabricFiltered)); - // Attribute Paths - uint16_t pathCount = 0; - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kPathCountTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(pathCount)); + // Attribute Paths + uint16_t pathCount = 0; + ReturnErrorOnFailure(reader.Next(kPathCountTag)); + ReturnErrorOnFailure(reader.Get(pathCount)); - subscriptions[currentSubscriptionIndex].mAttributePaths = Platform::ScopedMemoryBufferWithSize(); - if (pathCount) + subscriptionInfo.mAttributePaths = Platform::ScopedMemoryBufferWithSize(); + if (pathCount) + { + subscriptionInfo.mAttributePaths.Calloc(pathCount); + for (uint16_t pathIndex = 0; pathIndex < pathCount; pathIndex++) { - subscriptions[currentSubscriptionIndex].mAttributePaths.Calloc(pathCount); - for (uint16_t currentPathIndex = 0; currentPathIndex < pathCount; currentPathIndex++) - { - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kEndpointIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure( - reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mEndpointId)); + ReturnErrorOnFailure(reader.Next(kEndpointIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mAttributePaths[pathIndex].mEndpointId)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kClusterIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure( - reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mClusterId)); + ReturnErrorOnFailure(reader.Next(kClusterIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mAttributePaths[pathIndex].mClusterId)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kAttributeIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure( - reader.Get(subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mAttributeId)); - } + ReturnErrorOnFailure(reader.Next(kAttributeIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mAttributePaths[pathIndex].mAttributeId)); } + } - // Event Paths - pathCount = 0; - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kPathCountTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(pathCount)); + // Event Paths + pathCount = 0; + ReturnErrorOnFailure(reader.Next(kPathCountTag)); + ReturnErrorOnFailure(reader.Get(pathCount)); - subscriptions[currentSubscriptionIndex].mEventPaths = Platform::ScopedMemoryBufferWithSize(); - if (pathCount) + subscriptionInfo.mEventPaths = Platform::ScopedMemoryBufferWithSize(); + if (pathCount) + { + subscriptionInfo.mEventPaths.Calloc(pathCount); + for (uint16_t pathIndex = 0; pathIndex < pathCount; pathIndex++) { - subscriptions[currentSubscriptionIndex].mEventPaths.Calloc(pathCount); - for (uint16_t currentPathIndex = 0; currentPathIndex < pathCount; currentPathIndex++) - { - EventPathType eventPathType; - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kPathTypeTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Get(eventPathType)); + EventPathType eventPathType; + ReturnErrorOnFailure(reader.Next(kEventPathTypeTag)); + ReturnErrorOnFailure(reader.Get(eventPathType)); - subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mIsUrgentEvent = - (eventPathType == EventPathType::kUrgent); + subscriptionInfo.mEventPaths[pathIndex].mIsUrgentEvent = (eventPathType == EventPathType::kUrgent); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kEndpointIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure( - reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEndpointId)); + ReturnErrorOnFailure(reader.Next(kEndpointIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mEventPaths[pathIndex].mEndpointId)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kClusterIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure( - reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mClusterId)); + ReturnErrorOnFailure(reader.Next(kClusterIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mEventPaths[pathIndex].mClusterId)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.Next(kEventIdTag)); - DeleteSubscriptionsAndReturnLogErrorOnFailure( - reader.Get(subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEventId)); - } + ReturnErrorOnFailure(reader.Next(kEventIdTag)); + ReturnErrorOnFailure(reader.Get(subscriptionInfo.mEventPaths[pathIndex].mEventId)); } - - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.ExitContainer(subscriptionContainerType)); } - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.ExitContainer(arrayType)); - DeleteSubscriptionsAndReturnLogErrorOnFailure(reader.VerifyEndOfContainer()); + ReturnErrorOnFailure(reader.ExitContainer(subscriptionContainerType)); return CHIP_NO_ERROR; } -CHIP_ERROR SimpleSubscriptionResumptionStorage::SaveSubscriptions(const ScopedNodeId & node, const SubscriptionList & subscriptions) +CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(TLV::TLVWriter & writer, SubscriptionInfo & subscriptionInfo) { - Platform::ScopedMemoryBuffer backingBuffer; - backingBuffer.Calloc(MaxIndexSize()); - VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY); - - TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), MaxIndexSize()); - - TLV::TLVType arrayType; - ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, arrayType)); + TLV::TLVType subscriptionContainerType; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, subscriptionContainerType)); + ReturnErrorOnFailure(writer.Put(kPeerNodeIdTag, subscriptionInfo.mNodeId)); + ReturnErrorOnFailure(writer.Put(kFabricIndexTag, subscriptionInfo.mFabricIndex)); + ReturnErrorOnFailure(writer.Put(kSubscriptionIdTag, subscriptionInfo.mSubscriptionId)); + ReturnErrorOnFailure(writer.Put(kMinIntervalTag, subscriptionInfo.mMinInterval)); + ReturnErrorOnFailure(writer.Put(kMaxIntervalTag, subscriptionInfo.mMaxInterval)); + ReturnErrorOnFailure(writer.Put(kFabricFilteredTag, subscriptionInfo.mFabricFiltered)); + + ReturnErrorOnFailure(writer.Put(kPathCountTag, static_cast(subscriptionInfo.mAttributePaths.AllocatedCount()))); + for (size_t currentPathIndex = 0; currentPathIndex < subscriptionInfo.mAttributePaths.AllocatedCount(); currentPathIndex++) + { + ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptionInfo.mAttributePaths[currentPathIndex].mEndpointId)); + ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptionInfo.mAttributePaths[currentPathIndex].mClusterId)); + ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptionInfo.mAttributePaths[currentPathIndex].mAttributeId)); + } - for (size_t currentSubscriptionIndex = 0; currentSubscriptionIndex < subscriptions.mSize; currentSubscriptionIndex++) + ReturnErrorOnFailure(writer.Put(kPathCountTag, static_cast(subscriptionInfo.mEventPaths.AllocatedCount()))); + for (size_t currentPathIndex = 0; currentPathIndex < subscriptionInfo.mEventPaths.AllocatedCount(); currentPathIndex++) { - TLV::TLVType subscriptionContainerType; - ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, subscriptionContainerType)); - ReturnErrorOnFailure(writer.Put(kSubscriptionIdTag, subscriptions[currentSubscriptionIndex].mSubscriptionId)); - ReturnErrorOnFailure(writer.Put(kMinIntervalTag, subscriptions[currentSubscriptionIndex].mMinInterval)); - ReturnErrorOnFailure(writer.Put(kMaxIntervalTag, subscriptions[currentSubscriptionIndex].mMaxInterval)); - ReturnErrorOnFailure(writer.Put(kFabricFilteredTag, subscriptions[currentSubscriptionIndex].mFabricFiltered)); - - ReturnErrorOnFailure(writer.Put( - kPathCountTag, static_cast(subscriptions[currentSubscriptionIndex].mAttributePaths.AllocatedCount()))); - for (size_t currentPathIndex = 0; - currentPathIndex < subscriptions[currentSubscriptionIndex].mAttributePaths.AllocatedCount(); currentPathIndex++) + if (subscriptionInfo.mEventPaths[currentPathIndex].mIsUrgentEvent) { - ReturnErrorOnFailure( - writer.Put(kEndpointIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mEndpointId)); - ReturnErrorOnFailure( - writer.Put(kClusterIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mClusterId)); - ReturnErrorOnFailure(writer.Put( - kAttributeIdTag, subscriptions[currentSubscriptionIndex].mAttributePaths[currentPathIndex].mAttributeId)); + ReturnErrorOnFailure(writer.Put(kEventPathTypeTag, EventPathType::kUrgent)); } - - ReturnErrorOnFailure( - writer.Put(kPathCountTag, static_cast(subscriptions[currentSubscriptionIndex].mEventPaths.AllocatedCount()))); - for (size_t currentPathIndex = 0; currentPathIndex < subscriptions[currentSubscriptionIndex].mEventPaths.AllocatedCount(); - currentPathIndex++) + else { - if (subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mIsUrgentEvent) - { - ReturnErrorOnFailure(writer.Put(kPathTypeTag, EventPathType::kUrgent)); - } - else - { - ReturnErrorOnFailure(writer.Put(kPathTypeTag, EventPathType::kNonUrgent)); - } - ReturnErrorOnFailure( - writer.Put(kEndpointIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEndpointId)); - ReturnErrorOnFailure( - writer.Put(kClusterIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mClusterId)); - ReturnErrorOnFailure( - writer.Put(kAttributeIdTag, subscriptions[currentSubscriptionIndex].mEventPaths[currentPathIndex].mEventId)); + ReturnErrorOnFailure(writer.Put(kEventPathTypeTag, EventPathType::kNonUrgent)); } - - ReturnErrorOnFailure(writer.EndContainer(subscriptionContainerType)); + ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptionInfo.mEventPaths[currentPathIndex].mEndpointId)); + ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptionInfo.mEventPaths[currentPathIndex].mClusterId)); + ReturnErrorOnFailure(writer.Put(kEventIdTag, subscriptionInfo.mEventPaths[currentPathIndex].mEventId)); } - ReturnErrorOnFailure(writer.EndContainer(arrayType)); - - const auto len = writer.GetLengthWritten(); - VerifyOrReturnError(CanCastTo(len), CHIP_ERROR_BUFFER_TOO_SMALL); - - writer.Finalize(backingBuffer); - - ReturnErrorOnFailure(mStorage->SyncSetKeyValue(GetStorageKey(node).KeyName(), backingBuffer.Get(), static_cast(len))); + ReturnErrorOnFailure(writer.EndContainer(subscriptionContainerType)); return CHIP_NO_ERROR; } CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscriptionInfo) { - ScopedNodeId subscriptionNode = ScopedNodeId(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex); - // Load index and update if fabric/node is new - SubscriptionIndex subscriptionIndex; - LoadIndex(subscriptionIndex, 1); - bool nodeIsNew = true; - for (size_t i = 0; i < subscriptionIndex.mSize; i++) + // Ensure this version of CHIP_IM_MAX_NUM_SUBSCRIPTIONS is recorded on Save + size_t countMax = MaxCount(); + uint32_t countMaxToSave = static_cast(std::max(countMax, CHIP_IM_MAX_NUM_SUBSCRIPTIONS)); + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), + &countMaxToSave, static_cast(sizeof(uint32_t)))); + + // Find empty index or duplicate if exists + size_t subscriptionIndex; + size_t firstEmptySubscriptionIndex = countMax; // initialize to out of bounds as "not set" + size_t count = 0; + for (subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) { - if (subscriptionNode == subscriptionIndex[i]) + SubscriptionInfo currentSubscriptionInfo; + CHIP_ERROR err = Load(subscriptionIndex, currentSubscriptionInfo); + + // if empty and firstEmptySubscriptionIndex isn't set yet, then mark empty spot + if ((firstEmptySubscriptionIndex == countMax) && (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)) { - nodeIsNew = false; - break; + firstEmptySubscriptionIndex = subscriptionIndex; } - } - if (nodeIsNew) - { - if (!subscriptionIndex.mSize) + + // delete duplicate + if (err == CHIP_NO_ERROR) { - subscriptionIndex.mNodes = std::unique_ptr(new (std::nothrow) ScopedNodeId[1]); - if (!subscriptionIndex.mNodes) + if ((subscriptionInfo.mNodeId == currentSubscriptionInfo.mNodeId) && + (subscriptionInfo.mFabricIndex == currentSubscriptionInfo.mFabricIndex) && + (subscriptionInfo.mSubscriptionId == currentSubscriptionInfo.mSubscriptionId)) { - return CHIP_ERROR_NO_MEMORY; + Delete(subscriptionIndex); + // if duplicate is the first empty spot, then also set it + if (firstEmptySubscriptionIndex == countMax) + { + firstEmptySubscriptionIndex = subscriptionIndex; + } + } + else + { + count++; } } - - if (subscriptionIndex.mSize == CHIP_IM_MAX_NUM_SUBSCRIPTIONS) - { - return CHIP_ERROR_NO_MEMORY; - } - subscriptionIndex[subscriptionIndex.mSize++] = subscriptionNode; - SaveIndex(subscriptionIndex); } - // Load existing subscriptions for node, then combine and save state - SubscriptionList subscriptions; - CHIP_ERROR err = FindByScopedNodeId(subscriptionNode, subscriptions, 1); // ask to allocate 1 extra space for new subscription - if (err != CHIP_NO_ERROR) + // Fail if already have more persisted subscriptions than currently allowed + if (count >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) { - return err; + return CHIP_ERROR_NO_MEMORY; } - // if this is the first subscription, allocate space for 1 - if (!subscriptions.mSize) + // Fail if no empty space + if (firstEmptySubscriptionIndex == countMax) { - subscriptions.mSubscriptions = std::unique_ptr(new (std::nothrow) SubscriptionInfo[1]); - if (!subscriptions.mSubscriptions) - { - return CHIP_ERROR_NO_MEMORY; - } + return CHIP_ERROR_NO_MEMORY; } - // Sanity check for duplicate subscription and remove - for (size_t i = 0; i < subscriptions.mSize; i++) + // Now construct subscription state and save + Platform::ScopedMemoryBuffer backingBuffer; + backingBuffer.Calloc(MaxSubscriptionSize()); + if (backingBuffer.Get() == nullptr) { - if (subscriptionInfo.mSubscriptionId == subscriptions[i].mSubscriptionId) - { - subscriptions.mSize--; - // if not last element, move last element here, essentially deleting this - if (i < subscriptions.mSize) - { - subscriptions[i] = std::move(subscriptions[subscriptions.mSize]); - } - break; - } + return CHIP_ERROR_NO_MEMORY; } - // Sanity check this will not go over fabric limit - count - size_t totalSubscriptions = subscriptions.mSize; - for (size_t i = 0; i < subscriptionIndex.mSize; i++) - { - // This node has already been loaded and counted - if (subscriptionNode == subscriptionIndex[i]) - { - continue; - } + TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), MaxSubscriptionSize()); - SubscriptionList otherSubscriptions; - err = FindByScopedNodeId(subscriptionIndex[i], otherSubscriptions); - if (err == CHIP_NO_ERROR) - { - totalSubscriptions += otherSubscriptions.mSize; - } - } - // Not using CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC per explanation - // in MaxStateSize() header comment block - if (totalSubscriptions == CHIP_IM_MAX_NUM_SUBSCRIPTIONS) - { - return CHIP_ERROR_NO_MEMORY; - } + ReturnErrorOnFailure(Save(writer, subscriptionInfo)); + + const auto len = writer.GetLengthWritten(); + VerifyOrReturnError(CanCastTo(len), CHIP_ERROR_BUFFER_TOO_SMALL); - // Merge new subscription in and save - subscriptions[subscriptions.mSize++] = std::move(subscriptionInfo); - return SaveSubscriptions(subscriptionNode, subscriptions); + writer.Finalize(backingBuffer); + + ReturnErrorOnFailure( + mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumption(firstEmptySubscriptionIndex).KeyName(), + backingBuffer.Get(), static_cast(len))); + + return CHIP_NO_ERROR; } -CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(const SubscriptionInfo & subscriptionInfo) +CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(NodeId nodeId, FabricIndex fabricIndex, SubscriptionId subscriptionId) { - ScopedNodeId subscriptionNode = ScopedNodeId(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex); - // load existing subscriptions, then search for subscription - SubscriptionList subscriptions; - CHIP_ERROR err = FindByScopedNodeId(subscriptionNode, subscriptions); + CHIP_ERROR deleteErr = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; + size_t countMax = MaxCount(); - if (err != CHIP_NO_ERROR) + size_t count = 0; + for (size_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) { - return err; - } + SubscriptionInfo subscriptionInfo; + CHIP_ERROR err = Load(subscriptionIndex, subscriptionInfo); - bool subscriptionsChanged = false; - for (size_t i = 0; i < subscriptions.mSize; i++) - { - if (subscriptionInfo.mSubscriptionId == subscriptions[i].mSubscriptionId) + // delete duplicate + if (err == CHIP_NO_ERROR) { - subscriptions.mSize--; - // if not last element, move last element here, essentially deleting this - if (i < subscriptions.mSize) + if ((nodeId == subscriptionInfo.mNodeId) && (fabricIndex == subscriptionInfo.mFabricIndex) && + (subscriptionId == subscriptionInfo.mSubscriptionId)) { - subscriptions[i] = std::move(subscriptions[subscriptions.mSize]); + deleteErr = Delete(subscriptionIndex); + } + else + { + count++; } - - subscriptionsChanged = true; - break; } } - if (subscriptionsChanged) + // if there are no persisted subscriptions, the MaxCount can also be deleted + if (count == 0) { - if (subscriptions.mSize) - { - return SaveSubscriptions(subscriptionNode, subscriptions); - } + DeleteMaxCount(); + } - // Remove node from index - SubscriptionIndex subscriptionIndex; - LoadIndex(subscriptionIndex); - for (size_t i = 0; i < subscriptionIndex.mSize; i++) - { - if (subscriptionNode == subscriptionIndex[i]) - { - subscriptionIndex.mSize--; - // if not last element, move last element here, essentially deleting this - if (i < subscriptionIndex.mSize) - { - subscriptionIndex[i] = std::move(subscriptionIndex[subscriptionIndex.mSize]); - } - break; - } - } - SaveIndex(subscriptionIndex); + ChipLogProgress(InteractionModel, "JEFFTEST: Delete final count %zu", count); - return mStorage->SyncDeleteKeyValue(GetStorageKey(subscriptionNode).KeyName()); - } + return deleteErr; +} - return CHIP_NO_ERROR; +CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteMaxCount() +{ + return mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName()); } CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteAll(FabricIndex fabricIndex) { - SubscriptionIndex subscriptionIndex; - LoadIndex(subscriptionIndex); + CHIP_ERROR deleteErr = CHIP_NO_ERROR; + size_t countMax = MaxCount(); - size_t i = 0; - bool indexChanged = false; - while (i < subscriptionIndex.mSize) + size_t count = 0; + for (size_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) { - if (subscriptionIndex[i].GetFabricIndex() == fabricIndex) - { - mStorage->SyncDeleteKeyValue(GetStorageKey(subscriptionIndex[i]).KeyName()); + SubscriptionInfo subscriptionInfo; + CHIP_ERROR err = Load(subscriptionIndex, subscriptionInfo); - // Update count and exit if exhausted all nodes - --subscriptionIndex.mSize; - if (i == subscriptionIndex.mSize) + if (err == CHIP_NO_ERROR) + { + if (fabricIndex == subscriptionInfo.mFabricIndex) { - break; + err = Delete(subscriptionIndex); + if ((err != CHIP_NO_ERROR) && (err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)) + { + deleteErr = err; + } + } + else + { + count++; } - - // Move the last element into this hole and keep looping - subscriptionIndex[i] = subscriptionIndex[subscriptionIndex.mSize]; - indexChanged = true; - } - else - { - i++; } } - if (indexChanged) + // if there are no persisted subscriptions, the MaxCount can also be deleted + if (count == 0) { - SaveIndex(subscriptionIndex); + CHIP_ERROR err = DeleteMaxCount(); + + if ((err != CHIP_NO_ERROR) && (err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)) + { + deleteErr = err; + } } - return CHIP_NO_ERROR; -} + ChipLogProgress(InteractionModel, "JEFFTEST: DeleteAll final count %zu", count); -StorageKeyName SimpleSubscriptionResumptionStorage::GetStorageKey(const ScopedNodeId & node) -{ - return DefaultStorageKeyAllocator::FabricSubscription(node.GetFabricIndex(), node.GetNodeId()); + return deleteErr; } } // namespace app diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index 46df17c3d32b3e..c7c4ee0ce092b0 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -27,6 +27,7 @@ #include #include #include +#include namespace chip { namespace app { @@ -37,6 +38,8 @@ namespace app { class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage { public: + static constexpr size_t kIteratorsMax = CHIP_CONFIG_MAX_GROUP_CONCURRENT_ITERATORS; + CHIP_ERROR Init(PersistentStorageDelegate * storage) { VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT); @@ -44,37 +47,46 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage return CHIP_NO_ERROR; } - static StorageKeyName GetStorageKey(const ScopedNodeId & node); - - CHIP_ERROR LoadIndex(SubscriptionIndex & index) override; - - CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions) override; + SubscriptionInfoIterator * IterateSubscriptions() override; CHIP_ERROR Save(SubscriptionInfo & subscriptionInfo) override; - CHIP_ERROR Delete(const SubscriptionInfo & subscriptionInfo) override; + CHIP_ERROR Delete(NodeId nodeId, FabricIndex fabricIndex, SubscriptionId subscriptionId) override; CHIP_ERROR DeleteAll(FabricIndex fabricIndex) override; -private: - CHIP_ERROR SaveIndex(const SubscriptionIndex & index); - CHIP_ERROR SaveSubscriptions(const ScopedNodeId & node, const SubscriptionList & subscriptions); - CHIP_ERROR LoadIndex(SubscriptionIndex & index, size_t allocateExtraSpace); - CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions, size_t allocateExtraSpace); - - static constexpr size_t MaxScopedNodeIdSize() { return TLV::EstimateStructOverhead(sizeof(NodeId), sizeof(FabricIndex)); } +protected: + CHIP_ERROR Save(TLV::TLVWriter & writer, SubscriptionInfo & subscriptionInfo); + CHIP_ERROR Load(size_t subscriptionIndex, SubscriptionInfo & subscriptionInfo); + CHIP_ERROR Delete(size_t subscriptionIndex); + size_t Count(); + size_t MaxCount(); + CHIP_ERROR DeleteMaxCount(); - static constexpr size_t MaxIndexSize() + class SimpleSubscriptionInfoIterator : public SubscriptionInfoIterator { - // The max size of the list is (1 byte control + bytes for actual value) times max number of list items - return TLV::EstimateStructOverhead((1 + MaxScopedNodeIdSize()) * CHIP_IM_MAX_NUM_SUBSCRIPTIONS); - } + public: + SimpleSubscriptionInfoIterator(SimpleSubscriptionResumptionStorage & storage); + size_t Count() override; + bool Next(SubscriptionInfo & output) override; + void Release() override; + + private: + SimpleSubscriptionResumptionStorage & mStorage; + uint16_t mNextIndex; + size_t mMaxCount; + }; + + static constexpr size_t MaxScopedNodeIdSize() { return TLV::EstimateStructOverhead(sizeof(NodeId), sizeof(FabricIndex)); } static constexpr size_t MaxSubscriptionPathsSize() { - return TLV::EstimateStructOverhead( - TLV::EstimateStructOverhead(sizeof(uint8_t), sizeof(EndpointId), sizeof(ClusterId), sizeof(AttributeId)) * - CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION); + // IM engine declares an attribute path pool and an event path pool, and each pool + // includes CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS for subscriptions + return 2 * + TLV::EstimateStructOverhead( + TLV::EstimateStructOverhead(sizeof(uint8_t), sizeof(EndpointId), sizeof(ClusterId), sizeof(AttributeId)) * + CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS); } static constexpr size_t MaxSubscriptionSize() @@ -84,25 +96,6 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage sizeof(bool), MaxSubscriptionPathsSize()); } - static constexpr size_t MaxStateSize() - { - // Due to IM engine subscription eviction logic, effective allowed maximum - // subscriptions per fabric is higher than CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC - // when number of fabrics is fewer than maximum. And so max state size should use - // CHIP_IM_MAX_NUM_SUBSCRIPTIONS to better estimate, and allow IM engine eviction - // logic to trigger the right clean up when needed. - - // The max size of the list is (1 byte control + bytes for actual value) times max number of list items - return TLV::EstimateStructOverhead(1 + MaxSubscriptionSize() * CHIP_IM_MAX_NUM_SUBSCRIPTIONS); - } - - enum class SubscriptionPathType : uint8_t - { - kAttributePath = 0x1, - kUrgentEventPath = 0x2, - kNonUrgentEventPath = 0x3, - }; - enum class EventPathType : uint8_t { kUrgent = 0x1, @@ -111,39 +104,41 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage // TODO: consider alternate storage scheme to optimize space requirement - // Nodes TLV structure: - // Array of: - // Scoped Node ID struct of: - // Node ID - // Fabric index - - // Subscription TLV structure: - // Array of: - // Struct of: (Subscription info) - // Node ID - // Subscription ID - // Min interval - // Max interval - // Fabric filtered boolean - // Path Count x, with these fields repeating x times - // Type (attribute, urgent event, non-urgent event) - // Endpoint ID - // Cluster ID - // Attribute/event ID - static constexpr TLV::Tag kFabricIndexTag = TLV::ContextTag(1); + // Flat list of subscriptions indexed from from 0 to CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS-1 + // + // Each entry in list is a Subscription TLV structure: + // Struct of: + // Node ID + // Fabric Index + // Subscription ID + // Min interval + // Max interval + // Fabric filtered boolean + // Attribute Path Count x, with these fields repeating x times + // Endpoint ID + // Cluster ID + // Attribute/event ID + // Event Path Count x, with these fields repeating x times + // Event subscription type (urgent / non-urgent) + // Endpoint ID + // Cluster ID + // Event ID + static constexpr TLV::Tag kPeerNodeIdTag = TLV::ContextTag(2); + static constexpr TLV::Tag kFabricIndexTag = TLV::ContextTag(1); static constexpr TLV::Tag kSubscriptionIdTag = TLV::ContextTag(3); static constexpr TLV::Tag kMinIntervalTag = TLV::ContextTag(4); static constexpr TLV::Tag kMaxIntervalTag = TLV::ContextTag(5); static constexpr TLV::Tag kFabricFilteredTag = TLV::ContextTag(6); static constexpr TLV::Tag kPathCountTag = TLV::ContextTag(7); - static constexpr TLV::Tag kPathTypeTag = TLV::ContextTag(8); + static constexpr TLV::Tag kEventPathTypeTag = TLV::ContextTag(8); static constexpr TLV::Tag kEndpointIdTag = TLV::ContextTag(9); static constexpr TLV::Tag kClusterIdTag = TLV::ContextTag(10); static constexpr TLV::Tag kAttributeIdTag = TLV::ContextTag(11); static constexpr TLV::Tag kEventIdTag = TLV::ContextTag(12); PersistentStorageDelegate * mStorage; + ObjectPool mSubscriptionInfoIterators; }; } // namespace app } // namespace chip diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index 378571585a924e..cc90222cee9ee9 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -17,14 +17,14 @@ /** * @file - * This file defines the classes corresponding to CHIP Interaction Model Event Generatorr Delegate. - * + * This file defines the interface to store subscription information. */ #pragma once #include #include +#include namespace chip { namespace app { @@ -39,8 +39,6 @@ namespace app { class SubscriptionResumptionStorage { public: - // TODO: Create RAII ScopedMemoryBuffer replacement container that does not require is_trivial elements - // Structs to hold struct AttributePathParamsValues { @@ -86,27 +84,29 @@ class SubscriptionResumptionStorage Platform::ScopedMemoryBufferWithSize mEventPaths; }; - /** - * Struct to hold list of subscriptions - */ - struct SubscriptionList - { - size_t mSize; - std::unique_ptr mSubscriptions; - SubscriptionInfo & operator[](size_t index) { return mSubscriptions[index]; } - const SubscriptionInfo & operator[](size_t index) const { return mSubscriptions[index]; } - }; + using SubscriptionInfoIterator = CommonIterator; /** - * Struct to hold index of all nodes that have persisted subscriptions + * Struct to hold list of subscriptions */ - struct SubscriptionIndex - { - size_t mSize; - std::unique_ptr mNodes; - ScopedNodeId & operator[](size_t index) { return mNodes[index]; } - const ScopedNodeId & operator[](size_t index) const { return mNodes[index]; } - }; + // struct SubscriptionList + // { + // size_t mSize; + // std::unique_ptr mSubscriptions; + // SubscriptionInfo & operator[](size_t index) { return mSubscriptions[index]; } + // const SubscriptionInfo & operator[](size_t index) const { return mSubscriptions[index]; } + // }; + // + // /** + // * Struct to hold index of all nodes that have persisted subscriptions + // */ + // struct SubscriptionIndex + // { + // size_t mSize; + // std::unique_ptr mNodes; + // ScopedNodeId & operator[](size_t index) { return mNodes[index]; } + // const ScopedNodeId & operator[](size_t index) const { return mNodes[index]; } + // }; virtual ~SubscriptionResumptionStorage(){}; @@ -117,7 +117,7 @@ class SubscriptionResumptionStorage * @return CHIP_NO_ERROR on success, CHIP_ERROR_KEY_NOT_FOUND if no subscription resumption information can be found, else an * appropriate CHIP error on failure */ - virtual CHIP_ERROR LoadIndex(SubscriptionIndex & subscriberIndex) = 0; + // virtual CHIP_ERROR LoadIndex(SubscriptionIndex & subscriberIndex) = 0; /** * Recover subscription resumption info for a given fabric-scoped node identity. @@ -127,7 +127,14 @@ class SubscriptionResumptionStorage * @return CHIP_NO_ERROR on success, CHIP_ERROR_KEY_NOT_FOUND if no subscription resumption information can be found, else an * appropriate CHIP error on failure */ - virtual CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions) = 0; + // virtual CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions) = 0; + + /** + * Iterate through persisted subscriptions + * + * @return A valid iterator on success. Use CommonIterator accessor to retrieve SubscriptionInfo + */ + virtual SubscriptionInfoIterator * IterateSubscriptions() = 0; /** * Save subscription resumption information to storage. @@ -144,7 +151,14 @@ class SubscriptionResumptionStorage * subscription * @return CHIP_NO_ERROR on success, else an appropriate CHIP error on failure */ - virtual CHIP_ERROR Delete(const SubscriptionInfo & subscriptionInfo) = 0; + // virtual CHIP_ERROR Delete(const SubscriptionInfo & subscriptionInfo) = 0; + + /** + * Delete subscription resumption information by node ID, fabric index, and subscription ID. + * + * @return CHIP_NO_ERROR on success, else an appropriate CHIP error on failure + */ + virtual CHIP_ERROR Delete(NodeId nodeId, FabricIndex fabricIndex, SubscriptionId subscriptionId) = 0; /** * Remove all subscription resumption information associated with the specified diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 6afc8f8e2615fc..b2f023b3eb2db0 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -105,6 +105,7 @@ chip_test_suite("tests") { "TestPendingNotificationMap.cpp", "TestReadInteraction.cpp", "TestReportingEngine.cpp", + "TestSimpleSubscriptionResumptionStorage.cpp", "TestStatusIB.cpp", "TestStatusResponseMessage.cpp", "TestTimedHandler.cpp", diff --git a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp new file mode 100644 index 00000000000000..ecd0455b9279fa --- /dev/null +++ b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp @@ -0,0 +1,500 @@ +/* + * Copyright (c) 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. + */ + +#include +#include + +#include +#include + +#include + +class TestSimpleSubscriptionResumptionStorage : public chip::app::SimpleSubscriptionResumptionStorage +{ +public: + size_t TestMaxCount() { return MaxCount(); } + CHIP_ERROR TestSave(chip::TLV::TLVWriter & writer, + chip::app::SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo) + { + return Save(writer, subscriptionInfo); + } + static constexpr size_t TestMaxSubscriptionSize() { return MaxSubscriptionSize(); } +}; + +// Fictitious subsccription 1 +constexpr chip::FabricIndex fabric1 = 10; +constexpr chip::NodeId node1 = 12344321; +constexpr chip::SubscriptionId subscriptionId1 = 1; +constexpr uint16_t minInterval1 = 1; +constexpr uint16_t maxInterval1 = 10; +constexpr bool fabricFiltered1 = false; +// has only attribute subs +constexpr chip::EndpointId attributeEndpointId1_1 = 1; +constexpr chip::ClusterId attributeClusterId1_1 = 1; +constexpr chip::AttributeId attributeId1_1 = 1; +constexpr chip::EndpointId attributeEndpointId1_2 = 2; +constexpr chip::ClusterId attributeClusterId1_2 = 2; +constexpr chip::AttributeId attributeId1_2 = 2; + +// Fictitious subsccription 2 +constexpr chip::FabricIndex fabric2 = 14; +constexpr chip::NodeId node2 = 11223344; +constexpr chip::SubscriptionId subscriptionId2 = 2; +constexpr uint16_t minInterval2 = 2; +constexpr uint16_t maxInterval2 = 20; +constexpr bool fabricFiltered2 = true; +// has only event subs +constexpr chip::EndpointId eventEndpointId2_1 = 1; +constexpr chip::ClusterId eventClusterId2_1 = 1; +constexpr chip::AttributeId eventId2_1 = 1; +constexpr bool isUrgentEvent2_1 = false; +constexpr chip::EndpointId eventEndpointId2_2 = 2; +constexpr chip::ClusterId eventClusterId2_2 = 2; +constexpr chip::AttributeId eventId2_2 = 2; +constexpr bool isUrgentEvent2_2 = true; + +// Fictitious subsccription 3 +constexpr chip::FabricIndex fabric3 = 18; +constexpr chip::NodeId node3 = 44332211; +constexpr chip::SubscriptionId subscriptionId3 = 3; +constexpr uint16_t minInterval3 = 3; +constexpr uint16_t maxInterval3 = 30; +constexpr bool fabricFiltered3 = true; +// has both attributes and events +constexpr chip::EndpointId attributeEndpointId3_1 = 1; +constexpr chip::ClusterId attributeClusterId3_1 = 1; +constexpr chip::AttributeId attributeId3_1 = 1; +constexpr chip::EndpointId attributeEndpointId3_2 = 2; +constexpr chip::ClusterId attributeClusterId3_2 = 2; +constexpr chip::AttributeId attributeId3_2 = 2; +constexpr chip::EndpointId eventEndpointId3_1 = 1; +constexpr chip::ClusterId eventClusterId3_1 = 1; +constexpr chip::AttributeId eventId3_1 = 1; +constexpr bool isUrgentEvent3_1 = false; +constexpr chip::EndpointId eventEndpointId3_2 = 2; +constexpr chip::ClusterId eventClusterId3_2 = 2; +constexpr chip::AttributeId eventId3_2 = 2; +constexpr bool isUrgentEvent3_2 = true; + +struct TestSubscriptionInfo : public chip::app::SubscriptionResumptionStorage::SubscriptionInfo +{ + bool operator==(const SubscriptionInfo & that) const + { + if ((mNodeId != that.mNodeId) || (mFabricIndex != that.mFabricIndex) || (mSubscriptionId != that.mSubscriptionId) || + (mMinInterval != that.mMinInterval) || (mMaxInterval != that.mMaxInterval) || (mFabricFiltered != that.mFabricFiltered)) + { + return false; + } + if ((mAttributePaths.AllocatedCount() != that.mAttributePaths.AllocatedCount()) || + (mEventPaths.AllocatedCount() != that.mEventPaths.AllocatedCount())) + { + return false; + } + for (size_t i = 0; i < mAttributePaths.AllocatedCount(); i++) + { + if ((mAttributePaths[i].mEndpointId != that.mAttributePaths[i].mEndpointId) || + (mAttributePaths[i].mClusterId != that.mAttributePaths[i].mClusterId) || + (mAttributePaths[i].mAttributeId != that.mAttributePaths[i].mAttributeId)) + { + return false; + } + } + for (size_t i = 0; i < mEventPaths.AllocatedCount(); i++) + { + if ((mEventPaths[i].mEndpointId != that.mEventPaths[i].mEndpointId) || + (mEventPaths[i].mClusterId != that.mEventPaths[i].mClusterId) || + (mEventPaths[i].mEventId != that.mEventPaths[i].mEventId) || + (mEventPaths[i].mIsUrgentEvent != that.mEventPaths[i].mIsUrgentEvent)) + { + return false; + } + } + return true; + } +}; + +void TestCount(nlTestSuite * inSuite, void * inContext) +{ + chip::TestPersistentStorageDelegate storage; + TestSimpleSubscriptionResumptionStorage subscriptionStorage; + subscriptionStorage.Init(&storage); + + // Check by default it returns correct value + NL_TEST_ASSERT(inSuite, subscriptionStorage.TestMaxCount() == CHIP_IM_MAX_NUM_SUBSCRIPTIONS); + + // Force larger value and check that it returns the larger value + uint32_t countMaxToSave = 2 * CHIP_IM_MAX_NUM_SUBSCRIPTIONS; + storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMaxToSave, + static_cast(sizeof(uint32_t))); + NL_TEST_ASSERT(inSuite, subscriptionStorage.TestMaxCount() == (2 * CHIP_IM_MAX_NUM_SUBSCRIPTIONS)); + + // Reset + storage.SyncDeleteKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName()); + + // Write some subscriptions and see the counts are correct + chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = node1, .mFabricIndex = fabric1 }; + + for (size_t i = 0; i < (CHIP_IM_MAX_NUM_SUBSCRIPTIONS / 2); i++) + { + subscriptionInfo.mSubscriptionId = static_cast(i); + subscriptionStorage.Save(subscriptionInfo); + } + + // Make sure iterator counts correctly + auto * iterator = subscriptionStorage.IterateSubscriptions(); + NL_TEST_ASSERT(inSuite, iterator->Count() == (CHIP_IM_MAX_NUM_SUBSCRIPTIONS / 2)); + + // Verify subscriptions manually count correctly + size_t count = 0; + while (iterator->Next(subscriptionInfo)) + { + count++; + } + iterator->Release(); + NL_TEST_ASSERT(inSuite, count == (CHIP_IM_MAX_NUM_SUBSCRIPTIONS / 2)); + + // Delete all and verify iterator counts 0 + CHIP_ERROR err = subscriptionStorage.DeleteAll(fabric1); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + iterator = subscriptionStorage.IterateSubscriptions(); + NL_TEST_ASSERT(inSuite, iterator->Count() == 0); + + // Verify subscriptions manually count correctly + count = 0; + while (iterator->Next(subscriptionInfo)) + { + count++; + } + iterator->Release(); + NL_TEST_ASSERT(inSuite, count == 0); + + NL_TEST_ASSERT(inSuite, false); +} + +void TestState(nlTestSuite * inSuite, void * inContext) +{ + chip::TestPersistentStorageDelegate storage; + TestSimpleSubscriptionResumptionStorage subscriptionStorage; + subscriptionStorage.Init(&storage); + + chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo1 = { + .mNodeId = node1, + .mFabricIndex = fabric1, + .mSubscriptionId = subscriptionId1, + .mMinInterval = minInterval1, + .mMaxInterval = maxInterval1, + .mFabricFiltered = fabricFiltered1, + }; + subscriptionInfo1.mAttributePaths.Calloc(2); + subscriptionInfo1.mAttributePaths[0].mEndpointId = attributeEndpointId1_1; + subscriptionInfo1.mAttributePaths[0].mClusterId = attributeClusterId1_1; + subscriptionInfo1.mAttributePaths[0].mAttributeId = attributeId1_1; + subscriptionInfo1.mAttributePaths[1].mEndpointId = attributeEndpointId1_2; + subscriptionInfo1.mAttributePaths[1].mClusterId = attributeClusterId1_2; + subscriptionInfo1.mAttributePaths[1].mAttributeId = attributeId1_2; + + chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo2 = { + .mNodeId = node2, + .mFabricIndex = fabric2, + .mSubscriptionId = subscriptionId2, + .mMinInterval = minInterval2, + .mMaxInterval = maxInterval2, + .mFabricFiltered = fabricFiltered2, + }; + subscriptionInfo2.mEventPaths.Calloc(2); + subscriptionInfo2.mEventPaths[0].mEndpointId = eventEndpointId2_1; + subscriptionInfo2.mEventPaths[0].mClusterId = eventClusterId2_1; + subscriptionInfo2.mEventPaths[0].mEventId = eventId2_1; + subscriptionInfo2.mEventPaths[0].mIsUrgentEvent = isUrgentEvent2_1; + subscriptionInfo2.mEventPaths[1].mEndpointId = eventEndpointId2_2; + subscriptionInfo2.mEventPaths[1].mClusterId = eventClusterId2_2; + subscriptionInfo2.mEventPaths[1].mEventId = eventId2_2; + subscriptionInfo2.mEventPaths[1].mIsUrgentEvent = isUrgentEvent2_2; + + chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo3 = { + .mNodeId = node3, + .mFabricIndex = fabric3, + .mSubscriptionId = subscriptionId3, + .mMinInterval = minInterval3, + .mMaxInterval = maxInterval3, + .mFabricFiltered = fabricFiltered3, + }; + subscriptionInfo3.mAttributePaths.Calloc(2); + subscriptionInfo3.mAttributePaths[0].mEndpointId = attributeEndpointId3_1; + subscriptionInfo3.mAttributePaths[0].mClusterId = attributeClusterId3_1; + subscriptionInfo3.mAttributePaths[0].mAttributeId = attributeId3_1; + subscriptionInfo3.mAttributePaths[1].mEndpointId = attributeEndpointId3_2; + subscriptionInfo3.mAttributePaths[1].mClusterId = attributeClusterId3_2; + subscriptionInfo3.mAttributePaths[1].mAttributeId = attributeId3_2; + subscriptionInfo3.mEventPaths.Calloc(2); + subscriptionInfo3.mEventPaths[0].mEndpointId = eventEndpointId3_1; + subscriptionInfo3.mEventPaths[0].mClusterId = eventClusterId3_1; + subscriptionInfo3.mEventPaths[0].mEventId = eventId3_1; + subscriptionInfo2.mEventPaths[0].mIsUrgentEvent = isUrgentEvent3_1; + subscriptionInfo3.mEventPaths[1].mEndpointId = eventEndpointId3_2; + subscriptionInfo3.mEventPaths[1].mClusterId = eventClusterId3_2; + subscriptionInfo3.mEventPaths[1].mEventId = eventId3_2; + subscriptionInfo2.mEventPaths[1].mIsUrgentEvent = isUrgentEvent3_2; + + CHIP_ERROR err; + err = subscriptionStorage.Save(subscriptionInfo1); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = subscriptionStorage.Save(subscriptionInfo2); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = subscriptionStorage.Save(subscriptionInfo3); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + auto * iterator = subscriptionStorage.IterateSubscriptions(); + NL_TEST_ASSERT(inSuite, iterator->Count() == 3); + + // Verify subscriptions manually count correctly + TestSubscriptionInfo subscriptionInfo; + NL_TEST_ASSERT(inSuite, iterator->Next(subscriptionInfo)); + NL_TEST_ASSERT(inSuite, subscriptionInfo == subscriptionInfo1); + NL_TEST_ASSERT(inSuite, iterator->Next(subscriptionInfo)); + NL_TEST_ASSERT(inSuite, subscriptionInfo == subscriptionInfo2); + NL_TEST_ASSERT(inSuite, iterator->Next(subscriptionInfo)); + NL_TEST_ASSERT(inSuite, subscriptionInfo == subscriptionInfo3); + // Verify at end of list + NL_TEST_ASSERT(inSuite, !iterator->Next(subscriptionInfo)); + iterator->Release(); + + // Delete fabric 1 and subscription 2 and check only 3 remains. + subscriptionStorage.Delete(subscriptionInfo1.mNodeId, subscriptionInfo1.mFabricIndex, subscriptionInfo1.mSubscriptionId); + subscriptionStorage.DeleteAll(subscriptionInfo2.mFabricIndex); + + iterator = subscriptionStorage.IterateSubscriptions(); + NL_TEST_ASSERT(inSuite, iterator->Count() == 1); + NL_TEST_ASSERT(inSuite, iterator->Next(subscriptionInfo)); + NL_TEST_ASSERT(inSuite, subscriptionInfo == subscriptionInfo3); + // Verify at end of list + NL_TEST_ASSERT(inSuite, !iterator->Next(subscriptionInfo)); + iterator->Release(); + + // Delete 3 also, and see that both count is 0 and MaxCount is removed from storage + subscriptionStorage.DeleteAll(subscriptionInfo3.mFabricIndex); + iterator = subscriptionStorage.IterateSubscriptions(); + NL_TEST_ASSERT(inSuite, iterator->Count() == 0); + NL_TEST_ASSERT(inSuite, !iterator->Next(subscriptionInfo)); + iterator->Release(); + + uint32_t countMax = 0; + uint16_t len = sizeof(countMax); + err = storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMax, len); + NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); +} + +static constexpr chip::TLV::Tag kTestValue1Tag = chip::TLV::ContextTag(30); +static constexpr chip::TLV::Tag kTestValue2Tag = chip::TLV::ContextTag(31); + +void TestStateUnexpectedFields(nlTestSuite * inSuite, void * inContext) +{ + chip::TestPersistentStorageDelegate storage; + TestSimpleSubscriptionResumptionStorage subscriptionStorage; + subscriptionStorage.Init(&storage); + + // Write additional entries at the end of TLV and see it still loads correctly + chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo1 = { + .mNodeId = node1, + .mFabricIndex = fabric1, + .mSubscriptionId = subscriptionId1, + .mMinInterval = minInterval1, + .mMaxInterval = maxInterval1, + .mFabricFiltered = fabricFiltered1, + }; + subscriptionInfo1.mAttributePaths.Calloc(2); + subscriptionInfo1.mAttributePaths[0].mEndpointId = attributeEndpointId1_1; + subscriptionInfo1.mAttributePaths[0].mClusterId = attributeClusterId1_1; + subscriptionInfo1.mAttributePaths[0].mAttributeId = attributeId1_1; + subscriptionInfo1.mAttributePaths[1].mEndpointId = attributeEndpointId1_2; + subscriptionInfo1.mAttributePaths[1].mClusterId = attributeClusterId1_2; + subscriptionInfo1.mAttributePaths[1].mAttributeId = attributeId1_2; + + chip::Platform::ScopedMemoryBuffer backingBuffer; + backingBuffer.Calloc(subscriptionStorage.TestMaxSubscriptionSize()); + NL_TEST_ASSERT(inSuite, backingBuffer.Get() != nullptr); + chip::TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), subscriptionStorage.TestMaxSubscriptionSize()); + + NL_TEST_ASSERT(inSuite, subscriptionStorage.TestSave(writer, subscriptionInfo1) == CHIP_NO_ERROR); + + // Additional stuff + chip::TLV::TLVType containerType; + NL_TEST_ASSERT(inSuite, + writer.StartContainer(chip::TLV::AnonymousTag(), chip::TLV::kTLVType_Structure, containerType) == CHIP_NO_ERROR); + uint32_t value1 = 1; + uint32_t value2 = 2; + NL_TEST_ASSERT(inSuite, writer.Put(kTestValue1Tag, value1) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, writer.Put(kTestValue2Tag, value2) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, writer.EndContainer(containerType) == CHIP_NO_ERROR); + + const auto len = writer.GetLengthWritten(); + + writer.Finalize(backingBuffer); + + NL_TEST_ASSERT(inSuite, + storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumption(0).KeyName(), + backingBuffer.Get(), static_cast(len)) == CHIP_NO_ERROR); + + // Now read back and verify + auto * iterator = subscriptionStorage.IterateSubscriptions(); + NL_TEST_ASSERT(inSuite, iterator->Count() == 1); + TestSubscriptionInfo subscriptionInfo; + NL_TEST_ASSERT(inSuite, iterator->Next(subscriptionInfo)); + NL_TEST_ASSERT(inSuite, subscriptionInfo == subscriptionInfo1); + iterator->Release(); +} + +void TestStateTooBigToLoad(nlTestSuite * inSuite, void * inContext) +{ + chip::TestPersistentStorageDelegate storage; + TestSimpleSubscriptionResumptionStorage subscriptionStorage; + subscriptionStorage.Init(&storage); + + // Write additional too-big data at the end of TLV and see it fails to loads and entry deleted + chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo1 = { + .mNodeId = node1, + .mFabricIndex = fabric1, + .mSubscriptionId = subscriptionId1, + .mMinInterval = minInterval1, + .mMaxInterval = maxInterval1, + .mFabricFiltered = fabricFiltered1, + }; + subscriptionInfo1.mAttributePaths.Calloc(2); + subscriptionInfo1.mAttributePaths[0].mEndpointId = attributeEndpointId1_1; + subscriptionInfo1.mAttributePaths[0].mClusterId = attributeClusterId1_1; + subscriptionInfo1.mAttributePaths[0].mAttributeId = attributeId1_1; + subscriptionInfo1.mAttributePaths[1].mEndpointId = attributeEndpointId1_2; + subscriptionInfo1.mAttributePaths[1].mClusterId = attributeClusterId1_2; + subscriptionInfo1.mAttributePaths[1].mAttributeId = attributeId1_2; + + chip::Platform::ScopedMemoryBuffer backingBuffer; + backingBuffer.Calloc(subscriptionStorage.TestMaxSubscriptionSize() * 2); + NL_TEST_ASSERT(inSuite, backingBuffer.Get() != nullptr); + chip::TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), subscriptionStorage.TestMaxSubscriptionSize() * 2); + + NL_TEST_ASSERT(inSuite, subscriptionStorage.TestSave(writer, subscriptionInfo1) == CHIP_NO_ERROR); + + // Additional too-many bytes + chip::TLV::TLVType containerType; + NL_TEST_ASSERT(inSuite, + writer.StartContainer(chip::TLV::AnonymousTag(), chip::TLV::kTLVType_Structure, containerType) == CHIP_NO_ERROR); + // Write MaxSubscriptionSize() to guarantee Load failure + chip::Platform::ScopedMemoryBuffer additionalBytes; + additionalBytes.Calloc(subscriptionStorage.TestMaxSubscriptionSize()); + NL_TEST_ASSERT(inSuite, additionalBytes.Get() != nullptr); + NL_TEST_ASSERT(inSuite, + writer.PutBytes(kTestValue1Tag, additionalBytes.Get(), + static_cast(subscriptionStorage.TestMaxSubscriptionSize())) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, writer.EndContainer(containerType) == CHIP_NO_ERROR); + + const auto len = writer.GetLengthWritten(); + + writer.Finalize(backingBuffer); + + NL_TEST_ASSERT(inSuite, + storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumption(0).KeyName(), + backingBuffer.Get(), static_cast(len)) == CHIP_NO_ERROR); + + // Now read back and verify + auto * iterator = subscriptionStorage.IterateSubscriptions(); + NL_TEST_ASSERT(inSuite, iterator->Count() == 1); + TestSubscriptionInfo subscriptionInfo; + NL_TEST_ASSERT(inSuite, !iterator->Next(subscriptionInfo)); + NL_TEST_ASSERT(inSuite, iterator->Count() == 0); + iterator->Release(); +} + +void TestStateJunkData(nlTestSuite * inSuite, void * inContext) +{ + chip::TestPersistentStorageDelegate storage; + TestSimpleSubscriptionResumptionStorage subscriptionStorage; + subscriptionStorage.Init(&storage); + + chip::Platform::ScopedMemoryBuffer junkBytes; + junkBytes.Calloc(subscriptionStorage.TestMaxSubscriptionSize() / 2); + NL_TEST_ASSERT(inSuite, junkBytes.Get() != nullptr); + NL_TEST_ASSERT(inSuite, + storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumption(0).KeyName(), junkBytes.Get(), + static_cast(subscriptionStorage.TestMaxSubscriptionSize() / 2)) == + CHIP_NO_ERROR); + + // Now read back and verify + auto * iterator = subscriptionStorage.IterateSubscriptions(); + NL_TEST_ASSERT(inSuite, iterator->Count() == 1); + TestSubscriptionInfo subscriptionInfo; + NL_TEST_ASSERT(inSuite, !iterator->Next(subscriptionInfo)); + NL_TEST_ASSERT(inSuite, iterator->Count() == 0); + iterator->Release(); +} +/** + * Set up the test suite. + */ +int Test_Setup(void * inContext) +{ + VerifyOrReturnError(CHIP_NO_ERROR == chip::Platform::MemoryInit(), FAILURE); + + return SUCCESS; +} + +/** + * Tear down the test suite. + */ +int Test_Teardown(void * inContext) +{ + chip::Platform::MemoryShutdown(); + return SUCCESS; +} + +// Test Suite + +/** + * Test Suite that lists all the test functions. + */ +// clang-format off +static const nlTest sTests[] = +{ + NL_TEST_DEF("TestCount", TestCount), + NL_TEST_DEF("TestState", TestState), + NL_TEST_DEF("TestStateUnexpectedFields", TestStateUnexpectedFields), + NL_TEST_DEF("TestStateTooBigToLoad", TestStateTooBigToLoad), + NL_TEST_DEF("TestStateJunkData", TestStateJunkData), + + NL_TEST_SENTINEL() +}; +// clang-format on + +// clang-format off +static nlTestSuite sSuite = +{ + "Test-CHIP-SimpleSubscriptionResumptionStorage", + &sTests[0], + &Test_Setup, &Test_Teardown +}; +// clang-format on + +/** + * Main + */ +int TestSimpleSubscriptionResumptionStorage() +{ + // Run test suit against one context + nlTestRunner(&sSuite, nullptr); + + return (nlTestRunnerStats(&sSuite)); +} + +CHIP_REGISTER_TEST_SUITE(TestSimpleSubscriptionResumptionStorage) diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index 3d2897f9cad190..6de13b9841d2c4 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -813,25 +813,6 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; #define CHIP_IM_MAX_NUM_COMMAND_HANDLER 4 #endif -/** - * @def CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION - * - * @brief The maximum number of path per subscription. - */ -#ifndef CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION -#define CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION 3 -#endif - -/** - * @def CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC - * - * @brief Defines the maximum number of ReadHandler for subscriptions per fabric. - * - */ -#ifndef CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC -#define CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC 3 -#endif - /** * @def CHIP_IM_MAX_NUM_SUBSCRIPTIONS * @@ -842,7 +823,7 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; * */ #ifndef CHIP_IM_MAX_NUM_SUBSCRIPTIONS -#define CHIP_IM_MAX_NUM_SUBSCRIPTIONS (CHIP_CONFIG_MAX_FABRICS * CHIP_IM_MAX_NUM_SUBSCRIPTIONS_PER_FABRIC) +#define CHIP_IM_MAX_NUM_SUBSCRIPTIONS (CHIP_CONFIG_MAX_FABRICS * 3) #endif /** @@ -872,7 +853,7 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; * @brief The maximum number of path objects for subscriptions, limits the number of attributes being subscribed at the same time. */ #ifndef CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS -#define CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS (CHIP_IM_MAX_NUM_SUBSCRIPTIONS * CHIP_IM_MAX_NUM_PATH_PER_SUBSCRIPTION) +#define CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS (CHIP_IM_MAX_NUM_SUBSCRIPTIONS * 3) #endif /** @@ -1369,3 +1350,14 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; #ifndef CHIP_CONFIG_PERSIST_SUBSCRIPTIONS #define CHIP_CONFIG_PERSIST_SUBSCRIPTIONS 0 #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + +/** + * @def CHIP_CONFIG_MAX_SUBSCRIPTION_RESUMPTION_STORAGE_CONCURRENT_ITERATORS + * + * @brief Defines the number of simultaneous subscription resumption iterators that can be allocated + * + * Number of iterator instances that can be allocated at any one time + */ +#ifndef CHIP_CONFIG_MAX_SUBSCRIPTION_RESUMPTION_STORAGE_CONCURRENT_ITERATORS +#define CHIP_CONFIG_MAX_SUBSCRIPTION_RESUMPTION_STORAGE_CONCURRENT_ITERATORS 2 +#endif diff --git a/src/lib/support/DefaultStorageKeyAllocator.h b/src/lib/support/DefaultStorageKeyAllocator.h index 025eb489b331f9..c5a612db7fe8ea 100644 --- a/src/lib/support/DefaultStorageKeyAllocator.h +++ b/src/lib/support/DefaultStorageKeyAllocator.h @@ -192,12 +192,11 @@ class DefaultStorageKeyAllocator static StorageKeyName IMEventNumber() { return StorageKeyName::FromConst("g/im/ec"); } // Subscription resumption - static StorageKeyName FabricSubscription(FabricIndex fabric, NodeId nodeId) + static StorageKeyName SubscriptionResumption(size_t index) { - return StorageKeyName::Formatted("f/%x/su/%08" PRIX32 "%08" PRIX32, fabric, static_cast(nodeId >> 32), - static_cast(nodeId)); + return StorageKeyName::Formatted("g/su/%x", static_cast(index)); } - static StorageKeyName SubscriptionResumptionIndex() { return StorageKeyName::FromConst("g/sui"); } + static StorageKeyName SubscriptionResumptionMaxCount() { return StorageKeyName::Formatted("g/sum"); } }; } // namespace chip From 3a509f44c9146ea4fe89a479e96baf40fba0d346 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 08:33:08 -0800 Subject: [PATCH 26/41] Fix unit test build warning and minor PR comment change --- src/app/SimpleSubscriptionResumptionStorage.cpp | 4 ---- src/app/SubscriptionResumptionStorage.h | 14 +++++++------- .../TestSimpleSubscriptionResumptionStorage.cpp | 12 ++++++------ 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 9a7e53bf5620e3..15e2d0fb86d784 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -381,8 +381,6 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(NodeId nodeId, FabricInde DeleteMaxCount(); } - ChipLogProgress(InteractionModel, "JEFFTEST: Delete final count %zu", count); - return deleteErr; } @@ -430,8 +428,6 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteAll(FabricIndex fabricInde } } - ChipLogProgress(InteractionModel, "JEFFTEST: DeleteAll final count %zu", count); - return deleteErr; } diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index cc90222cee9ee9..ffbf8e727d8f81 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -42,9 +42,9 @@ class SubscriptionResumptionStorage // Structs to hold struct AttributePathParamsValues { - EndpointId mEndpointId; // uint16 - ClusterId mClusterId; // uint32 - AttributeId mAttributeId; // uint32 + ClusterId mClusterId; + AttributeId mAttributeId; + EndpointId mEndpointId; void SetValues(AttributePathParams & params) { mEndpointId = params.mEndpointId; @@ -55,10 +55,10 @@ class SubscriptionResumptionStorage }; struct EventPathParamsValues { - EndpointId mEndpointId; // uint16 - ClusterId mClusterId; // uint32 - EventId mEventId; // uint32 - bool mIsUrgentEvent; // uint8 + ClusterId mClusterId; + EventId mEventId; + EndpointId mEndpointId; + bool mIsUrgentEvent; void SetValues(EventPathParams & params) { mEndpointId = params.mEndpointId; diff --git a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp index ecd0455b9279fa..6de6c7ce88ac1e 100644 --- a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp +++ b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp @@ -23,7 +23,7 @@ #include -class TestSimpleSubscriptionResumptionStorage : public chip::app::SimpleSubscriptionResumptionStorage +class SimpleSubscriptionResumptionStorageTest : public chip::app::SimpleSubscriptionResumptionStorage { public: size_t TestMaxCount() { return MaxCount(); } @@ -130,7 +130,7 @@ struct TestSubscriptionInfo : public chip::app::SubscriptionResumptionStorage::S void TestCount(nlTestSuite * inSuite, void * inContext) { chip::TestPersistentStorageDelegate storage; - TestSimpleSubscriptionResumptionStorage subscriptionStorage; + SimpleSubscriptionResumptionStorageTest subscriptionStorage; subscriptionStorage.Init(&storage); // Check by default it returns correct value @@ -188,7 +188,7 @@ void TestCount(nlTestSuite * inSuite, void * inContext) void TestState(nlTestSuite * inSuite, void * inContext) { chip::TestPersistentStorageDelegate storage; - TestSimpleSubscriptionResumptionStorage subscriptionStorage; + SimpleSubscriptionResumptionStorageTest subscriptionStorage; subscriptionStorage.Init(&storage); chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo1 = { @@ -304,7 +304,7 @@ static constexpr chip::TLV::Tag kTestValue2Tag = chip::TLV::ContextTag(31); void TestStateUnexpectedFields(nlTestSuite * inSuite, void * inContext) { chip::TestPersistentStorageDelegate storage; - TestSimpleSubscriptionResumptionStorage subscriptionStorage; + SimpleSubscriptionResumptionStorageTest subscriptionStorage; subscriptionStorage.Init(&storage); // Write additional entries at the end of TLV and see it still loads correctly @@ -361,7 +361,7 @@ void TestStateUnexpectedFields(nlTestSuite * inSuite, void * inContext) void TestStateTooBigToLoad(nlTestSuite * inSuite, void * inContext) { chip::TestPersistentStorageDelegate storage; - TestSimpleSubscriptionResumptionStorage subscriptionStorage; + SimpleSubscriptionResumptionStorageTest subscriptionStorage; subscriptionStorage.Init(&storage); // Write additional too-big data at the end of TLV and see it fails to loads and entry deleted @@ -421,7 +421,7 @@ void TestStateTooBigToLoad(nlTestSuite * inSuite, void * inContext) void TestStateJunkData(nlTestSuite * inSuite, void * inContext) { chip::TestPersistentStorageDelegate storage; - TestSimpleSubscriptionResumptionStorage subscriptionStorage; + SimpleSubscriptionResumptionStorageTest subscriptionStorage; subscriptionStorage.Init(&storage); chip::Platform::ScopedMemoryBuffer junkBytes; From 69608983b637176eb4b8958adb17e5431fe26629 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 08:36:19 -0800 Subject: [PATCH 27/41] Update src/app/SubscriptionResumptionStorage.h Co-authored-by: Michael Sandstedt --- src/app/SubscriptionResumptionStorage.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index ffbf8e727d8f81..e2c657836bc9ab 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -30,11 +30,7 @@ namespace chip { namespace app { /** - * A SubscriptionPersistenceDelegate is used to persist subscriptions when they are established. - * - * Allows application to append any type of TLV data as part of an event log entry. Events - * have a standard header applicable to all events and this class provides the - * ability to add additional data past such standard header. + * The SubscriptionResumptionStorage interface is used to persist subscriptions when they are established. */ class SubscriptionResumptionStorage { From 194361e80bc74ac02620bc30fd5f76e925dd7230 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 09:01:30 -0800 Subject: [PATCH 28/41] Minor changes to address PR comments --- src/app/ReadHandler.cpp | 76 ++++++++++--------- .../SimpleSubscriptionResumptionStorage.cpp | 5 +- src/app/SimpleSubscriptionResumptionStorage.h | 5 +- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index f132bcf465f78c..a5aac9ec06b3aa 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -721,46 +721,48 @@ CHIP_ERROR ReadHandler::ProcessSubscribeRequest(System::PacketBufferHandle && aP #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage(); - if (subscriptionResumptionStorage) - { - SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = GetInitiatorNodeId(), - .mFabricIndex = GetAccessingFabricIndex(), - .mSubscriptionId = mSubscriptionId, - .mMinInterval = mMinIntervalFloorSeconds, - .mMaxInterval = mMaxInterval, - .mFabricFiltered = IsFabricFiltered() }; - ObjectList * attributePath = mpAttributePathList; - size_t attributePathCount = 0; - while (attributePath) - { - attributePathCount++; - attributePath = attributePath->mpNext; - } - attributePath = mpAttributePathList; - subscriptionInfo.mAttributePaths.Calloc(attributePathCount); - for (size_t i = 0; i < attributePathCount; i++) - { - subscriptionInfo.mAttributePaths[i].SetValues(attributePath->mValue); - attributePath = attributePath->mpNext; - } + if (!subscriptionResumptionStorage) + { + return CHIP_NO_ERROR; + } - ObjectList * eventPath = mpEventPathList; - size_t eventPathCount = 0; - while (eventPath) - { - eventPathCount++; - eventPath = eventPath->mpNext; - } - eventPath = mpEventPathList; - subscriptionInfo.mEventPaths.Calloc(eventPathCount); - for (size_t i = 0; i < eventPathCount; i++) - { - subscriptionInfo.mEventPaths[i].SetValues(eventPath->mValue); - eventPath = eventPath->mpNext; - } + SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = GetInitiatorNodeId(), + .mFabricIndex = GetAccessingFabricIndex(), + .mSubscriptionId = mSubscriptionId, + .mMinInterval = mMinIntervalFloorSeconds, + .mMaxInterval = mMaxInterval, + .mFabricFiltered = IsFabricFiltered() }; + ObjectList * attributePath = mpAttributePathList; + size_t attributePathCount = 0; + while (attributePath) + { + attributePathCount++; + attributePath = attributePath->mpNext; + } + attributePath = mpAttributePathList; + subscriptionInfo.mAttributePaths.Calloc(attributePathCount); + for (size_t i = 0; i < attributePathCount; i++) + { + subscriptionInfo.mAttributePaths[i].SetValues(attributePath->mValue); + attributePath = attributePath->mpNext; + } - err = subscriptionResumptionStorage->Save(subscriptionInfo); + ObjectList * eventPath = mpEventPathList; + size_t eventPathCount = 0; + while (eventPath) + { + eventPathCount++; + eventPath = eventPath->mpNext; } + eventPath = mpEventPathList; + subscriptionInfo.mEventPaths.Calloc(eventPathCount); + for (size_t i = 0; i < eventPathCount; i++) + { + subscriptionInfo.mEventPaths[i].SetValues(eventPath->mValue); + eventPath = eventPath->mpNext; + } + + err = subscriptionResumptionStorage->Save(subscriptionInfo); #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS return CHIP_NO_ERROR; diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 15e2d0fb86d784..605770a63b71f8 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -17,9 +17,8 @@ /** * @file - * This file defines the CHIP CASE Session object that provides - * APIs for constructing a secure session using a certificate from the device's - * operational credentials. + * This file defines a basic implementation of SubscriptionResumptionStorage that + * persists subscriptions in a flat list in TLV. */ #include diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index c7c4ee0ce092b0..bdd0f81abb4a5f 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -17,9 +17,8 @@ /** * @file - * This file defines the CHIP CASE Session object that provides - * APIs for constructing a secure session using a certificate from the device's - * operational credentials. + * This file defines a basic implementation of SubscriptionResumptionStorage that + * persists subscriptions in a flat list in TLV. */ #pragma once From f8d463b4a268a6f163224f0ba3bbc550aa700dd8 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 11:40:18 -0800 Subject: [PATCH 29/41] Address PR review comments: Unit test structs constructed explicitly in place for clarity IM engine ResumeSubscriptions nullptr check and exit conditions fix --- src/app/InteractionModelEngine.cpp | 11 +- src/app/ReadHandler.cpp | 10 +- ...estSimpleSubscriptionResumptionStorage.cpp | 197 +++++++----------- 3 files changed, 82 insertions(+), 136 deletions(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index d42990e33a21ec..f47c4a6b8b75f5 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1586,6 +1586,11 @@ void InteractionModelEngine::OnFabricRemoved(const FabricTable & fabricTable, Fa CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() { #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + if (!mpSubscriptionResumptionStorage) + { + return CHIP_NO_ERROR; + } + SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo; auto * iterator = mpSubscriptionResumptionStorage->IterateSubscriptions(); while (iterator->Next(subscriptionInfo)) @@ -1595,8 +1600,7 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() if (!EnsureResourceForSubscription(subscriptionInfo.mFabricIndex, requestedAttributePathCount, requestedEventPathCount)) { ChipLogProgress(InteractionModel, "no resource for Subscription resumption"); - mpSubscriptionResumptionStorage->Delete(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex, - subscriptionInfo.mSubscriptionId); + iterator->Release(); return CHIP_ERROR_NO_MEMORY; } @@ -1604,8 +1608,7 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() if (handler == nullptr) { ChipLogProgress(InteractionModel, "no resource for ReadHandler creation"); - mpSubscriptionResumptionStorage->Delete(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex, - subscriptionInfo.mSubscriptionId); + iterator->Release(); return CHIP_ERROR_NO_MEMORY; } diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index a5aac9ec06b3aa..89135942cc9a2c 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -79,13 +79,13 @@ void ReadHandler::ResumeSubscription(CASESessionManager & caseSessionManager, mMaxInterval = subscriptionInfo.mMaxInterval; SetStateFlag(ReadHandlerFlags::FabricFiltered, subscriptionInfo.mFabricFiltered); - // Move dynamically allocated attributes and events from the SubscriptionInfo struct into the object pool managed by the IM - // engine - CHIP_ERROR err; + // Move dynamically allocated attributes and events from the SubscriptionInfo struct into + // the object pool managed by the IM engine for (size_t i = 0; i < subscriptionInfo.mAttributePaths.AllocatedCount(); i++) { AttributePathParams attributePathParams = subscriptionInfo.mAttributePaths[i].GetParams(); - err = InteractionModelEngine::GetInstance()->PushFrontAttributePathList(mpAttributePathList, attributePathParams); + CHIP_ERROR err = + InteractionModelEngine::GetInstance()->PushFrontAttributePathList(mpAttributePathList, attributePathParams); if (err != CHIP_NO_ERROR) { Close(); @@ -95,7 +95,7 @@ void ReadHandler::ResumeSubscription(CASESessionManager & caseSessionManager, for (size_t i = 0; i < subscriptionInfo.mEventPaths.AllocatedCount(); i++) { EventPathParams eventPathParams = subscriptionInfo.mEventPaths[i].GetParams(); - err = InteractionModelEngine::GetInstance()->PushFrontEventPathParamsList(mpEventPathList, eventPathParams); + CHIP_ERROR err = InteractionModelEngine::GetInstance()->PushFrontEventPathParamsList(mpEventPathList, eventPathParams); if (err != CHIP_NO_ERROR) { Close(); diff --git a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp index 6de6c7ce88ac1e..8740ff36db9928 100644 --- a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp +++ b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp @@ -35,61 +35,6 @@ class SimpleSubscriptionResumptionStorageTest : public chip::app::SimpleSubscrip static constexpr size_t TestMaxSubscriptionSize() { return MaxSubscriptionSize(); } }; -// Fictitious subsccription 1 -constexpr chip::FabricIndex fabric1 = 10; -constexpr chip::NodeId node1 = 12344321; -constexpr chip::SubscriptionId subscriptionId1 = 1; -constexpr uint16_t minInterval1 = 1; -constexpr uint16_t maxInterval1 = 10; -constexpr bool fabricFiltered1 = false; -// has only attribute subs -constexpr chip::EndpointId attributeEndpointId1_1 = 1; -constexpr chip::ClusterId attributeClusterId1_1 = 1; -constexpr chip::AttributeId attributeId1_1 = 1; -constexpr chip::EndpointId attributeEndpointId1_2 = 2; -constexpr chip::ClusterId attributeClusterId1_2 = 2; -constexpr chip::AttributeId attributeId1_2 = 2; - -// Fictitious subsccription 2 -constexpr chip::FabricIndex fabric2 = 14; -constexpr chip::NodeId node2 = 11223344; -constexpr chip::SubscriptionId subscriptionId2 = 2; -constexpr uint16_t minInterval2 = 2; -constexpr uint16_t maxInterval2 = 20; -constexpr bool fabricFiltered2 = true; -// has only event subs -constexpr chip::EndpointId eventEndpointId2_1 = 1; -constexpr chip::ClusterId eventClusterId2_1 = 1; -constexpr chip::AttributeId eventId2_1 = 1; -constexpr bool isUrgentEvent2_1 = false; -constexpr chip::EndpointId eventEndpointId2_2 = 2; -constexpr chip::ClusterId eventClusterId2_2 = 2; -constexpr chip::AttributeId eventId2_2 = 2; -constexpr bool isUrgentEvent2_2 = true; - -// Fictitious subsccription 3 -constexpr chip::FabricIndex fabric3 = 18; -constexpr chip::NodeId node3 = 44332211; -constexpr chip::SubscriptionId subscriptionId3 = 3; -constexpr uint16_t minInterval3 = 3; -constexpr uint16_t maxInterval3 = 30; -constexpr bool fabricFiltered3 = true; -// has both attributes and events -constexpr chip::EndpointId attributeEndpointId3_1 = 1; -constexpr chip::ClusterId attributeClusterId3_1 = 1; -constexpr chip::AttributeId attributeId3_1 = 1; -constexpr chip::EndpointId attributeEndpointId3_2 = 2; -constexpr chip::ClusterId attributeClusterId3_2 = 2; -constexpr chip::AttributeId attributeId3_2 = 2; -constexpr chip::EndpointId eventEndpointId3_1 = 1; -constexpr chip::ClusterId eventClusterId3_1 = 1; -constexpr chip::AttributeId eventId3_1 = 1; -constexpr bool isUrgentEvent3_1 = false; -constexpr chip::EndpointId eventEndpointId3_2 = 2; -constexpr chip::ClusterId eventClusterId3_2 = 2; -constexpr chip::AttributeId eventId3_2 = 2; -constexpr bool isUrgentEvent3_2 = true; - struct TestSubscriptionInfo : public chip::app::SubscriptionResumptionStorage::SubscriptionInfo { bool operator==(const SubscriptionInfo & that) const @@ -181,8 +126,6 @@ void TestCount(nlTestSuite * inSuite, void * inContext) } iterator->Release(); NL_TEST_ASSERT(inSuite, count == 0); - - NL_TEST_ASSERT(inSuite, false); } void TestState(nlTestSuite * inSuite, void * inContext) @@ -192,63 +135,63 @@ void TestState(nlTestSuite * inSuite, void * inContext) subscriptionStorage.Init(&storage); chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo1 = { - .mNodeId = node1, - .mFabricIndex = fabric1, - .mSubscriptionId = subscriptionId1, - .mMinInterval = minInterval1, - .mMaxInterval = maxInterval1, - .mFabricFiltered = fabricFiltered1, + .mNodeId = 1111, + .mFabricIndex = 41, + .mSubscriptionId = 1, + .mMinInterval = 1, + .mMaxInterval = 11, + .mFabricFiltered = true, }; subscriptionInfo1.mAttributePaths.Calloc(2); - subscriptionInfo1.mAttributePaths[0].mEndpointId = attributeEndpointId1_1; - subscriptionInfo1.mAttributePaths[0].mClusterId = attributeClusterId1_1; - subscriptionInfo1.mAttributePaths[0].mAttributeId = attributeId1_1; - subscriptionInfo1.mAttributePaths[1].mEndpointId = attributeEndpointId1_2; - subscriptionInfo1.mAttributePaths[1].mClusterId = attributeClusterId1_2; - subscriptionInfo1.mAttributePaths[1].mAttributeId = attributeId1_2; + subscriptionInfo1.mAttributePaths[0].mEndpointId = 1; + subscriptionInfo1.mAttributePaths[0].mClusterId = 1; + subscriptionInfo1.mAttributePaths[0].mAttributeId = 1; + subscriptionInfo1.mAttributePaths[1].mEndpointId = 2; + subscriptionInfo1.mAttributePaths[1].mClusterId = 2; + subscriptionInfo1.mAttributePaths[1].mAttributeId = 2; chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo2 = { - .mNodeId = node2, - .mFabricIndex = fabric2, - .mSubscriptionId = subscriptionId2, - .mMinInterval = minInterval2, - .mMaxInterval = maxInterval2, - .mFabricFiltered = fabricFiltered2, + .mNodeId = 2222, + .mFabricIndex = 42, + .mSubscriptionId = 2, + .mMinInterval = 2, + .mMaxInterval = 12, + .mFabricFiltered = false, }; subscriptionInfo2.mEventPaths.Calloc(2); - subscriptionInfo2.mEventPaths[0].mEndpointId = eventEndpointId2_1; - subscriptionInfo2.mEventPaths[0].mClusterId = eventClusterId2_1; - subscriptionInfo2.mEventPaths[0].mEventId = eventId2_1; - subscriptionInfo2.mEventPaths[0].mIsUrgentEvent = isUrgentEvent2_1; - subscriptionInfo2.mEventPaths[1].mEndpointId = eventEndpointId2_2; - subscriptionInfo2.mEventPaths[1].mClusterId = eventClusterId2_2; - subscriptionInfo2.mEventPaths[1].mEventId = eventId2_2; - subscriptionInfo2.mEventPaths[1].mIsUrgentEvent = isUrgentEvent2_2; + subscriptionInfo2.mEventPaths[0].mEndpointId = 3; + subscriptionInfo2.mEventPaths[0].mClusterId = 3; + subscriptionInfo2.mEventPaths[0].mEventId = 3; + subscriptionInfo2.mEventPaths[0].mIsUrgentEvent = false; + subscriptionInfo2.mEventPaths[1].mEndpointId = 4; + subscriptionInfo2.mEventPaths[1].mClusterId = 4; + subscriptionInfo2.mEventPaths[1].mEventId = 4; + subscriptionInfo2.mEventPaths[1].mIsUrgentEvent = true; chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo3 = { - .mNodeId = node3, - .mFabricIndex = fabric3, - .mSubscriptionId = subscriptionId3, - .mMinInterval = minInterval3, - .mMaxInterval = maxInterval3, - .mFabricFiltered = fabricFiltered3, + .mNodeId = 3333, + .mFabricIndex = 43, + .mSubscriptionId = 3, + .mMinInterval = 3, + .mMaxInterval = 13, + .mFabricFiltered = true, }; subscriptionInfo3.mAttributePaths.Calloc(2); - subscriptionInfo3.mAttributePaths[0].mEndpointId = attributeEndpointId3_1; - subscriptionInfo3.mAttributePaths[0].mClusterId = attributeClusterId3_1; - subscriptionInfo3.mAttributePaths[0].mAttributeId = attributeId3_1; - subscriptionInfo3.mAttributePaths[1].mEndpointId = attributeEndpointId3_2; - subscriptionInfo3.mAttributePaths[1].mClusterId = attributeClusterId3_2; - subscriptionInfo3.mAttributePaths[1].mAttributeId = attributeId3_2; + subscriptionInfo3.mAttributePaths[0].mEndpointId = 5; + subscriptionInfo3.mAttributePaths[0].mClusterId = 5; + subscriptionInfo3.mAttributePaths[0].mAttributeId = 5; + subscriptionInfo3.mAttributePaths[1].mEndpointId = 6; + subscriptionInfo3.mAttributePaths[1].mClusterId = 6; + subscriptionInfo3.mAttributePaths[1].mAttributeId = 6; subscriptionInfo3.mEventPaths.Calloc(2); - subscriptionInfo3.mEventPaths[0].mEndpointId = eventEndpointId3_1; - subscriptionInfo3.mEventPaths[0].mClusterId = eventClusterId3_1; - subscriptionInfo3.mEventPaths[0].mEventId = eventId3_1; - subscriptionInfo2.mEventPaths[0].mIsUrgentEvent = isUrgentEvent3_1; - subscriptionInfo3.mEventPaths[1].mEndpointId = eventEndpointId3_2; - subscriptionInfo3.mEventPaths[1].mClusterId = eventClusterId3_2; - subscriptionInfo3.mEventPaths[1].mEventId = eventId3_2; - subscriptionInfo2.mEventPaths[1].mIsUrgentEvent = isUrgentEvent3_2; + subscriptionInfo3.mEventPaths[0].mEndpointId = 7; + subscriptionInfo3.mEventPaths[0].mClusterId = 7; + subscriptionInfo3.mEventPaths[0].mEventId = 7; + subscriptionInfo2.mEventPaths[0].mIsUrgentEvent = true; + subscriptionInfo3.mEventPaths[1].mEndpointId = 8; + subscriptionInfo3.mEventPaths[1].mClusterId = 8; + subscriptionInfo3.mEventPaths[1].mEventId = 8; + subscriptionInfo2.mEventPaths[1].mIsUrgentEvent = false; CHIP_ERROR err; err = subscriptionStorage.Save(subscriptionInfo1); @@ -309,20 +252,20 @@ void TestStateUnexpectedFields(nlTestSuite * inSuite, void * inContext) // Write additional entries at the end of TLV and see it still loads correctly chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo1 = { - .mNodeId = node1, - .mFabricIndex = fabric1, - .mSubscriptionId = subscriptionId1, - .mMinInterval = minInterval1, - .mMaxInterval = maxInterval1, - .mFabricFiltered = fabricFiltered1, + .mNodeId = 4444, + .mFabricIndex = 44, + .mSubscriptionId = 4, + .mMinInterval = 4, + .mMaxInterval = 14, + .mFabricFiltered = true, }; subscriptionInfo1.mAttributePaths.Calloc(2); - subscriptionInfo1.mAttributePaths[0].mEndpointId = attributeEndpointId1_1; - subscriptionInfo1.mAttributePaths[0].mClusterId = attributeClusterId1_1; - subscriptionInfo1.mAttributePaths[0].mAttributeId = attributeId1_1; - subscriptionInfo1.mAttributePaths[1].mEndpointId = attributeEndpointId1_2; - subscriptionInfo1.mAttributePaths[1].mClusterId = attributeClusterId1_2; - subscriptionInfo1.mAttributePaths[1].mAttributeId = attributeId1_2; + subscriptionInfo1.mAttributePaths[0].mEndpointId = 9; + subscriptionInfo1.mAttributePaths[0].mClusterId = 9; + subscriptionInfo1.mAttributePaths[0].mAttributeId = 9; + subscriptionInfo1.mAttributePaths[1].mEndpointId = 10; + subscriptionInfo1.mAttributePaths[1].mClusterId = 10; + subscriptionInfo1.mAttributePaths[1].mAttributeId = 10; chip::Platform::ScopedMemoryBuffer backingBuffer; backingBuffer.Calloc(subscriptionStorage.TestMaxSubscriptionSize()); @@ -366,20 +309,20 @@ void TestStateTooBigToLoad(nlTestSuite * inSuite, void * inContext) // Write additional too-big data at the end of TLV and see it fails to loads and entry deleted chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo1 = { - .mNodeId = node1, - .mFabricIndex = fabric1, - .mSubscriptionId = subscriptionId1, - .mMinInterval = minInterval1, - .mMaxInterval = maxInterval1, - .mFabricFiltered = fabricFiltered1, + .mNodeId = 5555, + .mFabricIndex = 45, + .mSubscriptionId = 5, + .mMinInterval = 5, + .mMaxInterval = 15, + .mFabricFiltered = false, }; subscriptionInfo1.mAttributePaths.Calloc(2); - subscriptionInfo1.mAttributePaths[0].mEndpointId = attributeEndpointId1_1; - subscriptionInfo1.mAttributePaths[0].mClusterId = attributeClusterId1_1; - subscriptionInfo1.mAttributePaths[0].mAttributeId = attributeId1_1; - subscriptionInfo1.mAttributePaths[1].mEndpointId = attributeEndpointId1_2; - subscriptionInfo1.mAttributePaths[1].mClusterId = attributeClusterId1_2; - subscriptionInfo1.mAttributePaths[1].mAttributeId = attributeId1_2; + subscriptionInfo1.mAttributePaths[0].mEndpointId = 11; + subscriptionInfo1.mAttributePaths[0].mClusterId = 11; + subscriptionInfo1.mAttributePaths[0].mAttributeId = 11; + subscriptionInfo1.mAttributePaths[1].mEndpointId = 12; + subscriptionInfo1.mAttributePaths[1].mClusterId = 12; + subscriptionInfo1.mAttributePaths[1].mAttributeId = 12; chip::Platform::ScopedMemoryBuffer backingBuffer; backingBuffer.Calloc(subscriptionStorage.TestMaxSubscriptionSize() * 2); From 3506edf5dea26029f7f003ea265e34aa0cd475f5 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 13:26:53 -0800 Subject: [PATCH 30/41] Address PR comments: Nullptr checks Minor refactor Unit test fix --- src/app/InteractionModelEngine.cpp | 5 +- src/app/ReadHandler.cpp | 26 +++++---- src/app/ReadHandler.h | 13 ++++- .../SimpleSubscriptionResumptionStorage.cpp | 56 +++++++++---------- src/app/SimpleSubscriptionResumptionStorage.h | 10 ++-- src/app/SubscriptionResumptionStorage.h | 22 -------- ...estSimpleSubscriptionResumptionStorage.cpp | 10 ++-- 7 files changed, 62 insertions(+), 80 deletions(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index f47c4a6b8b75f5..82ebdc0315e29e 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1586,10 +1586,7 @@ void InteractionModelEngine::OnFabricRemoved(const FabricTable & fabricTable, Fa CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() { #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - if (!mpSubscriptionResumptionStorage) - { - return CHIP_NO_ERROR; - } + ReturnErrorCodeIf(!mpSubscriptionResumptionStorage, CHIP_NO_ERROR); SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo; auto * iterator = mpSubscriptionResumptionStorage->IterateSubscriptions(); diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 89135942cc9a2c..c11d9c9e70b410 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -136,10 +136,10 @@ ReadHandler::~ReadHandler() InteractionModelEngine::GetInstance()->ReleaseDataVersionFilterList(mpDataVersionFilterList); } -void ReadHandler::Close(bool keepPersisted) +void ReadHandler::Close(CloseOptions options) { #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - if (!keepPersisted) + if (options == CloseOptions::kDropPersistedSubscriptions) { auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage(); if (subscriptionResumptionStorage) @@ -370,7 +370,7 @@ void ReadHandler::OnResponseTimeout(Messaging::ExchangeContext * apExchangeConte ChipLogValueExchange(apExchangeContext)); #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS // TODO: Have a retry mechanism tied to wake interval for IC devices - CloseButKeepPersisted(); + Close(CloseOptions::kKeepPersistedSubscriptions); #else Close(); #endif @@ -720,11 +720,16 @@ CHIP_ERROR ReadHandler::ProcessSubscribeRequest(System::PacketBufferHandle && aP mExchangeCtx->WillSendMessage(); #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + PersistSubscription(); +#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + + return CHIP_NO_ERROR; +} + +void ReadHandler::PersistSubscription() +{ auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage(); - if (!subscriptionResumptionStorage) - { - return CHIP_NO_ERROR; - } + VerifyOrReturn(subscriptionResumptionStorage != nullptr); SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = GetInitiatorNodeId(), .mFabricIndex = GetAccessingFabricIndex(), @@ -741,6 +746,7 @@ CHIP_ERROR ReadHandler::ProcessSubscribeRequest(System::PacketBufferHandle && aP } attributePath = mpAttributePathList; subscriptionInfo.mAttributePaths.Calloc(attributePathCount); + VerifyOrReturn(subscriptionInfo.mAttributePaths.Get() != nullptr); for (size_t i = 0; i < attributePathCount; i++) { subscriptionInfo.mAttributePaths[i].SetValues(attributePath->mValue); @@ -756,16 +762,14 @@ CHIP_ERROR ReadHandler::ProcessSubscribeRequest(System::PacketBufferHandle && aP } eventPath = mpEventPathList; subscriptionInfo.mEventPaths.Calloc(eventPathCount); + VerifyOrReturn(subscriptionInfo.mEventPaths.Get() != nullptr); for (size_t i = 0; i < eventPathCount; i++) { subscriptionInfo.mEventPaths[i].SetValues(eventPath->mValue); eventPath = eventPath->mpNext; } - err = subscriptionResumptionStorage->Save(subscriptionInfo); -#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - - return CHIP_NO_ERROR; + subscriptionResumptionStorage->Save(subscriptionInfo); } void ReadHandler::OnUnblockHoldReportCallback(System::Layer * apSystemLayer, void * apAppState) diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index f7a85cf686e4ab..9e12c2f800d4f9 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -263,7 +263,8 @@ class ReadHandler : public Messaging::ExchangeDelegate * * @brief Resume a persisted subscription * - * Used after ReadHandler(ManagementCallback & apCallback). This + * Used after ReadHandler(ManagementCallback & apCallback). This will start a CASE session + * with the subscriber if one doesn't already exist, and send full priming report when connected. */ void ResumeSubscription(CASESessionManager & caseSessionManager, SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo); @@ -381,6 +382,11 @@ class ReadHandler : public Messaging::ExchangeDelegate AwaitingDestruction, ///< The object has completed its work and is awaiting destruction by the application. }; + enum class CloseOptions : uint8_t + { + kDropPersistedSubscriptions, + kKeepPersistedSubscriptions + }; /** * Called internally to signal the completion of all work on this objecta and signal to a registered callback that it's * safe to release this object. @@ -389,8 +395,7 @@ class ReadHandler : public Messaging::ExchangeDelegate * CHIP_CONFIG_PERSIST_SUBSCRIPTIONS not enabled * */ - void Close(bool keepPersisted = false); - void CloseButKeepPersisted() { Close(true); } + void Close(CloseOptions options = CloseOptions::kDropPersistedSubscriptions); static void OnUnblockHoldReportCallback(System::Layer * apSystemLayer, void * apAppState); static void OnRefreshSubscribeTimerSyncCallback(System::Layer * apSystemLayer, void * apAppState); @@ -410,6 +415,8 @@ class ReadHandler : public Messaging::ExchangeDelegate const char * GetStateStr() const; + void PersistSubscription(); + // Helpers for managing our state flags properly. void SetStateFlag(ReadHandlerFlags aFlag, bool aValue = true); void ClearStateFlag(ReadHandlerFlags aFlag); diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 605770a63b71f8..9eae581d7c7373 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -54,7 +54,7 @@ SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::SimpleSubsc size_t SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Count() { - return mStorage.Count(); + return static_cast(mStorage.Count()); } bool SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Next(SubscriptionInfo & output) @@ -90,14 +90,14 @@ SubscriptionResumptionStorage::SubscriptionInfoIterator * SimpleSubscriptionResu return mSubscriptionInfoIterators.CreateObject(*this); } -size_t SimpleSubscriptionResumptionStorage::MaxCount() +uint16_t SimpleSubscriptionResumptionStorage::MaxCount() { // Storage should try to hold the last known CHIP_IM_MAX_NUM_SUBSCRIPTIONS and // use the larger of that value and the current CHIP_IM_MAX_NUM_SUBSCRIPTIONS // value to make sure a reduced CHIP_IM_MAX_NUM_SUBSCRIPTIONS value would not // cause storage leaks / orphaned persisted subscriptions - uint32_t countMax = 0; + uint16_t countMax = 0; uint16_t len = sizeof(countMax); CHIP_ERROR err = mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMax, len); @@ -106,14 +106,14 @@ size_t SimpleSubscriptionResumptionStorage::MaxCount() return CHIP_IM_MAX_NUM_SUBSCRIPTIONS; } - return max(static_cast(countMax), CHIP_IM_MAX_NUM_SUBSCRIPTIONS); + return max(countMax, CHIP_IM_MAX_NUM_SUBSCRIPTIONS); } -size_t SimpleSubscriptionResumptionStorage::Count() +uint16_t SimpleSubscriptionResumptionStorage::Count() { - size_t countMax = MaxCount(); - size_t subscriptionCount = 0; - for (size_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) + uint16_t countMax = MaxCount(); + uint16_t subscriptionCount = 0; + for (uint16_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) { if (mStorage->SyncDoesKeyExist(DefaultStorageKeyAllocator::SubscriptionResumption(subscriptionIndex).KeyName())) { @@ -124,19 +124,16 @@ size_t SimpleSubscriptionResumptionStorage::Count() return subscriptionCount; } -CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(size_t subscriptionIndex) +CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(uint16_t subscriptionIndex) { return mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumption(subscriptionIndex).KeyName()); } -CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(size_t subscriptionIndex, SubscriptionInfo & subscriptionInfo) +CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(uint16_t subscriptionIndex, SubscriptionInfo & subscriptionInfo) { Platform::ScopedMemoryBuffer backingBuffer; backingBuffer.Calloc(MaxSubscriptionSize()); - if (backingBuffer.Get() == nullptr) - { - return CHIP_ERROR_NO_MEMORY; - } + ReturnErrorCodeIf(backingBuffer.Get() == nullptr, CHIP_ERROR_NO_MEMORY); uint16_t len = static_cast(MaxSubscriptionSize()); ReturnErrorOnFailure(mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumption(subscriptionIndex).KeyName(), @@ -182,6 +179,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(size_t subscriptionIndex, S if (pathCount) { subscriptionInfo.mAttributePaths.Calloc(pathCount); + ReturnErrorCodeIf(subscriptionInfo.mAttributePaths.Get() == nullptr, CHIP_ERROR_NO_MEMORY); for (uint16_t pathIndex = 0; pathIndex < pathCount; pathIndex++) { ReturnErrorOnFailure(reader.Next(kEndpointIdTag)); @@ -204,6 +202,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(size_t subscriptionIndex, S if (pathCount) { subscriptionInfo.mEventPaths.Calloc(pathCount); + ReturnErrorCodeIf(subscriptionInfo.mEventPaths.Get() == nullptr, CHIP_ERROR_NO_MEMORY); for (uint16_t pathIndex = 0; pathIndex < pathCount; pathIndex++) { EventPathType eventPathType; @@ -271,15 +270,15 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(TLV::TLVWriter & writer, Su CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscriptionInfo) { // Ensure this version of CHIP_IM_MAX_NUM_SUBSCRIPTIONS is recorded on Save - size_t countMax = MaxCount(); - uint32_t countMaxToSave = static_cast(std::max(countMax, CHIP_IM_MAX_NUM_SUBSCRIPTIONS)); + uint16_t countMax = MaxCount(); + uint16_t countMaxToSave = std::max(countMax, CHIP_IM_MAX_NUM_SUBSCRIPTIONS); ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), - &countMaxToSave, static_cast(sizeof(uint32_t)))); + &countMaxToSave, sizeof(uint16_t))); // Find empty index or duplicate if exists - size_t subscriptionIndex; - size_t firstEmptySubscriptionIndex = countMax; // initialize to out of bounds as "not set" - size_t count = 0; + uint16_t subscriptionIndex; + uint16_t firstEmptySubscriptionIndex = countMax; // initialize to out of bounds as "not set" + uint16_t count = 0; for (subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) { SubscriptionInfo currentSubscriptionInfo; @@ -327,10 +326,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip // Now construct subscription state and save Platform::ScopedMemoryBuffer backingBuffer; backingBuffer.Calloc(MaxSubscriptionSize()); - if (backingBuffer.Get() == nullptr) - { - return CHIP_ERROR_NO_MEMORY; - } + ReturnErrorCodeIf(backingBuffer.Get() == nullptr, CHIP_ERROR_NO_MEMORY); TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), MaxSubscriptionSize()); @@ -351,10 +347,10 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(NodeId nodeId, FabricIndex fabricIndex, SubscriptionId subscriptionId) { CHIP_ERROR deleteErr = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; - size_t countMax = MaxCount(); + uint16_t countMax = MaxCount(); - size_t count = 0; - for (size_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) + uint16_t count = 0; + for (uint16_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) { SubscriptionInfo subscriptionInfo; CHIP_ERROR err = Load(subscriptionIndex, subscriptionInfo); @@ -391,10 +387,10 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteMaxCount() CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteAll(FabricIndex fabricIndex) { CHIP_ERROR deleteErr = CHIP_NO_ERROR; - size_t countMax = MaxCount(); + uint16_t countMax = MaxCount(); - size_t count = 0; - for (size_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) + uint16_t count = 0; + for (uint16_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) { SubscriptionInfo subscriptionInfo; CHIP_ERROR err = Load(subscriptionIndex, subscriptionInfo); diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index bdd0f81abb4a5f..31394056835e20 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -56,10 +56,10 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage protected: CHIP_ERROR Save(TLV::TLVWriter & writer, SubscriptionInfo & subscriptionInfo); - CHIP_ERROR Load(size_t subscriptionIndex, SubscriptionInfo & subscriptionInfo); - CHIP_ERROR Delete(size_t subscriptionIndex); - size_t Count(); - size_t MaxCount(); + CHIP_ERROR Load(uint16_t subscriptionIndex, SubscriptionInfo & subscriptionInfo); + CHIP_ERROR Delete(uint16_t subscriptionIndex); + uint16_t Count(); + uint16_t MaxCount(); CHIP_ERROR DeleteMaxCount(); class SimpleSubscriptionInfoIterator : public SubscriptionInfoIterator @@ -73,7 +73,7 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage private: SimpleSubscriptionResumptionStorage & mStorage; uint16_t mNextIndex; - size_t mMaxCount; + uint16_t mMaxCount; }; static constexpr size_t MaxScopedNodeIdSize() { return TLV::EstimateStructOverhead(sizeof(NodeId), sizeof(FabricIndex)); } diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index e2c657836bc9ab..f58f6d3be5c5d5 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -82,28 +82,6 @@ class SubscriptionResumptionStorage using SubscriptionInfoIterator = CommonIterator; - /** - * Struct to hold list of subscriptions - */ - // struct SubscriptionList - // { - // size_t mSize; - // std::unique_ptr mSubscriptions; - // SubscriptionInfo & operator[](size_t index) { return mSubscriptions[index]; } - // const SubscriptionInfo & operator[](size_t index) const { return mSubscriptions[index]; } - // }; - // - // /** - // * Struct to hold index of all nodes that have persisted subscriptions - // */ - // struct SubscriptionIndex - // { - // size_t mSize; - // std::unique_ptr mNodes; - // ScopedNodeId & operator[](size_t index) { return mNodes[index]; } - // const ScopedNodeId & operator[](size_t index) const { return mNodes[index]; } - // }; - virtual ~SubscriptionResumptionStorage(){}; /** diff --git a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp index 8740ff36db9928..3803fafc51f87b 100644 --- a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp +++ b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp @@ -82,16 +82,16 @@ void TestCount(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, subscriptionStorage.TestMaxCount() == CHIP_IM_MAX_NUM_SUBSCRIPTIONS); // Force larger value and check that it returns the larger value - uint32_t countMaxToSave = 2 * CHIP_IM_MAX_NUM_SUBSCRIPTIONS; + uint16_t countMaxToSave = 2 * CHIP_IM_MAX_NUM_SUBSCRIPTIONS; storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMaxToSave, - static_cast(sizeof(uint32_t))); + sizeof(uint16_t)); NL_TEST_ASSERT(inSuite, subscriptionStorage.TestMaxCount() == (2 * CHIP_IM_MAX_NUM_SUBSCRIPTIONS)); // Reset storage.SyncDeleteKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName()); // Write some subscriptions and see the counts are correct - chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = node1, .mFabricIndex = fabric1 }; + chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = 6666, .mFabricIndex = 46 }; for (size_t i = 0; i < (CHIP_IM_MAX_NUM_SUBSCRIPTIONS / 2); i++) { @@ -113,7 +113,7 @@ void TestCount(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, count == (CHIP_IM_MAX_NUM_SUBSCRIPTIONS / 2)); // Delete all and verify iterator counts 0 - CHIP_ERROR err = subscriptionStorage.DeleteAll(fabric1); + CHIP_ERROR err = subscriptionStorage.DeleteAll(46); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); iterator = subscriptionStorage.IterateSubscriptions(); NL_TEST_ASSERT(inSuite, iterator->Count() == 0); @@ -235,7 +235,7 @@ void TestState(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, !iterator->Next(subscriptionInfo)); iterator->Release(); - uint32_t countMax = 0; + uint16_t countMax = 0; uint16_t len = sizeof(countMax); err = storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMax, len); NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); From 122224cc89a02be3437180e0db90b3fd1d013865 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 14:28:36 -0800 Subject: [PATCH 31/41] Changed storage MaxCount mechanics to Init time clean up --- .../SimpleSubscriptionResumptionStorage.cpp | 77 ++++++++----------- src/app/SimpleSubscriptionResumptionStorage.h | 9 +-- ...estSimpleSubscriptionResumptionStorage.cpp | 51 ++++++++---- 3 files changed, 71 insertions(+), 66 deletions(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 9eae581d7c7373..009b6b4ca8c7cc 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -49,7 +49,6 @@ SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::SimpleSubsc mStorage(storage) { mNextIndex = 0; - mMaxCount = mStorage.MaxCount(); } size_t SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Count() @@ -59,7 +58,7 @@ size_t SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Coun bool SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Next(SubscriptionInfo & output) { - for (; mNextIndex < mMaxCount; mNextIndex++) + for (; mNextIndex < CHIP_IM_MAX_NUM_SUBSCRIPTIONS; mNextIndex++) { CHIP_ERROR err = mStorage.Load(mNextIndex, output); if (err == CHIP_NO_ERROR) @@ -85,35 +84,42 @@ void SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Releas mStorage.mSubscriptionInfoIterators.ReleaseObject(this); } -SubscriptionResumptionStorage::SubscriptionInfoIterator * SimpleSubscriptionResumptionStorage::IterateSubscriptions() +CHIP_ERROR SimpleSubscriptionResumptionStorage::Init(PersistentStorageDelegate * storage) { - return mSubscriptionInfoIterators.CreateObject(*this); -} + VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + mStorage = storage; -uint16_t SimpleSubscriptionResumptionStorage::MaxCount() -{ - // Storage should try to hold the last known CHIP_IM_MAX_NUM_SUBSCRIPTIONS and - // use the larger of that value and the current CHIP_IM_MAX_NUM_SUBSCRIPTIONS - // value to make sure a reduced CHIP_IM_MAX_NUM_SUBSCRIPTIONS value would not - // cause storage leaks / orphaned persisted subscriptions - - uint16_t countMax = 0; - uint16_t len = sizeof(countMax); + uint16_t countMax; + uint16_t len = sizeof(countMax); CHIP_ERROR err = mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMax, len); - if (err != CHIP_NO_ERROR) + // If there's a previous MacCount and it's larger than CHIP_IM_MAX_NUM_SUBSCRIPTIONS, + // clean up subscriptions beyond the limit + if ((err == CHIP_NO_ERROR) && (countMax != CHIP_IM_MAX_NUM_SUBSCRIPTIONS)) { - return CHIP_IM_MAX_NUM_SUBSCRIPTIONS; + for (uint16_t subscriptionIndex = CHIP_IM_MAX_NUM_SUBSCRIPTIONS; subscriptionIndex < countMax; subscriptionIndex++) + { + Delete(subscriptionIndex); + } } - return max(countMax, CHIP_IM_MAX_NUM_SUBSCRIPTIONS); + // Always save the current CHIP_IM_MAX_NUM_SUBSCRIPTIONS + uint16_t countMaxToSave = CHIP_IM_MAX_NUM_SUBSCRIPTIONS; + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), + &countMaxToSave, sizeof(uint16_t))); + + return CHIP_NO_ERROR; +} + +SubscriptionResumptionStorage::SubscriptionInfoIterator * SimpleSubscriptionResumptionStorage::IterateSubscriptions() +{ + return mSubscriptionInfoIterators.CreateObject(*this); } uint16_t SimpleSubscriptionResumptionStorage::Count() { - uint16_t countMax = MaxCount(); uint16_t subscriptionCount = 0; - for (uint16_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) + for (uint16_t subscriptionIndex = 0; subscriptionIndex < CHIP_IM_MAX_NUM_SUBSCRIPTIONS; subscriptionIndex++) { if (mStorage->SyncDoesKeyExist(DefaultStorageKeyAllocator::SubscriptionResumption(subscriptionIndex).KeyName())) { @@ -269,23 +275,16 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(TLV::TLVWriter & writer, Su CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscriptionInfo) { - // Ensure this version of CHIP_IM_MAX_NUM_SUBSCRIPTIONS is recorded on Save - uint16_t countMax = MaxCount(); - uint16_t countMaxToSave = std::max(countMax, CHIP_IM_MAX_NUM_SUBSCRIPTIONS); - ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), - &countMaxToSave, sizeof(uint16_t))); - // Find empty index or duplicate if exists uint16_t subscriptionIndex; - uint16_t firstEmptySubscriptionIndex = countMax; // initialize to out of bounds as "not set" - uint16_t count = 0; - for (subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) + uint16_t firstEmptySubscriptionIndex = CHIP_IM_MAX_NUM_SUBSCRIPTIONS; // initialize to out of bounds as "not set" + for (subscriptionIndex = 0; subscriptionIndex < CHIP_IM_MAX_NUM_SUBSCRIPTIONS; subscriptionIndex++) { SubscriptionInfo currentSubscriptionInfo; CHIP_ERROR err = Load(subscriptionIndex, currentSubscriptionInfo); // if empty and firstEmptySubscriptionIndex isn't set yet, then mark empty spot - if ((firstEmptySubscriptionIndex == countMax) && (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)) + if ((firstEmptySubscriptionIndex == CHIP_IM_MAX_NUM_SUBSCRIPTIONS) && (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)) { firstEmptySubscriptionIndex = subscriptionIndex; } @@ -299,26 +298,16 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip { Delete(subscriptionIndex); // if duplicate is the first empty spot, then also set it - if (firstEmptySubscriptionIndex == countMax) + if (firstEmptySubscriptionIndex == CHIP_IM_MAX_NUM_SUBSCRIPTIONS) { firstEmptySubscriptionIndex = subscriptionIndex; } } - else - { - count++; - } } } - // Fail if already have more persisted subscriptions than currently allowed - if (count >= CHIP_IM_MAX_NUM_SUBSCRIPTIONS) - { - return CHIP_ERROR_NO_MEMORY; - } - // Fail if no empty space - if (firstEmptySubscriptionIndex == countMax) + if (firstEmptySubscriptionIndex == CHIP_IM_MAX_NUM_SUBSCRIPTIONS) { return CHIP_ERROR_NO_MEMORY; } @@ -347,10 +336,9 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(NodeId nodeId, FabricIndex fabricIndex, SubscriptionId subscriptionId) { CHIP_ERROR deleteErr = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; - uint16_t countMax = MaxCount(); uint16_t count = 0; - for (uint16_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) + for (uint16_t subscriptionIndex = 0; subscriptionIndex < CHIP_IM_MAX_NUM_SUBSCRIPTIONS; subscriptionIndex++) { SubscriptionInfo subscriptionInfo; CHIP_ERROR err = Load(subscriptionIndex, subscriptionInfo); @@ -387,10 +375,9 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteMaxCount() CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteAll(FabricIndex fabricIndex) { CHIP_ERROR deleteErr = CHIP_NO_ERROR; - uint16_t countMax = MaxCount(); uint16_t count = 0; - for (uint16_t subscriptionIndex = 0; subscriptionIndex < countMax; subscriptionIndex++) + for (uint16_t subscriptionIndex = 0; subscriptionIndex < CHIP_IM_MAX_NUM_SUBSCRIPTIONS; subscriptionIndex++) { SubscriptionInfo subscriptionInfo; CHIP_ERROR err = Load(subscriptionIndex, subscriptionInfo); diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index 31394056835e20..11c8c709b5439c 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -39,12 +39,7 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage public: static constexpr size_t kIteratorsMax = CHIP_CONFIG_MAX_GROUP_CONCURRENT_ITERATORS; - CHIP_ERROR Init(PersistentStorageDelegate * storage) - { - VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT); - mStorage = storage; - return CHIP_NO_ERROR; - } + CHIP_ERROR Init(PersistentStorageDelegate * storage); SubscriptionInfoIterator * IterateSubscriptions() override; @@ -59,7 +54,6 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage CHIP_ERROR Load(uint16_t subscriptionIndex, SubscriptionInfo & subscriptionInfo); CHIP_ERROR Delete(uint16_t subscriptionIndex); uint16_t Count(); - uint16_t MaxCount(); CHIP_ERROR DeleteMaxCount(); class SimpleSubscriptionInfoIterator : public SubscriptionInfoIterator @@ -73,7 +67,6 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage private: SimpleSubscriptionResumptionStorage & mStorage; uint16_t mNextIndex; - uint16_t mMaxCount; }; static constexpr size_t MaxScopedNodeIdSize() { return TLV::EstimateStructOverhead(sizeof(NodeId), sizeof(FabricIndex)); } diff --git a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp index 3803fafc51f87b..f74075000e663d 100644 --- a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp +++ b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp @@ -26,7 +26,6 @@ class SimpleSubscriptionResumptionStorageTest : public chip::app::SimpleSubscriptionResumptionStorage { public: - size_t TestMaxCount() { return MaxCount(); } CHIP_ERROR TestSave(chip::TLV::TLVWriter & writer, chip::app::SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo) { @@ -78,18 +77,6 @@ void TestCount(nlTestSuite * inSuite, void * inContext) SimpleSubscriptionResumptionStorageTest subscriptionStorage; subscriptionStorage.Init(&storage); - // Check by default it returns correct value - NL_TEST_ASSERT(inSuite, subscriptionStorage.TestMaxCount() == CHIP_IM_MAX_NUM_SUBSCRIPTIONS); - - // Force larger value and check that it returns the larger value - uint16_t countMaxToSave = 2 * CHIP_IM_MAX_NUM_SUBSCRIPTIONS; - storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMaxToSave, - sizeof(uint16_t)); - NL_TEST_ASSERT(inSuite, subscriptionStorage.TestMaxCount() == (2 * CHIP_IM_MAX_NUM_SUBSCRIPTIONS)); - - // Reset - storage.SyncDeleteKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName()); - // Write some subscriptions and see the counts are correct chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = 6666, .mFabricIndex = 46 }; @@ -128,6 +115,43 @@ void TestCount(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, count == 0); } +void TestMaxCount(nlTestSuite * inSuite, void * inContext) +{ + // Force large MacCount value and check that Init resets it properly, and deletes extra subs: + + chip::TestPersistentStorageDelegate storage; + SimpleSubscriptionResumptionStorageTest subscriptionStorage; + + // First set a large MaxCount before Init + uint16_t countMaxToSave = 2 * CHIP_IM_MAX_NUM_SUBSCRIPTIONS; + CHIP_ERROR err = storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), + &countMaxToSave, sizeof(uint16_t)); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + // Then write something beyond CHIP_IM_MAX_NUM_SUBSCRIPTIONS + chip::Platform::ScopedMemoryBuffer junkBytes; + junkBytes.Calloc(subscriptionStorage.TestMaxSubscriptionSize() / 2); + NL_TEST_ASSERT(inSuite, junkBytes.Get() != nullptr); + NL_TEST_ASSERT(inSuite, + storage.SyncSetKeyValue( + chip::DefaultStorageKeyAllocator::SubscriptionResumption(CHIP_IM_MAX_NUM_SUBSCRIPTIONS + 1).KeyName(), + junkBytes.Get(), static_cast(subscriptionStorage.TestMaxSubscriptionSize() / 2)) == CHIP_NO_ERROR); + + subscriptionStorage.Init(&storage); + + // First check the MaxCount is reset to CHIP_IM_MAX_NUM_SUBSCRIPTIONS + uint16_t countMax = 0; + uint16_t len = sizeof(countMax); + err = storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMax, len); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, countMax == CHIP_IM_MAX_NUM_SUBSCRIPTIONS); + + // Then check the fake sub is no more + NL_TEST_ASSERT(inSuite, + !storage.SyncDoesKeyExist( + chip::DefaultStorageKeyAllocator::SubscriptionResumption(CHIP_IM_MAX_NUM_SUBSCRIPTIONS + 1).KeyName())); +} + void TestState(nlTestSuite * inSuite, void * inContext) { chip::TestPersistentStorageDelegate storage; @@ -411,6 +435,7 @@ int Test_Teardown(void * inContext) static const nlTest sTests[] = { NL_TEST_DEF("TestCount", TestCount), + NL_TEST_DEF("TestMaxCount", TestMaxCount), NL_TEST_DEF("TestState", TestState), NL_TEST_DEF("TestStateUnexpectedFields", TestStateUnexpectedFields), NL_TEST_DEF("TestStateTooBigToLoad", TestStateTooBigToLoad), From 22cb37cab1d785dcd7e0450c8481c8dcecb64747 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 14:44:19 -0800 Subject: [PATCH 32/41] Clean up comments and unused commented-out old code --- src/app/SubscriptionResumptionStorage.h | 30 +------------------------ 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index f58f6d3be5c5d5..044c2afd96c28d 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -35,7 +35,7 @@ namespace app { class SubscriptionResumptionStorage { public: - // Structs to hold + // Structs to hold path param values as is_trivial struct struct AttributePathParamsValues { ClusterId mClusterId; @@ -84,25 +84,6 @@ class SubscriptionResumptionStorage virtual ~SubscriptionResumptionStorage(){}; - /** - * Recover fabric-scoped node identities of persisted subscribers. - * - * @param subscriberIndex the nodes for previously persisted subscribers - * @return CHIP_NO_ERROR on success, CHIP_ERROR_KEY_NOT_FOUND if no subscription resumption information can be found, else an - * appropriate CHIP error on failure - */ - // virtual CHIP_ERROR LoadIndex(SubscriptionIndex & subscriberIndex) = 0; - - /** - * Recover subscription resumption info for a given fabric-scoped node identity. - * - * @param node the node for which to recover subscription resumption information - * @param subscriptions (out) recovered subscriptions info - * @return CHIP_NO_ERROR on success, CHIP_ERROR_KEY_NOT_FOUND if no subscription resumption information can be found, else an - * appropriate CHIP error on failure - */ - // virtual CHIP_ERROR FindByScopedNodeId(ScopedNodeId node, SubscriptionList & subscriptions) = 0; - /** * Iterate through persisted subscriptions * @@ -118,15 +99,6 @@ class SubscriptionResumptionStorage */ virtual CHIP_ERROR Save(SubscriptionInfo & subscriptionInfo) = 0; - /** - * Save subscription resumption information to storage. - * - * @param subscriptionInfo the subscription information to delete - only node and subscriptionId will be used to find the - * subscription - * @return CHIP_NO_ERROR on success, else an appropriate CHIP error on failure - */ - // virtual CHIP_ERROR Delete(const SubscriptionInfo & subscriptionInfo) = 0; - /** * Delete subscription resumption information by node ID, fabric index, and subscription ID. * From 2445ab5a273e5b104459ec72bdd053a4365d5dd4 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 15:02:01 -0800 Subject: [PATCH 33/41] Addressed PR comments: Removed AllocatedCount, and made AllocatedSize return count of elements --- src/app/InteractionModelEngine.cpp | 4 ++-- src/app/ReadHandler.cpp | 4 ++-- src/app/SimpleSubscriptionResumptionStorage.cpp | 8 ++++---- src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp | 8 ++++---- src/lib/support/ScopedBuffer.h | 7 ++----- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 82ebdc0315e29e..cc2012d1816475 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1592,8 +1592,8 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions() auto * iterator = mpSubscriptionResumptionStorage->IterateSubscriptions(); while (iterator->Next(subscriptionInfo)) { - auto requestedAttributePathCount = subscriptionInfo.mAttributePaths.AllocatedCount(); - auto requestedEventPathCount = subscriptionInfo.mEventPaths.AllocatedCount(); + auto requestedAttributePathCount = subscriptionInfo.mAttributePaths.AllocatedSize(); + auto requestedEventPathCount = subscriptionInfo.mEventPaths.AllocatedSize(); if (!EnsureResourceForSubscription(subscriptionInfo.mFabricIndex, requestedAttributePathCount, requestedEventPathCount)) { ChipLogProgress(InteractionModel, "no resource for Subscription resumption"); diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index c11d9c9e70b410..95f843b64de8b8 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -81,7 +81,7 @@ void ReadHandler::ResumeSubscription(CASESessionManager & caseSessionManager, // Move dynamically allocated attributes and events from the SubscriptionInfo struct into // the object pool managed by the IM engine - for (size_t i = 0; i < subscriptionInfo.mAttributePaths.AllocatedCount(); i++) + for (size_t i = 0; i < subscriptionInfo.mAttributePaths.AllocatedSize(); i++) { AttributePathParams attributePathParams = subscriptionInfo.mAttributePaths[i].GetParams(); CHIP_ERROR err = @@ -92,7 +92,7 @@ void ReadHandler::ResumeSubscription(CASESessionManager & caseSessionManager, return; } } - for (size_t i = 0; i < subscriptionInfo.mEventPaths.AllocatedCount(); i++) + for (size_t i = 0; i < subscriptionInfo.mEventPaths.AllocatedSize(); i++) { EventPathParams eventPathParams = subscriptionInfo.mEventPaths[i].GetParams(); CHIP_ERROR err = InteractionModelEngine::GetInstance()->PushFrontEventPathParamsList(mpEventPathList, eventPathParams); diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 009b6b4ca8c7cc..296fd35ff0b9b4 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -244,16 +244,16 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(TLV::TLVWriter & writer, Su ReturnErrorOnFailure(writer.Put(kMaxIntervalTag, subscriptionInfo.mMaxInterval)); ReturnErrorOnFailure(writer.Put(kFabricFilteredTag, subscriptionInfo.mFabricFiltered)); - ReturnErrorOnFailure(writer.Put(kPathCountTag, static_cast(subscriptionInfo.mAttributePaths.AllocatedCount()))); - for (size_t currentPathIndex = 0; currentPathIndex < subscriptionInfo.mAttributePaths.AllocatedCount(); currentPathIndex++) + ReturnErrorOnFailure(writer.Put(kPathCountTag, static_cast(subscriptionInfo.mAttributePaths.AllocatedSize()))); + for (size_t currentPathIndex = 0; currentPathIndex < subscriptionInfo.mAttributePaths.AllocatedSize(); currentPathIndex++) { ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptionInfo.mAttributePaths[currentPathIndex].mEndpointId)); ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptionInfo.mAttributePaths[currentPathIndex].mClusterId)); ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptionInfo.mAttributePaths[currentPathIndex].mAttributeId)); } - ReturnErrorOnFailure(writer.Put(kPathCountTag, static_cast(subscriptionInfo.mEventPaths.AllocatedCount()))); - for (size_t currentPathIndex = 0; currentPathIndex < subscriptionInfo.mEventPaths.AllocatedCount(); currentPathIndex++) + ReturnErrorOnFailure(writer.Put(kPathCountTag, static_cast(subscriptionInfo.mEventPaths.AllocatedSize()))); + for (size_t currentPathIndex = 0; currentPathIndex < subscriptionInfo.mEventPaths.AllocatedSize(); currentPathIndex++) { if (subscriptionInfo.mEventPaths[currentPathIndex].mIsUrgentEvent) { diff --git a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp index f74075000e663d..69312b6797a645 100644 --- a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp +++ b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp @@ -43,12 +43,12 @@ struct TestSubscriptionInfo : public chip::app::SubscriptionResumptionStorage::S { return false; } - if ((mAttributePaths.AllocatedCount() != that.mAttributePaths.AllocatedCount()) || - (mEventPaths.AllocatedCount() != that.mEventPaths.AllocatedCount())) + if ((mAttributePaths.AllocatedSize() != that.mAttributePaths.AllocatedSize()) || + (mEventPaths.AllocatedSize() != that.mEventPaths.AllocatedSize())) { return false; } - for (size_t i = 0; i < mAttributePaths.AllocatedCount(); i++) + for (size_t i = 0; i < mAttributePaths.AllocatedSize(); i++) { if ((mAttributePaths[i].mEndpointId != that.mAttributePaths[i].mEndpointId) || (mAttributePaths[i].mClusterId != that.mAttributePaths[i].mClusterId) || @@ -57,7 +57,7 @@ struct TestSubscriptionInfo : public chip::app::SubscriptionResumptionStorage::S return false; } } - for (size_t i = 0; i < mEventPaths.AllocatedCount(); i++) + for (size_t i = 0; i < mEventPaths.AllocatedSize(); i++) { if ((mEventPaths[i].mEndpointId != that.mEventPaths[i].mEndpointId) || (mEventPaths[i].mClusterId != that.mEventPaths[i].mClusterId) || diff --git a/src/lib/support/ScopedBuffer.h b/src/lib/support/ScopedBuffer.h index 7493c0f4a715f4..ffba6c08478839 100644 --- a/src/lib/support/ScopedBuffer.h +++ b/src/lib/support/ScopedBuffer.h @@ -186,11 +186,8 @@ class ScopedMemoryBufferWithSize : public ScopedMemoryBuffer ~ScopedMemoryBufferWithSize() { mCount = 0; } - // return the size in bytes - inline size_t AllocatedSize() const { return mCount * sizeof(T); } - - // return the count - inline size_t AllocatedCount() const { return mCount; } + // return the size as count of elements + inline size_t AllocatedSize() const { return mCount; } void Free() { From 72e95cd76f95f9bc7b118bbc41d2bad6f3b64fe6 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:25:14 -0800 Subject: [PATCH 34/41] Update src/app/SimpleSubscriptionResumptionStorage.cpp Co-authored-by: Michael Sandstedt --- src/app/SimpleSubscriptionResumptionStorage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 009b6b4ca8c7cc..bd654bed4c5d9f 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -93,7 +93,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Init(PersistentStorageDelegate * uint16_t len = sizeof(countMax); CHIP_ERROR err = mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMax, len); - // If there's a previous MacCount and it's larger than CHIP_IM_MAX_NUM_SUBSCRIPTIONS, + // If there's a previous countMax and it's larger than CHIP_IM_MAX_NUM_SUBSCRIPTIONS, // clean up subscriptions beyond the limit if ((err == CHIP_NO_ERROR) && (countMax != CHIP_IM_MAX_NUM_SUBSCRIPTIONS)) { From 952007f544d13f5d58edc050a67ad989fba34f18 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:46:40 -0800 Subject: [PATCH 35/41] Update src/app/InteractionModelEngine.cpp Co-authored-by: Michael Sandstedt --- src/app/InteractionModelEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index cc2012d1816475..7662ca2d540832 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -666,7 +666,7 @@ Status InteractionModelEngine::OnUnsolicitedReportData(Messaging::ExchangeContex return Status::Success; } - ChipLogDetail(InteractionModel, "Received report with invalid subscriptionId %lu", (unsigned long) subscriptionId); + ChipLogDetail(InteractionModel, "Received report with invalid subscriptionId %" PRIu32, subscriptionId); return Status::InvalidSubscription; } From 866ba53802cfa723a23c4c89a09ccf230080d198 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:06:28 -0800 Subject: [PATCH 36/41] Remove reference to previously removed variable for config that turns the feature off --- src/app/ReadHandler.cpp | 7 ++----- src/app/ReadHandler.h | 9 ++++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 95f843b64de8b8..1b04360454dfc8 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -139,7 +139,7 @@ ReadHandler::~ReadHandler() void ReadHandler::Close(CloseOptions options) { #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - if (options == CloseOptions::kDropPersistedSubscriptions) + if (options == CloseOptions::kDropPersistedSubscription) { auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage(); if (subscriptionResumptionStorage) @@ -147,9 +147,6 @@ void ReadHandler::Close(CloseOptions options) subscriptionResumptionStorage->Delete(GetInitiatorNodeId(), GetAccessingFabricIndex(), mSubscriptionId); } } -#else - // We disregard the keepPersisted API that is only used if the subscription persistence feature is enabled. - (void) keepPersisted; #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS MoveToState(HandlerState::AwaitingDestruction); mManagementCallback.OnDone(*this); @@ -370,7 +367,7 @@ void ReadHandler::OnResponseTimeout(Messaging::ExchangeContext * apExchangeConte ChipLogValueExchange(apExchangeContext)); #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS // TODO: Have a retry mechanism tied to wake interval for IC devices - Close(CloseOptions::kKeepPersistedSubscriptions); + Close(CloseOptions::kKeepPersistedSubscription); #else Close(); #endif diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 9e12c2f800d4f9..5ffbb4f5a2fa9a 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -384,18 +384,17 @@ class ReadHandler : public Messaging::ExchangeDelegate enum class CloseOptions : uint8_t { - kDropPersistedSubscriptions, - kKeepPersistedSubscriptions + kDropPersistedSubscription, + kKeepPersistedSubscription }; /** * Called internally to signal the completion of all work on this objecta and signal to a registered callback that it's * safe to release this object. * - * @param keepPersisted Keep the subscription persisted in storage for later resumption. Ignored if - * CHIP_CONFIG_PERSIST_SUBSCRIPTIONS not enabled + * @param options This specifies whether to drop or keep the subscription * */ - void Close(CloseOptions options = CloseOptions::kDropPersistedSubscriptions); + void Close(CloseOptions options = CloseOptions::kDropPersistedSubscription); static void OnUnblockHoldReportCallback(System::Layer * apSystemLayer, void * apAppState); static void OnRefreshSubscribeTimerSyncCallback(System::Layer * apSystemLayer, void * apAppState); From cedd2278a897572de7663444dc5e54165d915b96 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 19 Jan 2023 22:45:16 -0800 Subject: [PATCH 37/41] Addressed PR comments and enabled chip-tool for testing Added setters for SubscriptionInfo attribute and event paths Fixed wrong constant Enabled server interactions for chiptool --- .../chip-tool/commands/common/CHIPCommand.cpp | 3 +- src/app/ReadHandler.cpp | 33 +------------ src/app/SimpleSubscriptionResumptionStorage.h | 2 +- src/app/SubscriptionResumptionStorage.h | 46 ++++++++++++++++++- 4 files changed, 49 insertions(+), 35 deletions(-) diff --git a/examples/chip-tool/commands/common/CHIPCommand.cpp b/examples/chip-tool/commands/common/CHIPCommand.cpp index 53dc8184e8d395..ebed939f8b5054 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.cpp +++ b/examples/chip-tool/commands/common/CHIPCommand.cpp @@ -119,7 +119,8 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack() // Make sure different commissioners run on different ports. port = static_cast(port + CurrentCommissionerId()); } - factoryInitParams.listenPort = port; + factoryInitParams.listenPort = port; + factoryInitParams.enableServerInteractions = true; ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().Init(factoryInitParams)); ReturnErrorOnFailure(GetAttestationTrustStore(mPaaTrustStorePath.ValueOr(nullptr), &sTrustStore)); diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 1b04360454dfc8..ed9bbe71bebebb 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -734,37 +734,8 @@ void ReadHandler::PersistSubscription() .mMinInterval = mMinIntervalFloorSeconds, .mMaxInterval = mMaxInterval, .mFabricFiltered = IsFabricFiltered() }; - ObjectList * attributePath = mpAttributePathList; - size_t attributePathCount = 0; - while (attributePath) - { - attributePathCount++; - attributePath = attributePath->mpNext; - } - attributePath = mpAttributePathList; - subscriptionInfo.mAttributePaths.Calloc(attributePathCount); - VerifyOrReturn(subscriptionInfo.mAttributePaths.Get() != nullptr); - for (size_t i = 0; i < attributePathCount; i++) - { - subscriptionInfo.mAttributePaths[i].SetValues(attributePath->mValue); - attributePath = attributePath->mpNext; - } - - ObjectList * eventPath = mpEventPathList; - size_t eventPathCount = 0; - while (eventPath) - { - eventPathCount++; - eventPath = eventPath->mpNext; - } - eventPath = mpEventPathList; - subscriptionInfo.mEventPaths.Calloc(eventPathCount); - VerifyOrReturn(subscriptionInfo.mEventPaths.Get() != nullptr); - for (size_t i = 0; i < eventPathCount; i++) - { - subscriptionInfo.mEventPaths[i].SetValues(eventPath->mValue); - eventPath = eventPath->mpNext; - } + VerifyOrReturn(subscriptionInfo.SetAttributePaths(mpAttributePathList) == CHIP_NO_ERROR); + VerifyOrReturn(subscriptionInfo.SetEventPaths(mpEventPathList) == CHIP_NO_ERROR); subscriptionResumptionStorage->Save(subscriptionInfo); } diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index 11c8c709b5439c..4764f0df8d0a46 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -37,7 +37,7 @@ namespace app { class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage { public: - static constexpr size_t kIteratorsMax = CHIP_CONFIG_MAX_GROUP_CONCURRENT_ITERATORS; + static constexpr size_t kIteratorsMax = CHIP_CONFIG_MAX_SUBSCRIPTION_RESUMPTION_STORAGE_CONCURRENT_ITERATORS; CHIP_ERROR Init(PersistentStorageDelegate * storage); diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index 044c2afd96c28d..7e7ddbcbbcaf51 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -41,7 +41,7 @@ class SubscriptionResumptionStorage ClusterId mClusterId; AttributeId mAttributeId; EndpointId mEndpointId; - void SetValues(AttributePathParams & params) + void SetValues(const AttributePathParams & params) { mEndpointId = params.mEndpointId; mClusterId = params.mClusterId; @@ -55,7 +55,7 @@ class SubscriptionResumptionStorage EventId mEventId; EndpointId mEndpointId; bool mIsUrgentEvent; - void SetValues(EventPathParams & params) + void SetValues(const EventPathParams & params) { mEndpointId = params.mEndpointId; mClusterId = params.mClusterId; @@ -78,6 +78,48 @@ class SubscriptionResumptionStorage bool mFabricFiltered; Platform::ScopedMemoryBufferWithSize mAttributePaths; Platform::ScopedMemoryBufferWithSize mEventPaths; + CHIP_ERROR SetAttributePaths(const ObjectList * pAttributePathList) + { + mAttributePaths.Free(); + const ObjectList * attributePath = pAttributePathList; + size_t attributePathCount = 0; + while (attributePath) + { + attributePathCount++; + attributePath = attributePath->mpNext; + } + ReturnErrorCodeIf((attributePathCount * sizeof(AttributePathParamsValues)) > UINT16_MAX, CHIP_ERROR_NO_MEMORY); + mAttributePaths.Calloc(attributePathCount); + ReturnErrorCodeIf(mAttributePaths.Get() == nullptr, CHIP_ERROR_NO_MEMORY); + attributePath = pAttributePathList; + for (size_t i = 0; i < attributePathCount; i++) + { + mAttributePaths[i].SetValues(attributePath->mValue); + attributePath = attributePath->mpNext; + } + return CHIP_NO_ERROR; + } + CHIP_ERROR SetEventPaths(const ObjectList * pEventPathList) + { + mEventPaths.Free(); + const ObjectList * eventPath = pEventPathList; + size_t eventPathCount = 0; + while (eventPath) + { + eventPathCount++; + eventPath = eventPath->mpNext; + } + ReturnErrorCodeIf((eventPathCount * sizeof(EventPathParamsValues)) > UINT16_MAX, CHIP_ERROR_NO_MEMORY); + mEventPaths.Calloc(eventPathCount); + ReturnErrorCodeIf(mEventPaths.Get() == nullptr, CHIP_ERROR_NO_MEMORY); + eventPath = pEventPathList; + for (size_t i = 0; i < eventPathCount; i++) + { + mEventPaths[i].SetValues(eventPath->mValue); + eventPath = eventPath->mpNext; + } + return CHIP_NO_ERROR; + } }; using SubscriptionInfoIterator = CommonIterator; From b11ca7e99f2449716d9ab12f6f6aa9910c93aa45 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Fri, 20 Jan 2023 09:35:41 -0800 Subject: [PATCH 38/41] Changed storage of attribute/event paths to proper List/Structure TLV --- src/app/ReadHandler.cpp | 6 +- .../SimpleSubscriptionResumptionStorage.cpp | 59 +++++++++++++++---- src/app/SimpleSubscriptionResumptionStorage.h | 53 +++++++++-------- 3 files changed, 80 insertions(+), 38 deletions(-) diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index ed9bbe71bebebb..983b67d565d675 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -737,7 +737,11 @@ void ReadHandler::PersistSubscription() VerifyOrReturn(subscriptionInfo.SetAttributePaths(mpAttributePathList) == CHIP_NO_ERROR); VerifyOrReturn(subscriptionInfo.SetEventPaths(mpEventPathList) == CHIP_NO_ERROR); - subscriptionResumptionStorage->Save(subscriptionInfo); + CHIP_ERROR err = subscriptionResumptionStorage->Save(subscriptionInfo); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DataManagement, "Failed to save subscription info error: '%" CHIP_ERROR_FORMAT, err.Format()); + } } void ReadHandler::OnUnblockHoldReportCallback(System::Layer * apSystemLayer, void * apAppState) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 3e1ffa32eb19e9..1ea97372639ca7 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -37,12 +37,15 @@ constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kSubscriptionIdTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kMinIntervalTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kMaxIntervalTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kFabricFilteredTag; -constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kPathCountTag; -constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventPathTypeTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kAttributePathsListTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventPathsListTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kAttributePathTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventPathTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEndpointIdTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kClusterIdTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kAttributeIdTag; constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventIdTag; +constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventPathTypeTag; SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::SimpleSubscriptionInfoIterator( SimpleSubscriptionResumptionStorage & storage) : @@ -177,15 +180,21 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(uint16_t subscriptionIndex, ReturnErrorOnFailure(reader.Get(subscriptionInfo.mFabricFiltered)); // Attribute Paths - uint16_t pathCount = 0; - ReturnErrorOnFailure(reader.Next(kPathCountTag)); - ReturnErrorOnFailure(reader.Get(pathCount)); + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_List, kAttributePathsListTag)); + TLV::TLVType attributesListType; + ReturnErrorOnFailure(reader.EnterContainer(attributesListType)); + + size_t pathCount = 0; + ReturnErrorOnFailure(reader.CountRemainingInContainer(&pathCount)); // If a stack struct is being reused to iterate, free the previous paths ScopedMemoryBuffer subscriptionInfo.mAttributePaths.Free(); - subscriptionInfo.mAttributePaths = Platform::ScopedMemoryBufferWithSize(); if (pathCount) { + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, kAttributePathTag)); + TLV::TLVType attributeContainerType; + ReturnErrorOnFailure(reader.EnterContainer(attributeContainerType)); + subscriptionInfo.mAttributePaths.Calloc(pathCount); ReturnErrorCodeIf(subscriptionInfo.mAttributePaths.Get() == nullptr, CHIP_ERROR_NO_MEMORY); for (uint16_t pathIndex = 0; pathIndex < pathCount; pathIndex++) @@ -199,22 +208,29 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(uint16_t subscriptionIndex, ReturnErrorOnFailure(reader.Next(kAttributeIdTag)); ReturnErrorOnFailure(reader.Get(subscriptionInfo.mAttributePaths[pathIndex].mAttributeId)); } + ReturnErrorOnFailure(reader.ExitContainer(attributeContainerType)); } + ReturnErrorOnFailure(reader.ExitContainer(attributesListType)); // Event Paths - pathCount = 0; - ReturnErrorOnFailure(reader.Next(kPathCountTag)); - ReturnErrorOnFailure(reader.Get(pathCount)); + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_List, kEventPathsListTag)); + TLV::TLVType eventsListType; + ReturnErrorOnFailure(reader.EnterContainer(eventsListType)); + + ReturnErrorOnFailure(reader.CountRemainingInContainer(&pathCount)); // If a stack struct is being reused to iterate, free the previous paths ScopedMemoryBuffer subscriptionInfo.mEventPaths.Free(); - subscriptionInfo.mEventPaths = Platform::ScopedMemoryBufferWithSize(); if (pathCount) { subscriptionInfo.mEventPaths.Calloc(pathCount); ReturnErrorCodeIf(subscriptionInfo.mEventPaths.Get() == nullptr, CHIP_ERROR_NO_MEMORY); for (uint16_t pathIndex = 0; pathIndex < pathCount; pathIndex++) { + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, kEventPathTag)); + TLV::TLVType eventContainerType; + ReturnErrorOnFailure(reader.EnterContainer(eventContainerType)); + EventPathType eventPathType; ReturnErrorOnFailure(reader.Next(kEventPathTypeTag)); ReturnErrorOnFailure(reader.Get(eventPathType)); @@ -229,8 +245,11 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(uint16_t subscriptionIndex, ReturnErrorOnFailure(reader.Next(kEventIdTag)); ReturnErrorOnFailure(reader.Get(subscriptionInfo.mEventPaths[pathIndex].mEventId)); + + ReturnErrorOnFailure(reader.ExitContainer(eventContainerType)); } } + ReturnErrorOnFailure(reader.ExitContainer(eventsListType)); ReturnErrorOnFailure(reader.ExitContainer(subscriptionContainerType)); @@ -248,17 +267,30 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(TLV::TLVWriter & writer, Su ReturnErrorOnFailure(writer.Put(kMaxIntervalTag, subscriptionInfo.mMaxInterval)); ReturnErrorOnFailure(writer.Put(kFabricFilteredTag, subscriptionInfo.mFabricFiltered)); - ReturnErrorOnFailure(writer.Put(kPathCountTag, static_cast(subscriptionInfo.mAttributePaths.AllocatedSize()))); + // Attribute paths + TLV::TLVType attributesListType; + ReturnErrorOnFailure(writer.StartContainer(kAttributePathsListTag, TLV::kTLVType_List, attributesListType)); for (size_t pathIndex = 0; pathIndex < subscriptionInfo.mAttributePaths.AllocatedSize(); pathIndex++) { + TLV::TLVType attributeContainerType = TLV::kTLVType_Structure; + ReturnErrorOnFailure(writer.StartContainer(kAttributePathTag, TLV::kTLVType_Structure, attributeContainerType)); + ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptionInfo.mAttributePaths[pathIndex].mEndpointId)); ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptionInfo.mAttributePaths[pathIndex].mClusterId)); ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptionInfo.mAttributePaths[pathIndex].mAttributeId)); + + ReturnErrorOnFailure(writer.EndContainer(attributeContainerType)); } + ReturnErrorOnFailure(writer.EndContainer(attributesListType)); - ReturnErrorOnFailure(writer.Put(kPathCountTag, static_cast(subscriptionInfo.mEventPaths.AllocatedSize()))); + // Event paths + TLV::TLVType eventsListType; + ReturnErrorOnFailure(writer.StartContainer(kEventPathsListTag, TLV::kTLVType_List, eventsListType)); for (size_t pathIndex = 0; pathIndex < subscriptionInfo.mEventPaths.AllocatedSize(); pathIndex++) { + TLV::TLVType eventContainerType = TLV::kTLVType_Structure; + ReturnErrorOnFailure(writer.StartContainer(kEventPathTag, TLV::kTLVType_Structure, eventContainerType)); + if (subscriptionInfo.mEventPaths[pathIndex].mIsUrgentEvent) { ReturnErrorOnFailure(writer.Put(kEventPathTypeTag, EventPathType::kUrgent)); @@ -270,7 +302,10 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(TLV::TLVWriter & writer, Su ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptionInfo.mEventPaths[pathIndex].mEndpointId)); ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptionInfo.mEventPaths[pathIndex].mClusterId)); ReturnErrorOnFailure(writer.Put(kEventIdTag, subscriptionInfo.mEventPaths[pathIndex].mEventId)); + + ReturnErrorOnFailure(writer.EndContainer(eventContainerType)); } + ReturnErrorOnFailure(writer.EndContainer(eventsListType)); ReturnErrorOnFailure(writer.EndContainer(subscriptionContainerType)); diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index 4764f0df8d0a46..35c2618360a7c6 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -94,40 +94,43 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage kNonUrgent = 0x2, }; - // TODO: consider alternate storage scheme to optimize space requirement - // Flat list of subscriptions indexed from from 0 to CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS-1 // // Each entry in list is a Subscription TLV structure: - // Struct of: + // Structure of: (Subscription info) // Node ID // Fabric Index // Subscription ID // Min interval // Max interval // Fabric filtered boolean - // Attribute Path Count x, with these fields repeating x times - // Endpoint ID - // Cluster ID - // Attribute/event ID - // Event Path Count x, with these fields repeating x times - // Event subscription type (urgent / non-urgent) - // Endpoint ID - // Cluster ID - // Event ID - - static constexpr TLV::Tag kPeerNodeIdTag = TLV::ContextTag(2); - static constexpr TLV::Tag kFabricIndexTag = TLV::ContextTag(1); - static constexpr TLV::Tag kSubscriptionIdTag = TLV::ContextTag(3); - static constexpr TLV::Tag kMinIntervalTag = TLV::ContextTag(4); - static constexpr TLV::Tag kMaxIntervalTag = TLV::ContextTag(5); - static constexpr TLV::Tag kFabricFilteredTag = TLV::ContextTag(6); - static constexpr TLV::Tag kPathCountTag = TLV::ContextTag(7); - static constexpr TLV::Tag kEventPathTypeTag = TLV::ContextTag(8); - static constexpr TLV::Tag kEndpointIdTag = TLV::ContextTag(9); - static constexpr TLV::Tag kClusterIdTag = TLV::ContextTag(10); - static constexpr TLV::Tag kAttributeIdTag = TLV::ContextTag(11); - static constexpr TLV::Tag kEventIdTag = TLV::ContextTag(12); + // List of: + // Structure of: (Attribute path) + // Endpoint ID + // Cluster ID + // Attribute ID + // List of: + // Structure of: (Event path) + // Event subscription type (urgent / non-urgent) + // Endpoint ID + // Cluster ID + // Event ID + + static constexpr TLV::Tag kPeerNodeIdTag = TLV::ContextTag(1); + static constexpr TLV::Tag kFabricIndexTag = TLV::ContextTag(2); + static constexpr TLV::Tag kSubscriptionIdTag = TLV::ContextTag(3); + static constexpr TLV::Tag kMinIntervalTag = TLV::ContextTag(4); + static constexpr TLV::Tag kMaxIntervalTag = TLV::ContextTag(5); + static constexpr TLV::Tag kFabricFilteredTag = TLV::ContextTag(6); + static constexpr TLV::Tag kAttributePathsListTag = TLV::ContextTag(7); + static constexpr TLV::Tag kEventPathsListTag = TLV::ContextTag(8); + static constexpr TLV::Tag kAttributePathTag = TLV::ContextTag(9); + static constexpr TLV::Tag kEventPathTag = TLV::ContextTag(10); + static constexpr TLV::Tag kEndpointIdTag = TLV::ContextTag(11); + static constexpr TLV::Tag kClusterIdTag = TLV::ContextTag(12); + static constexpr TLV::Tag kAttributeIdTag = TLV::ContextTag(13); + static constexpr TLV::Tag kEventIdTag = TLV::ContextTag(14); + static constexpr TLV::Tag kEventPathTypeTag = TLV::ContextTag(16); PersistentStorageDelegate * mStorage; ObjectPool mSubscriptionInfoIterators; From 29cbb72a1e55b7330e923c012f20af71c19b72bf Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Fri, 20 Jan 2023 10:05:13 -0800 Subject: [PATCH 39/41] Fixed attribute load --- src/app/SimpleSubscriptionResumptionStorage.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 1ea97372639ca7..933d180d2a7326 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -191,14 +191,14 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(uint16_t subscriptionIndex, subscriptionInfo.mAttributePaths.Free(); if (pathCount) { - ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, kAttributePathTag)); - TLV::TLVType attributeContainerType; - ReturnErrorOnFailure(reader.EnterContainer(attributeContainerType)); - subscriptionInfo.mAttributePaths.Calloc(pathCount); ReturnErrorCodeIf(subscriptionInfo.mAttributePaths.Get() == nullptr, CHIP_ERROR_NO_MEMORY); for (uint16_t pathIndex = 0; pathIndex < pathCount; pathIndex++) { + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, kAttributePathTag)); + TLV::TLVType attributeContainerType; + ReturnErrorOnFailure(reader.EnterContainer(attributeContainerType)); + ReturnErrorOnFailure(reader.Next(kEndpointIdTag)); ReturnErrorOnFailure(reader.Get(subscriptionInfo.mAttributePaths[pathIndex].mEndpointId)); @@ -207,8 +207,9 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(uint16_t subscriptionIndex, ReturnErrorOnFailure(reader.Next(kAttributeIdTag)); ReturnErrorOnFailure(reader.Get(subscriptionInfo.mAttributePaths[pathIndex].mAttributeId)); + + ReturnErrorOnFailure(reader.ExitContainer(attributeContainerType)); } - ReturnErrorOnFailure(reader.ExitContainer(attributeContainerType)); } ReturnErrorOnFailure(reader.ExitContainer(attributesListType)); From 4e121e23deb00452fc23add51b8d09a64f569eba Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Fri, 20 Jan 2023 12:01:59 -0800 Subject: [PATCH 40/41] Make Unit Test names more unique and tighten CHIP_CONFIG_PERSIST_SUBSCRIPTIONS usage --- src/app/ReadHandler.cpp | 7 ++- src/app/ReadHandler.h | 2 + .../SimpleSubscriptionResumptionStorage.cpp | 4 ++ src/app/SimpleSubscriptionResumptionStorage.h | 4 ++ src/app/server/Server.cpp | 4 +- src/app/server/Server.h | 2 + ...estSimpleSubscriptionResumptionStorage.cpp | 44 ++++++++++++------- 7 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 983b67d565d675..b393edad4fc01e 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -41,8 +41,11 @@ using Status = Protocols::InteractionModel::Status; ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, InteractionType aInteractionType) : mExchangeCtx(*this), - mManagementCallback(apCallback), mOnConnectedCallback(HandleDeviceConnected, this), - mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) + mManagementCallback(apCallback) +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + , + mOnConnectedCallback(HandleDeviceConnected, this), mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) +#endif { VerifyOrDie(apExchangeContext != nullptr); diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 5ffbb4f5a2fa9a..1655cb27875987 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -504,9 +504,11 @@ class ReadHandler : public Messaging::ExchangeDelegate BitFlags mFlags; InteractionType mInteractionType = InteractionType::Read; +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS // Callbacks to handle server-initiated session success/failure chip::Callback::Callback mOnConnectedCallback; chip::Callback::Callback mOnConnectionFailureCallback; +#endif }; } // namespace app } // namespace chip diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 933d180d2a7326..259924a7477c29 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -28,6 +28,8 @@ #include #include +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + namespace chip { namespace app { @@ -455,3 +457,5 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteAll(FabricIndex fabricInde } // namespace app } // namespace chip + +#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index 35c2618360a7c6..9e3d276298543c 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -28,6 +28,8 @@ #include #include +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + namespace chip { namespace app { @@ -137,3 +139,5 @@ class SimpleSubscriptionResumptionStorage : public SubscriptionResumptionStorage }; } // namespace app } // namespace chip + +#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index fc6665deb552e5..d7f76abe19a0aa 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -481,15 +481,15 @@ CHIP_ERROR Server::SendUserDirectedCommissioningRequest(chip::Transport::PeerAdd } #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS void Server::ResumeSubscriptions() { -#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS CHIP_ERROR err = chip::app::InteractionModelEngine::GetInstance()->ResumeSubscriptions(); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Error when trying to resume subscriptions : %" CHIP_ERROR_FORMAT, err.Format()); } -#endif } +#endif } // namespace chip diff --git a/src/app/server/Server.h b/src/app/server/Server.h index 61ab16a232c359..6fcf79f01bf2af 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -378,10 +378,12 @@ class Server void InitFailSafe(); +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS /** * @brief Called at Server::Init time to resume persisted subscriptions if the feature flag is enabled */ void ResumeSubscriptions(); +#endif class GroupDataProviderListener final : public Credentials::GroupDataProvider::GroupListener { diff --git a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp index 69312b6797a645..67b1f0757d7b14 100644 --- a/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp +++ b/src/app/tests/TestSimpleSubscriptionResumptionStorage.cpp @@ -18,6 +18,8 @@ #include #include +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + #include #include @@ -71,7 +73,7 @@ struct TestSubscriptionInfo : public chip::app::SubscriptionResumptionStorage::S } }; -void TestCount(nlTestSuite * inSuite, void * inContext) +void TestSubscriptionCount(nlTestSuite * inSuite, void * inContext) { chip::TestPersistentStorageDelegate storage; SimpleSubscriptionResumptionStorageTest subscriptionStorage; @@ -115,7 +117,7 @@ void TestCount(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, count == 0); } -void TestMaxCount(nlTestSuite * inSuite, void * inContext) +void TestSubscriptionMaxCount(nlTestSuite * inSuite, void * inContext) { // Force large MacCount value and check that Init resets it properly, and deletes extra subs: @@ -152,7 +154,7 @@ void TestMaxCount(nlTestSuite * inSuite, void * inContext) chip::DefaultStorageKeyAllocator::SubscriptionResumption(CHIP_IM_MAX_NUM_SUBSCRIPTIONS + 1).KeyName())); } -void TestState(nlTestSuite * inSuite, void * inContext) +void TestSubscriptionState(nlTestSuite * inSuite, void * inContext) { chip::TestPersistentStorageDelegate storage; SimpleSubscriptionResumptionStorageTest subscriptionStorage; @@ -268,7 +270,7 @@ void TestState(nlTestSuite * inSuite, void * inContext) static constexpr chip::TLV::Tag kTestValue1Tag = chip::TLV::ContextTag(30); static constexpr chip::TLV::Tag kTestValue2Tag = chip::TLV::ContextTag(31); -void TestStateUnexpectedFields(nlTestSuite * inSuite, void * inContext) +void TestSubscriptionStateUnexpectedFields(nlTestSuite * inSuite, void * inContext) { chip::TestPersistentStorageDelegate storage; SimpleSubscriptionResumptionStorageTest subscriptionStorage; @@ -325,7 +327,7 @@ void TestStateUnexpectedFields(nlTestSuite * inSuite, void * inContext) iterator->Release(); } -void TestStateTooBigToLoad(nlTestSuite * inSuite, void * inContext) +void TestSubscriptionStateTooBigToLoad(nlTestSuite * inSuite, void * inContext) { chip::TestPersistentStorageDelegate storage; SimpleSubscriptionResumptionStorageTest subscriptionStorage; @@ -385,7 +387,7 @@ void TestStateTooBigToLoad(nlTestSuite * inSuite, void * inContext) iterator->Release(); } -void TestStateJunkData(nlTestSuite * inSuite, void * inContext) +void TestSubscriptionStateJunkData(nlTestSuite * inSuite, void * inContext) { chip::TestPersistentStorageDelegate storage; SimpleSubscriptionResumptionStorageTest subscriptionStorage; @@ -410,7 +412,7 @@ void TestStateJunkData(nlTestSuite * inSuite, void * inContext) /** * Set up the test suite. */ -int Test_Setup(void * inContext) +int TestSubscription_Setup(void * inContext) { VerifyOrReturnError(CHIP_NO_ERROR == chip::Platform::MemoryInit(), FAILURE); @@ -420,7 +422,7 @@ int Test_Setup(void * inContext) /** * Tear down the test suite. */ -int Test_Teardown(void * inContext) +int TestSubscription_Teardown(void * inContext) { chip::Platform::MemoryShutdown(); return SUCCESS; @@ -434,12 +436,12 @@ int Test_Teardown(void * inContext) // clang-format off static const nlTest sTests[] = { - NL_TEST_DEF("TestCount", TestCount), - NL_TEST_DEF("TestMaxCount", TestMaxCount), - NL_TEST_DEF("TestState", TestState), - NL_TEST_DEF("TestStateUnexpectedFields", TestStateUnexpectedFields), - NL_TEST_DEF("TestStateTooBigToLoad", TestStateTooBigToLoad), - NL_TEST_DEF("TestStateJunkData", TestStateJunkData), + NL_TEST_DEF("TestSubscriptionCount", TestSubscriptionCount), + NL_TEST_DEF("TestSubscriptionMaxCount", TestSubscriptionMaxCount), + NL_TEST_DEF("TestSubscriptionState", TestSubscriptionState), + NL_TEST_DEF("TestSubscriptionStateUnexpectedFields", TestSubscriptionStateUnexpectedFields), + NL_TEST_DEF("TestSubscriptionStateTooBigToLoad", TestSubscriptionStateTooBigToLoad), + NL_TEST_DEF("TestSubscriptionStateJunkData", TestSubscriptionStateJunkData), NL_TEST_SENTINEL() }; @@ -450,7 +452,7 @@ static nlTestSuite sSuite = { "Test-CHIP-SimpleSubscriptionResumptionStorage", &sTests[0], - &Test_Setup, &Test_Teardown + &TestSubscription_Setup, &TestSubscription_Teardown }; // clang-format on @@ -466,3 +468,15 @@ int TestSimpleSubscriptionResumptionStorage() } CHIP_REGISTER_TEST_SUITE(TestSimpleSubscriptionResumptionStorage) + +#else // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + +/** + * Main + */ +int TestSimpleSubscriptionResumptionStorage() +{ + return 0; +} + +#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS From b8ef2e63280f055041b0161be66e91a7d6cd2062 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Fri, 20 Jan 2023 14:08:49 -0800 Subject: [PATCH 41/41] Addressed PR comments and CI issues: Delete() error return clarification Comment doc cleanup Fix loop variable build warning Revert chip-tool server interactions enablement --- .../chip-tool/commands/common/CHIPCommand.cpp | 3 +- src/app/ReadHandler.h | 2 +- .../SimpleSubscriptionResumptionStorage.cpp | 34 +++++++++++++------ src/app/SimpleSubscriptionResumptionStorage.h | 6 ++-- src/app/SubscriptionResumptionStorage.h | 4 --- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/examples/chip-tool/commands/common/CHIPCommand.cpp b/examples/chip-tool/commands/common/CHIPCommand.cpp index ebed939f8b5054..53dc8184e8d395 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.cpp +++ b/examples/chip-tool/commands/common/CHIPCommand.cpp @@ -119,8 +119,7 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack() // Make sure different commissioners run on different ports. port = static_cast(port + CurrentCommissionerId()); } - factoryInitParams.listenPort = port; - factoryInitParams.enableServerInteractions = true; + factoryInitParams.listenPort = port; ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().Init(factoryInitParams)); ReturnErrorOnFailure(GetAttestationTrustStore(mPaaTrustStorePath.ValueOr(nullptr), &sTrustStore)); diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 1655cb27875987..a933aa76e51389 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -382,7 +382,7 @@ class ReadHandler : public Messaging::ExchangeDelegate AwaitingDestruction, ///< The object has completed its work and is awaiting destruction by the application. }; - enum class CloseOptions : uint8_t + enum class CloseOptions { kDropPersistedSubscription, kKeepPersistedSubscription diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp index 259924a7477c29..1f51f751639522 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.cpp +++ b/src/app/SimpleSubscriptionResumptionStorage.cpp @@ -23,13 +23,14 @@ #include +// TODO: move the conditional compilation into BUILD.gn config options +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + #include #include #include #include -#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - namespace chip { namespace app { @@ -195,7 +196,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(uint16_t subscriptionIndex, { subscriptionInfo.mAttributePaths.Calloc(pathCount); ReturnErrorCodeIf(subscriptionInfo.mAttributePaths.Get() == nullptr, CHIP_ERROR_NO_MEMORY); - for (uint16_t pathIndex = 0; pathIndex < pathCount; pathIndex++) + for (size_t pathIndex = 0; pathIndex < pathCount; pathIndex++) { ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, kAttributePathTag)); TLV::TLVType attributeContainerType; @@ -228,7 +229,7 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(uint16_t subscriptionIndex, { subscriptionInfo.mEventPaths.Calloc(pathCount); ReturnErrorCodeIf(subscriptionInfo.mEventPaths.Get() == nullptr, CHIP_ERROR_NO_MEMORY); - for (uint16_t pathIndex = 0; pathIndex < pathCount; pathIndex++) + for (size_t pathIndex = 0; pathIndex < pathCount; pathIndex++) { ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, kEventPathTag)); TLV::TLVType eventContainerType; @@ -377,36 +378,47 @@ CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscrip CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(NodeId nodeId, FabricIndex fabricIndex, SubscriptionId subscriptionId) { - CHIP_ERROR deleteErr = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; + bool subscriptionFound = false; + CHIP_ERROR lastDeleteErr = CHIP_NO_ERROR; - uint16_t count = 0; + uint16_t remainingSubscriptionsCount = 0; for (uint16_t subscriptionIndex = 0; subscriptionIndex < CHIP_IM_MAX_NUM_SUBSCRIPTIONS; subscriptionIndex++) { SubscriptionInfo subscriptionInfo; CHIP_ERROR err = Load(subscriptionIndex, subscriptionInfo); - // delete duplicate + // delete match if (err == CHIP_NO_ERROR) { if ((nodeId == subscriptionInfo.mNodeId) && (fabricIndex == subscriptionInfo.mFabricIndex) && (subscriptionId == subscriptionInfo.mSubscriptionId)) { - deleteErr = Delete(subscriptionIndex); + subscriptionFound = true; + CHIP_ERROR deleteErr = Delete(subscriptionIndex); + if (deleteErr != CHIP_NO_ERROR) + { + lastDeleteErr = deleteErr; + } } else { - count++; + remainingSubscriptionsCount++; } } } // if there are no persisted subscriptions, the MaxCount can also be deleted - if (count == 0) + if (remainingSubscriptionsCount == 0) { DeleteMaxCount(); } - return deleteErr; + if (lastDeleteErr != CHIP_NO_ERROR) + { + return lastDeleteErr; + } + + return subscriptionFound ? CHIP_NO_ERROR : CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; } CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteMaxCount() diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h index 9e3d276298543c..95efa2d032aac8 100644 --- a/src/app/SimpleSubscriptionResumptionStorage.h +++ b/src/app/SimpleSubscriptionResumptionStorage.h @@ -24,12 +24,14 @@ #pragma once #include + +// TODO: move the conditional compilation into BUILD.gn config options +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + #include #include #include -#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - namespace chip { namespace app { diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h index 7e7ddbcbbcaf51..38a522fc445add 100644 --- a/src/app/SubscriptionResumptionStorage.h +++ b/src/app/SubscriptionResumptionStorage.h @@ -137,14 +137,11 @@ class SubscriptionResumptionStorage * Save subscription resumption information to storage. * * @param subscriptionInfo the subscription information to save - caller should expect the passed in value is consumed - * @return CHIP_NO_ERROR on success, else an appropriate CHIP error on failure */ virtual CHIP_ERROR Save(SubscriptionInfo & subscriptionInfo) = 0; /** * Delete subscription resumption information by node ID, fabric index, and subscription ID. - * - * @return CHIP_NO_ERROR on success, else an appropriate CHIP error on failure */ virtual CHIP_ERROR Delete(NodeId nodeId, FabricIndex fabricIndex, SubscriptionId subscriptionId) = 0; @@ -154,7 +151,6 @@ class SubscriptionResumptionStorage * and is considered successful. * * @param fabricIndex the index of the fabric for which to remove subscription resumption information - * @return CHIP_NO_ERROR on success, else an appropriate CHIP error on failure */ virtual CHIP_ERROR DeleteAll(FabricIndex fabricIndex) = 0; };