diff --git a/examples/all-clusters-app/esp32/main/Kconfig.projbuild b/examples/all-clusters-app/esp32/main/Kconfig.projbuild
index 2cec0c32093734..e1d8805e689326 100644
--- a/examples/all-clusters-app/esp32/main/Kconfig.projbuild
+++ b/examples/all-clusters-app/esp32/main/Kconfig.projbuild
@@ -59,6 +59,10 @@ menu "Demo"
             depends on IDF_TARGET_ESP32H2
     endchoice
 
+    config CHIP_PROJECT_CONFIG
+        string "CHIP Project Configuration file"
+        default "main/include/CHIPProjectConfig.h"
+
     choice
       prompt "Rendezvous Mode"
       default RENDEZVOUS_MODE_BLE if BT_ENABLED
diff --git a/examples/all-clusters-app/esp32/main/include/CHIPProjectConfig.h b/examples/all-clusters-app/esp32/main/include/CHIPProjectConfig.h
new file mode 100644
index 00000000000000..9fff1bd10e67b4
--- /dev/null
+++ b/examples/all-clusters-app/esp32/main/include/CHIPProjectConfig.h
@@ -0,0 +1,38 @@
+/*
+ *
+ *    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
+ *          Example project configuration file for CHIP.
+ *
+ *          This is a place to put application or project-specific overrides
+ *          to the default configuration values for general CHIP features.
+ *
+ */
+
+#pragma once
+
+/**
+ * @def CONFIG_BUILD_FOR_HOST_UNIT_TEST
+ *
+ * @brief Defines whether we're currently building for unit testing, which enables a set of features
+ *        that are only utilized in those tests. This flag should not be enabled on devices. If you have a test
+ *        that uses this flag, either appropriately conditionalize the entire test on this flag, or to exclude
+ *        the compliation of that test source file entirely.
+ */
+#define CONFIG_BUILD_FOR_HOST_UNIT_TEST 1
diff --git a/examples/all-clusters-minimal-app/esp32/main/Kconfig.projbuild b/examples/all-clusters-minimal-app/esp32/main/Kconfig.projbuild
index 171af4f0ba2c24..9fe8f460e6b0a4 100644
--- a/examples/all-clusters-minimal-app/esp32/main/Kconfig.projbuild
+++ b/examples/all-clusters-minimal-app/esp32/main/Kconfig.projbuild
@@ -48,6 +48,10 @@ menu "Demo"
             depends on IDF_TARGET_ESP32C2
     endchoice
 
+    config CHIP_PROJECT_CONFIG
+        string "CHIP Project Configuration file"
+        default "main/include/CHIPProjectConfig.h"
+
     choice
       prompt "Rendezvous Mode"
       default RENDEZVOUS_MODE_BLE if BT_ENABLED
diff --git a/examples/all-clusters-minimal-app/esp32/main/include/CHIPProjectConfig.h b/examples/all-clusters-minimal-app/esp32/main/include/CHIPProjectConfig.h
new file mode 100644
index 00000000000000..9fff1bd10e67b4
--- /dev/null
+++ b/examples/all-clusters-minimal-app/esp32/main/include/CHIPProjectConfig.h
@@ -0,0 +1,38 @@
+/*
+ *
+ *    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
+ *          Example project configuration file for CHIP.
+ *
+ *          This is a place to put application or project-specific overrides
+ *          to the default configuration values for general CHIP features.
+ *
+ */
+
+#pragma once
+
+/**
+ * @def CONFIG_BUILD_FOR_HOST_UNIT_TEST
+ *
+ * @brief Defines whether we're currently building for unit testing, which enables a set of features
+ *        that are only utilized in those tests. This flag should not be enabled on devices. If you have a test
+ *        that uses this flag, either appropriately conditionalize the entire test on this flag, or to exclude
+ *        the compliation of that test source file entirely.
+ */
+#define CONFIG_BUILD_FOR_HOST_UNIT_TEST 1
diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn
index e2b96e44868c0d..87508380a08083 100644
--- a/src/app/BUILD.gn
+++ b/src/app/BUILD.gn
@@ -122,7 +122,7 @@ source_set("global-attributes") {
   ]
 }
 
