Skip to content

Commit 2d97bb7

Browse files
[ICD] Update and Document ICDManager interface (#33081)
* Update and Document ICDManager interface * Apply suggestions from code review Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Update ICDManagementCluster test to cover off the Check-In message randomness * Address comments and update comments * restyle --------- Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
1 parent 63c4709 commit 2d97bb7

File tree

10 files changed

+581
-72
lines changed

10 files changed

+581
-72
lines changed

examples/lit-icd-app/nrfconnect/main/AppTask.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,8 @@ void AppTask::ButtonEventHandler(uint32_t buttonState, uint32_t hasChanged)
296296

297297
void AppTask::IcdUatEventHandler(const AppEvent &)
298298
{
299-
Server::GetInstance().GetICDManager().UpdateOperationState(ICDManager::OperationalState::ActiveMode);
299+
// Temporarily claim network activity, until we implement a "user trigger" reason for ICD wakeups.
300+
PlatformMgr().ScheduleWork([](intptr_t) { ICDNotifier::GetInstance().NotifyNetworkActivityNotification(); });
300301
}
301302

302303
void AppTask::FunctionTimerTimeoutCallback(k_timer * timer)

examples/platform/silabs/BaseApplication.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,7 @@ void BaseApplication::ButtonHandler(AppEvent * aEvent)
515515
SILABS_LOG("Network is already provisioned, Ble advertisement not enabled");
516516
#if CHIP_CONFIG_ENABLE_ICD_SERVER
517517
// Temporarily claim network activity, until we implement a "user trigger" reason for ICD wakeups.
518-
PlatformMgr().LockChipStack();
519-
ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
520-
PlatformMgr().UnlockChipStack();
518+
PlatformMgr().ScheduleWork([](intptr_t) { ICDNotifier::GetInstance().NotifyNetworkActivityNotification(); });
521519
#endif // CHIP_CONFIG_ENABLE_ICD_SERVER
522520
}
523521
}

src/app/icd/server/ICDManager.cpp

+15-8
Original file line numberDiff line numberDiff line change
@@ -378,12 +378,13 @@ void ICDManager::UpdateICDMode()
378378
if (ICDConfigurationData::GetInstance().GetICDMode() != tempMode)
379379
{
380380
ICDConfigurationData::GetInstance().SetICDMode(tempMode);
381-
postObserverEvent(ObserverEventType::ICDModeChange);
382381

383382
// Can't use attribute accessors/Attributes::OperatingMode::Set in unit tests
384383
#if !CONFIG_BUILD_FOR_HOST_UNIT_TEST
385384
Attributes::OperatingMode::Set(kRootEndpointId, static_cast<OperatingModeEnum>(tempMode));
386385
#endif
386+
387+
postObserverEvent(ObserverEventType::ICDModeChange);
387388
}
388389

389390
// When in SIT mode, the slow poll interval SHOULDN'T be greater than the SIT mode polling threshold, per spec.
@@ -433,6 +434,8 @@ void ICDManager::UpdateOperationState(OperationalState state)
433434
{
434435
ChipLogError(AppServer, "Failed to set Slow Polling Interval: err %" CHIP_ERROR_FORMAT, err.Format());
435436
}
437+
438+
postObserverEvent(ObserverEventType::EnterIdleMode);
436439
}
437440
else if (state == OperationalState::ActiveMode)
438441
{
@@ -447,17 +450,22 @@ void ICDManager::UpdateOperationState(OperationalState state)
447450

448451
if (activeModeDuration == kZero && !mKeepActiveFlags.HasAny())
449452
{
450-
// A Network Activity triggered the active mode and activeModeDuration is 0.
453+
// Network Activity triggered the active mode and activeModeDuration is 0.
451454
// Stay active for at least Active Mode Threshold.
452455
activeModeDuration = ICDConfigurationData::GetInstance().GetActiveModeThreshold();
453456
}
454457

455458
DeviceLayer::SystemLayer().StartTimer(activeModeDuration, OnActiveModeDone, this);
456459

457460
Milliseconds32 activeModeJitterInterval = Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS);
461+
// TODO(#33074): Edge case when we transition to IdleMode with this condition being true
462+
// (activeModeDuration == kZero && !mKeepActiveFlags.HasAny())
458463
activeModeJitterInterval =
459464
(activeModeDuration >= activeModeJitterInterval) ? activeModeDuration - activeModeJitterInterval : kZero;
460465

