[ReadHandler] Removed Scheduling of report from OnReadHandlerCreated  (#28536)

* Removed Scheduling of report from OnReadHandlerCreated since it caused immediate scheduling before the intervals are negotiated

* Separated Read reports from Subscription reports and renamed flags and accessors for clarity

* Apply suggestions from code review

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Put back rescheduling in the OnReadHandlerSubscribed, added a CanEmitReadReport() method for readhandler for read request. Fixed call order in TestReportScheduler tests

* Removed redundancy by removing un-necessary OnSubscriptionAction(), added comment for OnReadHandlerSubscribed and modified reporting condition for Read reports to be sent independently from the report scheduler

* Apply suggestions from code review

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Appplied change to method name and fixe condition in SetStateFlag

* Update src/app/ReadHandler.h

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Removed un-necessary check in SetStateFlag

---------

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp
index 4e9e8fc..1295cc6 100644
--- a/src/app/ReadHandler.cpp
+++ b/src/app/ReadHandler.cpp
@@ -79,7 +79,6 @@
 
     VerifyOrDie(observer != nullptr);
     mObserver = observer;
-    mObserver->OnReadHandlerCreated(this);
 }
 
 #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
@@ -92,7 +91,6 @@
 
     VerifyOrDie(observer != nullptr);
     mObserver = observer;
-    mObserver->OnReadHandlerCreated(this);
 }
 
 void ReadHandler::ResumeSubscription(CASESessionManager & caseSessionManager,
@@ -235,6 +233,7 @@
                 {
                     appCallback->OnSubscriptionEstablished(*this);
                 }
+                mObserver->OnSubscriptionEstablished(this);
             }
         }
         else
@@ -246,10 +245,10 @@
             return CHIP_NO_ERROR;
         }
 
-        MoveToState(HandlerState::GeneratingReports);
+        MoveToState(HandlerState::CanStartReporting);
         break;
 
-    case HandlerState::GeneratingReports:
+    case HandlerState::CanStartReporting:
     case HandlerState::Idle:
     default:
         err = CHIP_ERROR_INCORRECT_STATE;