-source_set("subscription-manager") {
+source_set("subscription-info-provider") {
   sources = [ "SubscriptionsInfoProvider.h" ]
 
   public_deps = [ "${chip_root}/src/lib/core" ]
@@ -207,9 +207,10 @@ static_library("interaction-model") {
     ":app_config",
     ":constants",
     ":paths",
-    ":subscription-manager",
+    ":subscription-info-provider",
     "${chip_root}/src/app/MessageDef",
     "${chip_root}/src/app/icd/server:icd-server-config",
+    "${chip_root}/src/app/icd/server:manager",
     "${chip_root}/src/app/icd/server:observer",
     "${chip_root}/src/app/util:af-types",
     "${chip_root}/src/app/util:callbacks",
diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp
index 81bea8af9b4626..d0b020caf96315 100644
--- a/src/app/InteractionModelEngine.cpp
+++ b/src/app/InteractionModelEngine.cpp
@@ -336,16 +336,10 @@ bool InteractionModelEngine::SubjectHasActiveSubscription(FabricIndex aFabricInd
 {
     bool isActive = false;
     mReadHandlers.ForEachActiveObject([aFabricIndex, subjectID, &isActive](ReadHandler * handler) {
-        if (!handler->IsType(ReadHandler::InteractionType::Subscribe))
-        {
-            return Loop::Continue;
-        }
+        VerifyOrReturnValue(handler->IsType(ReadHandler::InteractionType::Subscribe), Loop::Continue);
 
         Access::SubjectDescriptor subject = handler->GetSubjectDescriptor();
-        if (subject.fabricIndex != aFabricIndex)
-        {
-            return Loop::Continue;
-        }
+        VerifyOrReturnValue(subject.fabricIndex == aFabricIndex, Loop::Continue);
 
         if (subject.authMode == Access::AuthMode::kCase)
         {
@@ -353,13 +347,9 @@ bool InteractionModelEngine::SubjectHasActiveSubscription(FabricIndex aFabricInd
             {
                 isActive = handler->IsActiveSubscription();
 
-                // Exit loop only if isActive is set to true
-                // Otherwise keep looking for another subscription that could
-                // match the subject
-                if (isActive)
-                {
-                    return Loop::Break;
-                }
+                // Exit loop only if isActive is set to true.
+                // Otherwise keep looking for another subscription that could match the subject.
+                VerifyOrReturnValue(!isActive, Loop::Break);
             }
         }
 
@@ -371,8 +361,30 @@ bool InteractionModelEngine::SubjectHasActiveSubscription(FabricIndex aFabricInd
 
 bool InteractionModelEngine::SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subjectID)
 {
-    // TODO(#30281) : Implement persisted sub check and verify how persistent subscriptions affects this at ICDManager::Init
-    return false;
+    bool persistedSubMatches = false;
+
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+    auto * iterator = mpSubscriptionResumptionStorage->IterateSubscriptions();
+    // Verify that we were able to allocate an iterator. If not, we are probably currently trying to resubscribe to our persisted
+    // subscriptions. As such, we assume we have a persisted subscription and return true.
+    // If we don't have a persisted subscription for the given fabric index and subjectID, we will send a Check-In message next time
+    // we transition to ActiveMode.
+    VerifyOrReturnValue(iterator, true);
+
+    SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo;
+    while (iterator->Next(subscriptionInfo))
+    {
+        // TODO(#31873): Persistent subscription only stores the NodeID for now. We cannot check if the CAT matches
+        if (subscriptionInfo.mFabricIndex == aFabricIndex && subscriptionInfo.mNodeId == subjectID)
+        {
+            persistedSubMatches = true;
+            break;
+        }
+    }
+    iterator->Release();
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+
+    return persistedSubMatches;
 }
 
 void InteractionModelEngine::OnDone(CommandResponseSender & apResponderObj)
@@ -1917,22 +1929,22 @@ CHIP_ERROR InteractionModelEngine::ResumeSubscriptions()
     // future improvements: https://github.com/project-chip/connectedhomeip/issues/25439
 
     SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo;
-    auto * iterator           = mpSubscriptionResumptionStorage->IterateSubscriptions();
-    int subscriptionsToResume = 0;
-    uint16_t minInterval      = 0;
+    auto * iterator             = mpSubscriptionResumptionStorage->IterateSubscriptions();
+    mNumOfSubscriptionsToResume = 0;
+    uint16_t minInterval        = 0;
     while (iterator->Next(subscriptionInfo))
     {
-        subscriptionsToResume++;
+        mNumOfSubscriptionsToResume++;
         minInterval = std::max(minInterval, subscriptionInfo.mMinInterval);
     }
     iterator->Release();
 
-    if (subscriptionsToResume)
+    if (mNumOfSubscriptionsToResume)
     {
 #if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
         mSubscriptionResumptionScheduled = true;
 #endif
-        ChipLogProgress(InteractionModel, "Resuming %d subscriptions in %u seconds", subscriptionsToResume, minInterval);
+        ChipLogProgress(InteractionModel, "Resuming %d subscriptions in %u seconds", mNumOfSubscriptionsToResume, minInterval);
         ReturnErrorOnFailure(mpExchangeMgr->GetSessionManager()->SystemLayer()->StartTimer(System::Clock::Seconds16(minInterval),
                                                                                            ResumeSubscriptionsTimerCallback, this));
     }
@@ -2054,5 +2066,24 @@ bool InteractionModelEngine::HasSubscriptionsToResume()
 }
 #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
 
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+void InteractionModelEngine::DecrementNumSubscriptionsToResume()
+{
+    VerifyOrReturn(mNumOfSubscriptionsToResume > 0);
+#if CHIP_CONFIG_ENABLE_ICD_CIP && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+    VerifyOrDie(mICDManager);
+#endif // CHIP_CONFIG_ENABLE_ICD_CIP && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+
+    mNumOfSubscriptionsToResume--;
+
+#if CHIP_CONFIG_ENABLE_ICD_CIP && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+    if (!mNumOfSubscriptionsToResume)
+    {
+        mICDManager->SetBootUpResumeSubscriptionExecuted();
+    }
+#endif // CHIP_CONFIG_ENABLE_ICD_CIP && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+}
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+
 } // namespace app
 } // namespace chip
diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h
index 57d97f7517b085..935bca002b0dd1 100644
--- a/src/app/InteractionModelEngine.h
+++ b/src/app/InteractionModelEngine.h
@@ -25,6 +25,9 @@
 
 #pragma once
 
+// TODO(#32628): Remove the CHIPCore.h header when the esp32 build is correctly fixed
+#include <lib/core/CHIPCore.h>
+
 #include <access/AccessControl.h>
 #include <app/AppConfig.h>
 #include <app/AttributePathParams.h>
@@ -47,6 +50,7 @@
 #include <app/TimedHandler.h>
 #include <app/WriteClient.h>
 #include <app/WriteHandler.h>
+#include <app/icd/server/ICDServerConfig.h>
 #include <app/reporting/Engine.h>
 #include <app/reporting/ReportScheduler.h>
 #include <app/util/attribute-metadata.h>
@@ -66,6 +70,10 @@
 
 #include <app/CASESessionManager.h>
 
+#if CHIP_CONFIG_ENABLE_ICD_SERVER
+#include <app/icd/server/ICDManager.h> // nogncheck
+#endif                                 // CHIP_CONFIG_ENABLE_ICD_SERVER
+
 namespace chip {
 namespace app {
 
@@ -127,6 +135,10 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
 
     void Shutdown();
 
+#if CHIP_CONFIG_ENABLE_ICD_SERVER
+    void SetICDManager(ICDManager * manager) { mICDManager = manager; };
+#endif // CHIP_CONFIG_ENABLE_ICD_SERVER
+
     Messaging::ExchangeManager * GetExchangeManager(void) const { return mpExchangeMgr; }
 
     /**
@@ -317,6 +329,15 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
 
     bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subjectID) override;
 
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+    /**
+     * @brief Function decrements the number of subscriptions to resume counter - mNumOfSubscriptionsToResume.
+     *        This should be called after we have completed a re-subscribe attempt on a persisted subscription wether the attempt
+     *        was succesful or not.
+     */
+    void DecrementNumSubscriptionsToResume();
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+
 #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
     //
     // Get direct access to the underlying read handler pool
@@ -602,6 +623,10 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
 
     CommandHandlerInterface * mCommandHandlerList = nullptr;
 
+#if CHIP_CONFIG_ENABLE_ICD_SERVER
+    ICDManager * mICDManager = nullptr;
+#endif // CHIP_CONFIG_ENABLE_ICD_SERVER
+
     ObjectPool<CommandResponseSender, CHIP_IM_MAX_NUM_COMMAND_HANDLER> mCommandResponderObjs;
     ObjectPool<TimedHandler, CHIP_IM_MAX_NUM_TIMED_HANDLER> mTimedHandlers;
     WriteHandler mWriteHandlers[CHIP_IM_MAX_NUM_WRITE_HANDLER];
@@ -660,12 +685,21 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
 #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
 #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
 
-#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+    /**
+     * mNumOfSubscriptionsToResume tracks the number of subscriptions that the device will try to resume at its next resumption
+     * attempt. At boot up, the attempt will be at the highest min interval of all the subscriptions to resume.
+     * When the subscription timeout resumption feature is present, after the boot up attempt, the next attempt will be determined
+     * by ComputeTimeSecondsTillNextSubscriptionResumption.
+     */
+    int8_t mNumOfSubscriptionsToResume = 0;
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
     bool HasSubscriptionsToResume();
     uint32_t ComputeTimeSecondsTillNextSubscriptionResumption();
     uint32_t mNumSubscriptionResumptionRetries = 0;
     bool mSubscriptionResumptionScheduled      = false;
-#endif
+#endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
 
     FabricTable * mpFabricTable;
 
diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp
index 63da2a53aa7470..142df8015601ca 100644
--- a/src/app/ReadHandler.cpp
+++ b/src/app/ReadHandler.cpp
@@ -821,6 +821,7 @@ void ReadHandler::PersistSubscription()
     auto * subscriptionResumptionStorage = mManagementCallback.GetInteractionModelEngine()->GetSubscriptionResumptionStorage();
     VerifyOrReturn(subscriptionResumptionStorage != nullptr);
 
+    // TODO(#31873): We need to store the CAT information to enable better interactions with ICDs
     SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId         = GetInitiatorNodeId(),
                                                                          .mFabricIndex    = GetAccessingFabricIndex(),
                                                                          .mSubscriptionId = mSubscriptionId,
diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h
index c597bac8a58d70..bf7d229e51f346 100644
--- a/src/app/ReadHandler.h
+++ b/src/app/ReadHandler.h
@@ -70,6 +70,7 @@ class TestReportScheduler;
 } // namespace reporting
 
 class InteractionModelEngine;
+class TestInteractionModelEngine;
 
 /**
  *  @class ReadHandler
@@ -433,6 +434,7 @@ class ReadHandler : public Messaging::ExchangeDelegate
     //
     friend class chip::app::reporting::Engine;
     friend class chip::app::InteractionModelEngine;
+    friend class TestInteractionModelEngine;
 
     // The report scheduler needs to be able to access StateFlag private functions ShouldStartReporting(), CanStartReporting(),
     // ForceDirtyState() and IsDirty() to know when to schedule a run so it is declared as a friend class.
diff --git a/src/app/SubscriptionResumptionSessionEstablisher.cpp b/src/app/SubscriptionResumptionSessionEstablisher.cpp
index 3c6b3969512c89..6e016684410fc8 100644
--- a/src/app/SubscriptionResumptionSessionEstablisher.cpp
+++ b/src/app/SubscriptionResumptionSessionEstablisher.cpp
@@ -90,15 +90,23 @@ void SubscriptionResumptionSessionEstablisher::HandleDeviceConnected(void * cont
     AutoDeleteEstablisher establisher(static_cast<SubscriptionResumptionSessionEstablisher *>(context));
     SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = establisher->mSubscriptionInfo;
     InteractionModelEngine * imEngine                                  = InteractionModelEngine::GetInstance();
+
+    // Decrement the number of subscriptions to resume since we have completed our retry attempt for a given subscription.
+    // We do this before the readHandler creation since we do not care if the subscription has successfully been resumed or
+    // not. Counter only tracks the number of individual subscriptions we will try to resume.
+    imEngine->DecrementNumSubscriptionsToResume();
+
     if (!imEngine->EnsureResourceForSubscription(subscriptionInfo.mFabricIndex, subscriptionInfo.mAttributePaths.AllocatedSize(),
                                                  subscriptionInfo.mEventPaths.AllocatedSize()))
     {
+        // TODO - Should we keep the subscription here?
         ChipLogProgress(InteractionModel, "no resource for subscription resumption");
         return;
     }
     ReadHandler * readHandler = imEngine->mReadHandlers.CreateObject(*imEngine, imEngine->GetReportScheduler());
     if (readHandler == nullptr)
     {
+        // TODO - Should we keep the subscription here?
         ChipLogProgress(InteractionModel, "no resource for ReadHandler creation");
         return;
     }
@@ -118,10 +126,17 @@ void SubscriptionResumptionSessionEstablisher::HandleDeviceConnectionFailure(voi
                                                                              CHIP_ERROR error)
 {
     AutoDeleteEstablisher establisher(static_cast<SubscriptionResumptionSessionEstablisher *>(context));
+    InteractionModelEngine * imEngine                                  = InteractionModelEngine::GetInstance();
     SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = establisher->mSubscriptionInfo;
     ChipLogError(DataManagement, "Failed to establish CASE for subscription-resumption with error '%" CHIP_ERROR_FORMAT "'",
                  error.Format());
-    auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage();
+
+    // Decrement the number of subscriptions to resume since we have completed our retry attempt for a given subscription.
+    // We do this here since we were not able to connect to the subscriber thus we have completed our resumption attempt.
+    // Counter only tracks the number of individual subscriptions we will try to resume.
+    imEngine->DecrementNumSubscriptionsToResume();
+
+    auto * subscriptionResumptionStorage = imEngine->GetSubscriptionResumptionStorage();
     if (!subscriptionResumptionStorage)
     {
         ChipLogError(DataManagement, "Failed to get subscription resumption storage");
diff --git a/src/app/icd/server/BUILD.gn b/src/app/icd/server/BUILD.gn
index 9a10430a3303ff..a6c5096dde7d0c 100644
--- a/src/app/icd/server/BUILD.gn
+++ b/src/app/icd/server/BUILD.gn
@@ -22,6 +22,11 @@ buildconfig_header("icd-server-buildconfig") {
   header = "ICDServerBuildConfig.h"
   header_dir = "app/icd/server"
 
+  if (chip_enable_icd_lit || chip_enable_icd_checkin ||
+      chip_enable_icd_user_active_mode_trigger) {
+    assert(chip_enable_icd_server)
+  }
+
   if (chip_enable_icd_lit) {
     assert(chip_enable_icd_checkin && chip_enable_icd_user_active_mode_trigger)
   }
@@ -75,7 +80,7 @@ source_set("manager") {
     ":configuration-data",
     ":notifier",
     ":observer",
-    "${chip_root}/src/app:subscription-manager",
+    "${chip_root}/src/app:subscription-info-provider",
     "${chip_root}/src/credentials:credentials",
     "${chip_root}/src/messaging",
   ]
@@ -84,6 +89,7 @@ source_set("manager") {
     public_deps += [
       ":monitoring-table",
       ":sender",
+      "${chip_root}/src/app:app_config",
     ]
   }
 }
diff --git a/src/app/icd/server/ICDManager.cpp b/src/app/icd/server/ICDManager.cpp
index 931737783dde9f..f4442e85972ee2 100644
--- a/src/app/icd/server/ICDManager.cpp
+++ b/src/app/icd/server/ICDManager.cpp
@@ -105,6 +105,10 @@ void ICDManager::Shutdown()
     mFabricTable     = nullptr;
     mSubInfoProvider = nullptr;
     mICDSenderPool.ReleaseAll();
+
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+    mIsBootUpResumeSubscriptionExecuted = false;
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
 #endif // CHIP_CONFIG_ENABLE_ICD_CIP
 }
 
@@ -152,6 +156,7 @@ void ICDManager::SendCheckInMsgs()
 
         ICDMonitoringTable table(*mStorage, fabricInfo.GetFabricIndex(), supported_clients /*Table entry limit*/,
                                  mSymmetricKeystore);
+
         if (table.IsEmpty())
         {
             continue;
@@ -173,8 +178,7 @@ void ICDManager::SendCheckInMsgs()
                 continue;
             }
 
-            bool active = mSubInfoProvider->SubjectHasActiveSubscription(entry.fabricIndex, entry.monitoredSubject);
-            if (active)
+            if (!ShouldCheckInMsgsBeSentAtActiveModeFunction(entry.fabricIndex, entry.monitoredSubject))
             {
                 continue;
             }
@@ -204,8 +208,10 @@ void ICDManager::SendCheckInMsgs()
 #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
 }
 
-bool ICDManager::CheckInMessagesWouldBeSent()
+bool ICDManager::CheckInMessagesWouldBeSent(const std::function<ShouldCheckInMsgsBeSentFunction> & shouldCheckInMsgsBeSentFunction)
 {
+    VerifyOrReturnValue(shouldCheckInMsgsBeSentFunction, false);
+
     for (const auto & fabricInfo : *mFabricTable)
     {
         uint16_t supported_clients = ICDConfigurationData::GetInstance().GetClientsSupportedPerFabric();
@@ -234,7 +240,7 @@ bool ICDManager::CheckInMessagesWouldBeSent()
             }
 
             // At least one registration would require a Check-In message
-            VerifyOrReturnValue(mSubInfoProvider->SubjectHasActiveSubscription(entry.fabricIndex, entry.monitoredSubject), true);
+            VerifyOrReturnValue(!shouldCheckInMsgsBeSentFunction(entry.fabricIndex, entry.monitoredSubject), true);
         }
     }
 
@@ -242,7 +248,87 @@ bool ICDManager::CheckInMessagesWouldBeSent()
     return false;
 }
 
-void ICDManager::TriggerCheckInMessages()
+/**
+ * ShouldCheckInMsgsBeSentAtActiveModeFunction is used to determine if a Check-In message is required for a given registration.
+ * Due to how the ICD Check-In use-case interacts with the persistent subscription and subscription timeout resumption features,
+ * having a single implementation of the function renders the implementation very difficult to understand and maintain.
+ * Because of this, each valid feature combination has its own implementation of the function.
+ */
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+/**
+ * @brief Implementation for when the persistent subscription and subscription timeout resumption feature are present.
+ *        Function checks that there are no active or persisted subscriptions for a given fabricIndex or subjectID.
+ *
+ * @note When the persistent subscription and subscription timeout resumption feature are present, we need to check for
+ *       persisted subscription at each transition to ActiveMode since there will be persisted subscriptions during normal
+ *       operation for the subscription timeout resumption feature. Once we have finished all our subscription resumption attempts
+ *       for a given subscription, the entry is deleted from persisted storage which will enable us to send Check-In messages for
+ *       the client registration. This logic avoids the device sending a Check-In message while trying to resume subscriptions.
+ *
+ * @param aFabricIndex
+ * @param subjectID subjectID to check. Can be an operational node id or a CAT
+ *
+ * @return true Returns true if the fabricIndex and subjectId combination does not have an active or a persisted subscription.
+ * @return false Returns false if the fabricIndex and subjectId combination has an active or persisted subscription.
+ */
+bool ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID)
+{
+    return !(mSubInfoProvider->SubjectHasActiveSubscription(aFabricIndex, subjectID) ||
+             mSubInfoProvider->SubjectHasPersistedSubscription(aFabricIndex, subjectID));
+}
+#else
+/**
+ * @brief Implementation for when the persistent subscription feature is present without the subscription timeout resumption
+ * feature. Function checks that there are no active subscriptions. If the boot up subscription resumption has not been completed,
+ *        function also checks if there are persisted subscriptions.
+ *
+ * @note The persistent subscriptions feature tries to resume subscriptions at the highest min interval
+ *       of all the persisted subscriptions. As such, it is possible for the ICD to return to Idle Mode
+ *       until the timer elaspses. We do not want to send Check-In messages to clients with persisted subscriptions
+ *       until we have tried to resubscribe.
+ *
+ * @param aFabricIndex
+ * @param subjectID subjectID to check. Can be an opperationnal node id or a CAT
+ *
+ * @return true Returns true if the fabricIndex and subjectId combination does not have an active subscription.
+ *              If the boot up subscription resumption has not been completed, there must not be a persisted subscription either.
+ * @return false Returns false if the fabricIndex and subjectId combination has an active subscription.
+ *               If the boot up subscription resumption has not been completed,
+ *               returns false if the fabricIndex and subjectId combination has a persisted subscription.
+ */
+bool ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID)
+{
+    bool mightHaveSubscription = mSubInfoProvider->SubjectHasActiveSubscription(aFabricIndex, subjectID);
+    if (!mightHaveSubscription && !mIsBootUpResumeSubscriptionExecuted)
+    {
+        mightHaveSubscription = mSubInfoProvider->SubjectHasPersistedSubscription(aFabricIndex, subjectID);
+    }
+
+    return !mightHaveSubscription;
+}
+#endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#else
+/**
+ * @brief Implementation for when neither the persistent subscription nor the subscription timeout resumption features are present.
+ *        Function checks that there no active sbuscriptions for a given fabricIndex and subjectId combination.
+ *
+ * @note When neither the persistent subscription nor the subscription timeout resumption features are present, we only need to
+ *       check for active subscription since we will never have any persisted subscription.
+ *
+ * @param aFabricIndex
+ * @param subjectID subjectID to check. Can be an opperationnal node id or a CAT
+ *
+ * @return true Returns true if the fabricIndex and subjectId combination does not have an active subscription.
+ * @return false Returns false if the fabricIndex and subjectId combination has an active subscription.
+ */
+bool ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID)
+{
+    return !(mSubInfoProvider->SubjectHasActiveSubscription(aFabricIndex, subjectID));
+}
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+
+void ICDManager::TriggerCheckInMessages(const std::function<ShouldCheckInMsgsBeSentFunction> & verifier)
 {
     VerifyOrReturn(SupportsFeature(Feature::kCheckInProtocolSupport));
 
@@ -251,8 +337,7 @@ void ICDManager::TriggerCheckInMessages()
     VerifyOrReturn(mOperationalState == OperationalState::IdleMode);
 
     // If we don't have any Check-In messages to send, do nothing
-    VerifyOrReturn(CheckInMessagesWouldBeSent());
-
+    VerifyOrReturn(CheckInMessagesWouldBeSent(verifier));
     UpdateOperationState(OperationalState::ActiveMode);
 }
 #endif // CHIP_CONFIG_ENABLE_ICD_CIP
