Matter SDK Coverage Report
Current view: top level - app/icd/server - ICDManager.cpp (source / functions) Coverage Total Hit
Test: SHA:2a48c1efeab1c0f76f3adb3a0940b0f7de706453 Lines: 81.9 % 166 136
Test Date: 2026-01-31 08:14:20 Functions: 81.8 % 22 18

            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 :     SuccessOrDie(ICDNotifier::GetInstance().Subscribe(this));
      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(Optional<Access::SubjectDescriptor> specificSubject)
     140              : {
     141              : #if !(CONFIG_BUILD_FOR_HOST_UNIT_TEST)
     142              :     VerifyOrDie(SupportsFeature(Feature::kCheckInProtocolSupport));
     143              :     VerifyOrDie(mStorage != nullptr);
     144              :     VerifyOrDie(mFabricTable != nullptr);
     145              : 
     146              :     uint32_t counterValue   = ICDConfigurationData::GetInstance().GetICDCounter().GetNextCheckInCounterValue();
     147              :     bool counterIncremented = false;
     148              : 
     149              :     for (const auto & fabricInfo : *mFabricTable)
     150              :     {
     151              :         uint16_t supported_clients = ICDConfigurationData::GetInstance().GetClientsSupportedPerFabric();
     152              : 
     153              :         ICDMonitoringTable table(*mStorage, fabricInfo.GetFabricIndex(), supported_clients /*Table entry limit*/,
     154              :                                  mSymmetricKeystore);
     155              : 
     156              :         if (table.IsEmpty())
     157              :         {
     158              :             continue;
     159              :         }
     160              : 
     161              :         for (uint16_t i = 0; i < table.Limit(); i++)
     162              :         {
     163              :             ICDMonitoringEntry entry(mSymmetricKeystore);
     164              :             CHIP_ERROR err = table.Get(i, entry);
     165              :             if (err == CHIP_ERROR_NOT_FOUND)
     166              :             {
     167              :                 break;
     168              :             }
     169              : 
     170              :             if (err != CHIP_NO_ERROR)
     171              :             {
     172              :                 // Try to fetch the next entry upon failure (should not happen).
     173              :                 ChipLogError(AppServer, "Failed to retrieved ICDMonitoring entry for Check-In msg, will try next entry.");
     174              :                 continue;
     175              :             }
     176              : 
     177              :             if (specificSubject.HasValue() && !ShouldSendCheckInMessageForSpecificSubject(entry, specificSubject.Value()))
     178              :             {
     179              :                 continue;
     180              :             }
     181              : 
     182              :             if (!specificSubject.HasValue() &&
     183              :                 !ShouldCheckInMsgsBeSentAtActiveModeFunction(entry.fabricIndex, entry.monitoredSubject))
     184              :             {
     185              :                 continue;
     186              :             }
     187              : 
     188              :             if (!mICDCheckInBackOffStrategy->ShouldSendCheckInMessage(entry))
     189              :             {
     190              :                 // continue to next entry
     191              :                 continue;
     192              :             }
     193              : 
     194              :             // Increment counter only once to prevent depletion of the available range.
     195              :             if (!counterIncremented)
     196              :             {
     197              :                 counterIncremented = true;
     198              : 
     199              :                 if (CHIP_NO_ERROR != ICDConfigurationData::GetInstance().GetICDCounter().Advance())
     200              :                 {
     201              :                     ChipLogError(AppServer, "Incremented ICDCounter but failed to access/save to Persistent storage");
     202              :                 }
     203              :             }
     204              : 
     205              :             // SenderPool will be released upon transition from active to idle state
     206              :             // This will happen when all ICD Check-In messages are sent on the network
     207              :             ICDCheckInSender * sender = mICDSenderPool.CreateObject(mExchangeManager);
     208              :             VerifyOrReturn(sender != nullptr, ChipLogError(AppServer, "Failed to allocate ICDCheckinSender"));
     209              : 
     210              :             if (CHIP_NO_ERROR != sender->RequestResolve(entry, mFabricTable, counterValue))
     211              :             {
     212              :                 ChipLogError(AppServer, "Failed to send ICD Check-In");
     213              :             }
     214              :         }
     215              :     }
     216              : #endif // !(CONFIG_BUILD_FOR_HOST_UNIT_TEST)
     217              : }
     218              : 
     219              : bool ICDManager::ShouldSendCheckInMessageForSpecificSubject(const ICDMonitoringEntry & entry,
     220              :                                                             const Access::SubjectDescriptor & specificSubject)
     221              : {
     222              :     if (specificSubject.fabricIndex != entry.fabricIndex)
     223              :     {
     224              :         return false;
     225              :     }
     226              : 
     227              :     if (specificSubject.cats.CheckSubjectAgainstCATs(entry.monitoredSubject) || entry.monitoredSubject == specificSubject.subject)
     228              :     {
     229              :         ChipLogProgress(AppServer, "Proceed to send Check-In msg for specific subject: " ChipLogFormatX64,
     230              :                         ChipLogValueX64(specificSubject.subject));
     231              :         return true;
     232              :     }
     233              : 
     234              :     return false;
     235              : }
     236              : 
     237              : bool ICDManager::CheckInMessagesWouldBeSent(const std::function<ShouldCheckInMsgsBeSentFunction> & shouldCheckInMsgsBeSentFunction)
     238              : {
     239              :     VerifyOrReturnValue(shouldCheckInMsgsBeSentFunction, false);
     240              : 
     241              :     for (const auto & fabricInfo : *mFabricTable)
     242              :     {
     243              :         uint16_t supported_clients = ICDConfigurationData::GetInstance().GetClientsSupportedPerFabric();
     244              : 
     245              :         ICDMonitoringTable table(*mStorage, fabricInfo.GetFabricIndex(), supported_clients /*Table entry limit*/,
     246              :                                  mSymmetricKeystore);
     247              :         if (table.IsEmpty())
     248              :         {
     249              :             continue;
     250              :         }
     251              : 
     252              :         for (uint16_t i = 0; i < table.Limit(); i++)
     253              :         {
     254              :             ICDMonitoringEntry entry(mSymmetricKeystore);
     255              :             CHIP_ERROR err = table.Get(i, entry);
     256              :             if (err == CHIP_ERROR_NOT_FOUND)
     257              :             {
     258              :                 break;
     259              :             }
     260              : 
     261              :             if (err != CHIP_NO_ERROR)
     262              :             {
     263              :                 // Try to fetch the next entry upon failure (should not happen).
     264              :                 ChipLogError(AppServer, "Failed to retrieved ICDMonitoring entry, will try next entry.");
     265              :                 continue;
     266              :             }
     267              : 
     268              :             if (entry.clientType == ClientTypeEnum::kEphemeral)
     269              :             {
     270              :                 // If the registered client is ephemeral, no Check-In message would be sent to this client
     271              :                 continue;
     272              :             }
     273              : 
     274              :             // At least one registration would require a Check-In message
     275              :             VerifyOrReturnValue(!shouldCheckInMsgsBeSentFunction(entry.fabricIndex, entry.monitoredSubject), true);
     276              :         }
     277              :     }
     278              : 
     279              :     // None of the registrations would require a Check-In message
     280              :     return false;
     281              : }
     282              : 
     283              : /**
     284              :  * ShouldCheckInMsgsBeSentAtActiveModeFunction is used to determine if a Check-In message is required for a given registration.
     285              :  * Due to how the ICD Check-In use-case interacts with the persistent subscription and subscription timeout resumption features,
     286              :  * having a single implementation of the function renders the implementation very difficult to understand and maintain.
     287              :  * Because of this, each valid feature combination has its own implementation of the function.
     288              :  */
     289              : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
     290              : #if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
     291              : /**
     292              :  * @brief Implementation for when the persistent subscription and subscription timeout resumption feature are present.
     293              :  *        Function checks that there are no active or persisted subscriptions for a given fabricIndex or subjectID.
     294              :  *
     295              :  * @note When the persistent subscription and subscription timeout resumption feature are present, we need to check for
     296              :  *       persisted subscription at each transition to ActiveMode since there will be persisted subscriptions during normal
     297              :  *       operation for the subscription timeout resumption feature. Once we have finished all our subscription resumption attempts
     298              :  *       for a given subscription, the entry is deleted from persisted storage which will enable us to send Check-In messages for
     299              :  *       the client registration. This logic avoids the device sending a Check-In message while trying to resume subscriptions.
     300              :  *
     301              :  * @param aFabricIndex
     302              :  * @param subjectID subjectID to check. Can be an operational node id or a CAT
     303              :  *
     304              :  * @return true Returns true if the fabricIndex and subjectId combination does not have an active or a persisted subscription.
     305              :  * @return false Returns false if the fabricIndex and subjectId combination has an active or persisted subscription.
     306              :  */
     307              : bool ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID)
     308              : {
     309              :     return !(mSubInfoProvider->SubjectHasActiveSubscription(aFabricIndex, subjectID) ||
     310              :              mSubInfoProvider->SubjectHasPersistedSubscription(aFabricIndex, subjectID));
     311              : }
     312              : #else
     313              : /**
     314              :  * @brief Implementation for when the persistent subscription feature is present without the subscription timeout resumption
     315              :  * feature. Function checks that there are no active subscriptions. If the boot up subscription resumption has not been completed,
     316              :  *        function also checks if there are persisted subscriptions.
     317              :  *
     318              :  * @note The persistent subscriptions feature tries to resume subscriptions at the highest min interval
     319              :  *       of all the persisted subscriptions. As such, it is possible for the ICD to return to Idle Mode
     320              :  *       until the timer elaspses. We do not want to send Check-In messages to clients with persisted subscriptions
     321              :  *       until we have tried to resubscribe.
     322              :  *
     323              :  * @param aFabricIndex
     324              :  * @param subjectID subjectID to check. Can be an opperationnal node id or a CAT
     325              :  *
     326              :  * @return true Returns true if the fabricIndex and subjectId combination does not have an active subscription.
     327              :  *              If the boot up subscription resumption has not been completed, there must not be a persisted subscription either.
     328              :  * @return false Returns false if the fabricIndex and subjectId combination has an active subscription.
     329              :  *               If the boot up subscription resumption has not been completed,
     330              :  *               returns false if the fabricIndex and subjectId combination has a persisted subscription.
     331              :  */
     332              : bool ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID)
     333              : {
     334              :     bool mightHaveSubscription = mSubInfoProvider->SubjectHasActiveSubscription(aFabricIndex, subjectID);
     335              :     if (!mightHaveSubscription && !mIsBootUpResumeSubscriptionExecuted)
     336              :     {
     337              :         mightHaveSubscription = mSubInfoProvider->SubjectHasPersistedSubscription(aFabricIndex, subjectID);
     338              :     }
     339              : 
     340              :     return !mightHaveSubscription;
     341              : }
     342              : #endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
     343              : #else
     344              : /**
     345              :  * @brief Implementation for when neither the persistent subscription nor the subscription timeout resumption features are present.
     346              :  *        Function checks that there no active sbuscriptions for a given fabricIndex and subjectId combination.
     347              :  *
     348              :  * @note When neither the persistent subscription nor the subscription timeout resumption features are present, we only need to
     349              :  *       check for active subscription since we will never have any persisted subscription.
     350              :  *
     351              :  * @param aFabricIndex
     352              :  * @param subjectID subjectID to check. Can be an opperationnal node id or a CAT
     353              :  *
     354              :  * @return true Returns true if the fabricIndex and subjectId combination does not have an active subscription.
     355              :  * @return false Returns false if the fabricIndex and subjectId combination has an active subscription.
     356              :  */
     357              : bool ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID)
     358              : {
     359              :     return !(mSubInfoProvider->SubjectHasActiveSubscription(aFabricIndex, subjectID));
     360              : }
     361              : #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
     362              : 
     363              : void ICDManager::TriggerCheckInMessages(const std::function<ShouldCheckInMsgsBeSentFunction> & verifier)
     364              : {
     365              :     VerifyOrReturn(SupportsFeature(Feature::kCheckInProtocolSupport));
     366              : 
     367              :     // Only trigger Check-In messages when we are in IdleMode.
     368              :     // If we are already in ActiveMode, Check-In messages have already been sent.
     369              :     VerifyOrReturn(mOperationalState == OperationalState::IdleMode);
     370              : 
     371              :     // If we don't have any Check-In messages to send, do nothing
     372              :     VerifyOrReturn(CheckInMessagesWouldBeSent(verifier));
     373              :     UpdateOperationState(OperationalState::ActiveMode);
     374              : }
     375              : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
     376              : 
     377           10 : void ICDManager::UpdateICDMode()
     378              : {
     379           10 :     assertChipStackLockedByCurrentThread();
     380              : 
     381           10 :     ICDConfigurationData::ICDMode tempMode = ICDConfigurationData::ICDMode::SIT;
     382              : 
     383              : #if CHIP_CONFIG_ENABLE_ICD_LIT
     384              :     // Device can only switch to the LIT operating mode if LIT support is present
     385              :     if (SupportsFeature(Feature::kLongIdleTimeSupport))
     386              :     {
     387              : #if CHIP_CONFIG_ENABLE_ICD_DSLS
     388              :         // Ensure SIT mode is not requested
     389              :         if (SupportsFeature(Feature::kDynamicSitLitSupport) && !mSITModeRequested)
     390              :         {
     391              : #endif // CHIP_CONFIG_ENABLE_ICD_DSLS
     392              : 
     393              :             VerifyOrDie(mStorage != nullptr);
     394              :             VerifyOrDie(mFabricTable != nullptr);
     395              :             // We can only get to LIT Mode, if at least one client is registered with the ICD device
     396              :             for (const auto & fabricInfo : *mFabricTable)
     397              :             {
     398              :                 // We only need 1 valid entry to ensure LIT compliance
     399              :                 ICDMonitoringTable table(*mStorage, fabricInfo.GetFabricIndex(), 1 /*Table entry limit*/, mSymmetricKeystore);
     400              :                 if (!table.IsEmpty())
     401              :                 {
     402              :                     tempMode = ICDConfigurationData::ICDMode::LIT;
     403              :                     break;
     404              :                 }
     405              :             }
     406              : #if CHIP_CONFIG_ENABLE_ICD_DSLS
     407              :         }
     408              : #endif // CHIP_CONFIG_ENABLE_ICD_DSLS
     409              :     }
     410              : #endif // CHIP_CONFIG_ENABLE_ICD_LIT
     411              : 
     412           10 :     if (ICDConfigurationData::GetInstance().GetICDMode() != tempMode)
     413              :     {
     414            0 :         ICDConfigurationData::GetInstance().SetICDMode(tempMode);
     415            0 :         postObserverEvent(ObserverEventType::ICDModeChange);
     416              :     }
     417              : 
     418              :     // When in SIT mode, the slow poll interval SHOULDN'T be greater than the SIT mode polling threshold, per spec.
     419           20 :     if (ICDConfigurationData::GetInstance().GetICDMode() == ICDConfigurationData::ICDMode::SIT &&
     420           20 :         ICDConfigurationData::GetInstance().GetSlowPollingInterval() > ICDConfigurationData::GetInstance().GetSITPollingThreshold())
     421              :     {
     422            0 :         ChipLogDetail(AppServer, "The Slow Polling Interval of an ICD in SIT mode should be <= %" PRIu32 " seconds",
     423              :                       (ICDConfigurationData::GetInstance().GetSITPollingThreshold().count() / 1000));
     424              :     }
     425           10 : }
     426              : 
     427           43 : void ICDManager::UpdateOperationState(OperationalState state)
     428              : {
     429           43 :     assertChipStackLockedByCurrentThread();
     430              :     // Active mode can be re-triggered.
     431           43 :     VerifyOrReturn(mOperationalState != state || state == OperationalState::ActiveMode);
     432              : 
     433           43 :     ICDConfigurationData & configData = ICDConfigurationData::GetInstance();
     434           43 :     if (state == OperationalState::IdleMode)
     435              :     {
     436           23 :         mOperationalState = OperationalState::IdleMode;
     437              : 
     438              : #if CHIP_CONFIG_ENABLE_ICD_CIP
     439              :         std::function<ShouldCheckInMsgsBeSentFunction> sendCheckInMessagesOnActiveMode =
     440              :             std::bind(&ICDManager::ShouldCheckInMsgsBeSentAtActiveModeFunction, this, std::placeholders::_1, std::placeholders::_2);
     441              : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
     442              : 
     443              :         // When the ActiveModeDuration is set to 0, the ICDManager does not need to periodically transition to active mode.
     444              :         // Instead, It can stay in idle mode until a notification, Report or other network event automatically toggles the ICD into
     445              :         // active mode. The following conditions will schedule a transition to Active Mode after the Idle Mode duration expires.
     446              :         // - An ActiveModeDuration interval must be respected.
     447              :         // - The device state indicates to shorten its idle duration and report faster to provide better responsiveness
     448              :         // - Check-In messages must be sent
     449           23 :         if (configData.GetActiveModeDuration() > kZero || configData.ShouldUseShortIdle()
     450              : #if CHIP_CONFIG_ENABLE_ICD_CIP
     451              :             || CheckInMessagesWouldBeSent(sendCheckInMessagesOnActiveMode)
     452              : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
     453              :         )
     454              :         {
     455           22 :             TEMPORARY_RETURN_IGNORED DeviceLayer::SystemLayer().StartTimer(configData.GetModeBasedIdleModeDuration(),
     456              :                                                                            OnIdleModeDone, this);
     457              :         }
     458              : 
     459              : #if CHIP_CONFIG_ENABLE_ICD_CIP
     460              :         // Going back to Idle, all Check-In messages are sent
     461              :         mICDSenderPool.ReleaseAll();
     462              : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
     463              : 
     464           23 :         CHIP_ERROR err = DeviceLayer::ConnectivityMgr().SetPollingInterval(configData.GetSlowPollingInterval());
     465           46 :         if (err != CHIP_NO_ERROR)
     466              :         {
     467           23 :             ChipLogError(AppServer, "Failed to set Slow Polling Interval: err %" CHIP_ERROR_FORMAT, err.Format());
     468              :         }
     469              : 
     470           23 :         postObserverEvent(ObserverEventType::EnterIdleMode);
     471              :     }
     472           20 :     else if (state == OperationalState::ActiveMode)
     473              :     {
     474           20 :         if (mOperationalState == OperationalState::IdleMode)
     475              :         {
     476              :             // An event could have brought us to the active mode.
     477              :             // Make sure the idle mode timer is stopped
     478           16 :             DeviceLayer::SystemLayer().CancelTimer(OnIdleModeDone, this);
     479              : 
     480           16 :             mOperationalState                 = OperationalState::ActiveMode;
     481           16 :             Milliseconds32 activeModeDuration = configData.GetActiveModeDuration();
     482              : 
     483           16 :             if (activeModeDuration == kZero && !mKeepActiveFlags.HasAny())
     484              :             {
     485              :                 // Network Activity triggered the active mode and activeModeDuration is 0.
     486              :                 // Stay active for at least Active Mode Threshold.
     487            1 :                 activeModeDuration = configData.GetActiveModeThreshold();
     488              :             }
     489              : 
     490           16 :             TEMPORARY_RETURN_IGNORED DeviceLayer::SystemLayer().StartTimer(activeModeDuration, OnActiveModeDone, this);
     491              : 
     492           16 :             Milliseconds32 activeModeJitterInterval = Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS);
     493              :             // TODO(#33074): Edge case when we transition to IdleMode with this condition being true
     494              :             // (activeModeDuration == kZero && !mKeepActiveFlags.HasAny())
     495           16 :             activeModeJitterInterval =
     496           32 :                 (activeModeDuration >= activeModeJitterInterval) ? activeModeDuration - activeModeJitterInterval : kZero;
     497              : 
     498              :             // Reset this flag when we enter ActiveMode to avoid having a feedback loop that keeps us indefinitly in
     499              :             // ActiveMode.
     500           16 :             mTransitionToIdleCalled = false;
     501           16 :             TEMPORARY_RETURN_IGNORED DeviceLayer::SystemLayer().StartTimer(activeModeJitterInterval, OnTransitionToIdle, this);
     502              : 
     503           16 :             CHIP_ERROR err = DeviceLayer::ConnectivityMgr().SetPollingInterval(configData.GetFastPollingInterval());
     504           32 :             if (err != CHIP_NO_ERROR)
     505              :             {
     506           16 :                 ChipLogError(AppServer, "Failed to set Fast Polling Interval: err %" CHIP_ERROR_FORMAT, err.Format());
     507              :             }
     508              : 
     509              : #if CHIP_CONFIG_ENABLE_ICD_CIP
     510              :             SendCheckInMsgs();
     511              : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
     512              : 
     513           16 :             postObserverEvent(ObserverEventType::EnterActiveMode);
     514              :         }
     515              :         else
     516              :         {
     517            4 :             ExtendActiveMode(configData.GetActiveModeThreshold());
     518              :         }
     519              :     }
     520              : }
     521              : 
     522           10 : void ICDManager::SetKeepActiveModeRequirements(KeepActiveFlags flag, bool state)
     523              : {
     524           10 :     assertChipStackLockedByCurrentThread();
     525              : 
     526           10 :     mKeepActiveFlags.Set(flag, state);
     527           10 :     if (mOperationalState == OperationalState::IdleMode && mKeepActiveFlags.HasAny())
     528              :     {
     529            4 :         UpdateOperationState(OperationalState::ActiveMode);
     530              :     }
     531           10 :     else if (mOperationalState == OperationalState::ActiveMode && !mKeepActiveFlags.HasAny() &&
     532            4 :              !DeviceLayer::SystemLayer().IsTimerActive(OnActiveModeDone, this))
     533              :     {
     534              :         // The normal active period had ended and nothing else requires the system to be active.
     535            3 :         UpdateOperationState(OperationalState::IdleMode);
     536              :     }
     537           10 : }
     538              : 
     539            9 : void ICDManager::OnIdleModeDone(System::Layer * aLayer, void * appState)
     540              : {
     541            9 :     ICDManager * pICDManager = reinterpret_cast<ICDManager *>(appState);
     542            9 :     pICDManager->UpdateOperationState(OperationalState::ActiveMode);
     543            9 : }
     544              : 
     545           13 : void ICDManager::OnActiveModeDone(System::Layer * aLayer, void * appState)
     546              : {
     547           13 :     ICDManager * pICDManager = reinterpret_cast<ICDManager *>(appState);
     548              : 
     549              :     // Don't go to idle mode when we have a keep active requirement
     550           13 :     if (!pICDManager->mKeepActiveFlags.HasAny())
     551              :     {
     552           10 :         pICDManager->UpdateOperationState(OperationalState::IdleMode);
     553              :     }
     554           13 : }
     555              : 
     556           15 : void ICDManager::OnTransitionToIdle(System::Layer * aLayer, void * appState)
     557              : {
     558           15 :     ICDManager * pICDManager = reinterpret_cast<ICDManager *>(appState);
     559              : 
     560              :     // OnTransitionToIdle will trigger a report message if reporting is needed, which should extend the active mode until the
     561              :     // ack for the report is received.
     562           15 :     pICDManager->mTransitionToIdleCalled = true;
     563           15 :     pICDManager->postObserverEvent(ObserverEventType::TransitionToIdle);
     564           15 : }
     565              : 
     566              : /* ICDListener functions. */
     567              : 
     568            4 : void ICDManager::OnKeepActiveRequest(KeepActiveFlags request)
     569              : {
     570            4 :     assertChipStackLockedByCurrentThread();
     571            4 :     VerifyOrReturn(request < KeepActiveFlagsValues::kInvalidFlag);
     572              : 
     573            4 :     if (request.Has(KeepActiveFlag::kExchangeContextOpen))
     574              :     {
     575              :         // There can be multiple open exchange contexts at the same time.
     576              :         // Keep track of the requests count.
     577            1 :         this->mOpenExchangeContextCount++;
     578              :     }
     579              : 
     580              : #if CHIP_CONFIG_ENABLE_ICD_CIP
     581              :     if (request.Has(KeepActiveFlag::kCheckInInProgress))
     582              :     {
     583              :         // There can be multiple check-in at the same time.
     584              :         // Keep track of the requests count.
     585              :         this->mCheckInRequestCount++;
     586              :     }
     587              : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
     588              : 
     589            4 :     this->SetKeepActiveModeRequirements(request, true /* state */);
     590              : }
     591              : 
     592            4 : void ICDManager::OnActiveRequestWithdrawal(KeepActiveFlags request)
     593              : {
     594            4 :     assertChipStackLockedByCurrentThread();
     595            4 :     VerifyOrReturn(request < KeepActiveFlagsValues::kInvalidFlag);
     596              : 
     597            4 :     if (request.Has(KeepActiveFlag::kExchangeContextOpen))
     598              :     {
     599              :         // There can be multiple open exchange contexts at the same time.
     600              :         // Keep track of the requests count.
     601            1 :         if (this->mOpenExchangeContextCount > 0)
     602              :         {
     603            1 :             this->mOpenExchangeContextCount--;
     604              :         }
     605              :         else
     606              :         {
     607            0 :             ChipLogError(DeviceLayer, "The ICD Manager did not account for ExchangeContext closure");
     608              :         }
     609              : 
     610            1 :         if (this->mOpenExchangeContextCount == 0)
     611              :         {
     612            1 :             this->SetKeepActiveModeRequirements(KeepActiveFlag::kExchangeContextOpen, false /* state */);
     613              :         }
     614              :     }
     615              : 
     616              : #if CHIP_CONFIG_ENABLE_ICD_CIP
     617              :     if (request.Has(KeepActiveFlag::kCheckInInProgress))
     618              :     {
     619              :         // There can be multiple open exchange contexts at the same time.
     620              :         // Keep track of the requests count.
     621              :         if (this->mCheckInRequestCount > 0)
     622              :         {
     623              :             this->mCheckInRequestCount--;
     624              :         }
     625              :         else
     626              :         {
     627              :             ChipLogError(DeviceLayer, "The ICD Manager did not account for Check-In Sender start");
     628              :         }
     629              : 
     630              :         if (this->mCheckInRequestCount == 0)
     631              :         {
     632              :             this->SetKeepActiveModeRequirements(KeepActiveFlag::kCheckInInProgress, false /* state */);
     633              :         }
     634              :     }
     635              : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
     636              : 
     637            4 :     if (request.Has(KeepActiveFlag::kCommissioningWindowOpen) || request.Has(KeepActiveFlag::kFailSafeArmed))
     638              :     {
     639              :         // Only 1 request per type (kCommissioningWindowOpen, kFailSafeArmed)
     640              :         // remove requirement directly
     641            3 :         this->SetKeepActiveModeRequirements(request, false /* state */);
     642              :     }
     643              : }
     644              : 
     645              : #if CHIP_CONFIG_ENABLE_ICD_DSLS
     646              : void ICDManager::OnSITModeRequest()
     647              : {
     648              :     mSITModeRequested = true;
     649              :     this->UpdateICDMode();
     650              :     // Update the poll interval also to comply with SIT requirements
     651              :     UpdateOperationState(OperationalState::ActiveMode);
     652              : }
     653              : 
     654              : void ICDManager::OnSITModeRequestWithdrawal()
     655              : {
     656              :     mSITModeRequested = false;
     657              :     this->UpdateICDMode();
     658              :     // Update the poll interval also to comply with LIT requirements
     659              :     UpdateOperationState(OperationalState::ActiveMode);
     660              : }
     661              : #endif // CHIP_CONFIG_ENABLE_ICD_DSLS
     662              : 
     663            4 : void ICDManager::OnNetworkActivity()
     664              : {
     665            4 :     this->UpdateOperationState(OperationalState::ActiveMode);
     666            4 : }
     667              : 
     668            0 : void ICDManager::OnICDManagementServerEvent(ICDManagementEvents event)
     669              : {
     670            0 :     switch (event)
     671              :     {
     672            0 :     case ICDManagementEvents::kTableUpdated:
     673            0 :         this->UpdateICDMode();
     674            0 :         break;
     675            0 :     default:
     676            0 :         break;
     677              :     }
     678            0 : }
     679              : 
     680            3 : void ICDManager::OnSubscriptionReport()
     681              : {
     682              :     // If the device is already in ActiveMode, that means that all active subscriptions have already been marked dirty.
     683              :     // Since we only mark them dirty when we enter ActiveMode, it is not necessary to update the operational state a second time.
     684              :     // Doing so will only add an ActiveModeThreshold to the active time which we don't want to do here.
     685            3 :     VerifyOrReturn(mOperationalState == OperationalState::IdleMode);
     686            3 :     this->UpdateOperationState(OperationalState::ActiveMode);
     687              : }
     688              : 
     689              : #if CHIP_CONFIG_ENABLE_ICD_SERVER && CHIP_CONFIG_ENABLE_ICD_CIP && CHIP_CONFIG_ENABLE_ICD_CHECK_IN_ON_REPORT_TIMEOUT
     690              : void ICDManager::OnSendCheckIn(const Access::SubjectDescriptor & subject)
     691              : {
     692              :     ChipLogProgress(AppServer, "Received OnSendCheckIn for subject: " ChipLogFormatX64, ChipLogValueX64(subject.subject));
     693              :     SendCheckInMsgs(MakeOptional(subject));
     694              : }
     695              : #endif // CHIP_CONFIG_ENABLE_ICD_SERVER && CHIP_CONFIG_ENABLE_ICD_CIP && CHIP_CONFIG_ENABLE_ICD_CHECK_IN_ON_REPORT_TIMEOUT
     696              : 
     697            8 : void ICDManager::ExtendActiveMode(Milliseconds16 extendDuration)
     698              : {
     699            8 :     TEMPORARY_RETURN_IGNORED DeviceLayer::SystemLayer().ExtendTimerTo(extendDuration, OnActiveModeDone, this);
     700              : 
     701            8 :     Milliseconds32 activeModeJitterThreshold = Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS);
     702            8 :     activeModeJitterThreshold = (extendDuration >= activeModeJitterThreshold) ? extendDuration - activeModeJitterThreshold : kZero;
     703              : 
     704            8 :     if (!mTransitionToIdleCalled)
     705              :     {
     706            1 :         TEMPORARY_RETURN_IGNORED DeviceLayer::SystemLayer().ExtendTimerTo(activeModeJitterThreshold, OnTransitionToIdle, this);
     707              :     }
     708            8 : }
     709              : 
     710            2 : CHIP_ERROR ICDManager::HandleEventTrigger(uint64_t eventTrigger)
     711              : {
     712            2 :     eventTrigger                     = clearEndpointInEventTrigger(eventTrigger);
     713            2 :     ICDTestEventTriggerEvent trigger = static_cast<ICDTestEventTriggerEvent>(eventTrigger);
     714            2 :     CHIP_ERROR err                   = CHIP_NO_ERROR;
     715              : 
     716            2 :     switch (trigger)
     717              :     {
     718            1 :     case ICDTestEventTriggerEvent::kAddActiveModeReq:
     719            1 :         SetKeepActiveModeRequirements(KeepActiveFlag::kTestEventTriggerActiveMode, true);
     720            1 :         break;
     721            1 :     case ICDTestEventTriggerEvent::kRemoveActiveModeReq:
     722            1 :         SetKeepActiveModeRequirements(KeepActiveFlag::kTestEventTriggerActiveMode, false);
     723            1 :         break;
     724              : #if CHIP_CONFIG_ENABLE_ICD_CIP
     725              :     case ICDTestEventTriggerEvent::kInvalidateHalfCounterValues:
     726              :         err = ICDConfigurationData::GetInstance().GetICDCounter().InvalidateHalfCheckInCounterValues();
     727              :         break;
     728              :     case ICDTestEventTriggerEvent::kInvalidateAllCounterValues:
     729              :         err = ICDConfigurationData::GetInstance().GetICDCounter().InvalidateAllCheckInCounterValues();
     730              :         break;
     731              :     case ICDTestEventTriggerEvent::kForceMaximumCheckInBackOffState:
     732              :         err = mICDCheckInBackOffStrategy->ForceMaximumCheckInBackoff();
     733              :         break;
     734              : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
     735              : #if CHIP_CONFIG_ENABLE_ICD_DSLS
     736              :     case ICDTestEventTriggerEvent::kDSLSForceSitMode:
     737              :         OnSITModeRequest();
     738              :         break;
     739              :     case ICDTestEventTriggerEvent::kDSLSWithdrawSitMode:
     740              :         OnSITModeRequestWithdrawal();
     741              :         break;
     742              : #endif // CHIP_CONFIG_ENABLE_ICD_DSLS
     743            0 :     default:
     744            0 :         err = CHIP_ERROR_INVALID_ARGUMENT;
     745            0 :         break;
     746              :     }
     747              : 
     748            2 :     return err;
     749              : }
     750              : 
     751           10 : ICDManager::ObserverPointer * ICDManager::RegisterObserver(ICDStateObserver * observer)
     752              : {
     753           10 :     return mStateObserverPool.CreateObject(observer);
     754              : }
     755              : 
     756            0 : void ICDManager::ReleaseObserver(ICDStateObserver * observer)
     757              : {
     758            0 :     mStateObserverPool.ForEachActiveObject([this, observer](ObserverPointer * obs) {
     759            0 :         if (obs->mObserver == observer)
     760              :         {
     761            0 :             mStateObserverPool.ReleaseObject(obs);
     762            0 :             return Loop::Break;
     763              :         }
     764            0 :         return Loop::Continue;
     765              :     });
     766            0 : }
     767              : 
     768           54 : void ICDManager::postObserverEvent(ObserverEventType event)
     769              : {
     770           54 :     mStateObserverPool.ForEachActiveObject([event](ObserverPointer * obs) {
     771           54 :         switch (event)
     772              :         {
     773           16 :         case ObserverEventType::EnterActiveMode: {
     774           16 :             obs->mObserver->OnEnterActiveMode();
     775           16 :             return Loop::Continue;
     776              :         }
     777           23 :         case ObserverEventType::EnterIdleMode: {
     778           23 :             obs->mObserver->OnEnterIdleMode();
     779           23 :             return Loop::Continue;
     780              :         }
     781           15 :         case ObserverEventType::TransitionToIdle: {
     782           15 :             obs->mObserver->OnTransitionToIdle();
     783           15 :             return Loop::Continue;
     784              :         }
     785            0 :         case ObserverEventType::ICDModeChange: {
     786            0 :             obs->mObserver->OnICDModeChange();
     787            0 :             return Loop::Continue;
     788              :         }
     789            0 :         default: {
     790            0 :             ChipLogError(DeviceLayer, "Invalid ICD Observer event type");
     791            0 :             return Loop::Break;
     792              :         }
     793              :         }
     794              :     });
     795           54 : }
     796              : 
     797              : } // namespace app
     798              : } // namespace chip
        

Generated by: LCOV version 2.0-1