@@ -262,7 +261,7 @@
 
 CHIP_ERROR ReadHandler::SendStatusReport(Protocols::InteractionModel::Status aStatus)
 {
-    VerifyOrReturnLogError(mState == HandlerState::GeneratingReports, CHIP_ERROR_INCORRECT_STATE);
+    VerifyOrReturnLogError(mState == HandlerState::CanStartReporting, CHIP_ERROR_INCORRECT_STATE);
     if (IsPriming() || IsChunkedReport())
     {
         mSessionHandle.Grab(mExchangeCtx->GetSessionHandle());
@@ -286,7 +285,7 @@
 
 CHIP_ERROR ReadHandler::SendReportData(System::PacketBufferHandle && aPayload, bool aMoreChunks)
 {
-    VerifyOrReturnLogError(mState == HandlerState::GeneratingReports, CHIP_ERROR_INCORRECT_STATE);
+    VerifyOrReturnLogError(mState == HandlerState::CanStartReporting, CHIP_ERROR_INCORRECT_STATE);
     VerifyOrDie(!IsAwaitingReportResponse()); // Should not be reportable!
     if (IsPriming() || IsChunkedReport())
     {
@@ -335,7 +334,7 @@
         // Priming reports are handled when we send a SubscribeResponse.
         if (IsType(InteractionType::Subscribe) && !IsPriming() && !IsChunkedReport())
         {
-            mObserver->OnSubscriptionAction(this);
+            mObserver->OnSubscriptionReportSent(this);
         }
     }
     if (!aMoreChunks)
@@ -456,7 +455,7 @@
     ReturnErrorOnFailure(readRequestParser.GetIsFabricFiltered(&isFabricFiltered));
     SetStateFlag(ReadHandlerFlags::FabricFiltered, isFabricFiltered);
     ReturnErrorOnFailure(readRequestParser.ExitContainer());
-    MoveToState(HandlerState::GeneratingReports);
+    MoveToState(HandlerState::CanStartReporting);
 
     mExchangeCtx->WillSendMessage();
 
@@ -574,8 +573,8 @@
         return "Idle";
     case HandlerState::AwaitingDestruction:
         return "AwaitingDestruction";
-    case HandlerState::GeneratingReports:
-        return "GeneratingReports";
+    case HandlerState::CanStartReporting:
+        return "CanStartReporting";
 
     case HandlerState::AwaitingReportResponse:
         return "AwaitingReportResponse";
@@ -603,10 +602,17 @@
     // If we just unblocked sending reports, let's go ahead and schedule the reporting
     // engine to run to kick that off.
     //
-    if (aTargetState == HandlerState::GeneratingReports)
+    if (aTargetState == HandlerState::CanStartReporting)
     {
-        // mObserver will take care of scheduling the report as soon as allowed
-        mObserver->OnBecameReportable(this);
+        if (ShouldReportUnscheduled())
+        {
+            InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
+        }
+        else
+        {
+            // If we became reportable, the scheduler will schedule a run as soon as allowed
+            mObserver->OnBecameReportable(this);
+        }
     }
 }
 
@@ -647,8 +653,6 @@
     ReturnErrorOnFailure(writer.Finalize(&packet));
     VerifyOrReturnLogError(mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
 
-    mObserver->OnSubscriptionAction(this);
-
     ClearStateFlag(ReadHandlerFlags::PrimingReports);
     return mExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::SubscribeResponse, std::move(packet));
 }
@@ -785,7 +789,7 @@
     SetStateFlag(ReadHandlerFlags::FabricFiltered, isFabricFiltered);
     ReturnErrorOnFailure(Crypto::DRBG_get_bytes(reinterpret_cast<uint8_t *>(&mSubscriptionId), sizeof(mSubscriptionId)));
     ReturnErrorOnFailure(subscribeRequestParser.ExitContainer());
-    MoveToState(HandlerState::GeneratingReports);
+    MoveToState(HandlerState::CanStartReporting);
 
     mExchangeCtx->WillSendMessage();
 
@@ -872,11 +876,11 @@
 
 void ReadHandler::SetStateFlag(ReadHandlerFlags aFlag, bool aValue)
 {
-    bool oldReportable = IsReportable();
+    bool oldReportable = ShouldStartReporting();
     mFlags.Set(aFlag, aValue);
 
     // If we became reportable, schedule a reporting run.
-    if (!oldReportable && IsReportable())
+    if (!oldReportable && ShouldStartReporting())
     {
         // If we became reportable, the scheduler will schedule a run as soon as allowed
         mObserver->OnBecameReportable(this);
@@ -895,7 +899,7 @@
 
     _this->mSessionHandle.Grab(sessionHandle);
 
-    _this->MoveToState(HandlerState::GeneratingReports);
+    _this->MoveToState(HandlerState::CanStartReporting);
 
     ObjectList<AttributePathParams> * attributePath = _this->mpAttributePathList;
     while (attributePath)
diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h
index a87c121..ecdc0c1 100644
--- a/src/app/ReadHandler.h
+++ b/src/app/ReadHandler.h
@@ -166,20 +166,23 @@
     public:
         virtual ~Observer() = default;
 
-        /// @brief Callback invoked to notify a ReadHandler was created and can be registered
-        /// @param[in] apReadHandler  ReadHandler getting added
-        virtual void OnReadHandlerCreated(ReadHandler * apReadHandler) = 0;
+        /// @brief Callback invoked to notify a subscription was successfully established for the ReadHandler
+        /// @param[in] apReadHandler  ReadHandler that completed its subscription
+        virtual void OnSubscriptionEstablished(ReadHandler * apReadHandler) = 0;
 
-        /// @brief Callback invoked when a ReadHandler went from a non reportable state to a reportable state so a report can be
-        /// sent immediately if the minimal interval allows it. Otherwise the report should be rescheduled to the earliest time
-        /// allowed.
-        /// @param[in] apReadHandler  ReadHandler that became dirty
+        /// @brief Callback invoked when a ReadHandler went from a non reportable state to a reportable state. Indicates to the
+        /// observer that a report should be emitted when the min interval allows it.
+        ///
+        /// This will only be invoked for subscribe-type ReadHandler objects, and only after
+        /// OnSubscriptionEstablished has been called.
+        ///
+        /// @param[in] apReadHandler  ReadHandler that became dirty and in HandlerState::CanStartReporting state
         virtual void OnBecameReportable(ReadHandler * apReadHandler) = 0;
 
         /// @brief Callback invoked when the read handler needs to make sure to send a message to the subscriber within the next
         /// maxInterval time period.
         /// @param[in] apReadHandler ReadHandler that has generated a report
-        virtual void OnSubscriptionAction(ReadHandler * apReadHandler) = 0;
+        virtual void OnSubscriptionReportSent(ReadHandler * apReadHandler) = 0;
 
         /// @brief Callback invoked when a ReadHandler is getting removed so it can be unregistered
         /// @param[in] apReadHandler  ReadHandler getting destroyed
@@ -333,13 +336,22 @@
     bool IsIdle() const { return mState == HandlerState::Idle; }
 
     /// @brief Returns whether the ReadHandler is in a state where it can send a report and there is data to report.
-    bool IsReportable() const
+    bool ShouldStartReporting() const
     {
-        // Important: Anything that changes the state IsReportable must call mObserver->OnBecameReportable(this) for the scheduler
-        // to plan the next run accordingly.
-        return mState == HandlerState::GeneratingReports && IsDirty();
+        // Important: Anything that changes ShouldStartReporting() from false to true
+        // (which can only happen for subscriptions) must call
+        // mObserver->OnBecameReportable(this).
+        return CanStartReporting() && (ShouldReportUnscheduled() || IsDirty());
     }
-    bool IsGeneratingReports() const { return mState == HandlerState::GeneratingReports; }
+    /// @brief CanStartReporting() is true if the ReadHandler is in a state where it could generate
+    /// a (possibly empty) report if someone asked it to.
+    bool CanStartReporting() const { return mState == HandlerState::CanStartReporting; }
+    /// @brief ShouldReportUnscheduled() is true if the ReadHandler should be asked to generate reports
+    /// without consulting the report scheduler.
+    bool ShouldReportUnscheduled() const
+    {
+        return CanStartReporting() && (IsType(ReadHandler::InteractionType::Read) || IsPriming());
+    }
     bool IsAwaitingReportResponse() const { return mState == HandlerState::AwaitingReportResponse; }
 
     // Resets the path iterator to the beginning of the whole report for generating a series of new reports.
@@ -418,14 +430,14 @@
     friend class chip::app::reporting::Engine;
     friend class chip::app::InteractionModelEngine;
 
-    // The report scheduler needs to be able to access StateFlag private functions IsReportable(), IsGeneratingReports(),
+    // 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.
     friend class chip::app::reporting::ReportScheduler;
 
     enum class HandlerState : uint8_t
     {
         Idle,                   ///< The handler has been initialized and is ready
-        GeneratingReports,      ///< The handler has is now capable of generating reports and may generate one immediately
+        CanStartReporting,      ///< The handler has is now capable of generating reports and may generate one immediately
                                 ///< or later when other criteria are satisfied (e.g hold-off for min reporting interval).
         AwaitingReportResponse, ///< The handler has sent the report to the client and is awaiting a status response.
         AwaitingDestruction,    ///< The object has completed its work and is awaiting destruction by the application.
diff --git a/src/app/reporting/Engine.cpp b/src/app/reporting/Engine.cpp
index a1eae3a..0a3eedd 100644
--- a/src/app/reporting/Engine.cpp
+++ b/src/app/reporting/Engine.cpp
@@ -638,7 +638,7 @@
         ReadHandler * readHandler = imEngine->ActiveHandlerAt(mCurReadHandlerIdx % (uint32_t) imEngine->mReadHandlers.Allocated());
         VerifyOrDie(readHandler != nullptr);
 
-        if (imEngine->GetReportScheduler()->IsReportableNow(readHandler))
+        if (readHandler->ShouldReportUnscheduled() || imEngine->GetReportScheduler()->IsReportableNow(readHandler))
         {
             mRunningReadHandler = readHandler;
             CHIP_ERROR err      = BuildAndSendSingleReportData(readHandler);
@@ -831,7 +831,7 @@
             // We call AttributePathIsDirty for both read interactions and subscribe interactions, since we may send inconsistent
             // attribute data between two chunks. AttributePathIsDirty will not schedule a new run for read handlers which are
             // waiting for a response to the last message chunk for read interactions.
-            if (handler->IsGeneratingReports() || handler->IsAwaitingReportResponse())
+            if (handler->CanStartReporting() || handler->IsAwaitingReportResponse())
             {
                 for (auto object = handler->GetAttributePathList(); object != nullptr; object = object->mpNext)
                 {
diff --git a/src/app/reporting/ReportScheduler.h b/src/app/reporting/ReportScheduler.h
index 0682377..5b7b627 100644
--- a/src/app/reporting/ReportScheduler.h
+++ b/src/app/reporting/ReportScheduler.h
@@ -82,7 +82,7 @@
         {
             Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp();
 
-            return (mReadHandler->IsGeneratingReports() &&
+            return (mReadHandler->CanStartReporting() &&
                     (now >= mMinTimestamp && (mReadHandler->IsDirty() || now >= mMaxTimestamp || now >= mSyncTimestamp)));
         }
 
@@ -139,9 +139,13 @@
 
     /// @brief Check whether a ReadHandler is reportable right now, taking into account its minimum and maximum intervals.
     /// @param aReadHandler read handler to check
-    bool IsReportableNow(ReadHandler * aReadHandler) { return FindReadHandlerNode(aReadHandler)->IsReportableNow(); }
+    bool IsReportableNow(ReadHandler * aReadHandler)
+    {
+        ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
+        return (nullptr != node) ? node->IsReportableNow() : false;
+    }
     /// @brief Check if a ReadHandler is reportable without considering the timing
-    bool IsReadHandlerReportable(ReadHandler * aReadHandler) const { return aReadHandler->IsReportable(); }
+    bool IsReadHandlerReportable(ReadHandler * aReadHandler) const { return aReadHandler->ShouldStartReporting(); }
     /// @brief Sets the ForceDirty flag of a ReadHandler
     void HandlerForceDirtyState(ReadHandler * aReadHandler) { aReadHandler->ForceDirtyState(); }
 
diff --git a/src/app/reporting/ReportSchedulerImpl.cpp b/src/app/reporting/ReportSchedulerImpl.cpp
index 74f75a6..44be647 100644
--- a/src/app/reporting/ReportSchedulerImpl.cpp
+++ b/src/app/reporting/ReportSchedulerImpl.cpp
@@ -54,8 +54,10 @@
 #endif
 }
 
-/// @brief When a ReadHandler is added, register it, which will schedule an engine run
-void ReportSchedulerImpl::OnReadHandlerCreated(ReadHandler * aReadHandler)
+/// @brief When a ReadHandler is added, register it in the scheduler node pool. Scheduling the report here is un-necessary since the
+/// ReadHandler will call MoveToState(HandlerState::CanStartReporting);, which will call OnBecameReportable() and schedule the
+/// report.
+void ReportSchedulerImpl::OnSubscriptionEstablished(ReadHandler * aReadHandler)
 {
     ReadHandlerNode * newNode = FindReadHandlerNode(aReadHandler);
     // Handler must not be registered yet; it's just being constructed.
@@ -68,11 +70,6 @@
                     "Registered a ReadHandler that will schedule a report between system Timestamp: %" PRIu64
                     " and system Timestamp %" PRIu64 ".",
                     newNode->GetMinTimestamp().count(), newNode->GetMaxTimestamp().count());
-
-    Milliseconds32 newTimeout;
-    // No need to check for error here, since the node is already in the list otherwise we would have Died
-    CalculateNextReportTimeout(newTimeout, newNode);
-    ScheduleReport(newTimeout, newNode);
 }
 
 /// @brief When a ReadHandler becomes reportable, schedule, recalculate and reschedule the report.
@@ -85,7 +82,7 @@
     ScheduleReport(newTimeout, node);
 }
 
-void ReportSchedulerImpl::OnSubscriptionAction(ReadHandler * aReadHandler)
+void ReportSchedulerImpl::OnSubscriptionReportSent(ReadHandler * aReadHandler)
 {
     ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
     VerifyOrReturn(nullptr != node);
diff --git a/src/app/reporting/ReportSchedulerImpl.h b/src/app/reporting/ReportSchedulerImpl.h
index 573184a..003be7c 100644
--- a/src/app/reporting/ReportSchedulerImpl.h
+++ b/src/app/reporting/ReportSchedulerImpl.h
@@ -36,9 +36,9 @@
     void OnEnterActiveMode() override;
 
     // ReadHandlerObserver
-    void OnReadHandlerCreated(ReadHandler * aReadHandler) final;
+    void OnSubscriptionEstablished(ReadHandler * aReadHandler) final;
     void OnBecameReportable(ReadHandler * aReadHandler) final;
-    void OnSubscriptionAction(ReadHandler * aReadHandler) final;
+    void OnSubscriptionReportSent(ReadHandler * aReadHandler) final;
     void OnReadHandlerDestroyed(ReadHandler * aReadHandler) override;
 
     bool IsReportScheduled(ReadHandler * aReadHandler);
diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp
index 0f90756..ea464c3 100644
--- a/src/app/tests/TestReadInteraction.cpp
+++ b/src/app/tests/TestReadInteraction.cpp
@@ -2234,7 +2234,7 @@
                        reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) <
                            System::SystemClock().GetMonotonicTimestamp());
         NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsDirty());
-        NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsReportable());
+        NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->ShouldStartReporting());
 
         // And the non-urgent one should not have changed state either, since
         // it's waiting for the max-interval.
@@ -2245,7 +2245,7 @@
                        reportScheduler->GetMaxTimestampForHandler(nonUrgentDelegate.mpReadHandler) >
                            System::SystemClock().GetMonotonicTimestamp());
         NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty());