@@ -313,12 +398,16 @@ void ICDManager::UpdateOperationState(OperationalState state)
     {
         mOperationalState = OperationalState::IdleMode;
 
+#if CHIP_CONFIG_ENABLE_ICD_CIP
+        std::function<ShouldCheckInMsgsBeSentFunction> sendCheckInMessagesOnActiveMode =
+            std::bind(&ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction, this, std::placeholders::_1, std::placeholders::_2);
+#endif // CHIP_CONFIG_ENABLE_ICD_CIP
+
         // When the active mode interval is 0, we stay in idleMode until a notification brings the icd into active mode
         // unless the device would need to send Check-In messages
-        // TODO(#30281) : Verify how persistent subscriptions affects this at ICDManager::Init
         if (ICDConfigurationData::GetInstance().GetActiveModeDuration() > kZero
 #if CHIP_CONFIG_ENABLE_ICD_CIP
-            || CheckInMessagesWouldBeSent()
+            || CheckInMessagesWouldBeSent(sendCheckInMessagesOnActiveMode)
 #endif // CHIP_CONFIG_ENABLE_ICD_CIP
         )
         {
diff --git a/src/app/icd/server/ICDManager.h b/src/app/icd/server/ICDManager.h
index 5e391c1469143c..058285802eb309 100644
--- a/src/app/icd/server/ICDManager.h
+++ b/src/app/icd/server/ICDManager.h
@@ -31,6 +31,7 @@
 #include <app/icd/server/ICDStateObserver.h>
 #include <credentials/FabricTable.h>
 #include <crypto/SessionKeystore.h>
+#include <functional>
 #include <lib/support/BitFlags.h>
 #include <messaging/ExchangeMgr.h>
 #include <platform/CHIPDeviceConfig.h>
@@ -79,6 +80,17 @@ class ICDManager : public ICDListener
         ICDModeChange,
     };
 
+    /**
+     * @brief Verifier template function
+     *        This type can be used to implement specific verifiers that can be used in the CheckInMessagesWouldBeSent function.
+     *        The goal is to avoid having multiple functions that implement the iterator loop with only the check changing.
+     *
+     * @return true if at least one Check-In message would be sent
+     *         false No Check-In messages would be sent
+     */
+
+    using ShouldCheckInMsgsBeSentFunction = bool(FabricIndex aFabricIndex, NodeId subjectID);
+
     ICDManager() {}
     void Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, Crypto::SymmetricKeystore * symmetricKeyStore,
               Messaging::ExchangeManager * exchangeManager, SubscriptionsInfoProvider * manager);
