Skip to content

Commit f065566

Browse files
Implementation of StayActive request with test
1 parent 11ea431 commit f065566

13 files changed

+185
-38
lines changed

src/app/clusters/icd-management-server/icd-management-server.cpp

+9-12
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,6 @@ void ICDManagementServer::TriggerICDMTableUpdatedEvent()
364364

365365
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
366366

367-
Status ICDManagementServer::StayActiveRequest(FabricIndex fabricIndex)
368-
{
369-
// TODO: Implementent stay awake logic for end device
370-
// https://github.com/project-chip/connectedhomeip/issues/24259
371-
ICDNotifier::GetInstance().NotifyICDManagementEvent(ICDListener::ICDManagementEvents::kStayActiveRequestReceived);
372-
return InteractionModel::Status::UnsupportedCommand;
373-
}
374-
375367
void ICDManagementServer::Init(PersistentStorageDelegate & storage, Crypto::SymmetricKeystore * symmetricKeystore,
376368
ICDConfigurationData & icdConfigurationData)
377369
{
@@ -433,10 +425,15 @@ bool emberAfIcdManagementClusterUnregisterClientCallback(CommandHandler * comman
433425
bool emberAfIcdManagementClusterStayActiveRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
434426
const Commands::StayActiveRequest::DecodableType & commandData)
435427
{
436-
ICDManagementServer server;
437-
InteractionModel::Status status = server.StayActiveRequest(commandObj->GetAccessingFabricIndex());
438-
439-
commandObj->AddStatus(commandPath, status);
428+
#if CHIP_CONFIG_ENABLE_ICD_SERVER
429+
IcdManagement::Commands::StayActiveResponse::Type response;
430+
response.promisedActiveDuration = Server::GetInstance().GetICDManager().StayActiveRequest(commandData.stayActiveDuration);
431+
commandObj->AddResponse(commandPath, response);
432+
#else
433+
// Note: We only keep this line around for platforms examples that enable the ICD management server without building the sample
434+
// as an ICD.
435+
commandObj->AddStatus(commandPath, InteractionModel::Status::UnsupportedCommand);
436+
#endif // CHIP_CONFIG_ENABLE_ICD_SERVER
440437
return true;
441438
}
442439

src/app/clusters/icd-management-server/icd-management-server.h

-2
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,6 @@ class ICDManagementServer
6868
const chip::app::Clusters::IcdManagement::Commands::UnregisterClient::DecodableType & commandData);
6969
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
7070