466+
// Reset this flag when we enter ActiveMode to avoid having a feedback loop that keeps us indefinitly in
467+
// ActiveMode.
468+
mTransitionToIdleCalled = false;
461469
DeviceLayer::SystemLayer().StartTimer(activeModeJitterInterval, OnTransitionToIdle, this);
462470

463471
CHIP_ERROR err =
@@ -504,10 +512,6 @@ void ICDManager::OnIdleModeDone(System::Layer * aLayer, void * appState)
504512
{
505513
ICDManager * pICDManager = reinterpret_cast<ICDManager *>(appState);
506514
pICDManager->UpdateOperationState(OperationalState::ActiveMode);
507-
508-
// We only reset this flag when idle mode is complete to avoid re-triggering the check when an event brings us back to active,
509-
// which could cause a loop.
510-
pICDManager->mTransitionToIdleCalled = false;
511515
}
512516

513517
void ICDManager::OnActiveModeDone(System::Layer * aLayer, void * appState)
@@ -532,10 +536,10 @@ void ICDManager::OnTransitionToIdle(System::Layer * aLayer, void * appState)
532536
}
533537

534538
/* ICDListener functions. */
539+
535540
void ICDManager::OnKeepActiveRequest(KeepActiveFlags request)
536541
{
537542
assertChipStackLockedByCurrentThread();
538-
539543
VerifyOrReturn(request < KeepActiveFlagsValues::kInvalidFlag);
540544

541545
if (request.Has(KeepActiveFlag::kExchangeContextOpen))
@@ -560,7 +564,6 @@ void ICDManager::OnKeepActiveRequest(KeepActiveFlags request)
560564
void ICDManager::OnActiveRequestWithdrawal(KeepActiveFlags request)
561565
{
562566
assertChipStackLockedByCurrentThread();
563-
564567
VerifyOrReturn(request < KeepActiveFlagsValues::kInvalidFlag);
565568

566569
if (request.Has(KeepActiveFlag::kExchangeContextOpen))
@@ -697,6 +700,10 @@ void ICDManager::postObserverEvent(ObserverEventType event)
697700
obs->mObserver->OnEnterActiveMode();
698701
return Loop::Continue;
699702
}
703+
case ObserverEventType::EnterIdleMode: {
704+
obs->mObserver->OnEnterIdleMode();
705+
return Loop::Continue;
706+
}
700707
case ObserverEventType::TransitionToIdle: {
701708
obs->mObserver->OnTransitionToIdle();
702709
return Loop::Continue;

src/app/icd/server/ICDManager.h

+126-37
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ class TestICDManager;
5757
class ICDManager : public ICDListener, public TestEventTriggerHandler
5858
{
5959
public:
60-
// This structure is used for the creation an ObjectPool of ICDStateObserver pointers
60+
/**
61+
* @brief This structure is used for the creation an ObjectPool of ICDStateObserver pointers
62+
*/
6163
struct ObserverPointer
6264
{
6365
ObserverPointer(ICDStateObserver * obs) : mObserver(obs) {}
@@ -71,11 +73,31 @@ class ICDManager : public ICDListener, public TestEventTriggerHandler
7173
ActiveMode,
7274
};
7375

74-
// This enum class represents to all ICDStateObserver callbacks available from the
75-
// mStateObserverPool for the ICDManager.
76+
/**
77+
* @brief This enum class represents to all ICDStateObserver callbacks available from the
78+
* mStateObserverPool for the ICDManager.
79+
*
80+
* EnterActiveMode, TransitionToIdle and EnterIdleMode will always be called as a trio in the same order.
81+
* Each event will only be called once per cycle.
82+
* EnterActiveMode will always be called first, when the ICD has transitioned to ActiveMode.
83+
* TransitionToIdle will always be second. This event will only be called the first time there is
84+
* `ICD_ACTIVE_TIME_JITTER_MS` remaining to the ActiveMode timer.
85+
* When this event is called, the ICD is still in ActiveMode.
86+
* If the ActiveMode timer is increased due to the TransitionToIdle event, the event will not be called a second time in
87+
* a given cycle.
88+
* OnEnterIdleMode will always the third when the ICD has transitioned to IdleMode.
89+
*
90+
* The ICDModeChange event can occur independently from the EnterActiveMode, TransitionToIdle and EnterIdleMode.
91+
* It will typpically hapen at the ICDManager init when a client is already registered with the ICD before the
92+
* OnEnterIdleMode event or when a client send a register command after the OnEnterActiveMode event. Nothing prevents the
93+
* ICDModeChange event to happen multiple times per cycle or while the ICD is in IdleMode.
94+
*
95+
* See src/app/icd/server/ICDStateObserver.h for more information on the APIs each event triggers
96+
*/
7697
enum class ObserverEventType : uint8_t
7798
{
7899
EnterActiveMode,
100+
EnterIdleMode,
79101
TransitionToIdle,
80102
ICDModeChange,
81103
};
@@ -85,22 +107,31 @@ class ICDManager : public ICDListener, public TestEventTriggerHandler
85107
* This type can be used to implement specific verifiers that can be used in the CheckInMessagesWouldBeSent function.
86108
* The goal is to avoid having multiple functions that implement the iterator loop with only the check changing.
87109
*
88-
* @return true if at least one Check-In message would be sent
89-
* false No Check-In messages would be sent
110+
* @return true: if at least one Check-In message would be sent
111+
* false: No Check-In messages would be sent
90112
*/
91-
92113
using ShouldCheckInMsgsBeSentFunction = bool(FabricIndex aFabricIndex, NodeId subjectID);
93114

94-
ICDManager() {}
115+
ICDManager() = default;
116+
~ICDManager() = default;
117+
95118
void Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, Crypto::SymmetricKeystore * symmetricKeyStore,
96-
Messaging::ExchangeManager * exchangeManager, SubscriptionsInfoProvider * manager);
119+
Messaging::ExchangeManager * exchangeManager, SubscriptionsInfoProvider * subInfoProvider);
97120
void Shutdown();
98-
void UpdateICDMode();
99-
void UpdateOperationState(OperationalState state);
100-
void SetKeepActiveModeRequirements(KeepActiveFlags flag, bool state);
101-
bool IsKeepActive() { return mKeepActiveFlags.HasAny(); }
121+
122+
/**
123+
* @brief SupportsFeature verifies if a given FeatureMap bit is enabled
124+
*
125+
* @param[in] feature FeatureMap bit to verify
126+
*
127+
* @return true: if the FeatureMap bit is enabled in the ICDM cluster attribute.
128+
* false: if the FeatureMap bit is not enabled in the ICDM cluster attribute.
129+
* if we failed to read the FeatureMap attribute.
130+
*/
102131
bool SupportsFeature(Clusters::IcdManagement::Feature feature);
132+
103133
ICDConfigurationData::ICDMode GetICDMode() { return ICDConfigurationData::GetInstance().GetICDMode(); };
134+
104135
/**
105136
* @brief Adds the referenced observer in parameters to the mStateObserverPool
106137
* A maximum of CHIP_CONFIG_ICD_OBSERVERS_POOL_SIZE observers can be concurrently registered
@@ -111,36 +142,29 @@ class ICDManager : public ICDListener, public TestEventTriggerHandler
111142

112143
/**
113144
* @brief Remove the referenced observer in parameters from the mStateObserverPool
145+
* If the observer is not present in the object pool, we do nothing
114146
*/
115147
void ReleaseObserver(ICDStateObserver * observer);
116148

117-
/**
118-
* @brief Associates the ObserverEventType parameters to the correct
119-
* ICDStateObservers function and calls it for all observers in the mStateObserverPool
120-
*/
121-
void postObserverEvent(ObserverEventType event);
122-
OperationalState GetOperationalState() { return mOperationalState; }
123-
124149
/**
125150
* @brief Ensures that the remaining Active Mode duration is at least the smaller of 30000 milliseconds and stayActiveDuration.
126151
*
127-
* @param stayActiveDuration The duration (in milliseconds) requested by the client to stay in Active Mode
152+
* @param[in] stayActiveDuration The duration (in milliseconds) requested by the client to stay in Active Mode
128153
* @return The duration (in milliseconds) the device will stay in Active Mode
129154
*/
130155
uint32_t StayActiveRequest(uint32_t stayActiveDuration);
131156

132157
/**
133158
* @brief TestEventTriggerHandler for the ICD feature set
134159
*
135-
* @param eventTrigger Event trigger to handle.
160+
* @param[in] eventTrigger Event trigger to handle.
161+
*
136162
* @return CHIP_ERROR CHIP_NO_ERROR - No erros during the processing
137163
* CHIP_ERROR_INVALID_ARGUMENT - eventTrigger isn't a valid value
138164
*/
139165
CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override;
140166

141167
#if CHIP_CONFIG_ENABLE_ICD_CIP
142-
void SendCheckInMsgs();
143-
144168
/**
145169
* @brief Trigger the ICDManager to send Check-In message if necessary
146170
*
@@ -160,47 +184,108 @@ class ICDManager : public ICDListener, public TestEventTriggerHandler
160184

161185
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
162186
void SetTestFeatureMapValue(uint32_t featureMap) { mFeatureMap = featureMap; };
163-
#if !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
187+
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
164188
bool GetIsBootUpResumeSubscriptionExecuted() { return mIsBootUpResumeSubscriptionExecuted; };
165189
#endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
166190
#endif
167191

168192
// Implementation of ICDListener functions.
169193
// Callers must origin from the chip task context or hold the ChipStack lock.
194+
170195
void OnNetworkActivity() override;
171196
void OnKeepActiveRequest(KeepActiveFlags request) override;
172197
void OnActiveRequestWithdrawal(KeepActiveFlags request) override;
173198
void OnICDManagementServerEvent(ICDManagementEvents event) override;
174199
void OnSubscriptionReport() override;
175200

176-
protected:
201+
private:
177202
friend class TestICDManager;
203+
/**
204+
* @brief UpdateICDMode evaluates in which mode the ICD can be in; SIT or LIT mode.
205+
* If the current operating mode does not match the evaluated operating mode, function updates the ICDMode and triggers
206+
* all necessary operations.
207+
* For a SIT ICD, this function does nothing.
208+
* For a LIT ICD, the function checks if the ICD has a registration in the ICDMonitoringTable to determine which ICDMode
209+
* the ICD must be in.
210+
*/
211+
void UpdateICDMode();
178212

179213
/**
180-
* @brief Hepler function that extends the Active Mode duration as well as the Active Mode Jitter timer for the transition to
181-
* iddle mode.
214+
* @brief UpdateOperationState updates the OperationState of the ICD to the requested one.
215+
* IdleMode -> IdleMode : No actions are necessary, do nothing.
216+
* IdleMode -> ActiveMode : Transition the device to ActiveMode, start the ActiveMode timer and trigger all necessary
217+
* operations. These operations could be : Send Check-In messages
218+
* Send subscription reports
219+
* Process user actions
220+
* ActiveMode -> ActiveMode : Increase remaining ActiveMode timer to one ActiveModeThreshold.
221+
* If ActiveModeThreshold is 0, do nothing.
222+
* ActiveMode -> IdleMode : Transition ICD to IdleMode and start the IdleMode timer.
223+
*
224+
* @param state requested OperationalState for the ICD to transition to
225+
*/
226+
void UpdateOperationState(OperationalState state);
227+
228+
/**
229+
* @brief Set or Remove a keep ActiveMode requirement for the given flag
230+
* If state is true and the ICD is in IdleMode, transition the ICD to ActiveMode
231+
* If state is false and the ICD is in ActiveMode, check whether we can transition the ICD to IdleMode.
232+
* If we can, transition the ICD to IdleMode.
233+
*
234+
* @param flag KeepActiveFlag to remove or add
235+
* @param state true: adding a flag requirement
236+
* false: removing a flag requirement
237+
*/
238+
void SetKeepActiveModeRequirements(KeepActiveFlags flag, bool state);
239+
240+
/**
241+
* @brief Associates the ObserverEventType parameters to the correct
242+
* ICDStateObservers function and calls it for all observers in the mStateObserverPool
243+
*/
244+
void postObserverEvent(ObserverEventType event);
245+
246+
/**
247+
* @brief Hepler function that extends the ActiveMode timer as well as the Active Mode Jitter timer for the transition to
248+
* idle mode event.
182249
*/
183250
void ExtendActiveMode(System::Clock::Milliseconds16 extendDuration);
184251

252+
/**
253+
* @brief Timer callback function for when the IdleMode timer expires
254+
*
255+
* @param appState pointer to the ICDManager
256+
*/
185257
static void OnIdleModeDone(System::Layer * aLayer, void * appState);
258+
259+
/**
260+
* @brief Timer callback function for when the ActiveMode timer expires
261+
*
262+
* @param appState pointer to the ICDManager
263+
*/
186264
static void OnActiveModeDone(System::Layer * aLayer, void * appState);
187265

188266
/**
189-
* @brief Callback function called shortly before the device enters idle mode to allow checks to be made. This is currently only
190-
* called once to prevent entering in a loop if some events re-trigger this check (for instance if a check for subscription
191-
* before entering idle mode leads to emiting a report, we will re-enter UpdateOperationState and check again for subscription,
192-
* etc.)
267+
* @brief Timer Callback function called shortly before the device enters idle mode to allow checks to be made.
268+
* This is currently only called once to prevent entering in a loop if some events re-trigger this check (for instance if
269+
* a check for subscriptions before entering idle mode leads to emiting a report, we will re-enter UpdateOperationState
270+
* and check again for subscription, etc.)
271+
*
272+
* @param appState pointer to the ICDManager
193273
*/
194274
static void OnTransitionToIdle(System::Layer * aLayer, void * appState);
195275

196276
#if CHIP_CONFIG_ENABLE_ICD_CIP
197-
uint8_t mCheckInRequestCount = 0;
198-
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
199-
200-
uint8_t mOpenExchangeContextCount = 0;
277+
/**
278+
* @brief Function triggers all necessary Check-In messages to be sent.
279+
*
280+
* @note For each ICDMonitoring entry, we check if should send a Check-In message with
281+
* ShouldCheckInMsgsBeSentAtActiveModeFunction. If we should, we allocate an ICDCheckInSender which tries to send a
282+
* Check-In message to the registered client.
283+
*/
284+
void SendCheckInMsgs();
201285

202-
private:
203-
#if CHIP_CONFIG_ENABLE_ICD_CIP
286+
/**
287+
* @brief See function implementation in .cpp for details on this function.
288+
*/
204289
bool ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID);
205290

206291
/**
@@ -221,11 +306,15 @@ class ICDManager : public ICDListener, public TestEventTriggerHandler
221306
OperationalState mOperationalState = OperationalState::ActiveMode;
222307
bool mTransitionToIdleCalled = false;
223308
ObjectPool<ObserverPointer, CHIP_CONFIG_ICD_OBSERVERS_POOL_SIZE> mStateObserverPool;
309+
uint8_t mOpenExchangeContextCount = 0;
224310

225311
#if CHIP_CONFIG_ENABLE_ICD_CIP
312+
uint8_t mCheckInRequestCount = 0;
313+
226314
#if !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
227315
bool mIsBootUpResumeSubscriptionExecuted = false;
228316
#endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
317+
229318
PersistentStorageDelegate * mStorage = nullptr;
230319
FabricTable * mFabricTable = nullptr;
231320
Messaging::ExchangeManager * mExchangeManager = nullptr;

0 commit comments

Comments
 (0)