-        NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsReportable());
+        NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->ShouldStartReporting());
 
         // There should be no reporting run scheduled.  This is very important;
         // otherwise we can get a false-positive pass below because the run was
@@ -2257,12 +2257,12 @@
 
         // Urgent read handler should now be dirty, and reportable.
         NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->IsDirty());
-        NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->IsReportable());
+        NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->ShouldStartReporting());
         NL_TEST_ASSERT(apSuite, reportScheduler->IsReadHandlerReportable(delegate.mpReadHandler));
 
         // Non-urgent read handler should not be reportable.
         NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty());
-        NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsReportable());
+        NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->ShouldStartReporting());
 
         // Still no reporting should have happened.
         NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse);
diff --git a/src/app/tests/TestReportScheduler.cpp b/src/app/tests/TestReportScheduler.cpp
index 9d19f85..5f8fd3f 100644
--- a/src/app/tests/TestReportScheduler.cpp
+++ b/src/app/tests/TestReportScheduler.cpp
@@ -135,9 +135,6 @@
 
     static void TimerCallbackInterface(System::Layer * aLayer, void * aAppState)
     {
-        // Normaly we would call the callback here, thus scheduling an engine run, but we don't need it for this test as we simulate
-        // all the callbacks related to report emissions. The actual callback should look like this:
-        //
         TimerContext * context = static_cast<TimerContext *>(aAppState);
         context->TimerFired();
         ChipLogProgress(DataManagement, "Simluating engine run for Handler: %p", aAppState);
@@ -249,6 +246,23 @@
 class TestReportScheduler
 {
 public:
+    /// @brief Mimicks the various operations that happen on a subscription transaction after a read handler was created so that
+    /// readhandlers are in the expected state for further tests.
+    /// @param readHandler
+    /// @param scheduler
+    static CHIP_ERROR MockReadHandlerSubscriptionTransation(ReadHandler * readHandler, ReportScheduler * scheduler,
+                                                            uint8_t min_interval_seconds, uint8_t max_interval_seconds)
+    {
+        ReturnErrorOnFailure(readHandler->SetMaxReportingInterval(max_interval_seconds));
+        ReturnErrorOnFailure(readHandler->SetMinReportingIntervalForTests(min_interval_seconds));
+        readHandler->ClearStateFlag(ReadHandler::ReadHandlerFlags::PrimingReports);
+        readHandler->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription);
+        scheduler->OnSubscriptionEstablished(readHandler);
+        readHandler->MoveToState(ReadHandler::HandlerState::CanStartReporting);
+
+        return CHIP_NO_ERROR;
+    }
+
     static ReadHandler * GetReadHandlerFromPool(ReportScheduler * scheduler, uint32_t target)
     {
         uint32_t i        = 0;
@@ -285,6 +299,7 @@
         {
             ReadHandler * readHandler =
                 readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler);
+            sScheduler.OnSubscriptionEstablished(readHandler);
             NL_TEST_ASSERT(aSuite, nullptr != readHandler);
             VerifyOrReturn(nullptr != readHandler);
             NL_TEST_ASSERT(aSuite, nullptr != sScheduler.FindReadHandlerNode(readHandler));
@@ -349,31 +364,18 @@
         // Test OnReadHandler created
         ReadHandler * readHandler1 =
             readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler);
-
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMaxReportingInterval(2));
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMinReportingIntervalForTests(1));
-        ReadHandlerNode * node = sScheduler.FindReadHandlerNode(readHandler1);
-        node->SetIntervalTimeStamps(readHandler1);
-        readHandler1->MoveToState(ReadHandler::HandlerState::GeneratingReports);
+        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == MockReadHandlerSubscriptionTransation(readHandler1, &sScheduler, 1, 2));
         readHandler1->ForceDirtyState();
 
         // Clean read handler, will be triggered at max interval
         ReadHandler * readHandler2 =
             readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler);
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMaxReportingInterval(3));
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMinReportingIntervalForTests(0));
-        node = sScheduler.FindReadHandlerNode(readHandler2);
-        node->SetIntervalTimeStamps(readHandler2);
-        readHandler2->MoveToState(ReadHandler::HandlerState::GeneratingReports);
+        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == MockReadHandlerSubscriptionTransation(readHandler2, &sScheduler, 0, 3));
 
         // Clean read handler, will be triggered at max interval, but will be cancelled before
         ReadHandler * readHandler3 =
             readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler);
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler3->SetMaxReportingInterval(3));
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler3->SetMinReportingIntervalForTests(0));
-        node = sScheduler.FindReadHandlerNode(readHandler3);
-        node->SetIntervalTimeStamps(readHandler3);
-        readHandler3->MoveToState(ReadHandler::HandlerState::GeneratingReports);
+        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == MockReadHandlerSubscriptionTransation(readHandler3, &sScheduler, 0, 3));
 
         // Confirms that none of the ReadHandlers are currently reportable
         NL_TEST_ASSERT(aSuite, !sScheduler.IsReportableNow(readHandler1));