71-
chip::Protocols::InteractionModel::Status StayActiveRequest(chip::FabricIndex fabricIndex);
72-
7371
private:
7472
#if CHIP_CONFIG_ENABLE_ICD_CIP
7573
/**

src/app/icd/server/ICDConfigurationData.h

+4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class ICDConfigurationData
6161

6262
System::Clock::Milliseconds16 GetActiveModeThreshold() { return mActiveThreshold; }
6363

64+
System::Clock::Milliseconds32 GetMaxStayActiveDuration() { return kMaxStayActiveDuration; }
65+
6466
Protocols::SecureChannel::CheckInCounter & GetICDCounter() { return mICDCounter; }
6567

6668
uint16_t GetClientsSupportedPerFabric() { return mFabricClientsSupported; }
@@ -123,6 +125,8 @@ class ICDConfigurationData
123125

124126
static constexpr System::Clock::Seconds32 kMaxIdleModeDuration = System::Clock::Seconds32(18 * kSecondsPerHour);
125127
static constexpr System::Clock::Seconds32 kMinIdleModeDuration = System::Clock::Seconds32(1);
128+
// As defined in the spec, the maximum duration for the StayActiveDuration is 30s (spec 7.5.1)
129+
static constexpr System::Clock::Milliseconds32 kMaxStayActiveDuration = System::Clock::Milliseconds32(30000);
126130

127131
static_assert((CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC) <= kMaxIdleModeDuration.count(),
128132
"Spec requires the IdleModeDuration to be equal or inferior to 64800s.");

src/app/icd/server/ICDManager.cpp

+30-16
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,22 @@ bool ICDManager::SupportsFeature(Feature feature)
120120
#endif // !CONFIG_BUILD_FOR_HOST_UNIT_TEST
121121
}
122122

123+
uint32_t ICDManager::StayActiveRequest(uint32_t stayActiveDuration)
124+
{
125+
// This should only be called when the device is in ActiveMode
126+
VerifyOrReturnValue(mOperationalState == OperationalState::ActiveMode, 0);
127+
128+
uint32_t promisedActiveDuration =
129+
std::min(static_cast<uint32_t>(ICDConfigurationData::GetInstance().GetMaxStayActiveDuration().count()), stayActiveDuration);
130+
131+
// If the device is already in ActiveMode, we need to extend the active mode duration
132+
// for whichever is smallest between 30000 milliseconds and stayActiveDuration, taking in account the remaining active time.
133+
ExtendActiveMode(System::Clock::Milliseconds16(promisedActiveDuration));
134+
promisedActiveDuration = DeviceLayer::SystemLayer().GetRemainingTime(OnActiveModeDone, this).count();
135+
136+
return promisedActiveDuration;
137+
}
138+
123139
#if CHIP_CONFIG_ENABLE_ICD_CIP
124140
void ICDManager::SendCheckInMsgs()
125141
{
@@ -366,17 +382,7 @@ void ICDManager::UpdateOperationState(OperationalState state)
366382
}
367383
else
368384
{
369-
Milliseconds16 activeModeThreshold = ICDConfigurationData::GetInstance().GetActiveModeThreshold();
370-
DeviceLayer::SystemLayer().ExtendTimerTo(activeModeThreshold, OnActiveModeDone, this);
371-
372-
Milliseconds32 activeModeJitterThreshold = Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS);
373-
activeModeJitterThreshold =
374-
(activeModeThreshold >= activeModeJitterThreshold) ? activeModeThreshold - activeModeJitterThreshold : kZero;
375-
376-
if (!mTransitionToIdleCalled)
377-
{
378-
DeviceLayer::SystemLayer().ExtendTimerTo(activeModeJitterThreshold, OnTransitionToIdle, this);
379-
}
385+
ExtendActiveMode(ICDConfigurationData::GetInstance().GetActiveModeThreshold());
380386
}
381387
}
382388
}
@@ -521,11 +527,6 @@ void ICDManager::OnICDManagementServerEvent(ICDManagementEvents event)
521527
case ICDManagementEvents::kTableUpdated:
522528
this->UpdateICDMode();
523529
break;
524-
525-
case ICDManagementEvents::kStayActiveRequestReceived:
526-
// TODO : Implement the StayActiveRequest
527-
// https://github.com/project-chip/connectedhomeip/issues/24259
528-
break;
529530
default:
530531
break;
531532
}
@@ -540,6 +541,19 @@ void ICDManager::OnSubscriptionReport()
540541
this->UpdateOperationState(OperationalState::ActiveMode);
541542
}
542543

544+
void ICDManager::ExtendActiveMode(Milliseconds16 extendDuration)
545+
{
546+
DeviceLayer::SystemLayer().ExtendTimerTo(extendDuration, OnActiveModeDone, this);
547+
548+
Milliseconds32 activeModeJitterThreshold = Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS);
549+
activeModeJitterThreshold = (extendDuration >= activeModeJitterThreshold) ? extendDuration - activeModeJitterThreshold : kZero;
550+
551+
if (!mTransitionToIdleCalled)
552+
{
553+
DeviceLayer::SystemLayer().ExtendTimerTo(activeModeJitterThreshold, OnTransitionToIdle, this);
554+
}
555+
}
556+
543557
ICDManager::ObserverPointer * ICDManager::RegisterObserver(ICDStateObserver * observer)
544558
{
545559
return mStateObserverPool.CreateObject(observer);

src/app/icd/server/ICDManager.h

+18-1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ class ICDManager : public ICDListener
109109
void postObserverEvent(ObserverEventType event);
110110
OperationalState GetOperationalState() { return mOperationalState; }
111111

112+
/**
113+
* @brief Extends the Active Mode duration for whichever is smallest between 30000 milliseconds and stayActiveDuration, taking
114+
* in account the remaining active time.
115+
* @param stayActiveDuration The duration (in milliseconds) requested by the client to stay in Active Mode
116+
* @return The duration (in milliseconds) the device will stay in Active Mode
117+
*/
118+
uint32_t StayActiveRequest(uint32_t stayActiveDuration);
119+
112120
#if CHIP_CONFIG_ENABLE_ICD_CIP
113121
void SendCheckInMsgs();
114122

