Matter SDK Coverage Report
Current view: top level - app/icd/server - ICDManager.cpp (source / functions) Coverage Total Hit
Test: SHA:f84fe08d06f240e801b5d923f8a938a9938ca110 Lines: 81.8 % 165 135
Test Date: 2025-02-22 08:08:07 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 :     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
        

Generated by: LCOV version 2.0-1