[ICD] Add subscriber request max interval getter (#36021)
* Add subscriber request max interval getter
* Add unit-test for the SetMaxReportingInterval
* Restyled by clang-format
* fix conversation error
* Restyled by clang-format
---------
Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp
index 43dae0a..4794ead 100644
--- a/src/app/ReadHandler.cpp
+++ b/src/app/ReadHandler.cpp
@@ -741,7 +741,9 @@
ReturnErrorOnFailure(err);
ReturnErrorOnFailure(subscribeRequestParser.GetMinIntervalFloorSeconds(&mMinIntervalFloorSeconds));
- ReturnErrorOnFailure(subscribeRequestParser.GetMaxIntervalCeilingSeconds(&mMaxInterval));
+ ReturnErrorOnFailure(subscribeRequestParser.GetMaxIntervalCeilingSeconds(&mSubscriberRequestedMaxInterval));
+ mMaxInterval = mSubscriberRequestedMaxInterval;
+
VerifyOrReturnError(mMinIntervalFloorSeconds <= mMaxInterval, CHIP_ERROR_INVALID_ARGUMENT);
#if CHIP_CONFIG_ENABLE_ICD_SERVER
diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h
index 1ddcb4e..85976ec 100644
--- a/src/app/ReadHandler.h
+++ b/src/app/ReadHandler.h
@@ -229,12 +229,31 @@
const SingleLinkedListNode<EventPathParams> * GetEventPathList() const { return mpEventPathList; }
const SingleLinkedListNode<DataVersionFilter> * GetDataVersionFilterList() const { return mpDataVersionFilterList; }
+ /**
+ * @brief Returns the reporting intervals that will used by the ReadHandler for the subscription being requested.
+ * After the subscription is established, these will be the set reporting intervals and cannot be changed.
+ *
+ * @param[out] aMinInterval minimum time delta between two reports for the subscription
+ * @param[in] aMaxInterval maximum time delta between two reports for the subscription
+ */
void GetReportingIntervals(uint16_t & aMinInterval, uint16_t & aMaxInterval) const
{
aMinInterval = mMinIntervalFloorSeconds;
aMaxInterval = mMaxInterval;
}
+ /**
+ * @brief Returns the maximum reporting interval that was initially requested by the subscriber
+ * This is the same value as the mMaxInterval member if the max interval is not changed by the publisher.
+ *
+ * @note If the device is an ICD, the MaxInterval of a subscription is automatically set to a multiple of the IdleModeDuration.
+ * This function is the only way to get the requested max interval once the OnSubscriptionRequested application callback
+ * is called.
+ *
+ * @return uint16_t subscriber requested maximum reporting interval
+ */
+ inline uint16_t GetSubscriberRequestedMaxInterval() const { return mSubscriberRequestedMaxInterval; }
+
CHIP_ERROR SetMinReportingIntervalForTests(uint16_t aMinInterval)
{
VerifyOrReturnError(IsIdle(), CHIP_ERROR_INCORRECT_STATE);
@@ -254,7 +273,7 @@
{
VerifyOrReturnError(IsIdle(), CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mMinIntervalFloorSeconds <= aMaxInterval, CHIP_ERROR_INVALID_ARGUMENT);
- VerifyOrReturnError(aMaxInterval <= std::max(GetPublisherSelectedIntervalLimit(), mMaxInterval),
+ VerifyOrReturnError(aMaxInterval <= std::max(GetPublisherSelectedIntervalLimit(), mSubscriberRequestedMaxInterval),
CHIP_ERROR_INVALID_ARGUMENT);
mMaxInterval = aMaxInterval;
return CHIP_NO_ERROR;
@@ -542,9 +561,10 @@
// engine, the "oldest" subscription is the subscription with the smallest generation.
uint64_t mTransactionStartGeneration = 0;
- SubscriptionId mSubscriptionId = 0;
- uint16_t mMinIntervalFloorSeconds = 0;
- uint16_t mMaxInterval = 0;
+ SubscriptionId mSubscriptionId = 0;
+ uint16_t mMinIntervalFloorSeconds = 0;
+ uint16_t mMaxInterval = 0;
+ uint16_t mSubscriberRequestedMaxInterval = 0;
EventNumber mEventMin = 0;
diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp
index f8f192d..5baab57 100644
--- a/src/app/tests/TestReadInteraction.cpp
+++ b/src/app/tests/TestReadInteraction.cpp
@@ -343,6 +343,7 @@
void TestReadClient();
void TestReadUnexpectedSubscriptionId();
void TestReadHandler();
+ void TestReadHandlerSetMaxReportingInterval();
void TestReadClientGenerateAttributePathList();
void TestReadClientGenerateInvalidAttributePathList();
void TestReadClientInvalidReport();
@@ -568,6 +569,115 @@
EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u);
}
+TEST_F_FROM_FIXTURE(TestReadInteraction, TestReadHandlerSetMaxReportingInterval)
+{
+ System::PacketBufferTLVWriter writer;
+ System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
+ SubscribeRequestMessage::Builder subscribeRequestBuilder;
+
+ auto * engine = chip::app::InteractionModelEngine::GetInstance();
+ EXPECT_EQ(engine->Init(&GetExchangeManager(), &GetFabricTable(), gReportScheduler), CHIP_NO_ERROR);
+
+ uint16_t kIntervalInfMinInterval = 119;
+ uint16_t kMinInterval = 120;
+ uint16_t kMaxIntervalCeiling = 500;
+
+ Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false);
+
+ {
+
+ uint16_t minInterval;
+ uint16_t maxInterval;
+
+ // Configure ReadHandler
+ ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler,
+ CodegenDataModelProviderInstance());
+
+ writer.Init(std::move(subscribeRequestbuf));
+ EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR);
+
+ subscribeRequestBuilder.KeepSubscriptions(true);
+ EXPECT_EQ(subscribeRequestBuilder.GetError(), CHIP_NO_ERROR);
+
+ subscribeRequestBuilder.MinIntervalFloorSeconds(kMinInterval);
+ EXPECT_EQ(subscribeRequestBuilder.GetError(), CHIP_NO_ERROR);
+
+ subscribeRequestBuilder.MaxIntervalCeilingSeconds(kMaxIntervalCeiling);
+ EXPECT_EQ(subscribeRequestBuilder.GetError(), CHIP_NO_ERROR);
+
+ AttributePathIBs::Builder & attributePathListBuilder = subscribeRequestBuilder.CreateAttributeRequests();
+ EXPECT_EQ(attributePathListBuilder.GetError(), CHIP_NO_ERROR);
+
+ AttributePathIB::Builder & attributePathBuilder = attributePathListBuilder.CreatePath();
+ EXPECT_EQ(attributePathListBuilder.GetError(), CHIP_NO_ERROR);
+
+ attributePathBuilder.Node(1).Endpoint(2).Cluster(3).Attribute(4).ListIndex(5).EndOfAttributePathIB();
+ EXPECT_EQ(attributePathBuilder.GetError(), CHIP_NO_ERROR);
+
+ attributePathListBuilder.EndOfAttributePathIBs();
+ EXPECT_EQ(attributePathListBuilder.GetError(), CHIP_NO_ERROR);
+
+ subscribeRequestBuilder.IsFabricFiltered(false).EndOfSubscribeRequestMessage();
+ EXPECT_EQ(subscribeRequestBuilder.GetError(), CHIP_NO_ERROR);
+
+ EXPECT_EQ(subscribeRequestBuilder.GetError(), CHIP_NO_ERROR);
+ EXPECT_EQ(writer.Finalize(&subscribeRequestbuf), CHIP_NO_ERROR);
+
+ EXPECT_EQ(readHandler.ProcessSubscribeRequest(std::move(subscribeRequestbuf)), CHIP_NO_ERROR);
+
+#if CHIP_CONFIG_ENABLE_ICD_SERVER
+ // When an ICD build, the default behavior is to select the IdleModeDuration as MaxInterval
+ kMaxIntervalCeiling = readHandler.GetPublisherSelectedIntervalLimit();
+#endif
+ // Try to change the MaxInterval while ReadHandler is active
+ EXPECT_EQ(readHandler.SetMaxReportingInterval(340), CHIP_ERROR_INCORRECT_STATE);
+
+ readHandler.GetReportingIntervals(minInterval, maxInterval);
+ EXPECT_EQ(kMaxIntervalCeiling, maxInterval);
+ // Set ReadHandler to Idle to allow MaxInterval changes
+ readHandler.MoveToState(ReadHandler::HandlerState::Idle);
+
+ // TC1: MaxInterval < MinIntervalFloor
+ EXPECT_EQ(readHandler.SetMaxReportingInterval(kIntervalInfMinInterval), CHIP_ERROR_INVALID_ARGUMENT);
+
+ readHandler.GetReportingIntervals(minInterval, maxInterval);
+ EXPECT_EQ(kMaxIntervalCeiling, maxInterval);
+
+ // TC2: MaxInterval == MinIntervalFloor
+ EXPECT_EQ(readHandler.SetMaxReportingInterval(kMinInterval), CHIP_NO_ERROR);
+
+ readHandler.GetReportingIntervals(minInterval, maxInterval);
+ EXPECT_EQ(kMinInterval, maxInterval);
+
+ // TC3: Minterval < MaxInterval < max(GetPublisherSelectedIntervalLimit(), mSubscriberRequestedMaxInterval)
+ EXPECT_EQ(readHandler.SetMaxReportingInterval(kMaxIntervalCeiling), CHIP_NO_ERROR);
+
+ readHandler.GetReportingIntervals(minInterval, maxInterval);
+ EXPECT_EQ(kMaxIntervalCeiling, maxInterval);
+
+ // TC4: MaxInterval == Subscriber Requested Max Interval
+ EXPECT_EQ(readHandler.SetMaxReportingInterval(readHandler.GetSubscriberRequestedMaxInterval()), CHIP_NO_ERROR);
+
+ readHandler.GetReportingIntervals(minInterval, maxInterval);
+ EXPECT_EQ(readHandler.GetSubscriberRequestedMaxInterval(), maxInterval);
+
+ // TC4: MaxInterval == GetPublisherSelectedIntervalLimit()
+ EXPECT_EQ(readHandler.SetMaxReportingInterval(readHandler.GetPublisherSelectedIntervalLimit()), CHIP_NO_ERROR);
+
+ readHandler.GetReportingIntervals(minInterval, maxInterval);
+ EXPECT_EQ(readHandler.GetPublisherSelectedIntervalLimit(), maxInterval);
+
+ // TC5: MaxInterval > max(GetPublisherSelectedIntervalLimit(), mSubscriberRequestedMaxInterval)
+ EXPECT_EQ(readHandler.SetMaxReportingInterval(std::numeric_limits<uint16_t>::max()), CHIP_ERROR_INVALID_ARGUMENT);
+
+ readHandler.GetReportingIntervals(minInterval, maxInterval);
+ EXPECT_EQ(readHandler.GetPublisherSelectedIntervalLimit(), maxInterval);
+ }
+
+ engine->Shutdown();
+ EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u);
+}
+
TEST_F_FROM_FIXTURE(TestReadInteraction, TestReadClientGenerateAttributePathList)
{
MockInteractionModelApp delegate;
@@ -1517,6 +1627,7 @@
EXPECT_EQ(minInterval, kMinInterval);
EXPECT_EQ(maxInterval, idleModeDuration);
+ EXPECT_EQ(kMaxIntervalCeiling, readHandler.GetSubscriberRequestedMaxInterval());
}
engine->Shutdown();
@@ -1584,6 +1695,7 @@
EXPECT_EQ(minInterval, kMinInterval);
EXPECT_EQ(maxInterval, idleModeDuration);
+ EXPECT_EQ(kMaxIntervalCeiling, readHandler.GetSubscriberRequestedMaxInterval());
}
engine->Shutdown();
@@ -1651,6 +1763,7 @@
EXPECT_EQ(minInterval, kMinInterval);
EXPECT_EQ(maxInterval, (2 * idleModeDuration));
+ EXPECT_EQ(kMaxIntervalCeiling, readHandler.GetSubscriberRequestedMaxInterval());
}
engine->Shutdown();
@@ -1716,6 +1829,7 @@
EXPECT_EQ(minInterval, kMinInterval);
EXPECT_EQ(maxInterval, kMaxIntervalCeiling);
+ EXPECT_EQ(kMaxIntervalCeiling, readHandler.GetSubscriberRequestedMaxInterval());
}
engine->Shutdown();
@@ -1781,6 +1895,7 @@
EXPECT_EQ(minInterval, kMinInterval);
EXPECT_EQ(maxInterval, kMaxIntervalCeiling);
+ EXPECT_EQ(kMaxIntervalCeiling, readHandler.GetSubscriberRequestedMaxInterval());
}
engine->Shutdown();
@@ -1959,6 +2074,7 @@
uint16_t minInterval;
uint16_t maxInterval;
delegate.mpReadHandler->GetReportingIntervals(minInterval, maxInterval);
+ EXPECT_EQ(readPrepareParams.mMaxIntervalCeilingSeconds, delegate.mpReadHandler->GetSubscriberRequestedMaxInterval());
// Test empty report
// Advance monotonic timestamp for min interval to elapse
@@ -2028,6 +2144,7 @@
uint16_t minInterval;
uint16_t maxInterval;
delegate.mpReadHandler->GetReportingIntervals(minInterval, maxInterval);
+ EXPECT_EQ(readPrepareParams.mMaxIntervalCeilingSeconds, delegate.mpReadHandler->GetSubscriberRequestedMaxInterval());
EXPECT_EQ(engine->GetNumActiveReadHandlers(ReadHandler::InteractionType::Subscribe), 1u);
@@ -2760,6 +2877,7 @@
uint16_t minInterval;
uint16_t maxInterval;
delegate.mpReadHandler->GetReportingIntervals(minInterval, maxInterval);
+ EXPECT_EQ(readPrepareParams.mMaxIntervalCeilingSeconds, delegate.mpReadHandler->GetSubscriberRequestedMaxInterval());
// Advance monotonic timestamp for min interval to elapse
gMockClock.AdvanceMonotonic(System::Clock::Seconds16(maxInterval));