@@ -122,12 +134,26 @@ class ICDManager : public ICDListener
 
     /**
      * @brief Trigger the ICDManager to send Check-In message if necessary
+     *
+     * @param[in] function to use to determine if we need to send check-in messages
      */
-    void TriggerCheckInMessages();
+    void TriggerCheckInMessages(const std::function<ShouldCheckInMsgsBeSentFunction> & function);
+
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+    /**
+     * @brief Set mSubCheckInBootCheckExecuted to true
+     *        Function allows the InteractionModelEngine to notify the ICDManager that the boot up subscription resumption has been
+     *        completed.
+     */
+    void SetBootUpResumeSubscriptionExecuted() { mIsBootUpResumeSubscriptionExecuted = true; };
+#endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
 #endif // CHIP_CONFIG_ENABLE_ICD_CIP
 
-#ifdef CONFIG_BUILD_FOR_HOST_UNIT_TEST
+#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
     void SetTestFeatureMapValue(uint32_t featureMap) { mFeatureMap = featureMap; };
+#if !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+    bool GetIsBootUpResumeSubscriptionExecuted() { return mIsBootUpResumeSubscriptionExecuted; };
+#endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
 #endif
 
     // Implementation of ICDListener functions.
@@ -166,14 +192,18 @@ class ICDManager : public ICDListener
 
 private:
 #if CHIP_CONFIG_ENABLE_ICD_CIP
+    bool ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID);
+
     /**
      * @brief Function checks if at least one client registration would require a Check-In message
      *
+     * @param[in] function  function to use to determine if a Check-In message would be sent for a given registration
+     *
      * @return true At least one registration would require an Check-In message if we were entering ActiveMode.
-     * @return false None of the registration would require a Check-In message either because there are no registration or because
-     *               they all have associated subscriptions.
+     * @return false None of the registration would require a Check-In message either because there are no registration or
+     * because they all have associated subscriptions.
      */
-    bool CheckInMessagesWouldBeSent();
+    bool CheckInMessagesWouldBeSent(const std::function<ShouldCheckInMsgsBeSentFunction> & function);
 #endif // CHIP_CONFIG_ENABLE_ICD_CIP
 
     KeepActiveFlags mKeepActiveFlags{ 0 };
@@ -184,6 +214,9 @@ class ICDManager : public ICDListener
     ObjectPool<ObserverPointer, CHIP_CONFIG_ICD_OBSERVERS_POOL_SIZE> mStateObserverPool;
 
 #if CHIP_CONFIG_ENABLE_ICD_CIP
+#if !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+    bool mIsBootUpResumeSubscriptionExecuted = false;
+#endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
     PersistentStorageDelegate * mStorage           = nullptr;
     FabricTable * mFabricTable                     = nullptr;
     Messaging::ExchangeManager * mExchangeManager  = nullptr;
diff --git a/src/app/reporting/ReportScheduler.h b/src/app/reporting/ReportScheduler.h
index 5700eed6dfa025..f0e6ce3ff82f27 100644
--- a/src/app/reporting/ReportScheduler.h
+++ b/src/app/reporting/ReportScheduler.h
@@ -18,6 +18,9 @@
 
 #pragma once
 
+// TODO(#32628): Remove the CHIPCore.h header when the esp32 build is correctly fixed
+#include <lib/core/CHIPCore.h>
+
 #include <app/ReadHandler.h>
 #include <app/icd/server/ICDStateObserver.h>
 #include <lib/core/CHIPError.h>
@@ -213,7 +216,7 @@ class ReportScheduler : public ReadHandler::Observer, public ICDStateObserver
     /// @brief Get the number of ReadHandlers registered in the scheduler's node pool
     size_t GetNumReadHandlers() const { return mNodesPool.Allocated(); }
 
-#ifdef CONFIG_BUILD_FOR_HOST_UNIT_TEST
+#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
     Timestamp GetMinTimestampForHandler(const ReadHandler * aReadHandler)
     {
         ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp
index d2983636450691..eff11222bfae7b 100644
--- a/src/app/server/Server.cpp
+++ b/src/app/server/Server.cpp
@@ -333,6 +333,10 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams)
                                                                  &mCASESessionManager, mSubscriptionResumptionStorage);
     SuccessOrExit(err);
 
+#if CHIP_CONFIG_ENABLE_ICD_SERVER
+    app::InteractionModelEngine::GetInstance()->SetICDManager(&mICDManager);
+#endif // CHIP_CONFIG_ENABLE_ICD_SERVER
+
     // ICD Init needs to be after data model init and InteractionModel Init
 #if CHIP_CONFIG_ENABLE_ICD_SERVER
 
@@ -446,12 +450,14 @@ void Server::OnPlatformEvent(const DeviceLayer::ChipDeviceEvent & event)
         // We trigger Check-In messages before resuming subscriptions to avoid doing both.
         if (!mFailSafeContext.IsFailSafeArmed())
         {
-            mICDManager.TriggerCheckInMessages();
+            std::function<app::ICDManager::ShouldCheckInMsgsBeSentFunction> sendCheckInMessagesOnBootUp =
+                std::bind(&Server::ShouldCheckInMsgsBeSentAtBootFunction, this, std::placeholders::_1, std::placeholders::_2);
+            mICDManager.TriggerCheckInMessages(sendCheckInMessagesOnBootUp);
         }
 #endif // CHIP_CONFIG_ENABLE_ICD_SERVER && CHIP_CONFIG_ENABLE_ICD_CIP
 #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
         ResumeSubscriptions();
-#endif
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
         break;
 #if CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
     case DeviceEventType::kThreadConnectivityChange:
@@ -519,6 +525,19 @@ void Server::RejoinExistingMulticastGroups()
     }
 }
 
