Skip to content

Commit

Permalink
[ICD] Implement the support of the ICD Check-In BackOff (#34482)
Browse files Browse the repository at this point in the history
* Add MaximunCheckInBackOff attribute to ICDM cluster

* Add injectable ICD Check-In BackOff strategy

* Fix gn dependency

* Fix non CIP builds

* Fix switch case and add test

* Update zcl with extensions

* Fix zap generation

* Refactor ICDManager init to leverage a builder pattern

* Fix non LIT test builds

* Apply suggestions from code review

Co-authored-by: Boris Zbarsky <[email protected]>

---------

Co-authored-by: Boris Zbarsky <[email protected]>
  • Loading branch information
2 people authored and pull[bot] committed Oct 25, 2024
1 parent 049864c commit 18a59a1
Show file tree
Hide file tree
Showing 21 changed files with 408 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1813,6 +1813,7 @@ endpoint 0 {
ram attribute userActiveModeTriggerHint default = 0x111D;
ram attribute userActiveModeTriggerInstruction default = "Restart the application";
ram attribute operatingMode default = 0;
callback attribute maximumCheckInBackOff;
callback attribute generatedCommandList;
callback attribute acceptedCommandList;
callback attribute eventList;
Expand Down
30 changes: 23 additions & 7 deletions examples/lit-icd-app/lit-icd-common/lit-icd-server-app.zap
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@
}
],
"package": [
{
"pathRelativity": "relativeToZap",
"path": "../../../src/app/zap-templates/app-templates.json",
"type": "gen-templates-json",
"category": "matter",
"version": "chip-v1"
},
{
"pathRelativity": "relativeToZap",
"path": "../../../src/app/zap-templates/zcl/zcl.json",
"type": "zcl-properties",
"category": "matter",
"version": 1,
"description": "Matter SDK ZCL data"
},
{
"pathRelativity": "relativeToZap",
"path": "../../../src/app/zap-templates/app-templates.json",
"type": "gen-templates-json",
"category": "matter",
"version": "chip-v1"
}
],
"endpointTypes": [
Expand Down Expand Up @@ -3552,6 +3552,22 @@
"maxInterval": 65534,
"reportableChange": 0
},
{
"name": "MaximumCheckInBackOff",
"code": 9,
"mfgCode": null,
"side": "server",
"type": "int32u",
"included": 1,
"storageOption": "External",
"singleton": 0,
"bounded": 0,
"defaultValue": "",
"reportable": 1,
"minInterval": 1,
"maxInterval": 65534,
"reportableChange": 0
},
{
"name": "GeneratedCommandList",
"code": 65528,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class IcdManagementAttributeAccess : public AttributeAccessInterface
CHIP_ERROR ReadRegisteredClients(EndpointId endpoint, AttributeValueEncoder & encoder);
CHIP_ERROR ReadICDCounter(EndpointId endpoint, AttributeValueEncoder & encoder);
CHIP_ERROR ReadClientsSupportedPerFabric(EndpointId endpoint, AttributeValueEncoder & encoder);
CHIP_ERROR ReadMaximumCheckInBackOff(EndpointId endpoint, AttributeValueEncoder & encoder);

PersistentStorageDelegate * mStorage = nullptr;
Crypto::SymmetricKeystore * mSymmetricKeystore = nullptr;
Expand Down Expand Up @@ -102,6 +103,9 @@ CHIP_ERROR IcdManagementAttributeAccess::Read(const ConcreteReadAttributePath &

case IcdManagement::Attributes::ClientsSupportedPerFabric::Id:
return ReadClientsSupportedPerFabric(aPath.mEndpointId, aEncoder);

case IcdManagement::Attributes::MaximumCheckInBackOff::Id:
return ReadMaximumCheckInBackOff(aPath.mEndpointId, aEncoder);
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
}

Expand Down Expand Up @@ -221,6 +225,11 @@ CHIP_ERROR IcdManagementAttributeAccess::ReadClientsSupportedPerFabric(EndpointI
return encoder.Encode(mICDConfigurationData->GetClientsSupportedPerFabric());
}

CHIP_ERROR IcdManagementAttributeAccess::ReadMaximumCheckInBackOff(EndpointId endpoint, AttributeValueEncoder & encoder)
{
return encoder.Encode(mICDConfigurationData->GetMaximumCheckInBackoff().count());
}

/**
* @brief Function checks if the client has admin permissions to the cluster in the commandPath
*
Expand Down
17 changes: 17 additions & 0 deletions src/app/icd/server/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ source_set("notifier") {
]
}

source_set("check-in-back-off") {
sources = [ "ICDCheckInBackOffStrategy.h" ]

public_deps = [
":monitoring-table",
"${chip_root}/src/lib/core",
"${chip_root}/src/lib/support",
]
}

source_set("default-check-in-back-off") {
sources = [ "DefaultICDCheckInBackOffStrategy.h" ]

public_deps = [ ":check-in-back-off" ]
}

# ICD Manager source-set is broken out of the main source-set to enable unit tests
# All sources and configurations used by the ICDManager need to go in this source-set
source_set("manager") {
Expand All @@ -77,6 +93,7 @@ source_set("manager") {
deps = [ ":icd-server-config" ]

public_deps = [
":check-in-back-off",
":configuration-data",
":notifier",
":observer",
Expand Down
63 changes: 63 additions & 0 deletions src/app/icd/server/DefaultICDCheckInBackOffStrategy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
*
* Copyright (c) 2024 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <app/icd/server/ICDCheckInBackOffStrategy.h>
#include <lib/core/ClusterEnums.h>

namespace chip {
namespace app {

/**
* @brief Default ICD Check-In BackOff Strategy.
* The default strategy is based on the two types of controllers
* - kPermanent : Always send a Check-In message
* - kEphemeral : Never send a Check-In message
*
* This implementation represents a no back off strategy.
*/
class DefaultICDCheckInBackOffStrategy : public ICDCheckInBackOffStrategy
{
public:
DefaultICDCheckInBackOffStrategy() = default;
~DefaultICDCheckInBackOffStrategy() = default;

/**
* @brief Function checks if the entry is a permanent or ephemeral client.
* If the client is permanent, we should send a Check-In message.
* If the client is ephemeral, we should not send a Check-In message.
*
* @param entry Entry for which we are deciding whether we need to send a Check-In message or not.
* @return true If the client is permanent, return true.
* @return false If the client is not permanent, ephemeral or invalid, return false.
*/
bool ShouldSendCheckInMessage(const ICDMonitoringEntry & entry) override
{
return (entry.clientType == Clusters::IcdManagement::ClientTypeEnum::kPermanent);
}

/**
* @brief The default Check-In BackOff fundamentally implements a no back off strategy.
* As such, we don't need to execute anything to force the maximum Check-In BackOff.
*
*/
CHIP_ERROR ForceMaximumCheckInBackoff() override { return CHIP_NO_ERROR; }
};

} // namespace app
} // namespace chip
62 changes: 62 additions & 0 deletions src/app/icd/server/ICDCheckInBackOffStrategy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
*
* Copyright (c) 2024 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include <app/icd/server/ICDMonitoringTable.h>
#include <lib/core/CHIPError.h>