@@ -427,22 +429,20 @@
 
         ReadHandler * readHandler =
             readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler);
-        // Test OnReadHandler created registered the ReadHandler in the scheduler
+        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == MockReadHandlerSubscriptionTransation(readHandler, &sScheduler, 1, 2));
+
+        // Verifies OnSubscriptionEstablished registered the ReadHandler in the scheduler
         NL_TEST_ASSERT(aSuite, nullptr != sScheduler.FindReadHandlerNode(readHandler));
 
         // Should have registered the read handler in the scheduler and scheduled a report
         NL_TEST_ASSERT(aSuite, sScheduler.GetNumReadHandlers() == 1);
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler->SetMaxReportingInterval(2));
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler->SetMinReportingIntervalForTests(1));
+
         ReadHandlerNode * node = sScheduler.FindReadHandlerNode(readHandler);
-        node->SetIntervalTimeStamps(readHandler);
 
         // Test OnReportingIntervalsChanged modified the intervals and re-scheduled a report
         NL_TEST_ASSERT(aSuite, node->GetMinTimestamp().count() == 1000);
         NL_TEST_ASSERT(aSuite, node->GetMaxTimestamp().count() == 2000);
 
-        // Do those manually to avoid scheduling an engine run
-        readHandler->MoveToState(ReadHandler::HandlerState::GeneratingReports);
         NL_TEST_ASSERT(aSuite, sScheduler.IsReportScheduled(readHandler));
 
         NL_TEST_ASSERT(aSuite, nullptr != node);
@@ -459,9 +459,9 @@
         // Check that no report is scheduled since the min interval has expired, the timer should now be stopped
         NL_TEST_ASSERT(aSuite, !sScheduler.IsReportScheduled(readHandler));
 
-        // Test OnSubscriptionAction
+        // Test OnSubscriptionReportSent
         readHandler->ClearForceDirtyFlag();
-        readHandler->mObserver->OnSubscriptionAction(readHandler);
+        readHandler->mObserver->OnSubscriptionReportSent(readHandler);
         // Should have changed the scheduled timeout to the handlers max interval, to check, we wait for the min interval to
         // confirm it is not expired yet so the report should still be scheduled
 
@@ -505,19 +505,13 @@
 
         ReadHandler * readHandler1 =
             readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler);
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMaxReportingInterval(2));
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMinReportingIntervalForTests(0));
+        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == MockReadHandlerSubscriptionTransation(readHandler1, &syncScheduler, 0, 2));
         ReadHandlerNode * node1 = syncScheduler.FindReadHandlerNode(readHandler1);
-        node1->SetIntervalTimeStamps(readHandler1);
-        readHandler1->MoveToState(ReadHandler::HandlerState::GeneratingReports);
 
         ReadHandler * readHandler2 =
             readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler);
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMaxReportingInterval(3));
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMinReportingIntervalForTests(1));
+        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == MockReadHandlerSubscriptionTransation(readHandler2, &syncScheduler, 1, 3));
         ReadHandlerNode * node2 = syncScheduler.FindReadHandlerNode(readHandler2);