@@ -119,7 +127,10 @@ class ICDManager : public ICDListener
119127
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
120128

121129
#ifdef CONFIG_BUILD_FOR_HOST_UNIT_TEST
122-
void SetTestFeatureMapValue(uint32_t featureMap) { mFeatureMap = featureMap; };
130+
void SetTestFeatureMapValue(uint32_t featureMap)
131+
{
132+
mFeatureMap = featureMap;
133+
};
123134
#endif
124135

125136
// Implementation of ICDListener functions.
@@ -131,6 +142,12 @@ class ICDManager : public ICDListener
131142
void OnSubscriptionReport() override;
132143

133144
protected:
145+
/**
146+
* @brief Hepler function that extends the Active Mode duration by the extendDuration parameter
147+
* as well as the Active Mode Jitter timer for the transition to iddle mode.
148+
*/
149+
void ExtendActiveMode(System::Clock::Milliseconds16 extendDuration);
150+
134151
friend class TestICDManager;
135152

136153
static void OnIdleModeDone(System::Layer * aLayer, void * appState);

src/app/icd/server/ICDNotifier.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ class ICDListener
4848

4949
enum class ICDManagementEvents : uint8_t
5050
{
51-
kTableUpdated = 0x01,
52-
kStayActiveRequestReceived = 0x02,
51+
kTableUpdated = 0x01,
5352
};
5453

5554
using KeepActiveFlags = BitFlags<KeepActiveFlagsValues>;

src/app/tests/TestICDManager.cpp

+76
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,82 @@ class TestICDManager
540540
// After the init we should be in Idle mode
541541
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
542542
}
543+
544+
/* Test that verifies the logic of the ICDManager when it receives a StayActiveRequest*/
545+
static void TestICDMStayActive(nlTestSuite * aSuite, void * aContext)
546+
{
547+
TestContext * ctx = static_cast<TestContext *>(aContext);
548+
ICDNotifier notifier = ICDNotifier::GetInstance();
549+
550+
// Verify That ICDManager starts in Idle
551+
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
552+
553+
// Trigger a subscription report Put the ICD manager into active mode
554+
notifier.NotifySubscriptionReport();
555+
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
556+
557+
// Advance time by the duration of the stay active request - 1
558+
AdvanceClockAndRunEventLoop(ctx, icdConfigData.GetActiveModeDuration() - 1_ms);
559+
// Confirm ICD manager is in active mode
560+
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
561+
562+
uint32_t stayActiveRequestedMs = 20000;
563+
// Send a stay active request for 20 seconds
564+
uint32_t stayActivePromisedMs = ctx->mICDManager.StayActiveRequest(stayActiveRequestedMs);
565+
// confirm the promised time is the same as the requested time
566+
NL_TEST_ASSERT(aSuite, stayActivePromisedMs == stayActiveRequestedMs);
567+
568+
// Advance time by the duration of the stay stayActiveRequestedMs - 1 ms
569+
AdvanceClockAndRunEventLoop(ctx, System::Clock::Milliseconds32(stayActiveRequestedMs) - 1_ms);
570+
// Confirm ICD manager is in active mode
571+
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
572+
573+
// Advance time by 1ms and Confirm ICD manager is in idle mode
574+
AdvanceClockAndRunEventLoop(ctx, 1_ms);
575+
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
576+
577+
// Trigger a subscription report Put the ICD manager into active mode
578+
notifier.NotifySubscriptionReport();
579+
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
580+
581+
// Advance time by the duration of the stay active request - 1 ms
582+
AdvanceClockAndRunEventLoop(ctx, icdConfigData.GetActiveModeDuration() - 1_ms);
583+
stayActiveRequestedMs = 35000;
584+
// Send a stay active request for 35 seconds, which is higher than the maximum stay active duration (30 seconds)
585+
stayActivePromisedMs = ctx->mICDManager.StayActiveRequest(stayActiveRequestedMs);
586+
// confirm the promised time is the maximum stay active duration (30 seconds)
587+
NL_TEST_ASSERT(aSuite, stayActivePromisedMs == 30000);
588+
589+
// Advance time by the duration of the max stay active duration - 1 ms
590+
AdvanceClockAndRunEventLoop(ctx, System::Clock::Milliseconds32(30000) - 1_ms);
591+
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
592+
593+
// Advance time by 1ms and Confirm ICD manager is in idle mode
594+
AdvanceClockAndRunEventLoop(ctx, 1_ms);
595+
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
596+
597+
// Trigger a subscription report Put the ICD manager into active mode
598+
notifier.NotifySubscriptionReport();
599+
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
600+
601+
// Advance time by the duration of the stay active request - 1 ms
602+
AdvanceClockAndRunEventLoop(ctx, icdConfigData.GetActiveModeDuration() - 1_ms);
603+
stayActiveRequestedMs = 30000;
604+
// Send a stay active request for 30 seconds
605+
stayActivePromisedMs = ctx->mICDManager.StayActiveRequest(stayActiveRequestedMs);
606+
// confirm the promised time is the same as the requested time
607+
NL_TEST_ASSERT(aSuite, stayActivePromisedMs == 30000);
608+
609+
// Advance time by the duration of the stay active request - 10000 ms
610+
AdvanceClockAndRunEventLoop(ctx, System::Clock::Milliseconds32(stayActiveRequestedMs) - 10000_ms);
611+
// Confirm ICD manager is in active mode, we should have 20000 seconds left at that point
612+
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
613+
614+
stayActiveRequestedMs = 10000;
615+
stayActivePromisedMs = ctx->mICDManager.StayActiveRequest(stayActiveRequestedMs);
616+
// confirm the promised time is 20000 since the device is already planing to stay active longer than the requested time
617+
NL_TEST_ASSERT(aSuite, stayActivePromisedMs == 20000);
618+
}
543619
};
544620