namespace chip {
namespace app {

/**
* @brief This class defines the necessary interface a ICD Check-In BackOff strategy needs to implment to be consummed by the
* ICDManager class. The strategy is injected with the init server params when initializing the device Server class.
*/
class ICDCheckInBackOffStrategy
{
public:
virtual ~ICDCheckInBackOffStrategy() = default;

/**
* @brief Function is used by the ICDManager to determine if a Check-In message should be sent to the given entry based on the
* Check-In BackOff strategy.
*
* There are no requirements on how the Check-In BackOff strategy should behave.
* The only specified requirement is the maximum time between to Check-In message, MaximumCheckInBackOff.
* All strategies must respect this requirement.
*
* @param entry ICDMonitoringEntry for which we are about to send a Check-In message to.
*
* @return true ICDCheckInBackOffStrategy determines that we SHOULD send a Check-In message to the given entry
* @return false ICDCheckInBackOffStrategy determines that we SHOULD NOT send a Check-In message to the given entry
*/
virtual bool ShouldSendCheckInMessage(const ICDMonitoringEntry & entry) = 0;

/**
* @brief Function is used within the test event trigger to force the maximum BackOff state of the ICD Check-In BackOff
* strategy. This enables to validate the strategy and to certify it respects the MaximumCheckInBackOff interval during
* certification.
*
* Function sets the maxmimum BackOff state for all clients registered with the ICD
*
* @return CHIP_ERROR Any error returned during the forcing of the maximum BackOff state
*/
virtual CHIP_ERROR ForceMaximumCheckInBackoff() = 0;
};

} // namespace app
} // namespace chip
8 changes: 8 additions & 0 deletions src/app/icd/server/ICDConfigurationData.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class ICDConfigurationData