-        node2->SetIntervalTimeStamps(readHandler2);
-        readHandler2->MoveToState(ReadHandler::HandlerState::GeneratingReports);
 
         // Confirm all handler are currently registered in the scheduler
         NL_TEST_ASSERT(aSuite, syncScheduler.GetNumReadHandlers() == 2);
@@ -544,9 +538,9 @@
         NL_TEST_ASSERT(aSuite, !sScheduler.IsReportScheduled(readHandler2));
 
         // Simulate a report emission for readHandler1
-        readHandler1->mObserver->OnSubscriptionAction(readHandler1);
+        readHandler1->mObserver->OnSubscriptionReportSent(readHandler1);
         // Simulate a report emission for readHandler2
-        readHandler2->mObserver->OnSubscriptionAction(readHandler2);
+        readHandler2->mObserver->OnSubscriptionReportSent(readHandler2);
 
         // Validate that the max timestamp for both readhandlers got updated and that the next report emission is scheduled on
         //  the new max timestamp for readhandler1
@@ -572,13 +566,13 @@
         NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node2->GetMinTimestamp());
 
         // Simulate a report emission for readHandler1
-        readHandler1->mObserver->OnSubscriptionAction(readHandler1);
+        readHandler1->mObserver->OnSubscriptionReportSent(readHandler1);
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1));
 
         // ReadHandler 2 should still be reportable since it hasn't emitted a report yet
         NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2));
         readHandler2->ClearForceDirtyFlag();
