Skip to content

Commit a4a3c11

Browse files
[ICD]Retrigger subscription immediately when OnActiveModeNotification is invoked when state is FailedICDSubscription
1 parent 78092a6 commit a4a3c11

5 files changed

+87
-56
lines changed

src/app/InteractionModelEngine.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -1051,17 +1051,17 @@ void InteractionModelEngine::OnActiveModeNotification(ScopedNodeId aPeer)
10511051
}
10521052
}
10531053

1054-
void InteractionModelEngine::OnPeerTypeChange(ScopedNodeId aPeer, ReadClient::PeerType aType)
1054+
void InteractionModelEngine::OnPeerOperatingModeChange(ScopedNodeId aPeer, ReadClient::PeerOperatingMode aMode)
10551055
{
10561056
// TODO: Follow up to use a iterator function to avoid copy/paste here.
10571057
for (ReadClient * pListItem = mpActiveReadClientList; pListItem != nullptr;)
10581058
{
1059-
// It is possible that pListItem is destroyed by the app in OnPeerTypeChange.
1060-
// Get the next item before invoking `OnPeerTypeChange`.
1059+
// It is possible that pListItem is destroyed by the app in OnPeerOperatingModeChange.
1060+
// Get the next item before invoking `OnPeerOperatingModeChange`.
10611061
auto pNextItem = pListItem->GetNextClient();
10621062
if (ScopedNodeId(pListItem->GetPeerNodeId(), pListItem->GetFabricIndex()) == aPeer)
10631063
{
1064-
pListItem->OnPeerTypeChange(aType);
1064+
pListItem->OnPeerOperatingModeChange(aMode);
10651065
}
10661066
pListItem = pNextItem;
10671067
}

src/app/InteractionModelEngine.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,12 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
242242
void OnActiveModeNotification(ScopedNodeId aPeer);
243243

244244
/**
245-
* Used to notify when a peer becomes LIT ICD or vice versa.
245+
* Used to notify when a peer operating mode becomes LIT ICD or vice versa.
246246
*
247247
* ReadClient will call this function when it finds any updates of the OperatingMode attribute from ICD management
248248
* cluster. The application doesn't need to call this function, usually.
249249
*/
250-
void OnPeerTypeChange(ScopedNodeId aPeer, ReadClient::PeerType aType);
250+
void OnPeerOperatingModeChange(ScopedNodeId aPeer, ReadClient::PeerOperatingMode aMode);
251251

252252
/**
253253
* Add a read client to the internally tracked list of weak references. This list is used to

src/app/ReadClient.cpp

+56-34
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,18 @@ void ReadClient::Close(CHIP_ERROR aError, bool allowResubscription)
213213
if (aError == CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT)
214214
{
215215
VerifyOrDie(originalReason == CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT);
216-
ChipLogProgress(DataManagement, "ICD device is inactive mark subscription as InactiveICDSubscription");
216+
ChipLogProgress(DataManagement, "ICD device is inactive, mark subscription as InactiveICDSubscription");
217217
MoveToState(ClientState::InactiveICDSubscription);
218218
return;
219219
}
220+
if (aError == CHIP_ERROR_LIT_CASE_SUBSCRIBE_PRIMING_TIMEOUT)
221+
{
222+
ChipLogProgress(
223+
DataManagement,
224+
"ICD device is unreachable, mark subscription as FailedICDSubscription, retrying is scheduled");
225+
MoveToState(ClientState::FailedICDSubscription);
226+
return;
227+
}
220228
}
221229

222230
//
@@ -250,6 +258,8 @@ const char * ReadClient::GetStateStr() const
250258
return "SubscriptionActive";
251259
case ClientState::InactiveICDSubscription:
252260
return "InactiveICDSubscription";
261+
case ClientState::FailedICDSubscription:
262+
return "FailedICDSubscription";
253263
}
254264
#endif // CHIP_DETAIL_LOGGING
255265
return "N/A";
@@ -484,26 +494,27 @@ CHIP_ERROR ReadClient::GenerateDataVersionFilterList(DataVersionFilterIBs::Build
484494

485495
void ReadClient::OnActiveModeNotification()
486496
{
487-
// This function just tries to complete the deferred resubscription logic in `OnLivenessTimeoutCallback`.
488497
VerifyOrDie(mpImEngine->InActiveReadClientList(this));
489-
// If we are not in InactiveICDSubscription state, that means the liveness timeout has not been reached. Simply do nothing.
490-
VerifyOrReturn(IsInactiveICDSubscription());
498+
// If the current state is InactiveICDSubscription (LIT ICD unreachable) or FailedICDSubscription (session/subscription creation failed),
499+
// the previous close call should have executed ClearActiveSubscriptionState.
500+
// Upon receiving a check-in, cancel pending resubscription if any and initiate a new one immediately.
501+
// Case sessions need to be invalidated.
502+
VerifyOrReturn(IsInactiveICDSubscription() && IsFailedICDSubscription());
491503

492-
// When we reach here, the subscription definitely exceeded the liveness timeout. Just continue the unfinished resubscription
493-
// logic in `OnLivenessTimeoutCallback`.
494-
TriggerResubscriptionForLivenessTimeout(CHIP_ERROR_TIMEOUT);
504+
CloseSession();
505+
CancelResubscribeTimer();
506+
MoveToState(ClientState::Idle);
507+
OnResubscribeTimerCallback(nullptr, this);
495508
}
496509

497-
void ReadClient::OnPeerTypeChange(PeerType aType)
510+
void ReadClient::OnPeerOperatingModeChange(PeerOperatingMode aMode)
498511
{
499512
VerifyOrDie(mpImEngine->InActiveReadClientList(this));
513+
ChipLogProgress(DataManagement, "Peer operation mode is now %s LIT ICD.",
514+
mPeerOperatingMode == PeerOperatingMode::kLITICD ? "a" : "not a");
500515

501-
mIsPeerLIT = (aType == PeerType::kLITICD);
502-
503-
ChipLogProgress(DataManagement, "Peer is now %s LIT ICD.", mIsPeerLIT ? "a" : "not a");
504-
505-
// If the peer is no longer LIT, try to wake up the subscription and do resubscribe when necessary.
506-
if (!mIsPeerLIT)
516+
// If the peer operating mode is no longer LIT, try to wake up the subscription and do resubscribe when necessary.
517+
if (mPeerOperatingMode == PeerOperatingMode::kNormal)
507518
{
508519
OnActiveModeNotification();
509520
}
@@ -720,10 +731,18 @@ void ReadClient::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContex
720731
ChipLogError(DataManagement, "Time out! failed to receive report data from Exchange: " ChipLogFormatExchange,
721732
ChipLogValueExchange(apExchangeContext));
722733

723-
Close(CHIP_ERROR_TIMEOUT);
734+
if (IsPeerLIT() && (IsIdle() || IsAwaitingInitialReport() || IsAwaitingSubscribeResponse()))
735+
{
736+
ChipLogError(DataManagement, "LIT ICD device is unreachable during Subscription establishment procedure with state %s", GetStateStr());
737+
Close(CHIP_ERROR_LIT_CASE_SUBSCRIBE_PRIMING_TIMEOUT);
738+
}
739+
else
740+
{
741+
Close(CHIP_ERROR_TIMEOUT);
742+
}
724743
}
725744

726-
CHIP_ERROR ReadClient::ReadICDOperatingModeFromAttributeDataIB(TLV::TLVReader && aReader, PeerType & aType)
745+
CHIP_ERROR ReadClient::ReadICDOperatingModeFromAttributeDataIB(TLV::TLVReader && aReader, PeerOperatingMode & aMode)
727746
{
728747
Clusters::IcdManagement::Attributes::OperatingMode::TypeInfo::DecodableType operatingMode;
729748

@@ -733,10 +752,12 @@ CHIP_ERROR ReadClient::ReadICDOperatingModeFromAttributeDataIB(TLV::TLVReader &&
733752
switch (operatingMode)
734753
{
735754
case Clusters::IcdManagement::OperatingModeEnum::kSit:
736-
aType = PeerType::kNormal;
755+
aMode = PeerOperatingMode::kNormal;
737756
break;
738757
case Clusters::IcdManagement::OperatingModeEnum::kLit:
739-
aType = PeerType::kLITICD;
758+
aMode = PeerOperatingMode::kLITICD;
759+
// If operating mode is LIT, this device should be LIT ICD.
760+
mIsPeerLIT = true;
740761
break;
741762
default:
742763
err = CHIP_ERROR_INVALID_ARGUMENT;
@@ -843,14 +864,14 @@ CHIP_ERROR ReadClient::ProcessAttributeReportIBs(TLV::TLVReader & aAttributeRepo
843864
if (attributePath.MatchesConcreteAttributePath(ConcreteAttributePath(
844865
kRootEndpointId, Clusters::IcdManagement::Id, Clusters::IcdManagement::Attributes::OperatingMode::Id)))
845866
{
846-
PeerType peerType;
867+
PeerOperatingMode peerMode;
847868
TLV::TLVReader operatingModeTlvReader;
848869
operatingModeTlvReader.Init(dataReader);
849-
if (CHIP_NO_ERROR == ReadICDOperatingModeFromAttributeDataIB(std::move(operatingModeTlvReader), peerType))
870+
if (CHIP_NO_ERROR == ReadICDOperatingModeFromAttributeDataIB(std::move(operatingModeTlvReader), peerMode))
850871
{
851-
// It is safe to call `OnPeerTypeChange` since we are in the middle of parsing the attribute data, And
872+
// It is safe to call `OnPeerOperatingModeChange` since we are in the middle of parsing the attribute data, And
852873
// the subscription should be active so `OnActiveModeNotification` is a no-op in this case.
853-
InteractionModelEngine::GetInstance()->OnPeerTypeChange(mPeer, peerType);
874+
InteractionModelEngine::GetInstance()->OnPeerOperatingModeChange(mPeer, peerMode);
854875
}
855876
else
856877
{
@@ -1029,15 +1050,16 @@ void ReadClient::OnLivenessTimeoutCallback(System::Layer * apSystemLayer, void *
10291050
"Subscription Liveness timeout with SubscriptionID = 0x%08" PRIx32 ", Peer = %02x:" ChipLogFormatX64,
10301051
_this->mSubscriptionId, _this->GetFabricIndex(), ChipLogValueX64(_this->GetPeerNodeId()));
10311052

1032-
if (_this->mIsPeerLIT)
1053+
if (_this->IsPeerLIT())
10331054
{
10341055
subscriptionTerminationCause = CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT;
10351056
}
10361057

1037-
_this->TriggerResubscriptionForLivenessTimeout(subscriptionTerminationCause);
1058+
_this->CloseSession();
1059+
_this->Close(subscriptionTerminationCause);
10381060
}
10391061

1040-
void ReadClient::TriggerResubscriptionForLivenessTimeout(CHIP_ERROR aReason)
1062+
void ReadClient::CloseSession()
10411063
{
10421064
// We didn't get a message from the server on time; it's possible that it no
10431065
// longer has a useful CASE session to us. Mark defunct all sessions that
@@ -1060,8 +1082,6 @@ void ReadClient::TriggerResubscriptionForLivenessTimeout(CHIP_ERROR aReason)
10601082
session->MarkAsDefunct();
10611083
});
10621084
}
1063-
1064-
Close(aReason);
10651085
}
10661086

10671087
CHIP_ERROR ReadClient::ProcessSubscribeResponse(System::PacketBufferHandle && aPayload)
@@ -1270,9 +1290,7 @@ CHIP_ERROR ReadClient::DefaultResubscribePolicy(CHIP_ERROR aTerminationCause)
12701290
ChipLogProgress(DataManagement, "ICD device is inactive, skipping scheduling resubscribe within DefaultResubscribePolicy");
12711291
return CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT;
12721292
}
1273-
12741293
VerifyOrReturnError(IsIdle(), CHIP_ERROR_INCORRECT_STATE);
1275-
12761294
auto timeTillNextResubscription = ComputeTimeTillNextSubscription();
12771295
ChipLogProgress(DataManagement,
12781296
"Will try to resubscribe to %02x:" ChipLogFormatX64 " at retry index %" PRIu32 " after %" PRIu32
@@ -1305,9 +1323,8 @@ void ReadClient::HandleDeviceConnectionFailure(void * context, const Operational
13051323
{
13061324
ReadClient * const _this = static_cast<ReadClient *>(context);
13071325
VerifyOrDie(_this != nullptr);
1308-
1309-
ChipLogError(DataManagement, "Failed to establish CASE for re-subscription with error '%" CHIP_ERROR_FORMAT "'",
1310-
failureInfo.error.Format());
1326+
CHIP_ERROR err = failureInfo.error;
1327+
ChipLogError(DataManagement, "Failed to establish CASE for re-subscription with error '%" CHIP_ERROR_FORMAT "'", err.Format());
13111328

13121329
#if CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
13131330
#if CHIP_DETAIL_LOGGING
@@ -1322,7 +1339,13 @@ void ReadClient::HandleDeviceConnectionFailure(void * context, const Operational
13221339
_this->mMinimalResubscribeDelay = System::Clock::kZero;
13231340
#endif // CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
13241341

1325-
_this->Close(failureInfo.error);
1342+
if (_this->IsPeerLIT() && err == CHIP_ERROR_TIMEOUT && _this->IsIdle())
1343+
{
1344+
ChipLogError(DataManagement, "LIT ICD device is unreachable during CASE establishment procedure");
1345+
err = CHIP_ERROR_LIT_CASE_SUBSCRIBE_PRIMING_TIMEOUT;
1346+
}
1347+
1348+
_this->Close(err);
13261349
}
13271350

13281351
void ReadClient::OnResubscribeTimerCallback(System::Layer * /* If this starts being used, fix callers that pass nullptr */,
@@ -1334,7 +1357,6 @@ void ReadClient::OnResubscribeTimerCallback(System::Layer * /* If this starts be
13341357
_this->mIsResubscriptionScheduled = false;
13351358

13361359
CHIP_ERROR err;
1337-
13381360
ChipLogProgress(DataManagement, "OnResubscribeTimerCallback: ForceCASE = %d", _this->mForceCaseOnNextResub);
13391361
_this->mNumRetries++;
13401362

src/app/ReadClient.h

+16-15
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,6 @@ class ReadClient : public Messaging::ExchangeDelegate
165165
* ReadClient::DefaultResubscribePolicy is broken down into its constituent methods that are publicly available for
166166
* applications to call and sequence.
167167
*
168-
* If the peer is LIT ICD, and the timeout is reached, `aTerminationCause` will be
169-
* `CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT`. In this case, returning `CHIP_NO_ERROR` will still trigger a resubscribe
170-
* attempt, while returning `CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT` will put the subscription in the
171-
* `InactiveICDSubscription` state. In the latter case, OnResubscriptionNeeded will be called again when
172-
* `OnActiveModeNotification` is called.
173-
*
174168
* If the method is over-ridden, it's the application's responsibility to take the appropriate steps needed to eventually
175169
* call-back into the ReadClient object to schedule a re-subscription (by invoking ReadClient::ScheduleResubscription).
176170
*
@@ -292,7 +286,7 @@ class ReadClient : public Messaging::ExchangeDelegate
292286
Subscribe,
293287
};
294288

295-
enum class PeerType : uint8_t
289+
enum class PeerOperatingMode : uint8_t
296290
{
297291
kNormal,
298292
kLITICD,
@@ -350,12 +344,12 @@ class ReadClient : public Messaging::ExchangeDelegate
350344
/**
351345
* Re-activate an inactive subscription.
352346
*
353-
* When subscribing to LIT-ICD and liveness timeout reached and OnResubscriptionNeeded returns
354-
* CHIP_ERROR_LIT_SUBSCRIBE_INACTIVE_TIMEOUT, the read client will move to the InactiveICDSubscription state and
355-
* resubscription can be triggered via OnActiveModeNotification().
347+
* When subscribing to LIT-ICD and timeout reached, the read client will move to the InactiveICDSubscription state
348+
* and resubscription should have been scheduled by previous close call, resubscription can be triggered immediately via
349+
* OnActiveModeNotification() when receiving check-in message.
356350
*
357351
* If the subscription is not in the `InactiveICDSubscription` state, this function will do nothing. So it is always safe to
358-
* call this function when a check-in message is received.
352+
* call this function when a check-in message is received.
359353
*/
360354
void OnActiveModeNotification();
361355

@@ -503,6 +497,9 @@ class ReadClient : public Messaging::ExchangeDelegate
503497
*/
504498
Optional<System::Clock::Timeout> GetSubscriptionTimeout();
505499

500+
bool IsPeerLIT() { return mIsPeerLIT; }
501+
PeerOperatingMode GetLITOperatingMode() { return mPeerOperatingMode; }
502+
506503
private:
507504
friend class TestReadInteraction;
508505
friend class InteractionModelEngine;
@@ -514,6 +511,7 @@ class ReadClient : public Messaging::ExchangeDelegate
514511
AwaitingSubscribeResponse, ///< The client is waiting for subscribe response
515512
SubscriptionActive, ///< The client is maintaining subscription
516513
InactiveICDSubscription, ///< The client is waiting to resubscribe for LIT device
514+
FailedICDSubscription, ///< The client is failing during case establishment or subscription priming stage for LIT device
517515
};
518516

519517
enum class ReportType
@@ -534,20 +532,21 @@ class ReadClient : public Messaging::ExchangeDelegate
534532
void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override;
535533

536534
/**
537-
* Updates the type (LIT ICD or not) of the peer.
535+
* Updates the operating mode (LIT ICD or not) of the peer.
538536
*
539537
* When the subscription is active, this function will just set the flag. When the subscription is an InactiveICDSubscription,
540538
* setting the peer type to SIT or normal devices will also trigger a resubscription attempt.
541539
*
542540
*/
543-
void OnPeerTypeChange(PeerType aType);
541+
void OnPeerOperatingModeChange(PeerOperatingMode aMode);
544542

545543
/**
546544
* Check if current read client is being used
547545
*
548546
*/
549547
bool IsIdle() const { return mState == ClientState::Idle; }
550548
bool IsInactiveICDSubscription() const { return mState == ClientState::InactiveICDSubscription; }
549+
bool IsFailedICDSubscription() const { return mState == ClientState::FailedICDSubscription; }
551550
bool IsSubscriptionActive() const { return mState == ClientState::SubscriptionActive; }
552551
bool IsAwaitingInitialReport() const { return mState == ClientState::AwaitingInitialReport; }
553552
bool IsAwaitingSubscribeResponse() const { return mState == ClientState::AwaitingSubscribeResponse; }
@@ -562,7 +561,7 @@ class ReadClient : public Messaging::ExchangeDelegate
562561
CHIP_ERROR BuildDataVersionFilterList(DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder,
563562
const Span<AttributePathParams> & aAttributePaths,
564563
const Span<DataVersionFilter> & aDataVersionFilters, bool & aEncodedDataVersionList);
565-
CHIP_ERROR ReadICDOperatingModeFromAttributeDataIB(TLV::TLVReader && aReader, PeerType & aType);
564+
CHIP_ERROR ReadICDOperatingModeFromAttributeDataIB(TLV::TLVReader && aReader, PeerOperatingMode & aMode);
566565
CHIP_ERROR ProcessAttributeReportIBs(TLV::TLVReader & aAttributeDataIBsReader);
567566
CHIP_ERROR ProcessEventReportIBs(TLV::TLVReader & aEventReportIBsReader);
568567

@@ -572,7 +571,7 @@ class ReadClient : public Messaging::ExchangeDelegate
572571
CHIP_ERROR ComputeLivenessCheckTimerTimeout(System::Clock::Timeout * aTimeout);
573572
void CancelLivenessCheckTimer();
574573
void CancelResubscribeTimer();
575-
void TriggerResubscriptionForLivenessTimeout(CHIP_ERROR aReason);
574+
void CloseSession();
576575
void MoveToState(const ClientState aTargetState);
577576
CHIP_ERROR ProcessAttributePath(AttributePathIB::Parser & aAttributePath, ConcreteDataAttributePath & aClusterInfo);
578577
CHIP_ERROR ProcessReportData(System::PacketBufferHandle && aPayload, ReportType aReportType);
@@ -667,6 +666,8 @@ class ReadClient : public Messaging::ExchangeDelegate
667666

668667
bool mIsPeerLIT = false;
669668

669+
PeerOperatingMode mPeerOperatingMode = PeerOperatingMode::kNormal;
670+
670671
// End Of Container (0x18) uses one byte.
671672
static constexpr uint16_t kReservedSizeForEndOfContainer = 1;
672673
// Reserved size for the uint8_t InteractionModelRevision flag, which takes up 1 byte for the control tag and 1 byte for the

src/lib/core/CHIPError.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,15 @@ using CHIP_ERROR = ::chip::ChipError;
837837
*/
838838
#define CHIP_ERROR_TLV_CONTAINER_OPEN CHIP_CORE_ERROR(0x27)
839839

840-
// AVAILABLE: 0x28
840+
/**
841+
* @def CHIP_ERROR_LIT_CASE_SUBSCRIBE_PRIMING_TIMEOUT
842+
*
843+
* @brief
844+
* CASE fail to be established for Subscription or subscription fails to be established
845+
*
846+
*/
847+
#define CHIP_ERROR_LIT_CASE_SUBSCRIBE_PRIMING_TIMEOUT CHIP_CORE_ERROR(0x28)
848+
841849
// AVAILABLE: 0x29
842850

843851
/**

0 commit comments

Comments
 (0)