System::Clock::Milliseconds16 GetMinLitActiveModeThreshold() { return kMinLitActiveModeThreshold; }

System::Clock::Seconds32 GetMaximumCheckInBackoff() { return mMaximumCheckInBackOff; }

/**
* If ICD_ENFORCE_SIT_SLOW_POLL_LIMIT is set to 0, function will always return the configured Slow Polling interval
* (CHIP_DEVICE_CONFIG_ICD_SLOW_POLL_INTERVAL).
Expand Down Expand Up @@ -150,6 +152,12 @@ class ICDConfigurationData
"Spec requires the minimum of supported clients per fabric be equal or greater to 1.");
uint16_t mFabricClientsSupported = CHIP_CONFIG_ICD_CLIENTS_SUPPORTED_PER_FABRIC;

static_assert((CHIP_CONFIG_ICD_MAXIMUM_CHECK_IN_BACKOFF_SEC) <= kMaxIdleModeDuration.count(),
"Spec requires the MaximumCheckInBackOff to be equal or inferior to 64800s");
static_assert((CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC) <= (CHIP_CONFIG_ICD_MAXIMUM_CHECK_IN_BACKOFF_SEC),
"Spec requires the MaximumCheckInBackOff to be equal or superior to the IdleModeDuration");
System::Clock::Seconds32 mMaximumCheckInBackOff = System::Clock::Seconds32(CHIP_CONFIG_ICD_MAXIMUM_CHECK_IN_BACKOFF_SEC);

// SIT ICDs should have a SlowPollingThreshold shorter than or equal to 15s (spec 9.16.1.5)
static constexpr System::Clock::Milliseconds32 kSITPollingThreshold = System::Clock::Milliseconds32(15000);
System::Clock::Milliseconds32 mSlowPollingInterval = CHIP_DEVICE_CONFIG_ICD_SLOW_POLL_INTERVAL;
Expand Down
49 changes: 22 additions & 27 deletions src/app/icd/server/ICDManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@
namespace {
enum class ICDTestEventTriggerEvent : uint64_t
{
kAddActiveModeReq = 0x0046'0000'00000001,
kRemoveActiveModeReq = 0x0046'0000'00000002,
kInvalidateHalfCounterValues = 0x0046'0000'00000003,
kInvalidateAllCounterValues = 0x0046'0000'00000004,
kAddActiveModeReq = 0x0046'0000'00000001,
kRemoveActiveModeReq = 0x0046'0000'00000002,
kInvalidateHalfCounterValues = 0x0046'0000'00000003,
kInvalidateAllCounterValues = 0x0046'0000'00000004,
kForceMaximumCheckInBackOffState = 0x0046'0000'00000005,
};
} // namespace

Expand All @@ -51,15 +52,19 @@ using chip::Protocols::InteractionModel::Status;
static_assert(UINT8_MAX >= CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS,
"ICDManager::mOpenExchangeContextCount cannot hold count for the max exchange count");

void ICDManager::Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, Crypto::SymmetricKeystore * symmetricKeystore,
Messaging::ExchangeManager * exchangeManager, SubscriptionsInfoProvider * subInfoProvider)
void ICDManager::Init()
{
#if CHIP_CONFIG_ENABLE_ICD_CIP
VerifyOrDie(storage != nullptr);
VerifyOrDie(fabricTable != nullptr);
VerifyOrDie(symmetricKeystore != nullptr);
VerifyOrDie(exchangeManager != nullptr);
VerifyOrDie(subInfoProvider != nullptr);
VerifyOrDie(mStorage != nullptr);
VerifyOrDie(mFabricTable != nullptr);
VerifyOrDie(mSymmetricKeystore != nullptr);
VerifyOrDie(mExchangeManager != nullptr);
VerifyOrDie(mSubInfoProvider != nullptr);
VerifyOrDie(mICDCheckInBackOffStrategy != nullptr);

VerifyOrDie(ICDConfigurationData::GetInstance().GetICDCounter().Init(mStorage, DefaultStorageKeyAllocator::ICDCheckInCounter(),
ICDConfigurationData::kICDCounterPersistenceIncrement) ==
CHIP_NO_ERROR);
#endif // CHIP_CONFIG_ENABLE_ICD_CIP

#if CHIP_CONFIG_ENABLE_ICD_LIT
Expand All @@ -81,18 +86,6 @@ void ICDManager::Init(PersistentStorageDelegate * storage, FabricTable * fabricT

VerifyOrDie(ICDNotifier::GetInstance().Subscribe(this) == CHIP_NO_ERROR);

#if CHIP_CONFIG_ENABLE_ICD_CIP
mStorage = storage;
mFabricTable = fabricTable;
mSymmetricKeystore = symmetricKeystore;
mExchangeManager = exchangeManager;
mSubInfoProvider = subInfoProvider;

VerifyOrDie(ICDConfigurationData::GetInstance().GetICDCounter().Init(mStorage, DefaultStorageKeyAllocator::ICDCheckInCounter(),
ICDConfigurationData::kICDCounterPersistenceIncrement) ==
CHIP_NO_ERROR);
#endif // CHIP_CONFIG_ENABLE_ICD_CIP

UpdateICDMode();
UpdateOperationState(OperationalState::IdleMode);
}
Expand Down Expand Up @@ -188,15 +181,14 @@ void ICDManager::SendCheckInMsgs()
continue;
}

if (entry.clientType == ClientTypeEnum::kEphemeral)
if (!ShouldCheckInMsgsBeSentAtActiveModeFunction(entry.fabricIndex, entry.monitoredSubject))
{
// If the registered client is ephemeral, do not send a Check-In message
// continue to next entry
continue;
}

if (!ShouldCheckInMsgsBeSentAtActiveModeFunction(entry.fabricIndex, entry.monitoredSubject))
if (!mICDCheckInBackOffStrategy->ShouldSendCheckInMessage(entry))
{
// continue to next entry
continue;
}

Expand Down Expand Up @@ -689,6 +681,9 @@ CHIP_ERROR ICDManager::HandleEventTrigger(uint64_t eventTrigger)
case ICDTestEventTriggerEvent::kInvalidateAllCounterValues:
err = ICDConfigurationData::GetInstance().GetICDCounter().InvalidateAllCheckInCounterValues();
break;
case ICDTestEventTriggerEvent::kForceMaximumCheckInBackOffState:
err = mICDCheckInBackOffStrategy->ForceMaximumCheckInBackoff();
break;
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
default:
err = CHIP_ERROR_INVALID_ARGUMENT;
Expand Down
Loading

0 comments on commit 18a59a1

Please sign in to comment.