Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2023 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #include <app-common/zap-generated/attributes/Accessors.h>
19 : #include <app-common/zap-generated/ids/Attributes.h>
20 : #include <app-common/zap-generated/ids/Clusters.h>
21 : #include <app/icd/server/ICDConfigurationData.h>
22 : #include <app/icd/server/ICDManager.h>
23 : #include <app/icd/server/ICDServerConfig.h>
24 : #include <lib/core/ClusterEnums.h>
25 : #include <lib/support/CodeUtils.h>
26 : #include <lib/support/logging/CHIPLogging.h>
27 : #include <platform/ConnectivityManager.h>
28 : #include <platform/LockTracker.h>
29 : #include <platform/internal/CHIPDeviceLayerInternal.h>
30 :
31 : namespace {
32 : enum class ICDTestEventTriggerEvent : uint64_t
33 : {
34 : kAddActiveModeReq = 0x0046'0000'00000001,
35 : kRemoveActiveModeReq = 0x0046'0000'00000002,
36 : kInvalidateHalfCounterValues = 0x0046'0000'00000003,
37 : kInvalidateAllCounterValues = 0x0046'0000'00000004,
38 : kForceMaximumCheckInBackOffState = 0x0046'0000'00000005,
39 : kDSLSForceSitMode = 0x0046'0000'00000006,
40 : kDSLSWithdrawSitMode = 0x0046'0000'00000007,
41 : };
42 : } // namespace
43 :
44 : namespace chip {
45 : namespace app {
46 :
47 : using namespace chip::app;
48 : using namespace chip::app::Clusters;
49 : using namespace chip::app::Clusters::IcdManagement;
50 : using namespace System::Clock;
51 :
52 : using chip::Protocols::InteractionModel::Status;
53 :
54 : static_assert(UINT8_MAX >= CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS,
55 : "ICDManager::mOpenExchangeContextCount cannot hold count for the max exchange count");
56 :
57 10 : void ICDManager::Init()
58 : {
59 : #if CHIP_CONFIG_ENABLE_ICD_CIP
60 : VerifyOrDie(mStorage != nullptr);
61 : VerifyOrDie(mFabricTable != nullptr);
62 : VerifyOrDie(mSymmetricKeystore != nullptr);
63 : VerifyOrDie(mExchangeManager != nullptr);
64 : VerifyOrDie(mSubInfoProvider != nullptr);
65 : VerifyOrDie(mICDCheckInBackOffStrategy != nullptr);
66 :
67 : VerifyOrDie(ICDConfigurationData::GetInstance().GetICDCounter().Init(mStorage, DefaultStorageKeyAllocator::ICDCheckInCounter(),
68 : ICDConfigurationData::kICDCounterPersistenceIncrement) ==
69 : CHIP_NO_ERROR);
70 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
71 :
72 : #if CHIP_CONFIG_ENABLE_ICD_LIT
73 : // LIT ICD Verification Checks
74 : if (SupportsFeature(Feature::kLongIdleTimeSupport))
75 : {
76 : VerifyOrDieWithMsg(SupportsFeature(Feature::kCheckInProtocolSupport), AppServer,
77 : "The CheckIn protocol feature is required for LIT support.");
78 : VerifyOrDieWithMsg(SupportsFeature(Feature::kUserActiveModeTrigger), AppServer,
79 : "The user ActiveMode trigger feature is required for LIT support.");
80 : VerifyOrDieWithMsg(ICDConfigurationData::GetInstance().GetMinLitActiveModeThreshold() <=
81 : ICDConfigurationData::GetInstance().GetActiveModeThreshold(),
82 : AppServer, "The minimum ActiveModeThreshold value for a LIT ICD is 5 seconds.");
83 : }
84 : #endif // CHIP_CONFIG_ENABLE_ICD_LIT
85 :
86 10 : VerifyOrDie(ICDNotifier::GetInstance().Subscribe(this) == CHIP_NO_ERROR);
87 :
88 10 : UpdateICDMode();
89 10 : UpdateOperationState(OperationalState::IdleMode);
90 10 : }
91 :
92 10 : void ICDManager::Shutdown()
93 : {
94 10 : ICDNotifier::GetInstance().Unsubscribe(this);
95 :
96 : // cancel any running timer of the icd
97 10 : DeviceLayer::SystemLayer().CancelTimer(OnIdleModeDone, this);
98 10 : DeviceLayer::SystemLayer().CancelTimer(OnActiveModeDone, this);
99 10 : DeviceLayer::SystemLayer().CancelTimer(OnTransitionToIdle, this);
100 :
101 10 : ICDConfigurationData::GetInstance().SetICDMode(ICDConfigurationData::ICDMode::SIT);
102 10 : mOperationalState = OperationalState::ActiveMode;
103 10 : mStateObserverPool.ReleaseAll();
104 :
105 : #if CHIP_CONFIG_ENABLE_ICD_CIP
106 : mStorage = nullptr;
107 : mFabricTable = nullptr;
108 : mSubInfoProvider = nullptr;
109 : mICDSenderPool.ReleaseAll();
110 :
111 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
112 : mIsBootUpResumeSubscriptionExecuted = false;
113 : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
114 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
115 10 : }
116 :
117 0 : bool ICDManager::SupportsFeature(Feature feature)
118 : {
119 0 : return ICDConfigurationData::GetInstance().GetFeatureMap().Has(feature);
120 : }
121 :
122 4 : uint32_t ICDManager::StayActiveRequest(uint32_t stayActiveDuration)
123 : {
124 : // This should only be called when the device is in ActiveMode
125 4 : VerifyOrReturnValue(mOperationalState == OperationalState::ActiveMode, 0);
126 :
127 : uint32_t promisedActiveDuration =
128 4 : std::min(ICDConfigurationData::GetInstance().GetGuaranteedStayActiveDuration().count(), stayActiveDuration);
129 :
130 : // If the device is already in ActiveMode, we need to extend the active mode duration
131 : // for whichever is smallest between 30000 milliseconds and stayActiveDuration, taking in account the remaining active time.
132 4 : ExtendActiveMode(System::Clock::Milliseconds16(promisedActiveDuration));
133 4 : promisedActiveDuration = DeviceLayer::SystemLayer().GetRemainingTime(OnActiveModeDone, this).count();
134 :
135 4 : return promisedActiveDuration;
136 : }
137 :
138 : #if CHIP_CONFIG_ENABLE_ICD_CIP
139 : void ICDManager::SendCheckInMsgs()
140 : {
141 : #if !(CONFIG_BUILD_FOR_HOST_UNIT_TEST)
142 : VerifyOrDie(mStorage != nullptr);
143 : VerifyOrDie(mFabricTable != nullptr);
144 :
145 : uint32_t counterValue = ICDConfigurationData::GetInstance().GetICDCounter().GetNextCheckInCounterValue();
146 : bool counterIncremented = false;
147 :
148 : for (const auto & fabricInfo : *mFabricTable)
149 : {
150 : uint16_t supported_clients = ICDConfigurationData::GetInstance().GetClientsSupportedPerFabric();
151 :
152 : ICDMonitoringTable table(*mStorage, fabricInfo.GetFabricIndex(), supported_clients /*Table entry limit*/,
153 : mSymmetricKeystore);
154 :
155 : if (table.IsEmpty())
156 : {
157 : continue;
158 : }
159 :
160 : for (uint16_t i = 0; i < table.Limit(); i++)
161 : {
162 : ICDMonitoringEntry entry(mSymmetricKeystore);
163 : CHIP_ERROR err = table.Get(i, entry);
164 : if (err == CHIP_ERROR_NOT_FOUND)
165 : {
166 : break;
167 : }
168 :
169 : if (err != CHIP_NO_ERROR)
170 : {
171 : // Try to fetch the next entry upon failure (should not happen).
172 : ChipLogError(AppServer, "Failed to retrieved ICDMonitoring entry for Check-In msg, will try next entry.");
173 : continue;
174 : }
175 :
176 : if (!ShouldCheckInMsgsBeSentAtActiveModeFunction(entry.fabricIndex, entry.monitoredSubject))
177 : {
178 : continue;
179 : }
180 :
181 : if (!mICDCheckInBackOffStrategy->ShouldSendCheckInMessage(entry))
182 : {
183 : // continue to next entry
184 : continue;
185 : }
186 :
187 : // Increment counter only once to prevent depletion of the available range.
188 : if (!counterIncremented)
189 : {
190 : counterIncremented = true;
191 :
192 : if (CHIP_NO_ERROR != ICDConfigurationData::GetInstance().GetICDCounter().Advance())
193 : {
194 : ChipLogError(AppServer, "Incremented ICDCounter but failed to access/save to Persistent storage");
195 : }
196 : }
197 :
198 : // SenderPool will be released upon transition from active to idle state
199 : // This will happen when all ICD Check-In messages are sent on the network
200 : ICDCheckInSender * sender = mICDSenderPool.CreateObject(mExchangeManager);
201 : VerifyOrReturn(sender != nullptr, ChipLogError(AppServer, "Failed to allocate ICDCheckinSender"));
202 :
203 : if (CHIP_NO_ERROR != sender->RequestResolve(entry, mFabricTable, counterValue))
204 : {
205 : ChipLogError(AppServer, "Failed to send ICD Check-In");
206 : }
207 : }
208 : }
209 : #endif // !(CONFIG_BUILD_FOR_HOST_UNIT_TEST)
210 : }
211 :
212 : bool ICDManager::CheckInMessagesWouldBeSent(const std::function<ShouldCheckInMsgsBeSentFunction> & shouldCheckInMsgsBeSentFunction)
213 : {
214 : VerifyOrReturnValue(shouldCheckInMsgsBeSentFunction, false);
215 :
216 : for (const auto & fabricInfo : *mFabricTable)
217 : {
218 : uint16_t supported_clients = ICDConfigurationData::GetInstance().GetClientsSupportedPerFabric();
219 :
220 : ICDMonitoringTable table(*mStorage, fabricInfo.GetFabricIndex(), supported_clients /*Table entry limit*/,
221 : mSymmetricKeystore);
222 : if (table.IsEmpty())
223 : {
224 : continue;
225 : }
226 :
227 : for (uint16_t i = 0; i < table.Limit(); i++)
228 : {
229 : ICDMonitoringEntry entry(mSymmetricKeystore);
230 : CHIP_ERROR err = table.Get(i, entry);
231 : if (err == CHIP_ERROR_NOT_FOUND)
232 : {
233 : break;
234 : }
235 :
236 : if (err != CHIP_NO_ERROR)
237 : {
238 : // Try to fetch the next entry upon failure (should not happen).
239 : ChipLogError(AppServer, "Failed to retrieved ICDMonitoring entry, will try next entry.");
240 : continue;
241 : }
242 :
243 : if (entry.clientType == ClientTypeEnum::kEphemeral)
244 : {
245 : // If the registered client is ephemeral, no Check-In message would be sent to this client
246 : continue;
247 : }
248 :
249 : // At least one registration would require a Check-In message
250 : VerifyOrReturnValue(!shouldCheckInMsgsBeSentFunction(entry.fabricIndex, entry.monitoredSubject), true);
251 : }
252 : }
253 :
254 : // None of the registrations would require a Check-In message
255 : return false;
256 : }
257 :
258 : /**
259 : * ShouldCheckInMsgsBeSentAtActiveModeFunction is used to determine if a Check-In message is required for a given registration.
260 : * Due to how the ICD Check-In use-case interacts with the persistent subscription and subscription timeout resumption features,
261 : * having a single implementation of the function renders the implementation very difficult to understand and maintain.
262 : * Because of this, each valid feature combination has its own implementation of the function.
263 : */
264 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
265 : #if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
266 : /**
267 : * @brief Implementation for when the persistent subscription and subscription timeout resumption feature are present.
268 : * Function checks that there are no active or persisted subscriptions for a given fabricIndex or subjectID.
269 : *
270 : * @note When the persistent subscription and subscription timeout resumption feature are present, we need to check for
271 : * persisted subscription at each transition to ActiveMode since there will be persisted subscriptions during normal
272 : * operation for the subscription timeout resumption feature. Once we have finished all our subscription resumption attempts
273 : * for a given subscription, the entry is deleted from persisted storage which will enable us to send Check-In messages for
274 : * the client registration. This logic avoids the device sending a Check-In message while trying to resume subscriptions.
275 : *
276 : * @param aFabricIndex
277 : * @param subjectID subjectID to check. Can be an operational node id or a CAT
278 : *
279 : * @return true Returns true if the fabricIndex and subjectId combination does not have an active or a persisted subscription.
280 : * @return false Returns false if the fabricIndex and subjectId combination has an active or persisted subscription.
281 : */
282 : bool ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID)
283 : {
284 : return !(mSubInfoProvider->SubjectHasActiveSubscription(aFabricIndex, subjectID) ||
285 : mSubInfoProvider->SubjectHasPersistedSubscription(aFabricIndex, subjectID));
286 : }
287 : #else
288 : /**
289 : * @brief Implementation for when the persistent subscription feature is present without the subscription timeout resumption
290 : * feature. Function checks that there are no active subscriptions. If the boot up subscription resumption has not been completed,
291 : * function also checks if there are persisted subscriptions.
292 : *
293 : * @note The persistent subscriptions feature tries to resume subscriptions at the highest min interval
294 : * of all the persisted subscriptions. As such, it is possible for the ICD to return to Idle Mode
295 : * until the timer elaspses. We do not want to send Check-In messages to clients with persisted subscriptions
296 : * until we have tried to resubscribe.
297 : *
298 : * @param aFabricIndex
299 : * @param subjectID subjectID to check. Can be an opperationnal node id or a CAT
300 : *
301 : * @return true Returns true if the fabricIndex and subjectId combination does not have an active subscription.
302 : * If the boot up subscription resumption has not been completed, there must not be a persisted subscription either.
303 : * @return false Returns false if the fabricIndex and subjectId combination has an active subscription.
304 : * If the boot up subscription resumption has not been completed,
305 : * returns false if the fabricIndex and subjectId combination has a persisted subscription.
306 : */
307 : bool ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID)
308 : {
309 : bool mightHaveSubscription = mSubInfoProvider->SubjectHasActiveSubscription(aFabricIndex, subjectID);
310 : if (!mightHaveSubscription && !mIsBootUpResumeSubscriptionExecuted)
311 : {
312 : mightHaveSubscription = mSubInfoProvider->SubjectHasPersistedSubscription(aFabricIndex, subjectID);
313 : }
314 :
315 : return !mightHaveSubscription;
316 : }
317 : #endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
318 : #else
319 : /**
320 : * @brief Implementation for when neither the persistent subscription nor the subscription timeout resumption features are present.
321 : * Function checks that there no active sbuscriptions for a given fabricIndex and subjectId combination.
322 : *
323 : * @note When neither the persistent subscription nor the subscription timeout resumption features are present, we only need to
324 : * check for active subscription since we will never have any persisted subscription.
325 : *
326 : * @param aFabricIndex
327 : * @param subjectID subjectID to check. Can be an opperationnal node id or a CAT
328 : *
329 : * @return true Returns true if the fabricIndex and subjectId combination does not have an active subscription.
330 : * @return false Returns false if the fabricIndex and subjectId combination has an active subscription.
331 : */
332 : bool ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID)
333 : {
334 : return !(mSubInfoProvider->SubjectHasActiveSubscription(aFabricIndex, subjectID));
335 : }
336 : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
337 :
338 : void ICDManager::TriggerCheckInMessages(const std::function<ShouldCheckInMsgsBeSentFunction> & verifier)
339 : {
340 : VerifyOrReturn(SupportsFeature(Feature::kCheckInProtocolSupport));
341 :
342 : // Only trigger Check-In messages when we are in IdleMode.
343 : // If we are already in ActiveMode, Check-In messages have already been sent.
344 : VerifyOrReturn(mOperationalState == OperationalState::IdleMode);
345 :
346 : // If we don't have any Check-In messages to send, do nothing
347 : VerifyOrReturn(CheckInMessagesWouldBeSent(verifier));
348 : UpdateOperationState(OperationalState::ActiveMode);
349 : }
350 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
351 :
352 10 : void ICDManager::UpdateICDMode()
353 : {
354 10 : assertChipStackLockedByCurrentThread();
355 :
356 10 : ICDConfigurationData::ICDMode tempMode = ICDConfigurationData::ICDMode::SIT;
357 :
358 : #if CHIP_CONFIG_ENABLE_ICD_LIT
359 : // Device can only switch to the LIT operating mode if LIT support is present
360 : if (SupportsFeature(Feature::kLongIdleTimeSupport))
361 : {
362 : #if CHIP_CONFIG_ENABLE_ICD_DSLS
363 : // Ensure SIT mode is not requested
364 : if (SupportsFeature(Feature::kDynamicSitLitSupport) && !mSITModeRequested)
365 : {
366 : #endif // CHIP_CONFIG_ENABLE_ICD_DSLS
367 :
368 : VerifyOrDie(mStorage != nullptr);
369 : VerifyOrDie(mFabricTable != nullptr);
370 : // We can only get to LIT Mode, if at least one client is registered with the ICD device
371 : for (const auto & fabricInfo : *mFabricTable)
372 : {
373 : // We only need 1 valid entry to ensure LIT compliance
374 : ICDMonitoringTable table(*mStorage, fabricInfo.GetFabricIndex(), 1 /*Table entry limit*/, mSymmetricKeystore);
375 : if (!table.IsEmpty())
376 : {
377 : tempMode = ICDConfigurationData::ICDMode::LIT;
378 : break;
379 : }
380 : }
381 : #if CHIP_CONFIG_ENABLE_ICD_DSLS
382 : }
383 : #endif // CHIP_CONFIG_ENABLE_ICD_DSLS
384 : }
385 : #endif // CHIP_CONFIG_ENABLE_ICD_LIT
386 :
387 10 : if (ICDConfigurationData::GetInstance().GetICDMode() != tempMode)
388 : {
389 0 : ICDConfigurationData::GetInstance().SetICDMode(tempMode);
390 0 : postObserverEvent(ObserverEventType::ICDModeChange);
391 : }
392 :
393 : // When in SIT mode, the slow poll interval SHOULDN'T be greater than the SIT mode polling threshold, per spec.
394 20 : if (ICDConfigurationData::GetInstance().GetICDMode() == ICDConfigurationData::ICDMode::SIT &&
395 20 : ICDConfigurationData::GetInstance().GetSlowPollingInterval() > ICDConfigurationData::GetInstance().GetSITPollingThreshold())
396 : {
397 0 : ChipLogDetail(AppServer, "The Slow Polling Interval of an ICD in SIT mode should be <= %" PRIu32 " seconds",
398 : (ICDConfigurationData::GetInstance().GetSITPollingThreshold().count() / 1000));
399 : }
400 10 : }
401 :
402 43 : void ICDManager::UpdateOperationState(OperationalState state)
403 : {
404 43 : assertChipStackLockedByCurrentThread();
405 : // Active mode can be re-triggered.
406 43 : VerifyOrReturn(mOperationalState != state || state == OperationalState::ActiveMode);
407 :
408 43 : if (state == OperationalState::IdleMode)
409 : {
410 23 : mOperationalState = OperationalState::IdleMode;
411 :
412 : #if CHIP_CONFIG_ENABLE_ICD_CIP
413 : std::function<ShouldCheckInMsgsBeSentFunction> sendCheckInMessagesOnActiveMode =
414 : std::bind(&ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction, this, std::placeholders::_1, std::placeholders::_2);
415 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
416 :
417 : // When the active mode interval is 0, we stay in idleMode until a notification brings the icd into active mode
418 : // unless the device would need to send Check-In messages
419 23 : if (ICDConfigurationData::GetInstance().GetActiveModeDuration() > kZero
420 : #if CHIP_CONFIG_ENABLE_ICD_CIP
421 : || CheckInMessagesWouldBeSent(sendCheckInMessagesOnActiveMode)
422 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
423 : )
424 : {
425 22 : DeviceLayer::SystemLayer().StartTimer(ICDConfigurationData::GetInstance().GetIdleModeDuration(), OnIdleModeDone, this);
426 : }
427 :
428 23 : Milliseconds32 slowPollInterval = ICDConfigurationData::GetInstance().GetSlowPollingInterval();
429 :
430 : #if CHIP_CONFIG_ENABLE_ICD_CIP
431 : // Going back to Idle, all Check-In messages are sent
432 : mICDSenderPool.ReleaseAll();
433 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
434 :
435 23 : CHIP_ERROR err = DeviceLayer::ConnectivityMgr().SetPollingInterval(slowPollInterval);
436 23 : if (err != CHIP_NO_ERROR)
437 : {
438 23 : ChipLogError(AppServer, "Failed to set Slow Polling Interval: err %" CHIP_ERROR_FORMAT, err.Format());
439 : }
440 :
441 23 : postObserverEvent(ObserverEventType::EnterIdleMode);
442 : }
443 20 : else if (state == OperationalState::ActiveMode)
444 : {
445 20 : if (mOperationalState == OperationalState::IdleMode)
446 : {
447 : // An event could have brought us to the active mode.
448 : // Make sure the idle mode timer is stopped
449 16 : DeviceLayer::SystemLayer().CancelTimer(OnIdleModeDone, this);
450 :
451 16 : mOperationalState = OperationalState::ActiveMode;
452 16 : Milliseconds32 activeModeDuration = ICDConfigurationData::GetInstance().GetActiveModeDuration();
453 :
454 16 : if (activeModeDuration == kZero && !mKeepActiveFlags.HasAny())
455 : {
456 : // Network Activity triggered the active mode and activeModeDuration is 0.
457 : // Stay active for at least Active Mode Threshold.
458 1 : activeModeDuration = ICDConfigurationData::GetInstance().GetActiveModeThreshold();
459 : }
460 :
461 16 : DeviceLayer::SystemLayer().StartTimer(activeModeDuration, OnActiveModeDone, this);
462 :
463 16 : Milliseconds32 activeModeJitterInterval = Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS);
464 : // TODO(#33074): Edge case when we transition to IdleMode with this condition being true
465 : // (activeModeDuration == kZero && !mKeepActiveFlags.HasAny())
466 16 : activeModeJitterInterval =
467 32 : (activeModeDuration >= activeModeJitterInterval) ? activeModeDuration - activeModeJitterInterval : kZero;
468 :
469 : // Reset this flag when we enter ActiveMode to avoid having a feedback loop that keeps us indefinitly in
470 : // ActiveMode.
471 16 : mTransitionToIdleCalled = false;
472 16 : DeviceLayer::SystemLayer().StartTimer(activeModeJitterInterval, OnTransitionToIdle, this);
473 :
474 : CHIP_ERROR err =
475 16 : DeviceLayer::ConnectivityMgr().SetPollingInterval(ICDConfigurationData::GetInstance().GetFastPollingInterval());
476 16 : if (err != CHIP_NO_ERROR)
477 : {
478 16 : ChipLogError(AppServer, "Failed to set Fast Polling Interval: err %" CHIP_ERROR_FORMAT, err.Format());
479 : }
480 :
481 : #if CHIP_CONFIG_ENABLE_ICD_CIP
482 : if (SupportsFeature(Feature::kCheckInProtocolSupport))
483 : {
484 : SendCheckInMsgs();
485 : }
486 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
487 :
488 16 : postObserverEvent(ObserverEventType::EnterActiveMode);
489 : }
490 : else
491 : {
492 4 : ExtendActiveMode(ICDConfigurationData::GetInstance().GetActiveModeThreshold());
493 : }
494 : }
495 : }
496 :
497 10 : void ICDManager::SetKeepActiveModeRequirements(KeepActiveFlags flag, bool state)
498 : {
499 10 : assertChipStackLockedByCurrentThread();
500 :
501 10 : mKeepActiveFlags.Set(flag, state);
502 10 : if (mOperationalState == OperationalState::IdleMode && mKeepActiveFlags.HasAny())
503 : {
504 4 : UpdateOperationState(OperationalState::ActiveMode);
505 : }
506 10 : else if (mOperationalState == OperationalState::ActiveMode && !mKeepActiveFlags.HasAny() &&
507 4 : !DeviceLayer::SystemLayer().IsTimerActive(OnActiveModeDone, this))
508 : {
509 : // The normal active period had ended and nothing else requires the system to be active.
510 3 : UpdateOperationState(OperationalState::IdleMode);
511 : }
512 10 : }
513 :
514 9 : void ICDManager::OnIdleModeDone(System::Layer * aLayer, void * appState)
515 : {
516 9 : ICDManager * pICDManager = reinterpret_cast<ICDManager *>(appState);
517 9 : pICDManager->UpdateOperationState(OperationalState::ActiveMode);
518 9 : }
519 :
520 13 : void ICDManager::OnActiveModeDone(System::Layer * aLayer, void * appState)
521 : {
522 13 : ICDManager * pICDManager = reinterpret_cast<ICDManager *>(appState);
523 :
524 : // Don't go to idle mode when we have a keep active requirement
525 13 : if (!pICDManager->mKeepActiveFlags.HasAny())
526 : {
527 10 : pICDManager->UpdateOperationState(OperationalState::IdleMode);
528 : }
529 13 : }
530 :
531 15 : void ICDManager::OnTransitionToIdle(System::Layer * aLayer, void * appState)
532 : {
533 15 : ICDManager * pICDManager = reinterpret_cast<ICDManager *>(appState);
534 :
535 : // OnTransitionToIdle will trigger a report message if reporting is needed, which should extend the active mode until the
536 : // ack for the report is received.
537 15 : pICDManager->mTransitionToIdleCalled = true;
538 15 : pICDManager->postObserverEvent(ObserverEventType::TransitionToIdle);
539 15 : }
540 :
541 : /* ICDListener functions. */
542 :
543 4 : void ICDManager::OnKeepActiveRequest(KeepActiveFlags request)
544 : {
545 4 : assertChipStackLockedByCurrentThread();
546 4 : VerifyOrReturn(request < KeepActiveFlagsValues::kInvalidFlag);
547 :
548 4 : if (request.Has(KeepActiveFlag::kExchangeContextOpen))
549 : {
550 : // There can be multiple open exchange contexts at the same time.
551 : // Keep track of the requests count.
552 1 : this->mOpenExchangeContextCount++;
553 : }
554 :
555 : #if CHIP_CONFIG_ENABLE_ICD_CIP
556 : if (request.Has(KeepActiveFlag::kCheckInInProgress))
557 : {
558 : // There can be multiple check-in at the same time.
559 : // Keep track of the requests count.
560 : this->mCheckInRequestCount++;
561 : }
562 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
563 :
564 4 : this->SetKeepActiveModeRequirements(request, true /* state */);
565 : }
566 :
567 4 : void ICDManager::OnActiveRequestWithdrawal(KeepActiveFlags request)
568 : {
569 4 : assertChipStackLockedByCurrentThread();
570 4 : VerifyOrReturn(request < KeepActiveFlagsValues::kInvalidFlag);
571 :
572 4 : if (request.Has(KeepActiveFlag::kExchangeContextOpen))
573 : {
574 : // There can be multiple open exchange contexts at the same time.
575 : // Keep track of the requests count.
576 1 : if (this->mOpenExchangeContextCount > 0)
577 : {
578 1 : this->mOpenExchangeContextCount--;
579 : }
580 : else
581 : {
582 0 : ChipLogError(DeviceLayer, "The ICD Manager did not account for ExchangeContext closure");
583 : }
584 :
585 1 : if (this->mOpenExchangeContextCount == 0)
586 : {
587 1 : this->SetKeepActiveModeRequirements(KeepActiveFlag::kExchangeContextOpen, false /* state */);
588 : }
589 : }
590 :
591 : #if CHIP_CONFIG_ENABLE_ICD_CIP
592 : if (request.Has(KeepActiveFlag::kCheckInInProgress))
593 : {
594 : // There can be multiple open exchange contexts at the same time.
595 : // Keep track of the requests count.
596 : if (this->mCheckInRequestCount > 0)
597 : {
598 : this->mCheckInRequestCount--;
599 : }
600 : else
601 : {
602 : ChipLogError(DeviceLayer, "The ICD Manager did not account for Check-In Sender start");
603 : }
604 :
605 : if (this->mCheckInRequestCount == 0)
606 : {
607 : this->SetKeepActiveModeRequirements(KeepActiveFlag::kCheckInInProgress, false /* state */);
608 : }
609 : }
610 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
611 :
612 4 : if (request.Has(KeepActiveFlag::kCommissioningWindowOpen) || request.Has(KeepActiveFlag::kFailSafeArmed))
613 : {
614 : // Only 1 request per type (kCommissioningWindowOpen, kFailSafeArmed)
615 : // remove requirement directly
616 3 : this->SetKeepActiveModeRequirements(request, false /* state */);
617 : }
618 : }
619 :
620 : #if CHIP_CONFIG_ENABLE_ICD_DSLS
621 : void ICDManager::OnSITModeRequest()
622 : {
623 : mSITModeRequested = true;
624 : this->UpdateICDMode();
625 : // Update the poll interval also to comply with SIT requirements
626 : UpdateOperationState(OperationalState::ActiveMode);
627 : }
628 :
629 : void ICDManager::OnSITModeRequestWithdrawal()
630 : {
631 : mSITModeRequested = false;
632 : this->UpdateICDMode();
633 : // Update the poll interval also to comply with LIT requirements
634 : UpdateOperationState(OperationalState::ActiveMode);
635 : }
636 : #endif // CHIP_CONFIG_ENABLE_ICD_DSLS
637 :
638 4 : void ICDManager::OnNetworkActivity()
639 : {
640 4 : this->UpdateOperationState(OperationalState::ActiveMode);
641 4 : }
642 :
643 0 : void ICDManager::OnICDManagementServerEvent(ICDManagementEvents event)
644 : {
645 0 : switch (event)
646 : {
647 0 : case ICDManagementEvents::kTableUpdated:
648 0 : this->UpdateICDMode();
649 0 : break;
650 0 : default:
651 0 : break;
652 : }
653 0 : }
654 :
655 3 : void ICDManager::OnSubscriptionReport()
656 : {
657 : // If the device is already in ActiveMode, that means that all active subscriptions have already been marked dirty.
658 : // Since we only mark them dirty when we enter ActiveMode, it is not necessary to update the operational state a second time.
659 : // Doing so will only add an ActiveModeThreshold to the active time which we don't want to do here.
660 3 : VerifyOrReturn(mOperationalState == OperationalState::IdleMode);
661 3 : this->UpdateOperationState(OperationalState::ActiveMode);
662 : }
663 :
664 8 : void ICDManager::ExtendActiveMode(Milliseconds16 extendDuration)
665 : {
666 8 : DeviceLayer::SystemLayer().ExtendTimerTo(extendDuration, OnActiveModeDone, this);
667 :
668 8 : Milliseconds32 activeModeJitterThreshold = Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS);
669 8 : activeModeJitterThreshold = (extendDuration >= activeModeJitterThreshold) ? extendDuration - activeModeJitterThreshold : kZero;
670 :
671 8 : if (!mTransitionToIdleCalled)
672 : {
673 1 : DeviceLayer::SystemLayer().ExtendTimerTo(activeModeJitterThreshold, OnTransitionToIdle, this);
674 : }
675 8 : }
676 :
677 2 : CHIP_ERROR ICDManager::HandleEventTrigger(uint64_t eventTrigger)
678 : {
679 2 : ICDTestEventTriggerEvent trigger = static_cast<ICDTestEventTriggerEvent>(eventTrigger);
680 2 : CHIP_ERROR err = CHIP_NO_ERROR;
681 :
682 2 : switch (trigger)
683 : {
684 1 : case ICDTestEventTriggerEvent::kAddActiveModeReq:
685 1 : SetKeepActiveModeRequirements(KeepActiveFlag::kTestEventTriggerActiveMode, true);
686 1 : break;
687 1 : case ICDTestEventTriggerEvent::kRemoveActiveModeReq:
688 1 : SetKeepActiveModeRequirements(KeepActiveFlag::kTestEventTriggerActiveMode, false);
689 1 : break;
690 : #if CHIP_CONFIG_ENABLE_ICD_CIP
691 : case ICDTestEventTriggerEvent::kInvalidateHalfCounterValues:
692 : err = ICDConfigurationData::GetInstance().GetICDCounter().InvalidateHalfCheckInCounterValues();
693 : break;
694 : case ICDTestEventTriggerEvent::kInvalidateAllCounterValues:
695 : err = ICDConfigurationData::GetInstance().GetICDCounter().InvalidateAllCheckInCounterValues();
696 : break;
697 : case ICDTestEventTriggerEvent::kForceMaximumCheckInBackOffState:
698 : err = mICDCheckInBackOffStrategy->ForceMaximumCheckInBackoff();
699 : break;
700 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
701 : #if CHIP_CONFIG_ENABLE_ICD_DSLS
702 : case ICDTestEventTriggerEvent::kDSLSForceSitMode:
703 : OnSITModeRequest();
704 : break;
705 : case ICDTestEventTriggerEvent::kDSLSWithdrawSitMode:
706 : OnSITModeRequestWithdrawal();
707 : break;
708 : #endif // CHIP_CONFIG_ENABLE_ICD_DSLS
709 0 : default:
710 0 : err = CHIP_ERROR_INVALID_ARGUMENT;
711 0 : break;
712 : }
713 :
714 2 : return err;
715 : }
716 :
717 10 : ICDManager::ObserverPointer * ICDManager::RegisterObserver(ICDStateObserver * observer)
718 : {
719 10 : return mStateObserverPool.CreateObject(observer);
720 : }
721 :
722 0 : void ICDManager::ReleaseObserver(ICDStateObserver * observer)
723 : {
724 0 : mStateObserverPool.ForEachActiveObject([this, observer](ObserverPointer * obs) {
725 0 : if (obs->mObserver == observer)
726 : {
727 0 : mStateObserverPool.ReleaseObject(obs);
728 0 : return Loop::Break;
729 : }
730 0 : return Loop::Continue;
731 : });
732 0 : }
733 :
734 54 : void ICDManager::postObserverEvent(ObserverEventType event)
735 : {
736 54 : mStateObserverPool.ForEachActiveObject([event](ObserverPointer * obs) {
737 54 : switch (event)
738 : {
739 16 : case ObserverEventType::EnterActiveMode: {
740 16 : obs->mObserver->OnEnterActiveMode();
741 16 : return Loop::Continue;
742 : }
743 23 : case ObserverEventType::EnterIdleMode: {
744 23 : obs->mObserver->OnEnterIdleMode();
745 23 : return Loop::Continue;
746 : }
747 15 : case ObserverEventType::TransitionToIdle: {
748 15 : obs->mObserver->OnTransitionToIdle();
749 15 : return Loop::Continue;
750 : }
751 0 : case ObserverEventType::ICDModeChange: {
752 0 : obs->mObserver->OnICDModeChange();
753 0 : return Loop::Continue;
754 : }
755 0 : default: {
756 0 : ChipLogError(DeviceLayer, "Invalid ICD Observer event type");
757 0 : return Loop::Break;
758 : }
759 : }
760 : });
761 54 : }
762 :
763 : } // namespace app
764 : } // namespace chip
|