Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ICD] Add subscriber request max interval getter (#36021) #36176

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading