diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index b122333ac2..c33ebeca4e 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -61,7 +61,8 @@ namespace app { class AutoReleaseSubscriptionInfoIterator { public: - AutoReleaseSubscriptionInfoIterator(SubscriptionResumptionStorage::SubscriptionInfoIterator * iterator) : mIterator(iterator){}; + AutoReleaseSubscriptionInfoIterator(SubscriptionResumptionStorage::SubscriptionInfoIterator * iterator) : + mIterator(iterator) {}; ~AutoReleaseSubscriptionInfoIterator() { mIterator->Release(); } SubscriptionResumptionStorage::SubscriptionInfoIterator * operator->() const { return mIterator; } @@ -398,6 +399,27 @@ bool InteractionModelEngine::SubjectHasPersistedSubscription(FabricIndex aFabric return persistedSubMatches; } +bool InteractionModelEngine::FabricHasAtLeastOneActiveSubscription(FabricIndex aFabricIndex) +{ + bool hasActiveSubscription = false; + mReadHandlers.ForEachActiveObject([aFabricIndex, &hasActiveSubscription](ReadHandler * handler) { + VerifyOrReturnValue(handler->IsType(ReadHandler::InteractionType::Subscribe), Loop::Continue); + + Access::SubjectDescriptor subject = handler->GetSubjectDescriptor(); + VerifyOrReturnValue(subject.fabricIndex == aFabricIndex, Loop::Continue); + + if (subject.authMode == Access::AuthMode::kCase) + { + hasActiveSubscription = handler->IsActiveSubscription(); + VerifyOrReturnValue(!hasActiveSubscription, Loop::Break); + } + + return Loop::Continue; + }); + + return hasActiveSubscription; +} + void InteractionModelEngine::OnDone(CommandResponseSender & apResponderObj) { mCommandResponderObjs.ReleaseObject(&apResponderObj); diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index ba0a099bb7..072d323650 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -314,6 +314,8 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subjectID) override; + bool FabricHasAtLeastOneActiveSubscription(FabricIndex aFabricIndex) override; + #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS /** * @brief Function decrements the number of subscriptions to resume counter - mNumOfSubscriptionsToResume. diff --git a/src/app/SubscriptionsInfoProvider.h b/src/app/SubscriptionsInfoProvider.h index cb8d470cbb..6d04850ed3 100644 --- a/src/app/SubscriptionsInfoProvider.h +++ b/src/app/SubscriptionsInfoProvider.h @@ -35,7 +35,7 @@ namespace app { class SubscriptionsInfoProvider { public: - virtual ~SubscriptionsInfoProvider(){}; + virtual ~SubscriptionsInfoProvider() {}; /** * @brief Check if a given subject (CAT or operational NodeId) has at least 1 active subscription. @@ -61,6 +61,16 @@ class SubscriptionsInfoProvider * false subject doesn't have any persisted subscription with the device */ virtual bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subjectID) = 0; + + /** + * @brief Check if a given fabric has at least 1 active subscription. + * + * @param aFabricIndex fabric index for which we want to validate is has at least one active subscription + * + * @return true fabric has at least one active subscription + * @return false fabric doesn't have any active subscription or failed to validate + */ + virtual bool FabricHasAtLeastOneActiveSubscription(FabricIndex aFabricIndex) = 0; }; } // namespace app diff --git a/src/app/icd/server/tests/TestICDManager.cpp b/src/app/icd/server/tests/TestICDManager.cpp index f6f999c1a3..bdd02d5ca8 100644 --- a/src/app/icd/server/tests/TestICDManager.cpp +++ b/src/app/icd/server/tests/TestICDManager.cpp @@ -115,13 +115,14 @@ class TestSubscriptionsInfoProvider : public SubscriptionsInfoProvider { public: TestSubscriptionsInfoProvider() = default; - ~TestSubscriptionsInfoProvider(){}; + ~TestSubscriptionsInfoProvider() {}; void SetHasActiveSubscription(bool value) { mHasActiveSubscription = value; }; void SetHasPersistedSubscription(bool value) { mHasPersistedSubscription = value; }; bool SubjectHasActiveSubscription(FabricIndex aFabricIndex, NodeId subject) { return mHasActiveSubscription; }; bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subject) { return mHasPersistedSubscription; }; + bool FabricHasAtLeastOneActiveSubscription(FabricIndex aFabricIndex) { return false; }; private: bool mHasActiveSubscription = false; diff --git a/src/app/tests/TestInteractionModelEngine.cpp b/src/app/tests/TestInteractionModelEngine.cpp index 0a4a8bd5af..0809c565b6 100644 --- a/src/app/tests/TestInteractionModelEngine.cpp +++ b/src/app/tests/TestInteractionModelEngine.cpp @@ -68,6 +68,8 @@ class TestInteractionModelEngine : public chip::Test::AppContext void TestSubjectHasActiveSubscriptionSubWithCAT(); void TestSubscriptionResumptionTimer(); void TestDecrementNumSubscriptionsToResume(); + void TestFabricHasAtLeastOneActiveSubscription(); + void TestFabricHasAtLeastOneActiveSubscriptionWithMixedStates(); static int GetAttributePathListLength(SingleLinkedListNode * apattributePathParamsList); }; @@ -375,11 +377,6 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestSubjectHasActiveSubscription engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); - // Create and setup readHandler 2 - ReadHandler * readHandler2 = - engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, - reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); - // Verify that Bob still doesn't have an active subscription EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId)); @@ -392,6 +389,11 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestSubjectHasActiveSubscription // Verify that Alice still doesn't have an active subscription EXPECT_FALSE(engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId)); + // Create and setup readHandler 2 + ReadHandler * readHandler2 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); + // Set readHandler2 to active readHandler2->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true); @@ -710,5 +712,124 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestDecrementNumSubscriptionsToR } #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS +TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestFabricHasAtLeastOneActiveSubscription) +{ + NullReadHandlerCallback nullCallback; + InteractionModelEngine * engine = InteractionModelEngine::GetInstance(); + + FabricIndex fabricIndex1 = 1; + FabricIndex fabricIndex2 = 2; + + // Create ExchangeContexts + Messaging::ExchangeContext * exchangeCtx1 = NewExchangeToBob(nullptr, false); + ASSERT_TRUE(exchangeCtx1); + + Messaging::ExchangeContext * exchangeCtx2 = NewExchangeToAlice(nullptr, false); + ASSERT_TRUE(exchangeCtx2); + + // InteractionModelEngine init + EXPECT_EQ(CHIP_NO_ERROR, engine->Init(&GetExchangeManager(), &GetFabricTable(), reporting::GetDefaultReportScheduler())); + + // Verify that both fabrics have no active subscriptions + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex1)); + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex2)); + + // Create and setup readHandler 1 + ReadHandler * readHandler1 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); + + // Verify that fabric 1 still doesn't have an active subscription + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex1)); + + // Set readHandler1 to active + readHandler1->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true); + + // Verify that fabric 1 has an active subscription + EXPECT_TRUE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex1)); + + // Verify that fabric 2 still doesn't have an active subscription + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex2)); + + // Create and setup readHandler 2 + ReadHandler * readHandler2 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); + + // Set readHandler2 to active + readHandler2->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true); + + // Verify that fabric 2 has an active subscription + EXPECT_TRUE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex2)); + + // Clean up read handlers + engine->GetReadHandlerPool().ReleaseAll(); + + // Verify that both fabrics have no active subscriptions + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex1)); + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex2)); +} + +TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestFabricHasAtLeastOneActiveSubscriptionWithMixedStates) +{ + NullReadHandlerCallback nullCallback; + InteractionModelEngine * engine = InteractionModelEngine::GetInstance(); + + FabricIndex fabricIndex = 1; + + // Create ExchangeContexts + Messaging::ExchangeContext * exchangeCtx1 = NewExchangeToBob(nullptr, false); + ASSERT_TRUE(exchangeCtx1); + + Messaging::ExchangeContext * exchangeCtx2 = NewExchangeToBob(nullptr, false); + ASSERT_TRUE(exchangeCtx2); + + // InteractionModelEngine init + EXPECT_EQ(CHIP_NO_ERROR, engine->Init(&GetExchangeManager(), &GetFabricTable(), reporting::GetDefaultReportScheduler())); + + // Verify that the fabric has no active subscriptions + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Create and setup readHandler 1 + ReadHandler * readHandler1 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); + + // Verify that the fabric still doesn't have an active subscription + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Set readHandler1 to active + readHandler1->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true); + + // Verify that the fabric has an active subscription + EXPECT_TRUE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Create and setup readHandler 2 + ReadHandler * readHandler2 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); + + // Verify that the fabric still has an active subscription + EXPECT_TRUE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Set readHandler2 to inactive + readHandler2->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, false); + + // Verify that the fabric still has an active subscription + EXPECT_TRUE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Set readHandler1 to inactive + readHandler1->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, false); + + // Verify that the fabric doesn't have an active subscription + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Clean up read handlers + engine->GetReadHandlerPool().ReleaseAll(); + + // Verify that the fabric has no active subscriptions + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); +} + } // namespace app } // namespace chip