545621
} // namespace app

src/app/zap-templates/zcl/data-model/chip/icd-management-cluster.xml

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ limitations under the License.
111111
<command source="client" code="0x03" name="StayActiveRequest" response="StayActiveResponse" optional="true">
112112
<description>Request the end device to stay in Active Mode for an additional ActiveModeThreshold</description>
113113
<access op="invoke" privilege="manage"/>
114+
<arg name="StayActiveDuration" type="int32u" isNullable="false"/>
114115
</command>
115116

116117
<command source="server" code="0x04" name="StayActiveResponse" optional="true" disableDefaultResponse="true">

src/system/SystemLayer.h

+14
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ class DLL_EXPORT Layer
142142
* This method searches for the timer matching the provided parameters.
143143
* and returns whether it is still "running" and waiting to trigger or not.
144144
*
145+
* @note This is used to verify by how long the ExtendTimer method extends the timer, as it may ignore an extension request
146+
* if it is shorter than the current timer's remaining time.
147+
*
145148
* @param[in] onComplete A pointer to the function called when timer expires.
146149
* @param[in] appState A pointer to the application state object used when timer expires.
147150
*
@@ -150,6 +153,17 @@ class DLL_EXPORT Layer
150153
*/
151154
virtual bool IsTimerActive(TimerCompleteCallback onComplete, void * appState) = 0;
152155