-        readHandler2->mObserver->OnSubscriptionAction(readHandler2);
+        readHandler2->mObserver->OnSubscriptionReportSent(readHandler2);
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2));
 
         // Validate next report scheduled on the max timestamp of readHandler1
@@ -594,7 +588,7 @@
 
         // Simulate a report emission for readHandler1
         readHandler1->ClearForceDirtyFlag();
-        readHandler1->mObserver->OnSubscriptionAction(readHandler1);
+        readHandler1->mObserver->OnSubscriptionReportSent(readHandler1);
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1));
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2));
 
@@ -605,8 +599,8 @@
         sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(2000));
         NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1));
         NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2));
-        readHandler1->mObserver->OnSubscriptionAction(readHandler1);
-        readHandler2->mObserver->OnSubscriptionAction(readHandler2);
+        readHandler1->mObserver->OnSubscriptionReportSent(readHandler1);
+        readHandler2->mObserver->OnSubscriptionReportSent(readHandler2);
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1));
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2));
         NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node1->GetMaxTimestamp());
@@ -618,11 +612,8 @@
 
         ReadHandler * readHandler3 =
             readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler);
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler3->SetMaxReportingInterval(3));
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler3->SetMinReportingIntervalForTests(2));
+        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == MockReadHandlerSubscriptionTransation(readHandler3, &syncScheduler, 2, 3));
         ReadHandlerNode * node3 = syncScheduler.FindReadHandlerNode(readHandler3);
-        node3->SetIntervalTimeStamps(readHandler3);
-        readHandler3->MoveToState(ReadHandler::HandlerState::GeneratingReports);
 
         // Confirm all handler are currently registered in the scheduler
         NL_TEST_ASSERT(aSuite, syncScheduler.GetNumReadHandlers() == 3);
@@ -643,8 +634,8 @@
         readHandler2->mObserver->OnBecameReportable(readHandler2);
 
         // Simulate a report emission for readHandler1 and readHandler2
-        readHandler1->mObserver->OnSubscriptionAction(readHandler1);
-        readHandler1->mObserver->OnSubscriptionAction(readHandler2);
+        readHandler1->mObserver->OnSubscriptionReportSent(readHandler1);
+        readHandler1->mObserver->OnSubscriptionReportSent(readHandler2);
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1));
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2));
 
@@ -662,9 +653,9 @@
         readHandler2->mObserver->OnBecameReportable(readHandler2);
         readHandler3->mObserver->OnBecameReportable(readHandler3);
         // Engine run should happen here and send all reports
-        readHandler1->mObserver->OnSubscriptionAction(readHandler1);
-        readHandler2->mObserver->OnSubscriptionAction(readHandler2);
-        readHandler3->mObserver->OnSubscriptionAction(readHandler3);
+        readHandler1->mObserver->OnSubscriptionReportSent(readHandler1);
+        readHandler2->mObserver->OnSubscriptionReportSent(readHandler2);
+        readHandler3->mObserver->OnSubscriptionReportSent(readHandler3);
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1));
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2));
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler3));
@@ -675,11 +666,8 @@
         // Now simulate a new readHandler being added with a max forcing a conflict
         ReadHandler * readHandler4 =
             readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler);
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler4->SetMaxReportingInterval(1));
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler4->SetMinReportingIntervalForTests(0));
+        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == MockReadHandlerSubscriptionTransation(readHandler4, &syncScheduler, 0, 1));
         ReadHandlerNode * node4 = syncScheduler.FindReadHandlerNode(readHandler4);