+#if CHIP_CONFIG_ENABLE_ICD_CIP
+bool Server::ShouldCheckInMsgsBeSentAtBootFunction(FabricIndex aFabricIndex, NodeId subjectID)
+{
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+    // If at least one registration has a persisted entry, do not send Check-In message.
+    // The resumption of the persisted subscription will serve the same function a check-in would have served.
+    return !app::InteractionModelEngine::GetInstance()->SubjectHasPersistedSubscription(aFabricIndex, subjectID);
+#else
+    return true;
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+}
+#endif // CHIP_CONFIG_ENABLE_ICD_CIP
+
 void Server::GenerateShutDownEvent()
 {
     PlatformMgr().ScheduleWork([](intptr_t) { PlatformMgr().HandleServerShuttingDown(); });
@@ -561,6 +580,9 @@ void Server::Shutdown()
 
     chip::Dnssd::Resolver::Instance().Shutdown();
     chip::app::InteractionModelEngine::GetInstance()->Shutdown();
+#if CHIP_CONFIG_ENABLE_ICD_SERVER
+    app::InteractionModelEngine::GetInstance()->SetICDManager(nullptr);
+#endif // CHIP_CONFIG_ENABLE_ICD_SERVER
     mCommissioningWindowManager.Shutdown();
     mMessageCounterManager.Shutdown();
     mExchangeMgr.Shutdown();
diff --git a/src/app/server/Server.h b/src/app/server/Server.h
index 302471b8bebb89..8f6fcd5abecc66 100644
--- a/src/app/server/Server.h
+++ b/src/app/server/Server.h
@@ -369,6 +369,18 @@ class Server
 
 #if CHIP_CONFIG_ENABLE_ICD_SERVER
     app::ICDManager & GetICDManager() { return mICDManager; }
+
+#if CHIP_CONFIG_ENABLE_ICD_CIP
+    /**
+     * @brief Function to determine if a Check-In message would be sent at Boot up
+     *
+     * @param aFabricIndex client fabric index
+     * @param subjectID client subject ID
+     * @return true Check-In message would be sent on boot up.
+     * @return false Device has a persisted subscription with the client. See CHIP_CONFIG_PERSIST_SUBSCRIPTIONS.
+     */
+    bool ShouldCheckInMsgsBeSentAtBootFunction(FabricIndex aFabricIndex, NodeId subjectID);
+#endif // CHIP_CONFIG_ENABLE_ICD_CIP
 #endif // CHIP_CONFIG_ENABLE_ICD_SERVER
 
     /**
diff --git a/src/app/tests/TestICDManager.cpp b/src/app/tests/TestICDManager.cpp
index ba311f889989f8..ac8c220351fd86 100644
--- a/src/app/tests/TestICDManager.cpp
+++ b/src/app/tests/TestICDManager.cpp
@@ -48,10 +48,10 @@ namespace {
 constexpr uint16_t kMaxTestClients      = 2;
 constexpr FabricIndex kTestFabricIndex1 = 1;
 constexpr FabricIndex kTestFabricIndex2 = kMaxValidFabricIndex;
-constexpr uint64_t kClientNodeId11      = 0x100001;
-constexpr uint64_t kClientNodeId12      = 0x100002;
-constexpr uint64_t kClientNodeId21      = 0x200001;
-constexpr uint64_t kClientNodeId22      = 0x200002;
+constexpr NodeId kClientNodeId11        = 0x100001;
+constexpr NodeId kClientNodeId12        = 0x100002;
+constexpr NodeId kClientNodeId21        = 0x200001;
+constexpr NodeId kClientNodeId22        = 0x200002;
 
 constexpr uint8_t kKeyBuffer1a[] = {
     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
@@ -80,13 +80,15 @@ class TestSubscriptionsInfoProvider : public SubscriptionsInfoProvider
     TestSubscriptionsInfoProvider() = default;
     ~TestSubscriptionsInfoProvider(){};
 
-    void SetReturnValue(bool value) { mReturnValue = value; };
+    void SetHasActiveSubscription(bool value) { mHasActiveSubscription = value; };
+    void SetHasPersistedSubscription(bool value) { mHasPersistedSubscription = value; };
 
-    bool SubjectHasActiveSubscription(FabricIndex aFabricIndex, NodeId subject) { return mReturnValue; };
-    bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subject) { return mReturnValue; };
+    bool SubjectHasActiveSubscription(FabricIndex aFabricIndex, NodeId subject) { return mHasActiveSubscription; };
+    bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subject) { return mHasPersistedSubscription; };
 
 private:
-    bool mReturnValue = false;
+    bool mHasActiveSubscription    = false;
+    bool mHasPersistedSubscription = false;
 };
 
 class TestContext : public chip::Test::AppContext
@@ -197,7 +199,8 @@ class TestICDManager
         ctx->mICDManager.SetTestFeatureMapValue(0x07);
 
         // Set that there are no matching subscriptions
-        ctx->mSubInfoProvider.SetReturnValue(false);
+        ctx->mSubInfoProvider.SetHasActiveSubscription(false);
+        ctx->mSubInfoProvider.SetHasPersistedSubscription(false);
 
         // Set New durations for test case
         Milliseconds32 oldActiveModeDuration = icdConfigData.GetActiveModeDuration();
@@ -276,7 +279,8 @@ class TestICDManager
         ctx->mICDManager.SetTestFeatureMapValue(0x07);
 
         // Set that there are not matching subscriptions
-        ctx->mSubInfoProvider.SetReturnValue(true);
+        ctx->mSubInfoProvider.SetHasActiveSubscription(true);
+        ctx->mSubInfoProvider.SetHasPersistedSubscription(true);
 
         // Set New durations for test case
         Milliseconds32 oldActiveModeDuration = icdConfigData.GetActiveModeDuration();
@@ -617,6 +621,77 @@ class TestICDManager
         // confirm the promised time is 20000 since the device is already planing to stay active longer than the requested time
         NL_TEST_ASSERT(aSuite, stayActivePromisedMs == 20000);
     }
+
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+    static void TestShouldCheckInMsgsBeSentAtActiveModeFunction(nlTestSuite * aSuite, void * aContext)
+    {
+        TestContext * ctx = static_cast<TestContext *>(aContext);
+
+        // Test 1 - Has no ActiveSubscription & no persisted subscription
+        ctx->mSubInfoProvider.SetHasActiveSubscription(false);
+        ctx->mSubInfoProvider.SetHasPersistedSubscription(false);
+        NL_TEST_ASSERT(aSuite, ctx->mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
+
+        // Test 2 - Has no active subscription & a persisted subscription
+        ctx->mSubInfoProvider.SetHasActiveSubscription(false);
+        ctx->mSubInfoProvider.SetHasPersistedSubscription(true);
+        NL_TEST_ASSERT(aSuite, !(ctx->mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11)));
+
+        // Test 3 - Has an active subscription & a persisted subscription
+        ctx->mSubInfoProvider.SetHasActiveSubscription(true);
+        ctx->mSubInfoProvider.SetHasPersistedSubscription(true);
+        NL_TEST_ASSERT(aSuite, !(ctx->mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11)));
+    }
+#else
+    static void TestShouldCheckInMsgsBeSentAtActiveModeFunction(nlTestSuite * aSuite, void * aContext)
+    {
+        TestContext * ctx = static_cast<TestContext *>(aContext);
+
+        // Test 1 - Has no active subscription and no persisted subscription at boot up
+        ctx->mSubInfoProvider.SetHasActiveSubscription(false);
+        ctx->mSubInfoProvider.SetHasPersistedSubscription(false);
+        ctx->mICDManager.mIsBootUpResumeSubscriptionExecuted = false;
+        NL_TEST_ASSERT(aSuite, ctx->mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
+
+        // Test 2 - Has no active subscription and a persisted subscription at boot up
+        ctx->mSubInfoProvider.SetHasActiveSubscription(false);
+        ctx->mSubInfoProvider.SetHasPersistedSubscription(true);
+        ctx->mICDManager.mIsBootUpResumeSubscriptionExecuted = false;
+        NL_TEST_ASSERT(aSuite, !(ctx->mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11)));
+
+        // Test 3 - Has an active subscription and a persisted subscription during normal operations
+        ctx->mSubInfoProvider.SetHasActiveSubscription(true);
+        ctx->mSubInfoProvider.SetHasPersistedSubscription(true);
+        ctx->mICDManager.mIsBootUpResumeSubscriptionExecuted = true;
+        NL_TEST_ASSERT(aSuite, !(ctx->mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11)));
+
+        // Test 4 - Has no active subscription and a persisted subscription during normal operations
+        ctx->mSubInfoProvider.SetHasActiveSubscription(false);
+        ctx->mSubInfoProvider.SetHasPersistedSubscription(true);
+        ctx->mICDManager.mIsBootUpResumeSubscriptionExecuted = true;
+        NL_TEST_ASSERT(aSuite, ctx->mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
+    }
+#endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#else
+    static void TestShouldCheckInMsgsBeSentAtActiveModeFunction(nlTestSuite * aSuite, void * aContext)
+    {
+        TestContext * ctx = static_cast<TestContext *>(aContext);
+
+        // Test 1 - Has an active subscription
+        ctx->mSubInfoProvider.SetHasActiveSubscription(true);
+        NL_TEST_ASSERT(aSuite,
+                       ctx->mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11) == false);
+
+        // Test 2 - Has no active subscription
+        ctx->mSubInfoProvider.SetHasActiveSubscription(false);
+        NL_TEST_ASSERT(aSuite, ctx->mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
+
+        // Test 3 - Make sure that the persisted subscription has no impact
+        ctx->mSubInfoProvider.SetHasPersistedSubscription(true);
+        NL_TEST_ASSERT(aSuite, ctx->mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
+    }
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
 };
 
 } // namespace app
@@ -634,6 +709,7 @@ static const nlTest sTests[] = {
     NL_TEST_DEF("TestICDMRegisterUnregisterEvents", TestICDManager::TestICDMRegisterUnregisterEvents),
     NL_TEST_DEF("TestICDCounter", TestICDManager::TestICDCounter),
     NL_TEST_DEF("TestICDStayActive", TestICDManager::TestICDMStayActive),
+    NL_TEST_DEF("TestShouldCheckInMsgsBeSentAtActiveModeFunction", TestICDManager::TestShouldCheckInMsgsBeSentAtActiveModeFunction),
     NL_TEST_SENTINEL(),
 };
 
diff --git a/src/app/tests/TestInteractionModelEngine.cpp b/src/app/tests/TestInteractionModelEngine.cpp
index fcdc7d53e1594f..4f0b75315355ad 100644
--- a/src/app/tests/TestInteractionModelEngine.cpp
+++ b/src/app/tests/TestInteractionModelEngine.cpp
@@ -22,26 +22,49 @@
  *
  */
 
+#include <app/AppConfig.h>
 #include <app/InteractionModelEngine.h>
+#include <app/icd/server/ICDServerConfig.h>
 #include <app/reporting/tests/MockReportScheduler.h>
 #include <app/tests/AppTestContext.h>
 #include <app/util/mock/Constants.h>
 #include <app/util/mock/Functions.h>
-#include <lib/core/CHIPCore.h>
+#include <lib/core/CASEAuthTag.h>
 #include <lib/core/ErrorStr.h>
 #include <lib/core/TLV.h>
 #include <lib/core/TLVDebug.h>
 #include <lib/core/TLVUtilities.h>
 #include <lib/support/UnitTestContext.h>
+#include <lib/support/UnitTestExtendedAssertions.h>
 #include <lib/support/UnitTestRegistration.h>
 #include <messaging/ExchangeContext.h>
 #include <messaging/Flags.h>
 #include <platform/CHIPDeviceLayer.h>
 
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+#include <app/SimpleSubscriptionResumptionStorage.h>
+#include <lib/support/TestPersistentStorageDelegate.h>
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+
 #include <nlunit-test.h>
 
+namespace {
+
 using TestContext = chip::Test::AppContext;
 
+class NullReadHandlerCallback : public chip::app::ReadHandler::ManagementCallback
+{
+public:
+    void OnDone(chip::app::ReadHandler & apReadHandlerObj) override {}
+    chip::app::ReadHandler::ApplicationCallback * GetAppCallback() override { return nullptr; }
+    chip::app::InteractionModelEngine * GetInteractionModelEngine() override
+    {
+        return chip::app::InteractionModelEngine::GetInstance();
+    }
+};
+
+} // namespace
+
 namespace chip {
 namespace app {
 class TestInteractionModelEngine
@@ -49,10 +72,19 @@ class TestInteractionModelEngine
 public:
     static void TestAttributePathParamsPushRelease(nlTestSuite * apSuite, void * apContext);
     static void TestRemoveDuplicateConcreteAttribute(nlTestSuite * apSuite, void * apContext);
-#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+    static void TestSubjectHasPersistedSubscription(nlTestSuite * apSuite, void * apContext);
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
     static void TestSubscriptionResumptionTimer(nlTestSuite * apSuite, void * apContext);
-#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
     static int GetAttributePathListLength(SingleLinkedListNode<AttributePathParams> * apattributePathParamsList);
+    static void TestSubjectHasActiveSubscriptionSingleSubOneEntry(nlTestSuite * apSuite, void * apContext);
+    static void TestSubjectHasActiveSubscriptionSingleSubMultipleEntries(nlTestSuite * apSuite, void * apContext);
+    static void TestSubjectHasActiveSubscriptionMultipleSubsSingleEntry(nlTestSuite * apSuite, void * apContext);
+    static void TestSubjectHasActiveSubscriptionMultipleSubsMultipleEntries(nlTestSuite * apSuite, void * apContext);
+    static void TestSubjectHasActiveSubscriptionSubWithCAT(nlTestSuite * apSuite, void * apContext);
+    static void TestDecrementNumSubscriptionsToResume(nlTestSuite * apSuite, void * apContext);
 };
 
 int TestInteractionModelEngine::GetAttributePathListLength(SingleLinkedListNode<AttributePathParams> * apAttributePathParamsList)
@@ -229,7 +261,397 @@ void TestInteractionModelEngine::TestRemoveDuplicateConcreteAttribute(nlTestSuit
     InteractionModelEngine::GetInstance()->ReleaseAttributePathList(attributePathParamsList);
 }
 
-#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+/**
+ * @brief Test verifies the SubjectHasActiveSubscription with a single subscription with a single entry
+ */
+void TestInteractionModelEngine::TestSubjectHasActiveSubscriptionSingleSubOneEntry(nlTestSuite * apSuite, void * apContext)
+{
+    TestContext & ctx = *static_cast<TestContext *>(apContext);
+    NullReadHandlerCallback nullCallback;
+    InteractionModelEngine * engine = InteractionModelEngine::GetInstance();
+
+    NodeId bobNodeId           = 0x12344321ull;
+    FabricIndex bobFabricIndex = 1;
+
+    // Create ExchangeContext
+    Messaging::ExchangeContext * exchangeCtx1 = ctx.NewExchangeToBob(nullptr, false);
+    NL_TEST_ASSERT(apSuite, exchangeCtx1);
+
+    // InteractionModelEngine init
+    NL_TEST_ASSERT(apSuite,
+                   CHIP_NO_ERROR ==
+                       engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reporting::GetDefaultReportScheduler()));
+
+    // Verify that there are no active subscriptions
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+
+    // Create and setup readHandler 1
+    ReadHandler * readHandler1 = engine->GetReadHandlerPool().CreateObject(
+        nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
+
+    // Verify that Bob still doesn't have an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+
+    // Set readHandler1 to active
+    readHandler1->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true);
+
+    // Verify that Bob still has an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId));
+
+    // Clean up read handlers
+    engine->GetReadHandlerPool().ReleaseAll();
+
+    // Verify that there are no active subscriptions
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+}
+
+/**
+ * @brief Test verifies that the SubjectHasActiveSubscription will continue iterating till it fines at least one valid active
+ * subscription
+ */
+void TestInteractionModelEngine::TestSubjectHasActiveSubscriptionSingleSubMultipleEntries(nlTestSuite * apSuite, void * apContext)
+{
+    TestContext & ctx = *static_cast<TestContext *>(apContext);
+    NullReadHandlerCallback nullCallback;
+    InteractionModelEngine * engine = InteractionModelEngine::GetInstance();
+
+    NodeId bobNodeId           = 0x12344321ull;
+    FabricIndex bobFabricIndex = 1;
+
+    // Create ExchangeContexts
+    Messaging::ExchangeContext * exchangeCtx1 = ctx.NewExchangeToBob(nullptr, false);
+    NL_TEST_ASSERT(apSuite, exchangeCtx1);
+
+    Messaging::ExchangeContext * exchangeCtx2 = ctx.NewExchangeToBob(nullptr, false);
+    NL_TEST_ASSERT(apSuite, exchangeCtx1);
+
+    // InteractionModelEngine init
+    NL_TEST_ASSERT(apSuite,
+                   CHIP_NO_ERROR ==
+                       engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reporting::GetDefaultReportScheduler()));
+
+    // Verify that both Alice and Bob have no active subscriptions
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+
+    // Create readHandler 1
+    engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe,
+                                              reporting::GetDefaultReportScheduler());
+
+    // Verify that Bob still doesn't have an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+
+    // Create and setup readHandler 2
+    ReadHandler * readHandler2 = engine->GetReadHandlerPool().CreateObject(
+        nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
+
+    // Verify that Bob still doesn't have an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+
+    // Set readHandler2 to active
+    readHandler2->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true);
+
+    // Verify that Bob still doesn't have an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId));
+
+    // Release active ReadHandler
+    engine->GetReadHandlerPool().ReleaseObject(readHandler2);
+
+    // Verify that there are no active subscriptions
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+
+    // Clean up read handlers
+    engine->GetReadHandlerPool().ReleaseAll();
+}
+
+/**
+ * @brief Test validates that the SubjectHasActiveSubscription can support multiple subscriptions from different clients
+ */
+void TestInteractionModelEngine::TestSubjectHasActiveSubscriptionMultipleSubsSingleEntry(nlTestSuite * apSuite, void * apContext)
+{
+    TestContext & ctx = *static_cast<TestContext *>(apContext);
+    NullReadHandlerCallback nullCallback;
+    InteractionModelEngine * engine = InteractionModelEngine::GetInstance();
+
+    NodeId bobNodeId             = 0x12344321ull;
+    FabricIndex bobFabricIndex   = 1;
+    NodeId aliceNodeId           = 0x11223344ull;
+    FabricIndex aliceFabricIndex = 2;
+
+    // Create ExchangeContexts
+    Messaging::ExchangeContext * exchangeCtx1 = ctx.NewExchangeToBob(nullptr, false);
+    NL_TEST_ASSERT(apSuite, exchangeCtx1);
+
+    Messaging::ExchangeContext * exchangeCtx2 = ctx.NewExchangeToAlice(nullptr, false);
+    NL_TEST_ASSERT(apSuite, exchangeCtx2);
+
+    // InteractionModelEngine init
+    NL_TEST_ASSERT(apSuite,
+                   CHIP_NO_ERROR ==
+                       engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reporting::GetDefaultReportScheduler()));
+
+    // Verify that both Alice and Bob have no active subscriptions
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId) == false);
+
+    // Create and setup readHandler 1
+    ReadHandler * readHandler1 = engine->GetReadHandlerPool().CreateObject(
+        nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
+
+    // Create and setup readHandler 2
+    ReadHandler * readHandler2 = engine->GetReadHandlerPool().CreateObject(
+        nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
+
+    // Verify that Bob still doesn't have an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+
+    // Set readHandler1 to active
+    readHandler1->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true);
+
+    // Verify that Bob has an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId));
+
+    // Verify that Alice still doesn't have an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId) == false);
+
+    // Set readHandler2 to active
+    readHandler2->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true);
+
+    // Verify that Bob has an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId));
+
+    // Verify that Alice still doesn't have an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId));
+
+    // Set readHandler1 to inactive
+    readHandler1->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, false);
+
+    // Verify that Bob doesn't have an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+
+    // Verify that Alice still has an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId));
+
+    // Clean up read handlers
+    engine->GetReadHandlerPool().ReleaseAll();
+
+    // Verify that both Alice and Bob have no active subscriptions
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId) == false);
+}
+
+/**
+ * @brief Test validates that the SubjectHasActiveSubscription can find the active subscription even if there are multiple
+ * subscriptions for each client
+ */
+void TestInteractionModelEngine::TestSubjectHasActiveSubscriptionMultipleSubsMultipleEntries(nlTestSuite * apSuite,
+                                                                                             void * apContext)
+{
+    TestContext & ctx = *static_cast<TestContext *>(apContext);
+    NullReadHandlerCallback nullCallback;
+    InteractionModelEngine * engine = InteractionModelEngine::GetInstance();
+
+    NodeId bobNodeId             = 0x12344321ull;
+    FabricIndex bobFabricIndex   = 1;
+    NodeId aliceNodeId           = 0x11223344ull;
+    FabricIndex aliceFabricIndex = 2;
+
+    // Create ExchangeContexts
+    Messaging::ExchangeContext * exchangeCtx11 = ctx.NewExchangeToBob(nullptr, false);
+    NL_TEST_ASSERT(apSuite, exchangeCtx11);
+
+    Messaging::ExchangeContext * exchangeCtx12 = ctx.NewExchangeToBob(nullptr, false);
+    NL_TEST_ASSERT(apSuite, exchangeCtx12);
+
+    Messaging::ExchangeContext * exchangeCtx21 = ctx.NewExchangeToAlice(nullptr, false);
+    NL_TEST_ASSERT(apSuite, exchangeCtx21);
+
+    Messaging::ExchangeContext * exchangeCtx22 = ctx.NewExchangeToAlice(nullptr, false);
+    NL_TEST_ASSERT(apSuite, exchangeCtx22);
+
+    // InteractionModelEngine init
+    NL_TEST_ASSERT(apSuite,
+                   CHIP_NO_ERROR ==
+                       engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reporting::GetDefaultReportScheduler()));
+
+    // Verify that both Alice and Bob have no active subscriptions
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId) == false);
+
+    // Create and setup readHandler 1-1
+    engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx11, ReadHandler::InteractionType::Subscribe,
+                                              reporting::GetDefaultReportScheduler());
+
+    // Create and setup readHandler 1-2
+    ReadHandler * readHandler12 = engine->GetReadHandlerPool().CreateObject(
+        nullCallback, exchangeCtx12, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
+
+    // Create and setup readHandler 2-1
+    engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx21, ReadHandler::InteractionType::Subscribe,
+                                              reporting::GetDefaultReportScheduler());
+
+    // Create and setup readHandler 2-2
+    ReadHandler * readHandler22 = engine->GetReadHandlerPool().CreateObject(
+        nullCallback, exchangeCtx22, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
+
+    // Verify that both Alice and Bob have no active subscriptions
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId) == false);
+
+    // Set readHandler 1-2 to active
+    readHandler12->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true);
+
+    // Verify that Bob has an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId));
+
+    // Verify that Alice still doesn't have an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId) == false);
+
+    // Set readHandler 2-2 to active
+    readHandler22->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true);
+
+    // Verify that Bob has an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId));
+
+    // Verify that Alice still doesn't have an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId));
+
+    // Set readHandler1 to inactive
+    readHandler12->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, false);
+
+    // Verify that Bob doesn't have an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+
+    // Verify that Alice still has an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId));
+
+    // Clean up read handlers
+    engine->GetReadHandlerPool().ReleaseAll();
+
+    // Verify that both Alice and Bob have no active subscriptions
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId) == false);
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId) == false);
+}
+
+/**
+ * @brief Verifies that SubjectHasActiveSubscription support CATs as a subject-id
+ */
+void TestInteractionModelEngine::TestSubjectHasActiveSubscriptionSubWithCAT(nlTestSuite * apSuite, void * apContext)
+{
+    TestContext & ctx               = *reinterpret_cast<TestContext *>(apContext);
+    InteractionModelEngine * engine = InteractionModelEngine::GetInstance();
+    NullReadHandlerCallback nullCallback;
+
+    CASEAuthTag cat            = 0x1111'0001;
+    CASEAuthTag invalidCAT     = 0x1112'0001;
+    CATValues cats             = CATValues{ { cat } };
+    NodeId valideSubjectId     = NodeIdFromCASEAuthTag(cat);
+    NodeId invalideSubjectId   = NodeIdFromCASEAuthTag(invalidCAT);
+    FabricIndex bobFabricIndex = 1;
+
+    // InteractionModelEngine init
+    NL_TEST_ASSERT(apSuite,
+                   CHIP_NO_ERROR ==
+                       engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reporting::GetDefaultReportScheduler()));
+
+    // Make sure we are using CASE sessions, because there is no defunct-marking for PASE.
+    ctx.ExpireSessionBobToAlice();
+    ctx.ExpireSessionAliceToBob();
+    NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == ctx.CreateCASESessionBobToAlice(cats));
+    NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == ctx.CreateCASESessionAliceToBob(cats));
+
+    // Create ExchangeContexts
+    Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToBob(nullptr, false);
+    NL_TEST_ASSERT(apSuite, exchangeCtx);
+
+    // Create readHandler
+    ReadHandler * readHandler = engine->GetReadHandlerPool().CreateObject(
+        nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
+
+    // Verify there are not active subscriptions
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, valideSubjectId) == false);
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, invalideSubjectId) == false);
+
+    // Set readHandler to active
+    readHandler->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true);
+
+    // Verify tthat valid subjectID has an active subscription
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, valideSubjectId));
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, invalideSubjectId) == false);
+
+    // Clean up read handlers
+    engine->GetReadHandlerPool().ReleaseAll();
+
+    // Verify there are not active subscriptions
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, valideSubjectId) == false);
+    NL_TEST_ASSERT(apSuite, engine->SubjectHasActiveSubscription(bobFabricIndex, invalideSubjectId) == false);
+}
+
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+
+/**
+ * @brief Test verifies the SubjectHasPersistedSubscription with single and multiple persisted subscriptions.
+ */
+void TestInteractionModelEngine::TestSubjectHasPersistedSubscription(nlTestSuite * apSuite, void * apContext)
+{
+    TestContext & ctx = *static_cast<TestContext *>(apContext);
+    CHIP_ERROR err    = CHIP_NO_ERROR;
+
+    chip::TestPersistentStorageDelegate storage;
+    chip::app::SimpleSubscriptionResumptionStorage subscriptionStorage;
+
+    err = subscriptionStorage.Init(&storage);
+    NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+
+    err = InteractionModelEngine::GetInstance()->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(),
+                                                      app::reporting::GetDefaultReportScheduler(), nullptr, &subscriptionStorage);
+    NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+
+    NodeId nodeId1      = 1;
+    FabricIndex fabric1 = 1;
+    SubscriptionId sub1 = 1;
+    NodeId nodeId2      = 2;
+    FabricIndex fabric2 = 2;
+    SubscriptionId sub2 = 2;
+
+    SubscriptionResumptionStorage::SubscriptionInfo info1 = { .mNodeId         = nodeId1,
+                                                              .mFabricIndex    = fabric1,
+                                                              .mSubscriptionId = sub1 };
+    SubscriptionResumptionStorage::SubscriptionInfo info2 = { .mNodeId         = nodeId2,
+                                                              .mFabricIndex    = fabric2,
+                                                              .mSubscriptionId = sub2 };
+
+    // Test with no persisted subscriptions - Should return false
+    NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->SubjectHasPersistedSubscription(fabric1, nodeId1) == false);
+
+    // Add one entry
+    NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == subscriptionStorage.Save(info1));
+
+    // Verify that entry matches - Should return true
+    NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->SubjectHasPersistedSubscription(fabric1, nodeId1));
+
+    // Test with absent subscription - Should return false
+    NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->SubjectHasPersistedSubscription(fabric2, nodeId2) == false);
+
+    // Add second entry
+    NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == subscriptionStorage.Save(info2));
+
+    // Verify that entry matches - Should return true
+    NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->SubjectHasPersistedSubscription(fabric2, nodeId2));
+    NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->SubjectHasPersistedSubscription(fabric1, nodeId1));
+
+    // Remove an entry
+    NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == subscriptionStorage.Delete(nodeId1, fabric1, sub1));
+
+    // Test with absent subscription - Should return false
+    NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->SubjectHasPersistedSubscription(fabric1, nodeId1) == false);
+
+    // Clean Up entries
+    subscriptionStorage.DeleteAll(fabric1);
+    subscriptionStorage.DeleteAll(fabric2);
+}
+
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+
 void TestInteractionModelEngine::TestSubscriptionResumptionTimer(nlTestSuite * apSuite, void * apContext)
 {
     TestContext & ctx = *static_cast<TestContext *>(apContext);
@@ -259,7 +681,66 @@ void TestInteractionModelEngine::TestSubscriptionResumptionTimer(nlTestSuite * a
     timeTillNextResubscriptionMs = InteractionModelEngine::GetInstance()->ComputeTimeSecondsTillNextSubscriptionResumption();
     NL_TEST_ASSERT(apSuite, timeTillNextResubscriptionMs == CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION_MAX_RETRY_INTERVAL_SECS);
 }
-#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+
+#endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+
+void TestInteractionModelEngine::TestDecrementNumSubscriptionsToResume(nlTestSuite * apSuite, void * apContext)
+{
+    TestContext & ctx                       = *static_cast<TestContext *>(apContext);
+    CHIP_ERROR err                          = CHIP_NO_ERROR;
+    InteractionModelEngine * engine         = InteractionModelEngine::GetInstance();
+    constexpr uint8_t kNumberOfSubsToResume = 5;
+    uint8_t numberOfSubsRemaining           = kNumberOfSubsToResume;
+
+    err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler());
+    NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+
+#if CHIP_CONFIG_ENABLE_ICD_CIP && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+    ICDManager manager;
+    engine->SetICDManager(&manager);
+#endif // CHIP_CONFIG_ENABLE_ICD_CIP && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+
+    // Set number of subs
+    engine->mNumOfSubscriptionsToResume = kNumberOfSubsToResume;
+
+#if CHIP_CONFIG_ENABLE_ICD_CIP && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+    // Verify mIsBootUpResumeSubscriptionExecuted has not been set
+    NL_TEST_ASSERT(apSuite, !manager.GetIsBootUpResumeSubscriptionExecuted());
+#endif // CHIP_CONFIG_ENABLE_ICD_CIP && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+
+    // Decrease number of subs by 1
+    numberOfSubsRemaining--;
+    engine->DecrementNumSubscriptionsToResume();
+    NL_TEST_ASSERT_EQUALS(apSuite, numberOfSubsRemaining, engine->mNumOfSubscriptionsToResume);
+
+    // Decrease to 0 subs remaining
+    while (numberOfSubsRemaining > 0)
+    {
+#if CHIP_CONFIG_ENABLE_ICD_CIP && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+        // Verify mIsBootUpResumeSubscriptionExecuted has not been set
+        NL_TEST_ASSERT(apSuite, !manager.GetIsBootUpResumeSubscriptionExecuted());
+#endif // CHIP_CONFIG_ENABLE_ICD_CIP && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+
+        numberOfSubsRemaining--;
+        engine->DecrementNumSubscriptionsToResume();
+        NL_TEST_ASSERT_EQUALS(apSuite, numberOfSubsRemaining, engine->mNumOfSubscriptionsToResume);
+    }
+
+#if CHIP_CONFIG_ENABLE_ICD_CIP && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+    // Verify mIsBootUpResumeSubscriptionExecuted has been set
+    NL_TEST_ASSERT(apSuite, manager.GetIsBootUpResumeSubscriptionExecuted());
+#endif // CHIP_CONFIG_ENABLE_ICD_CIP && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+
+    // Make sure we don't rollover / go negative
+    engine->DecrementNumSubscriptionsToResume();
+    NL_TEST_ASSERT_EQUALS(apSuite, numberOfSubsRemaining, engine->mNumOfSubscriptionsToResume);
+
+    // Clean up
+#if CHIP_CONFIG_ENABLE_ICD_CIP && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+    engine->SetICDManager(nullptr);
+#endif // CHIP_CONFIG_ENABLE_ICD_CIP && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+}
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
 
 } // namespace app
 } // namespace chip