156+
/**
157+
* @brief
158+
* This method searches for the timer matching the provided parameters
159+
* and returns the remaining time left before it expires.
160+
* @param[in] onComplete A pointer to the function called when timer expires.
161+
* @param[in] appState A pointer to the application state object used when timer expires.
162+
*
163+
* @return The remaining time left before the timer expires.
164+
*/
165+
virtual Clock::Timeout GetRemainingTime(TimerCompleteCallback onComplete, void * appState) = 0;
166+
153167
/**
154168
* @brief This method cancels a one-shot timer, started earlier through @p StartTimer(). This method must
155169
* be called while in the Matter context (from the Matter event loop, or while holding the Matter

src/system/SystemLayerImplFreeRTOS.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ bool LayerImplFreeRTOS::IsTimerActive(TimerCompleteCallback onComplete, void * a
9696
return (mTimerList.GetRemainingTime(onComplete, appState) > Clock::kZero);
9797
}
9898

99+
Clock::Timeout LayerImplFreeRTOS::GetRemainingTime(TimerCompleteCallback onComplete, void * appState)
100+
{
101+
return mTimerList.GetRemainingTime(onComplete, appState);
102+
}
103+
99104
void LayerImplFreeRTOS::CancelTimer(TimerCompleteCallback onComplete, void * appState)
100105
{
101106
assertChipStackLockedByCurrentThread();

src/system/SystemLayerImplFreeRTOS.h

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class LayerImplFreeRTOS : public LayerFreeRTOS
4242
CHIP_ERROR StartTimer(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) override;
4343
CHIP_ERROR ExtendTimerTo(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) override;
4444
bool IsTimerActive(TimerCompleteCallback onComplete, void * appState) override;
45+
Clock::Timeout GetRemainingTime(TimerCompleteCallback onComplete, void * appState) override;
4546
void CancelTimer(TimerCompleteCallback onComplete, void * appState) override;
4647
CHIP_ERROR ScheduleWork(TimerCompleteCallback onComplete, void * appState) override;
4748

src/system/SystemLayerImplSelect.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ bool LayerImplSelect::IsTimerActive(TimerCompleteCallback onComplete, void * app
255255
return timerIsActive;
256256
}
257257

258+
Clock::Timeout LayerImplSelect::GetRemainingTime(TimerCompleteCallback onComplete, void * appState)
259+
{
260+
return mTimerList.GetRemainingTime(onComplete, appState);
261+
}
262+
258263
void LayerImplSelect::CancelTimer(TimerCompleteCallback onComplete, void * appState)
259264
{
260265
assertChipStackLockedByCurrentThread();

src/system/SystemLayerImplSelect.h

+21-5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class LayerImplSelect : public LayerSocketsLoop
6565
CHIP_ERROR StartTimer(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) override;
6666
CHIP_ERROR ExtendTimerTo(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) override;
6767
bool IsTimerActive(TimerCompleteCallback onComplete, void * appState) override;
68+
Clock::Timeout GetRemainingTime(TimerCompleteCallback onComplete, void * appState) override;
6869
void CancelTimer(TimerCompleteCallback onComplete, void * appState) override;
6970
CHIP_ERROR ScheduleWork(TimerCompleteCallback onComplete, void * appState) override;
7071

@@ -87,18 +88,33 @@ class LayerImplSelect : public LayerSocketsLoop
8788
void EventLoopEnds() override {}
8889

8990
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
90-
void SetDispatchQueue(dispatch_queue_t dispatchQueue) override { mDispatchQueue = dispatchQueue; };
91-
dispatch_queue_t GetDispatchQueue() override { return mDispatchQueue; };
91+
void SetDispatchQueue(dispatch_queue_t dispatchQueue) override
92+
{
93+
mDispatchQueue = dispatchQueue;
94+
};
95+
dispatch_queue_t GetDispatchQueue() override
96+
{
97+
return mDispatchQueue;
98+
};
9299
void HandleTimerComplete(TimerList::Node * timer);
93100
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
94-
virtual void SetLibEvLoop(struct ev_loop * aLibEvLoopP) override { mLibEvLoopP = aLibEvLoopP; };
95-
virtual struct ev_loop * GetLibEvLoop() override { return mLibEvLoopP; };
101+
virtual void SetLibEvLoop(struct ev_loop * aLibEvLoopP) override
102+
{
103+
mLibEvLoopP = aLibEvLoopP;
104+
};
105+
virtual struct ev_loop * GetLibEvLoop() override
106+
{
107+
return mLibEvLoopP;
108+
};
96109
static void HandleLibEvTimer(EV_P_ struct ev_timer * t, int revents);
97110
static void HandleLibEvIoWatcher(EV_P_ struct ev_io * i, int revents);
98111
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV
99112

100113
// Expose the result of WaitForEvents() for non-blocking socket implementations.
101-
bool IsSelectResultValid() const { return mSelectResult >= 0; }
114+
bool IsSelectResultValid() const
115+
{
116+
return mSelectResult >= 0;
117+
}
102118

103119
protected:
104120
static SocketEvents SocketEventsFromFDs(int socket, const fd_set & readfds, const fd_set & writefds, const fd_set & exceptfds);

0 commit comments

Comments
 (0)