Skip to content

Commit

Permalink
[ICD] Add subscriber request max interval getter (project-chip#36021)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
2 people authored and yyzhong-g committed Dec 11, 2024
1 parent 9791fb1 commit fdc26a9
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 5 deletions.
4 changes: 3 additions & 1 deletion src/app/ReadHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,9 @@ CHIP_ERROR ReadHandler::ProcessSubscribeRequest(System::PacketBufferHandle && aP
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
Expand Down
28 changes: 24 additions & 4 deletions src/app/ReadHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,31 @@ class ReadHandler : public Messaging::ExchangeDelegate
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);
Expand All @@ -254,7 +273,7 @@ class ReadHandler : public Messaging::ExchangeDelegate
{
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;
Expand Down Expand Up @@ -542,9 +561,10 @@ class ReadHandler : public Messaging::ExchangeDelegate
// 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;

Expand Down
118 changes: 118 additions & 0 deletions src/app/tests/TestReadInteraction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ class TestReadInteraction : public chip::Test::AppContext
void TestReadClient();
void TestReadUnexpectedSubscriptionId();
void TestReadHandler();
void TestReadHandlerSetMaxReportingInterval();
void TestReadClientGenerateAttributePathList();
void TestReadClientGenerateInvalidAttributePathList();
void TestReadClientInvalidReport();
Expand Down Expand Up @@ -568,6 +569,115 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestReadHandler)
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;
Expand Down Expand Up @@ -1517,6 +1627,7 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestICDProcessSubscribeRequestSupMaxInt

EXPECT_EQ(minInterval, kMinInterval);
EXPECT_EQ(maxInterval, idleModeDuration);
EXPECT_EQ(kMaxIntervalCeiling, readHandler.GetSubscriberRequestedMaxInterval());
}
engine->Shutdown();

Expand Down Expand Up @@ -1584,6 +1695,7 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestICDProcessSubscribeRequestInfMaxInt

EXPECT_EQ(minInterval, kMinInterval);
EXPECT_EQ(maxInterval, idleModeDuration);
EXPECT_EQ(kMaxIntervalCeiling, readHandler.GetSubscriberRequestedMaxInterval());
}
engine->Shutdown();

Expand Down Expand Up @@ -1651,6 +1763,7 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestICDProcessSubscribeRequestSupMinInt

EXPECT_EQ(minInterval, kMinInterval);
EXPECT_EQ(maxInterval, (2 * idleModeDuration));
EXPECT_EQ(kMaxIntervalCeiling, readHandler.GetSubscriberRequestedMaxInterval());
}
engine->Shutdown();

Expand Down Expand Up @@ -1716,6 +1829,7 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestICDProcessSubscribeRequestMaxMinInt

EXPECT_EQ(minInterval, kMinInterval);
EXPECT_EQ(maxInterval, kMaxIntervalCeiling);
EXPECT_EQ(kMaxIntervalCeiling, readHandler.GetSubscriberRequestedMaxInterval());
}
engine->Shutdown();

Expand Down Expand Up @@ -1781,6 +1895,7 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestICDProcessSubscribeRequestInvalidId

EXPECT_EQ(minInterval, kMinInterval);
EXPECT_EQ(maxInterval, kMaxIntervalCeiling);
EXPECT_EQ(kMaxIntervalCeiling, readHandler.GetSubscriberRequestedMaxInterval());
}
engine->Shutdown();

Expand Down Expand Up @@ -1959,6 +2074,7 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestSubscribeRoundtrip)
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
Expand Down Expand Up @@ -2028,6 +2144,7 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestSubscribeEarlyReport)
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);

Expand Down Expand Up @@ -2760,6 +2877,7 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestSubscribeInvalidAttributePathRoundt
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));
Expand Down

0 comments on commit fdc26a9

Please sign in to comment.