@@ -271,9 +752,18 @@ const nlTest sTests[] =
         {
                 NL_TEST_DEF("TestAttributePathParamsPushRelease", chip::app::TestInteractionModelEngine::TestAttributePathParamsPushRelease),
                 NL_TEST_DEF("TestRemoveDuplicateConcreteAttribute", chip::app::TestInteractionModelEngine::TestRemoveDuplicateConcreteAttribute),
-#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+                NL_TEST_DEF("TestSubjectHasPersistedSubscription", chip::app::TestInteractionModelEngine::TestSubjectHasPersistedSubscription),
+                NL_TEST_DEF("TestDecrementNumSubscriptionsToResume", chip::app::TestInteractionModelEngine::TestDecrementNumSubscriptionsToResume),
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
                 NL_TEST_DEF("TestSubscriptionResumptionTimer", chip::app::TestInteractionModelEngine::TestSubscriptionResumptionTimer),
-#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+                NL_TEST_DEF("TestSubjectHasActiveSubscriptionSingleSubOneEntry", chip::app::TestInteractionModelEngine::TestSubjectHasActiveSubscriptionSingleSubOneEntry),
+                NL_TEST_DEF("TestSubjectHasActiveSubscriptionSingleSubMultipleEntries", chip::app::TestInteractionModelEngine::TestSubjectHasActiveSubscriptionSingleSubMultipleEntries),
+                NL_TEST_DEF("TestSubjectHasActiveSubscriptionMultipleSubsSingleEntry", chip::app::TestInteractionModelEngine::TestSubjectHasActiveSubscriptionMultipleSubsSingleEntry),
+                NL_TEST_DEF("TestSubjectHasActiveSubscriptionMultipleSubsMultipleEntries", chip::app::TestInteractionModelEngine::TestSubjectHasActiveSubscriptionMultipleSubsMultipleEntries),
+                NL_TEST_DEF("TestSubjectHasActiveSubscriptionSubWithCAT", chip::app::TestInteractionModelEngine::TestSubjectHasActiveSubscriptionSubWithCAT),
                 NL_TEST_SENTINEL()
         };
 // clang-format on