-        node4->SetIntervalTimeStamps(readHandler4);
-        readHandler4->MoveToState(ReadHandler::HandlerState::GeneratingReports);
 
         // Confirm all handler are currently registered in the scheduler
         NL_TEST_ASSERT(aSuite, syncScheduler.GetNumReadHandlers() == 4);
@@ -704,9 +692,9 @@
         readHandler4->mObserver->OnBecameReportable(readHandler1);
         readHandler4->mObserver->OnBecameReportable(readHandler2);
         readHandler4->mObserver->OnBecameReportable(readHandler4);
-        readHandler4->mObserver->OnSubscriptionAction(readHandler1);
-        readHandler4->mObserver->OnSubscriptionAction(readHandler2);
-        readHandler4->mObserver->OnSubscriptionAction(readHandler4);
+        readHandler4->mObserver->OnSubscriptionReportSent(readHandler1);
+        readHandler4->mObserver->OnSubscriptionReportSent(readHandler2);
+        readHandler4->mObserver->OnSubscriptionReportSent(readHandler4);
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1));
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2));
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler4));
@@ -722,10 +710,10 @@
         syncScheduler.OnBecameReportable(readHandler2);
         syncScheduler.OnBecameReportable(readHandler3);
         syncScheduler.OnBecameReportable(readHandler4);
-        syncScheduler.OnSubscriptionAction(readHandler1);
-        syncScheduler.OnSubscriptionAction(readHandler2);
-        syncScheduler.OnSubscriptionAction(readHandler3);
-        syncScheduler.OnSubscriptionAction(readHandler4);
+        syncScheduler.OnSubscriptionReportSent(readHandler1);
+        syncScheduler.OnSubscriptionReportSent(readHandler2);
+        syncScheduler.OnSubscriptionReportSent(readHandler3);
+        syncScheduler.OnSubscriptionReportSent(readHandler4);
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1));
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2));
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler3));
@@ -759,19 +747,14 @@
         NL_TEST_ASSERT(aSuite, syncScheduler.GetNumReadHandlers() == 0);
 
         readHandler1->MoveToState(ReadHandler::HandlerState::Idle);
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMaxReportingInterval(2));
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMinReportingIntervalForTests(0));
-        readHandler1->MoveToState(ReadHandler::HandlerState::GeneratingReports);
-        syncScheduler.OnReadHandlerCreated(readHandler1);
+        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == MockReadHandlerSubscriptionTransation(readHandler1, &syncScheduler, 0, 2));
+
         // Forcing the dirty flag to make the scheduler call Engine::ScheduleRun() immediately
         readHandler1->ForceDirtyState();
         NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1));
 
         readHandler2->MoveToState(ReadHandler::HandlerState::Idle);
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMaxReportingInterval(4));
-        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMinReportingIntervalForTests(3));
-        readHandler2->MoveToState(ReadHandler::HandlerState::GeneratingReports);
-        syncScheduler.OnReadHandlerCreated(readHandler2);
+        NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == MockReadHandlerSubscriptionTransation(readHandler2, &syncScheduler, 3, 4));
         readHandler2->ForceDirtyState();
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2));
 
@@ -779,7 +762,7 @@
         node2 = syncScheduler.FindReadHandlerNode(readHandler2);
 
         readHandler1->ClearForceDirtyFlag(); // report got emited so clear dirty flag
-        syncScheduler.OnSubscriptionAction(readHandler1);
+        syncScheduler.OnSubscriptionReportSent(readHandler1);
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1));
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2));
 
@@ -795,7 +778,7 @@
         syncScheduler.OnBecameReportable(readHandler1);
 
         // simulate run with only readhandler1 reportable
-        syncScheduler.OnSubscriptionAction(readHandler1);
+        syncScheduler.OnSubscriptionReportSent(readHandler1);
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1));
         NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2));
         NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node2->GetMinTimestamp());
@@ -806,8 +789,8 @@
         NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2));
 
         readHandler2->ClearForceDirtyFlag();
-        syncScheduler.OnSubscriptionAction(readHandler1);
-        syncScheduler.OnSubscriptionAction(readHandler2);
+        syncScheduler.OnSubscriptionReportSent(readHandler1);
+        syncScheduler.OnSubscriptionReportSent(readHandler2);
 
         NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node1->GetMaxTimestamp());
         NL_TEST_ASSERT(aSuite, node2->GetSyncTimestamp() == node2->GetMaxTimestamp());