Skip to content

Commit

Permalink
Fix ShutdownSubscription (project-chip#21870)
Browse files Browse the repository at this point in the history
-- Use the triple set, fabric index, peer node id and subscription id to locate subscription and close it out
-- When close subscription with no error, we also need to clear out
active subscription state.
  • Loading branch information
yunhanw-google authored and isiu-apple committed Sep 16, 2022
1 parent 8ddb5bf commit c5b66a1
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 6 deletions.
6 changes: 5 additions & 1 deletion examples/chip-tool/commands/clusters/SubscriptionsCommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,23 @@ class ShutdownSubscription : public CHIPCommand
CHIPCommand("shutdown-one", credsIssuerConfig, "Shut down a single subscription, identified by its subscription id.")
{
AddArgument("subscription-id", 0, UINT64_MAX, &mSubscriptionId);
AddArgument("node-id", 0, UINT64_MAX, &mNodeId,
"The node id, scoped to the commissioner name the command is running under.");
}

/////////// CHIPCommand Interface /////////
CHIP_ERROR RunCommand() override
{
CHIP_ERROR err = chip::app::InteractionModelEngine::GetInstance()->ShutdownSubscription(mSubscriptionId);
CHIP_ERROR err = chip::app::InteractionModelEngine::GetInstance()->ShutdownSubscription(
chip::ScopedNodeId(mNodeId, CurrentCommissioner().GetFabricIndex()), mSubscriptionId);
SetCommandExitStatus(err);
return CHIP_NO_ERROR;
}
chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(10); }

private:
chip::SubscriptionId mSubscriptionId;
chip::NodeId mNodeId;
};

class ShutdownSubscriptionsForNode : public CHIPCommand
Expand Down
5 changes: 3 additions & 2 deletions src/app/InteractionModelEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,12 @@ uint32_t InteractionModelEngine::GetNumActiveWriteHandlers() const
return numActive;
}

CHIP_ERROR InteractionModelEngine::ShutdownSubscription(SubscriptionId aSubscriptionId)
CHIP_ERROR InteractionModelEngine::ShutdownSubscription(const ScopedNodeId & aPeerNodeId, SubscriptionId aSubscriptionId)
{
for (auto * readClient = mpActiveReadClientList; readClient != nullptr; readClient = readClient->GetNextClient())
{
if (readClient->IsSubscriptionType() && readClient->IsMatchingSubscriptionId(aSubscriptionId))
if (readClient->IsSubscriptionType() && readClient->IsMatchingSubscriptionId(aSubscriptionId) &&
readClient->GetFabricIndex() == aPeerNodeId.GetFabricIndex() && readClient->GetPeerNodeId() == aPeerNodeId.GetNodeId())
{
readClient->Close(CHIP_NO_ERROR);
return CHIP_NO_ERROR;
Expand Down
2 changes: 1 addition & 1 deletion src/app/InteractionModelEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
* @retval #CHIP_ERROR_KEY_NOT_FOUND If the subscription is not found.
* @retval #CHIP_NO_ERROR On success.
*/
CHIP_ERROR ShutdownSubscription(SubscriptionId aSubscriptionId);
CHIP_ERROR ShutdownSubscription(const ScopedNodeId & aPeerNodeId, SubscriptionId aSubscriptionId);

/**
* Tears down active subscriptions for a given peer node ID.
Expand Down
3 changes: 1 addition & 2 deletions src/app/ReadClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,9 @@ void ReadClient::Close(CHIP_ERROR aError, bool allowResubscription)
}
else
{
ClearActiveSubscriptionState();
if (aError != CHIP_NO_ERROR)
{
ClearActiveSubscriptionState();

//
// We infer that re-subscription was requested by virtue of having a non-zero list of event OR attribute paths present
// in mReadPrepareParams. This would only be the case if an application called SendAutoResubscribeRequest which
Expand Down
52 changes: 52 additions & 0 deletions src/app/tests/TestReadInteraction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ class TestReadInteraction
static void TestSubscribeSendInvalidStatusReport(nlTestSuite * apSuite, void * apContext);
static void TestReadHandlerInvalidSubscribeRequest(nlTestSuite * apSuite, void * apContext);
static void TestSubscribeInvalidateFabric(nlTestSuite * apSuite, void * apContext);
static void TestShutdownSubscription(nlTestSuite * apSuite, void * apContext);

private:
static void GenerateReportData(nlTestSuite * apSuite, void * apContext, System::PacketBufferHandle & aPayload,
Expand Down Expand Up @@ -3730,6 +3731,56 @@ void TestReadInteraction::TestSubscribeInvalidateFabric(nlTestSuite * apSuite, v
NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
}

void TestReadInteraction::TestShutdownSubscription(nlTestSuite * apSuite, void * apContext)
{
TestContext & ctx = *static_cast<TestContext *>(apContext);
CHIP_ERROR err = CHIP_NO_ERROR;

Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr();
// Shouldn't have anything in the retransmit table when starting the test.
NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0);

GenerateEvents(apSuite, apContext);

MockInteractionModelApp delegate;
auto * engine = chip::app::InteractionModelEngine::GetInstance();
err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable());
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);

ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice());
readPrepareParams.mpAttributePathParamsList = new chip::app::AttributePathParams[1];
readPrepareParams.mAttributePathParamsListSize = 1;

readPrepareParams.mpAttributePathParamsList[0].mEndpointId = Test::kMockEndpoint3;
readPrepareParams.mpAttributePathParamsList[0].mClusterId = Test::MockClusterId(2);
readPrepareParams.mpAttributePathParamsList[0].mAttributeId = Test::MockAttributeId(4);

readPrepareParams.mMinIntervalFloorSeconds = 0;
readPrepareParams.mMaxIntervalCeilingSeconds = 0;

{
app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate,
chip::app::ReadClient::InteractionType::Subscribe);

delegate.mGotReport = false;

err = readClient.SendAutoResubscribeRequest(std::move(readPrepareParams));
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);

ctx.DrainAndServiceIO();

NL_TEST_ASSERT(apSuite, delegate.mGotReport);
NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers(ReadHandler::InteractionType::Subscribe) == 1);

engine->ShutdownSubscription(chip::ScopedNodeId(readClient.GetPeerNodeId(), readClient.GetFabricIndex()),
readClient.GetSubscriptionId().Value());
NL_TEST_ASSERT(apSuite, readClient.IsIdle());
}
engine->Shutdown();
NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadClients() == 0);
NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
}

} // namespace app
} // namespace chip

Expand Down Expand Up @@ -3775,6 +3826,7 @@ const nlTest sTests[] =
NL_TEST_DEF("TestSubscribeSendInvalidStatusReport", chip::app::TestReadInteraction::TestSubscribeSendInvalidStatusReport),
NL_TEST_DEF("TestReadHandlerInvalidSubscribeRequest", chip::app::TestReadInteraction::TestReadHandlerInvalidSubscribeRequest),
NL_TEST_DEF("TestSubscribeInvalidateFabric", chip::app::TestReadInteraction::TestSubscribeInvalidateFabric),
NL_TEST_DEF("TestShutdownSubscription", chip::app::TestReadInteraction::TestShutdownSubscription),
NL_TEST_DEF("TestSubscribeUrgentWildcardEvent", chip::app::TestReadInteraction::TestSubscribeUrgentWildcardEvent),
NL_TEST_DEF("TestSubscribeWildcard", chip::app::TestReadInteraction::TestSubscribeWildcard),
NL_TEST_DEF("TestSubscribePartialOverlap", chip::app::TestReadInteraction::TestSubscribePartialOverlap),
Expand Down

0 comments on commit c5b66a1

Please sign in to comment.