diff --git a/src/messaging/tests/MessagingContext.cpp b/src/messaging/tests/MessagingContext.cpp
index 441492e80d5694..dc41292dc0f313 100644
--- a/src/messaging/tests/MessagingContext.cpp
+++ b/src/messaging/tests/MessagingContext.cpp
@@ -174,6 +174,13 @@ CHIP_ERROR MessagingContext::CreateCASESessionBobToAlice()
                                                         CryptoContext::SessionRole::kInitiator);
 }
 
+CHIP_ERROR MessagingContext::CreateCASESessionBobToAlice(const CATValues & cats)
+{
+    return mSessionManager.InjectCaseSessionWithTestKey(mSessionBobToAlice, kBobKeyId, kAliceKeyId, GetBobFabric()->GetNodeId(),
+                                                        GetAliceFabric()->GetNodeId(), mBobFabricIndex, mAliceAddress,
+                                                        CryptoContext::SessionRole::kInitiator, cats);
+}
+
 CHIP_ERROR MessagingContext::CreateSessionAliceToBob()
 {
     return mSessionManager.InjectPaseSessionWithTestKey(mSessionAliceToBob, kAliceKeyId, GetBobFabric()->GetNodeId(), kBobKeyId,
@@ -187,6 +194,13 @@ CHIP_ERROR MessagingContext::CreateCASESessionAliceToBob()
                                                         CryptoContext::SessionRole::kResponder);
 }
 
+CHIP_ERROR MessagingContext::CreateCASESessionAliceToBob(const CATValues & cats)
+{
+    return mSessionManager.InjectCaseSessionWithTestKey(mSessionAliceToBob, kAliceKeyId, kBobKeyId, GetAliceFabric()->GetNodeId(),
+                                                        GetBobFabric()->GetNodeId(), mAliceFabricIndex, mBobAddress,
+                                                        CryptoContext::SessionRole::kResponder, cats);
+}
+
 CHIP_ERROR MessagingContext::CreatePASESessionCharlieToDavid()
 {
     return mSessionManager.InjectPaseSessionWithTestKey(mSessionCharlieToDavid, kCharlieKeyId, 0xdeadbeef, kDavidKeyId,
diff --git a/src/messaging/tests/MessagingContext.h b/src/messaging/tests/MessagingContext.h
index 31143a727326be..7ae452b9678a90 100644
--- a/src/messaging/tests/MessagingContext.h
+++ b/src/messaging/tests/MessagingContext.h
@@ -19,6 +19,7 @@
 #include <credentials/PersistentStorageOpCertStore.h>
 #include <crypto/DefaultSessionKeystore.h>
 #include <crypto/PersistentStorageOperationalKeystore.h>
+#include <lib/core/CASEAuthTag.h>
 #include <lib/support/TestPersistentStorageDelegate.h>
 #include <messaging/ExchangeContext.h>
 #include <messaging/ExchangeMgr.h>
@@ -141,8 +142,10 @@ class MessagingContext : public PlatformMemoryUser
 
     CHIP_ERROR CreateSessionBobToAlice(); // Creates PASE session
     CHIP_ERROR CreateCASESessionBobToAlice();
+    CHIP_ERROR CreateCASESessionBobToAlice(const CATValues & cats);
     CHIP_ERROR CreateSessionAliceToBob(); // Creates PASE session
     CHIP_ERROR CreateCASESessionAliceToBob();
+    CHIP_ERROR CreateCASESessionAliceToBob(const CATValues & cats);
     CHIP_ERROR CreateSessionBobToFriends(); // Creates PASE session
     CHIP_ERROR CreatePASESessionCharlieToDavid();
     CHIP_ERROR CreatePASESessionDavidToCharlie();
diff --git a/src/test_driver/esp32/main/include/CHIPProjectConfig.h b/src/test_driver/esp32/main/include/CHIPProjectConfig.h
index 3a25601cfd884e..1e4dc9626b5033 100644
--- a/src/test_driver/esp32/main/include/CHIPProjectConfig.h
+++ b/src/test_driver/esp32/main/include/CHIPProjectConfig.h
@@ -30,4 +30,6 @@
 // Enable support functions for parsing command-line arguments
 #define CHIP_CONFIG_ENABLE_ARG_PARSER 1
 
+#define CONFIG_BUILD_FOR_HOST_UNIT_TEST 1
+
 #endif // CHIP_PROJECT_CONFIG_H