diff --git a/examples/platform/silabs/BaseApplication.cpp b/examples/platform/silabs/BaseApplication.cpp index 665c65bcda6a15..5eb8a6886ea72f 100644 --- a/examples/platform/silabs/BaseApplication.cpp +++ b/examples/platform/silabs/BaseApplication.cpp @@ -37,6 +37,7 @@ #endif // DISPLAY_ENABLED #include "SilabsDeviceDataProvider.h" +#include #include #include #include @@ -505,13 +506,12 @@ void BaseApplication::ButtonHandler(AppEvent * aEvent) else { SILABS_LOG("Network is already provisioned, Ble advertissement not enabled"); - DeviceLayer::ChipDeviceEvent event; - event.Type = DeviceLayer::DeviceEventType::kAppWakeUpEvent; - CHIP_ERROR err = DeviceLayer::PlatformMgr().PostEvent(&event); - if (err != CHIP_NO_ERROR) - { - ChipLogError(AppServer, "Failed to post App wake up Event event %" CHIP_ERROR_FORMAT, err.Format()); - } +#if CHIP_CONFIG_ENABLE_ICD_SERVER + // Temporarily claim network activity, until we implement a "user trigger" reason for ICD wakeups. + PlatformMgr().LockChipStack(); + ICDNotifier::GetInstance().BroadcastNetworkActivityNotification(); + PlatformMgr().UnlockChipStack(); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER } } else if (mFunctionTimerActive && mFunction == kFunction_FactoryReset) diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 1e73ea3c8201ee..a0159708922b8c 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -59,6 +59,7 @@ buildconfig_header("app_buildconfig") { "CHIP_CONFIG_ENABLE_ICD_SERVER=${chip_enable_icd_server}", "CHIP_CONFIG_ENABLE_READ_CLIENT=${chip_enable_read_client}", "ICD_REPORT_ON_ENTER_ACTIVE_MODE=${chip_report_on_active_mode}", + "ICD_MAX_NOTIFICATION_SUBSCRIBERS=${icd_max_notification_subscribers}", ] } @@ -236,6 +237,7 @@ static_library("app") { public_deps = [ ":app_config", "${chip_root}/src/access", + "${chip_root}/src/app/icd:notifier", "${chip_root}/src/app/icd:observer-srcs", "${chip_root}/src/lib/address_resolve", "${chip_root}/src/lib/support", @@ -246,7 +248,7 @@ static_library("app") { ] if (chip_enable_icd_server) { - public_deps += [ "${chip_root}/src/app/icd:server-srcs" ] + public_deps += [ "${chip_root}/src/app/icd:manager-srcs" ] } cflags = [ "-Wconversion" ] diff --git a/src/app/FailSafeContext.cpp b/src/app/FailSafeContext.cpp index b11ad8775c84df..2d886b17b34bc0 100644 --- a/src/app/FailSafeContext.cpp +++ b/src/app/FailSafeContext.cpp @@ -20,6 +20,7 @@ * Provides the implementation of the FailSafeContext object. */ +#include #include #include #include @@ -55,14 +56,9 @@ void FailSafeContext::SetFailSafeArmed(bool armed) #if CHIP_CONFIG_ENABLE_ICD_SERVER if (IsFailSafeArmed() != armed) { - DeviceLayer::ChipDeviceEvent event; - event.Type = DeviceLayer::DeviceEventType::kFailSafeStateChanged; - event.FailSafeState.armed = armed; - CHIP_ERROR err = DeviceLayer::PlatformMgr().PostEvent(&event); - if (err != CHIP_NO_ERROR) - { - ChipLogError(AppServer, "Failed to post kFailSafeStateChanged event %" CHIP_ERROR_FORMAT, err.Format()); - } + ICDListener::KeepActiveFlags activeRequest = ICDListener::KeepActiveFlags::kFailSafeArmed; + armed ? ICDNotifier::GetInstance().BroadcastActiveRequestNotification(activeRequest) + : ICDNotifier::GetInstance().BroadcastActiveRequestWithdrawal(activeRequest); } #endif mFailSafeArmed = armed; diff --git a/src/app/icd/BUILD.gn b/src/app/icd/BUILD.gn index c0034a88e7b9b8..132518887807cc 100644 --- a/src/app/icd/BUILD.gn +++ b/src/app/icd/BUILD.gn @@ -16,11 +16,20 @@ import("//build_overrides/chip.gni") import("icd.gni") # ICD Server sources and configurations - source_set("observer-srcs") { sources = [ "ICDStateObserver.h" ] } +source_set("notifier") { + sources = [ + "ICDNotifier.cpp", + "ICDNotifier.h", + ] + + deps = [ "${chip_root}/src/lib/core" ] + public_deps = [ "${chip_root}/src/app:app_config" ] +} + # 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-srcs") { @@ -31,6 +40,7 @@ source_set("manager-srcs") { public_deps = [ ":cluster-srcs", + ":notifier", ":observer-srcs", "${chip_root}/src/credentials:credentials", ] @@ -52,16 +62,3 @@ source_set("cluster-srcs") { "${chip_root}/src/protocols:im_status", ] } - -# servers-srcs source-set contains all the sources and configurations necessary to build the ICD Server functionality -# All sources, configurations and dependencies necessary for the ICD Server featureset need to go in this source-set -# -# The ICD Server featureset is enabled with the chip_enable_icd_server in the src/app/BUILD.gn file -source_set("server-srcs") { - sources = [ - "ICDEventManager.cpp", - "ICDEventManager.h", - ] - - public_deps = [ ":manager-srcs" ] -} diff --git a/src/app/icd/ICDEventManager.cpp b/src/app/icd/ICDEventManager.cpp deleted file mode 100644 index cbe3c5521a65d9..00000000000000 --- a/src/app/icd/ICDEventManager.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * - * Copyright (c) 2023 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. - */ - -#include - -using namespace chip::DeviceLayer; - -namespace chip { -namespace app { - -uint8_t ICDEventManager::expectedMsgCount = 0; -uint8_t ICDEventManager::awaitingAckCount = 0; -static_assert(UINT8_MAX >= CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS, - "ICDEventManager::expectedMsgCount cannot hold count for the max exchange count"); - -CHIP_ERROR ICDEventManager::Init(ICDManager * icdManager) -{ - VerifyOrReturnError(icdManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT); - mICDManager = icdManager; - PlatformMgr().AddEventHandler(ICDEventHandler, reinterpret_cast(mICDManager)); - - return CHIP_NO_ERROR; -} - -CHIP_ERROR ICDEventManager::Shutdown() -{ - PlatformMgr().RemoveEventHandler(ICDEventHandler, reinterpret_cast(nullptr)); - mICDManager = nullptr; - - return CHIP_NO_ERROR; -} - -void ICDEventManager::ICDEventHandler(const ChipDeviceEvent * event, intptr_t arg) -{ - ICDManager * icdManager = reinterpret_cast(arg); - - if (icdManager == nullptr) - { - return; - } - - switch (event->Type) - { - case DeviceEventType::kCommissioningWindowStatusChanged: - icdManager->SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kCommissioningWindowOpen, - event->CommissioningWindowStatus.open); - break; - case DeviceEventType::kFailSafeStateChanged: - icdManager->SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kFailSafeArmed, event->FailSafeState.armed); - break; - case DeviceEventType::kChipMsgSentEvent: - - // When we expect a response to a message sent, We keep the ICD in active mode until it is received - // Otherwise, just a kick off an active mode interval/active mode threshold - if (event->MessageSent.ExpectResponse) - { - expectedMsgCount++; - icdManager->SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kExpectingMsgResponse, true); - } - else - { - icdManager->UpdateOperationState(ICDManager::OperationalState::ActiveMode); - } - break; - case DeviceEventType::kChipMsgRxEventHandled: - if (event->RxEventContext.clearsExpectedResponse) - { - if (expectedMsgCount > 0) - { - expectedMsgCount--; - } - else - { - // Should we assert? - ChipLogError(DeviceLayer, "No response was expected by the ICD Manager"); - } - - if (expectedMsgCount == 0) - { - icdManager->SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kExpectingMsgResponse, false); - } - } - else if (event->RxEventContext.wasReceived) - { - icdManager->UpdateOperationState(ICDManager::OperationalState::ActiveMode); - } - break; - case DeviceEventType::kICDMsgAckSyncEvent: - // When a Reliable Message Context is awaiting an ack, we keep the ICD in its active mode - if (event->AckSync.awaitingAck) - { - awaitingAckCount++; - } - else if (awaitingAckCount > 0) - { - awaitingAckCount--; - } - - icdManager->SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kAwaitingMsgAck, (awaitingAckCount != 0)); - break; - case DeviceEventType::kAppWakeUpEvent: - icdManager->UpdateOperationState(ICDManager::OperationalState::ActiveMode); - break; - default: - break; - } -} -} // namespace app -} // namespace chip diff --git a/src/app/icd/ICDEventManager.h b/src/app/icd/ICDEventManager.h deleted file mode 100644 index c86b356e408657..00000000000000 --- a/src/app/icd/ICDEventManager.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * Copyright (c) 2023 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 - -#include -#include -#include - -namespace chip { -namespace app { - -/** - * @brief ICDEventManager class is responsible of processing Platform Events that affect an ICD's behaviour - * The class registers an Event Handler with the Platform Manager and dispatches the processing to the ICDManager class. - */ -class ICDEventManager -{ - -public: - ICDEventManager() = default; - - /** - * @brief Initialisation function of the ICDEventManager. - * Init function MUST be called before using the object - */ - CHIP_ERROR Init(ICDManager * icdManager); - CHIP_ERROR Shutdown(); - -private: - /** - * @brief Event Handler callback given to the PlatformManager - * Function dispatchs the event to the ICDManager member - */ - static void ICDEventHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg); - static uint8_t expectedMsgCount; - static uint8_t awaitingAckCount; - ICDManager * mICDManager; -}; - -} // namespace app -} // namespace chip diff --git a/src/app/icd/ICDManager.cpp b/src/app/icd/ICDManager.cpp index c02ef5aedd92ca..d46a166e361172 100644 --- a/src/app/icd/ICDManager.cpp +++ b/src/app/icd/ICDManager.cpp @@ -40,6 +40,10 @@ using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::app::Clusters::IcdManagement; +uint8_t ICDManager::OpenExchangeContextCount = 0; +static_assert(UINT8_MAX >= CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS, + "ICDManager::OpenExchangeContextCount cannot hold count for the max exchange count"); + void ICDManager::Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, ICDStateObserver * stateObserver) { VerifyOrDie(storage != nullptr); @@ -49,6 +53,7 @@ void ICDManager::Init(PersistentStorageDelegate * storage, FabricTable * fabricT mStorage = storage; mFabricTable = fabricTable; mStateObserver = stateObserver; + VerifyOrDie(ICDNotifier::GetInstance().Subscribe(this) == CHIP_NO_ERROR); uint32_t activeModeInterval = IcdManagementServer::GetInstance().GetActiveModeIntervalMs(); VerifyOrDie(kFastPollingInterval.count() < activeModeInterval); @@ -59,6 +64,7 @@ void ICDManager::Init(PersistentStorageDelegate * storage, FabricTable * fabricT void ICDManager::Shutdown() { + ICDNotifier::GetInstance().Unsubscribe(this); // cancel any running timer of the icd DeviceLayer::SystemLayer().CancelTimer(OnIdleModeDone, this); DeviceLayer::SystemLayer().CancelTimer(OnActiveModeDone, this); @@ -122,9 +128,14 @@ void ICDManager::UpdateOperationState(OperationalState state) if (state == OperationalState::IdleMode) { - mOperationalState = OperationalState::IdleMode; - uint32_t idleModeInterval = IcdManagementServer::GetInstance().GetIdleModeIntervalSec(); - DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(idleModeInterval), OnIdleModeDone, this); + mOperationalState = OperationalState::IdleMode; + + // When the active mode interval is 0, we stay in idleMode until a notification brings the icd into active mode + if (IcdManagementServer::GetInstance().GetActiveModeIntervalMs() > 0) + { + uint32_t idleModeInterval = IcdManagementServer::GetInstance().GetIdleModeIntervalSec(); + DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(idleModeInterval), OnIdleModeDone, this); + } System::Clock::Milliseconds32 slowPollInterval = GetSlowPollingInterval(); @@ -152,7 +163,16 @@ void ICDManager::UpdateOperationState(OperationalState state) mOperationalState = OperationalState::ActiveMode; uint32_t activeModeInterval = IcdManagementServer::GetInstance().GetActiveModeIntervalMs(); + + if (activeModeInterval == 0 && !mKeepActiveFlags.HasAny()) + { + // A Network Activity triggered the active mode and activeModeInterval is 0. + // Stay active for at least Active Mode Threshold. + activeModeInterval = IcdManagementServer::GetInstance().GetActiveModeThresholdMs(); + } + DeviceLayer::SystemLayer().StartTimer(System::Clock::Timeout(activeModeInterval), OnActiveModeDone, this); + uint32_t activeModeJitterInterval = (activeModeInterval >= ICD_ACTIVE_TIME_JITTER_MS) ? activeModeInterval - ICD_ACTIVE_TIME_JITTER_MS : 0; DeviceLayer::SystemLayer().StartTimer(System::Clock::Timeout(activeModeJitterInterval), OnTransitionToIdle, this); @@ -228,5 +248,59 @@ void ICDManager::OnTransitionToIdle(System::Layer * aLayer, void * appState) pIcdManager->mStateObserver->OnTransitionToIdle(); } +/* ICDListener functions. */ +void ICDManager::OnKeepActiveRequest(KeepActiveFlags request) +{ + assertChipStackLockedByCurrentThread(); + + if (request == KeepActiveFlags::kExchangeContextOpen) + { + // There can be multiple open exchange contexts at the same time. + // Keep track of the requests count. + this->OpenExchangeContextCount++; + this->SetKeepActiveModeRequirements(request, true /* state */); + } + else /* !kExchangeContextOpen */ + { + // Only 1 request per type (kCommissioningWindowOpen, kFailSafeArmed) + // set requirement directly + this->SetKeepActiveModeRequirements(request, true /* state */); + } +} + +void ICDManager::OnActiveRequestWithdrawal(KeepActiveFlags request) +{ + assertChipStackLockedByCurrentThread(); + + if (request == KeepActiveFlags::kExchangeContextOpen) + { + // There can be multiple open exchange contexts at the same time. + // Keep track of the requests count. + if (this->OpenExchangeContextCount > 0) + { + this->OpenExchangeContextCount--; + } + else + { + ChipLogError(DeviceLayer, "The ICD Manager did not account for ExchangeContext closure"); + } + + if (this->OpenExchangeContextCount == 0) + { + this->SetKeepActiveModeRequirements(request, false /* state */); + } + } + else /* !kExchangeContextOpen */ + { + // Only 1 request per type (kCommissioningWindowOpen, kFailSafeArmed) + // remove requirement directly + this->SetKeepActiveModeRequirements(request, false /* state */); + } +} + +void ICDManager::OnNetworkActivity() +{ + this->UpdateOperationState(OperationalState::ActiveMode); +} } // namespace app } // namespace chip diff --git a/src/app/icd/ICDManager.h b/src/app/icd/ICDManager.h index 7a84a1a3c4dfee..852a4b428e9411 100644 --- a/src/app/icd/ICDManager.h +++ b/src/app/icd/ICDManager.h @@ -16,6 +16,7 @@ */ #pragma once +#include #include #include #include @@ -33,7 +34,7 @@ class TestICDManager; /** * @brief ICD Manager is responsible of processing the events and triggering the correct action for an ICD */ -class ICDManager +class ICDManager : public ICDListener { public: enum class OperationalState : uint8_t @@ -48,14 +49,6 @@ class ICDManager LIT, // Long Interval Time ICD }; - enum class KeepActiveFlags : uint8_t - { - kCommissioningWindowOpen = 0x01, - kFailSafeArmed = 0x02, - kExpectingMsgResponse = 0x03, - kAwaitingMsgAck = 0x04, - }; - ICDManager() {} void Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, ICDStateObserver * stateObserver); void Shutdown(); @@ -70,6 +63,12 @@ class ICDManager static System::Clock::Milliseconds32 GetSlowPollingInterval() { return kSlowPollingInterval; } static System::Clock::Milliseconds32 GetFastPollingInterval() { return kFastPollingInterval; } + // Implementation of ICDListener functions. + // Callers must origin from the chip task context or be holding the ChipStack lock. + void OnNetworkActivity() override; + void OnKeepActiveRequest(KeepActiveFlags request) override; + void OnActiveRequestWithdrawal(KeepActiveFlags request) override; + protected: friend class TestICDManager; @@ -83,6 +82,8 @@ class ICDManager */ static void OnTransitionToIdle(System::Layer * aLayer, void * appState); + static uint8_t OpenExchangeContextCount; + private: // 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); @@ -97,6 +98,7 @@ class ICDManager bool SupportsCheckInProtocol(); BitFlags mKeepActiveFlags{ 0 }; + OperationalState mOperationalState = OperationalState::IdleMode; ICDMode mICDMode = ICDMode::SIT; PersistentStorageDelegate * mStorage = nullptr; diff --git a/src/app/icd/ICDNotifier.cpp b/src/app/icd/ICDNotifier.cpp new file mode 100644 index 00000000000000..a3b4588004809a --- /dev/null +++ b/src/app/icd/ICDNotifier.cpp @@ -0,0 +1,93 @@ +/* + * + * Copyright (c) 2023 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. + */ + +#include +#include +#include + +namespace chip { +namespace app { + +ICDNotifier ICDNotifier::sICDNotifier; + +ICDNotifier::~ICDNotifier() +{ + memset(mSubscribers, 0, sizeof(mSubscribers)); +} + +CHIP_ERROR ICDNotifier::Subscribe(ICDListener * subscriber) +{ + CHIP_ERROR err = CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; + for (auto & sub : mSubscribers) + { + if (sub == nullptr) + { + sub = subscriber; + err = CHIP_NO_ERROR; + break; + } + } + return err; +} + +void ICDNotifier::Unsubscribe(ICDListener * subscriber) +{ + for (auto & sub : mSubscribers) + { + if (sub == subscriber) + { + sub = nullptr; + break; + } + } +} + +void ICDNotifier::BroadcastNetworkActivityNotification() +{ + for (auto subscriber : mSubscribers) + { + if (subscriber != nullptr) + { + subscriber->OnNetworkActivity(); + } + } +} + +void ICDNotifier::BroadcastActiveRequestNotification(ICDListener::KeepActiveFlags request) +{ + for (auto subscriber : mSubscribers) + { + if (subscriber != nullptr) + { + subscriber->OnKeepActiveRequest(request); + } + } +} + +void ICDNotifier::BroadcastActiveRequestWithdrawal(ICDListener::KeepActiveFlags request) +{ + for (auto subscriber : mSubscribers) + { + if (subscriber != nullptr) + { + subscriber->OnActiveRequestWithdrawal(request); + } + } +} + +} // namespace app +} // namespace chip diff --git a/src/app/icd/ICDNotifier.h b/src/app/icd/ICDNotifier.h new file mode 100644 index 00000000000000..a90f3ddb675089 --- /dev/null +++ b/src/app/icd/ICDNotifier.h @@ -0,0 +1,97 @@ +/* + * + * Copyright (c) 2023 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 +#include + +class ICDListener; + +namespace chip { +namespace app { + +/** + * The ICDManager implements the ICDListener functions and is always subscribed to the ICDNotifier + * This allows other Matter modules to inform the ICDManager that it needs to go and may have to stay in Active Mode, + * outside of its standard ActiveModeInterval and IdleModeInterval, without being tightly coupled the application data model + * + * This implementation also allows other modules to implement an ICDListener and subscribe to ICDNotifier + * to couple behaviours with the ICD cycles. In such cases, ICD_MAX_NOTIFICATION_SUBSCRIBERS need to be adjusted + */ + +static_assert(ICD_MAX_NOTIFICATION_SUBSCRIBERS > 0, "At least 1 Subscriber is required for the ICD Manager"); + +class ICDListener +{ +public: + enum class KeepActiveFlags : uint8_t + { + kCommissioningWindowOpen = 0x01, + kFailSafeArmed = 0x02, + kExchangeContextOpen = 0x03, + }; + + virtual ~ICDListener() {} + + /** + * @brief This function is called for all subscribers of the ICDNotifier when it calls BroadcastNetworkActivityNotification + * It notifies the subscriber that a NetworkActivity occurred. For example, a message sent or received. + */ + virtual void OnNetworkActivity() = 0; + + /** + * @brief This function is called for all subscribers of the ICDNotifier when it calls BroadcastActiveRequestNotification + * It informs the subscriber that there is a need to place and keep the ICD in its Active Mode. + * + * @param request : Identity the request source + */ + virtual void OnKeepActiveRequest(KeepActiveFlags request) = 0; + + /** + * @brief This function is called for all subscribers of the ICDNotifier when it calls BroadcastActiveRequestWithdrawal + * It informs the subscriber that a previous request no longer needs ICD to maintain its Active Mode. + * + * @param request : The request source + */ + virtual void OnActiveRequestWithdrawal(KeepActiveFlags request) = 0; +}; + +class ICDNotifier +{ +public: + ~ICDNotifier(); + CHIP_ERROR Subscribe(ICDListener * subscriber); + void Unsubscribe(ICDListener * subscriber); + + /** + * The following Broacast* methods triggers all the registered ICDSubscribers related callback + * For thread-safety reason (mostly of the ICDManager, which is a full time subscriber), + * Those functions require to be called from the Chip Task Context, or by holding the chip stack lock. + */ + void BroadcastNetworkActivityNotification(); + void BroadcastActiveRequestNotification(ICDListener::KeepActiveFlags request); + void BroadcastActiveRequestWithdrawal(ICDListener::KeepActiveFlags request); + + static ICDNotifier & GetInstance() { return sICDNotifier; } + +private: + static ICDNotifier sICDNotifier; + ICDListener * mSubscribers[ICD_MAX_NOTIFICATION_SUBSCRIBERS] = {}; +}; + +} // namespace app +} // namespace chip diff --git a/src/app/icd/icd.gni b/src/app/icd/icd.gni index a576c8303c4fd6..cfb5a1a183be8a 100644 --- a/src/app/icd/icd.gni +++ b/src/app/icd/icd.gni @@ -23,4 +23,5 @@ declare_args() { # Matter SDK Configuration flag to make the ICD manager emit a report on entering active mode chip_report_on_active_mode = false + icd_max_notification_subscribers = 1 } diff --git a/src/app/server/BUILD.gn b/src/app/server/BUILD.gn index d77162909ee5e6..992b9454d11b04 100644 --- a/src/app/server/BUILD.gn +++ b/src/app/server/BUILD.gn @@ -50,6 +50,7 @@ static_library("server") { public_deps = [ "${chip_root}/src/app", + "${chip_root}/src/app/icd:notifier", "${chip_root}/src/lib/address_resolve", "${chip_root}/src/lib/dnssd", "${chip_root}/src/messaging", diff --git a/src/app/server/CommissioningWindowManager.cpp b/src/app/server/CommissioningWindowManager.cpp index 270f4dad57cb39..33eaeedb1eb619 100644 --- a/src/app/server/CommissioningWindowManager.cpp +++ b/src/app/server/CommissioningWindowManager.cpp @@ -15,6 +15,7 @@ * limitations under the License. */ +#include #include #include #include @@ -543,13 +544,14 @@ void CommissioningWindowManager::UpdateWindowStatus(CommissioningWindowStatusEnu { mWindowStatus = aNewStatus; #if CHIP_CONFIG_ENABLE_ICD_SERVER - DeviceLayer::ChipDeviceEvent event; - event.Type = DeviceLayer::DeviceEventType::kCommissioningWindowStatusChanged; - event.CommissioningWindowStatus.open = (mWindowStatus != CommissioningWindowStatusEnum::kWindowNotOpen); - CHIP_ERROR err = DeviceLayer::PlatformMgr().PostEvent(&event); - if (err != CHIP_NO_ERROR) + app::ICDListener::KeepActiveFlags request = app::ICDListener::KeepActiveFlags::kCommissioningWindowOpen; + if (mWindowStatus != CommissioningWindowStatusEnum::kWindowNotOpen) + { + app::ICDNotifier::GetInstance().BroadcastActiveRequestNotification(request); + } + else { - ChipLogError(AppServer, "Failed to post kCommissioningWindowStatusChanged event %" CHIP_ERROR_FORMAT, err.Format()); + app::ICDNotifier::GetInstance().BroadcastActiveRequestWithdrawal(request); } #endif // CHIP_CONFIG_ENABLE_ICD_SERVER } diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index 81ac0b15a755ba..cdeb7672491730 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -258,7 +258,6 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) // ICD Init needs to be after data model init #if CHIP_CONFIG_ENABLE_ICD_SERVER mICDManager.Init(mDeviceStorage, &GetFabricTable(), mReportScheduler); - mICDEventManager.Init(&mICDManager); #endif // CHIP_CONFIG_ENABLE_ICD_SERVER #if defined(CHIP_APP_USE_ECHO) @@ -496,7 +495,6 @@ void Server::Shutdown() Access::ResetAccessControlToDefault(); Credentials::SetGroupDataProvider(nullptr); #if CHIP_CONFIG_ENABLE_ICD_SERVER - mICDEventManager.Shutdown(); mICDManager.Shutdown(); #endif // CHIP_CONFIG_ENABLE_ICD_SERVER mAttributePersister.Shutdown(); diff --git a/src/app/server/Server.h b/src/app/server/Server.h index 7c77c9122b0f33..89dc4f12cf7132 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -68,8 +68,7 @@ #include #if CHIP_CONFIG_ENABLE_ICD_SERVER -#include // nogncheck -#include // nogncheck +#include // nogncheck #endif namespace chip { @@ -619,7 +618,6 @@ class Server System::Clock::Microseconds64 mInitTimestamp; #if CHIP_CONFIG_ENABLE_ICD_SERVER - app::ICDEventManager mICDEventManager; app::ICDManager mICDManager; #endif // CHIP_CONFIG_ENABLE_ICD_SERVER }; diff --git a/src/app/tests/TestICDManager.cpp b/src/app/tests/TestICDManager.cpp index 5af3a0efcf4bed..5d57be4245f116 100644 --- a/src/app/tests/TestICDManager.cpp +++ b/src/app/tests/TestICDManager.cpp @@ -17,6 +17,7 @@ */ #include #include +#include #include #include #include @@ -124,7 +125,7 @@ class TestICDManager // Events updating the Operation to Active mode can extend the current active mode time by 1 Active mode threshold. // Kick an active Threshold just before the end of the Active interval and validate that the active mode is extended. AdvanceClockAndRunEventLoop(ctx, IcdManagementServer::GetInstance().GetActiveModeIntervalMs() - 1); - ctx->mICDManager.UpdateOperationState(ICDManager::OperationalState::ActiveMode); + ICDNotifier::GetInstance().BroadcastNetworkActivityNotification(); AdvanceClockAndRunEventLoop(ctx, IcdManagementServer::GetInstance().GetActiveModeThresholdMs() / 2); NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); AdvanceClockAndRunEventLoop(ctx, IcdManagementServer::GetInstance().GetActiveModeThresholdMs()); @@ -134,9 +135,11 @@ class TestICDManager static void TestKeepActivemodeRequests(nlTestSuite * aSuite, void * aContext) { TestContext * ctx = static_cast(aContext); + typedef ICDListener::KeepActiveFlags ActiveFlag; + ICDNotifier notifier = ICDNotifier::GetInstance(); // Setting a requirement will transition the ICD to active mode. - ctx->mICDManager.SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kCommissioningWindowOpen, true); + notifier.BroadcastActiveRequestNotification(ActiveFlag::kCommissioningWindowOpen); NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); // Advance time so active mode interval expires. AdvanceClockAndRunEventLoop(ctx, IcdManagementServer::GetInstance().GetActiveModeIntervalMs() + 1); @@ -144,17 +147,17 @@ class TestICDManager NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); // Remove requirement. we should directly transition to idle mode. - ctx->mICDManager.SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kCommissioningWindowOpen, false); + notifier.BroadcastActiveRequestWithdrawal(ActiveFlag::kCommissioningWindowOpen); NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode); - ctx->mICDManager.SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kFailSafeArmed, true); + notifier.BroadcastActiveRequestNotification(ActiveFlag::kFailSafeArmed); // Requirement will transition us to active mode. NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); // Advance time, but by less than the active mode interval and remove the requirement. // We should stay in active mode. AdvanceClockAndRunEventLoop(ctx, IcdManagementServer::GetInstance().GetActiveModeIntervalMs() / 2); - ctx->mICDManager.SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kFailSafeArmed, false); + notifier.BroadcastActiveRequestWithdrawal(ActiveFlag::kFailSafeArmed); NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); // Advance time again, The activemode interval is completed. @@ -162,8 +165,8 @@ class TestICDManager NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode); // Set two requirements - ctx->mICDManager.SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kExpectingMsgResponse, true); - ctx->mICDManager.SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kAwaitingMsgAck, true); + notifier.BroadcastActiveRequestNotification(ActiveFlag::kFailSafeArmed); + notifier.BroadcastActiveRequestNotification(ActiveFlag::kExchangeContextOpen); NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); // advance time so the active mode interval expires. AdvanceClockAndRunEventLoop(ctx, IcdManagementServer::GetInstance().GetActiveModeIntervalMs() + 1); @@ -171,10 +174,10 @@ class TestICDManager NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); // remove 1 requirement. Active mode is maintained - ctx->mICDManager.SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kExpectingMsgResponse, false); + notifier.BroadcastActiveRequestWithdrawal(ActiveFlag::kFailSafeArmed); NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); // remove the last requirement - ctx->mICDManager.SetKeepActiveModeRequirements(ICDManager::KeepActiveFlags::kAwaitingMsgAck, false); + notifier.BroadcastActiveRequestWithdrawal(ActiveFlag::kExchangeContextOpen); NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode); } }; diff --git a/src/include/platform/CHIPDeviceEvent.h b/src/include/platform/CHIPDeviceEvent.h index 47573ba348fd78..bf4c55ca438377 100644 --- a/src/include/platform/CHIPDeviceEvent.h +++ b/src/include/platform/CHIPDeviceEvent.h @@ -195,11 +195,6 @@ enum PublicEventTypes */ kInterfaceIpAddressChanged, - /** - * Signals that the commissioning window has opened or closed. - */ - kCommissioningWindowStatusChanged, - /** * Commissioning has completed by a call to the general commissioning cluster command. */ @@ -211,11 +206,6 @@ enum PublicEventTypes */ kFailSafeTimerExpired, - /** - * Signals that the fail-safe state changed (Armed/Disarmed) - */ - kFailSafeStateChanged, - /** * */ @@ -249,45 +239,6 @@ enum PublicEventTypes * sending messages to other nodes. */ kServerReady, - - /** - * TODO ICD: kChipMsgSentEvent and kChipMsgRxEventHandled should be InternalEventTypes. - * However the ICD manager leverages those events and its event handler is registered as an application - * event handler. - * ICDEventManager will have to expose 'ICDEventHandler' publicly to 'DispatchEventToDeviceLayer'. - */ - - /** - * An Exchange Context sent a message. - * This event contains a with MessageSent structure. - */ - kChipMsgSentEvent, - - /** - * An Exchange Context that was waiting for a response is no longer waiting for it. - * This event can occur due to any of the following: - * - A response message was received. - * - An exchange context timed out waiting for a response. - * - The exchange context was closed while a response was expected. - * - * This event contains an RxEventContext structure. - */ - kChipMsgRxEventHandled, - - /** - * This event is used to sync the ICD with any Reliable Message exchange - * expecting an ack. The ICD shall stay in active Mode - * until the Reliable Message exchange post this event again - * informing the ICD that it is no longer awaiting the ack. - * - * This event contains an AckSync structure. - */ - kICDMsgAckSyncEvent, - - /** - * An application event occured that should wake up the system/device - */ - kAppWakeUpEvent, }; /** @@ -575,30 +526,6 @@ struct ChipDeviceEvent final { OtaState newState; } OtaStateChanged; - - struct - { - bool ExpectResponse; - } MessageSent; - - struct - { - /* - * wasReceived is only true when the event was triggered by a response message reception. - * See the brief of kChipMsgRxEventHandled, above in this file, for additional details. - */ - bool wasReceived; - bool clearsExpectedResponse; - } RxEventContext; - - struct - { - /* - * Set to true when a Reliable Message Context is awaiting for a ack to a message sent - * Set to false when the Reliable Message Context is no longer awaiting for a ack - */ - bool awaitingAck; - } AckSync; }; void Clear() { memset(this, 0, sizeof(*this)); } diff --git a/src/messaging/BUILD.gn b/src/messaging/BUILD.gn index 3c64ef64ebd3f1..cd805fb52facae 100644 --- a/src/messaging/BUILD.gn +++ b/src/messaging/BUILD.gn @@ -67,6 +67,7 @@ static_library("messaging") { public_deps = [ ":messaging_mrp_config", + "${chip_root}/src/app/icd:notifier", "${chip_root}/src/crypto", "${chip_root}/src/inet", "${chip_root}/src/lib/core", @@ -78,6 +79,6 @@ static_library("messaging") { ] if (chip_enable_icd_server) { - public_deps += [ "${chip_root}/src/app/icd:server-srcs" ] + public_deps += [ "${chip_root}/src/app/icd:manager-srcs" ] } } diff --git a/src/messaging/ExchangeContext.cpp b/src/messaging/ExchangeContext.cpp index 381c75d326b4ba..ac4a620a7ee66a 100644 --- a/src/messaging/ExchangeContext.cpp +++ b/src/messaging/ExchangeContext.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -46,10 +47,6 @@ #include #include -#if CONFIG_DEVICE_LAYER -#include -#endif - using namespace chip::Encoding; using namespace chip::Inet; using namespace chip::System; @@ -202,16 +199,9 @@ CHIP_ERROR ExchangeContext::SendMessage(Protocols::Id protocolId, uint8_t msgTyp } else { -#if CONFIG_DEVICE_LAYER && CHIP_CONFIG_ENABLE_ICD_SERVER - DeviceLayer::ChipDeviceEvent event; - event.Type = DeviceLayer::DeviceEventType::kChipMsgSentEvent; - event.MessageSent.ExpectResponse = currentMessageExpectResponse; - CHIP_ERROR status = DeviceLayer::PlatformMgr().PostEvent(&event); - if (status != CHIP_NO_ERROR) - { - ChipLogError(DeviceLayer, "Failed to post Message sent event %" CHIP_ERROR_FORMAT, status.Format()); - } -#endif // CONFIG_DEVICE_LAYER +#if CHIP_CONFIG_ENABLE_ICD_SERVER + app::ICDNotifier::GetInstance().BroadcastNetworkActivityNotification(); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER // Standalone acks are not application-level message sends. if (!isStandaloneAck) @@ -261,18 +251,6 @@ void ExchangeContext::DoClose(bool clearRetransTable) { // Cancel the response timer. CancelResponseTimer(); -#if CONFIG_DEVICE_LAYER && CHIP_CONFIG_ENABLE_ICD_SERVER - DeviceLayer::ChipDeviceEvent event; - event.Type = DeviceLayer::DeviceEventType::kChipMsgRxEventHandled; - event.RxEventContext.wasReceived = false; - event.RxEventContext.clearsExpectedResponse = true; - CHIP_ERROR status = DeviceLayer::PlatformMgr().PostEvent(&event); - if (status != CHIP_NO_ERROR) - { - ChipLogError(DeviceLayer, "Failed to post Msg Handled event at ExchangeContext closure %" CHIP_ERROR_FORMAT, - status.Format()); - } -#endif // CONFIG_DEVICE_LAYER && CHIP_CONFIG_ENABLE_ICD_SERVER } } @@ -347,6 +325,10 @@ ExchangeContext::ExchangeContext(ExchangeManager * em, uint16_t ExchangeId, cons // Do not request Ack for multicast SetAutoRequestAck(!session->IsGroupSession()); +#if CHIP_CONFIG_ENABLE_ICD_SERVER + app::ICDNotifier::GetInstance().BroadcastActiveRequestNotification(app::ICDListener::KeepActiveFlags::kExchangeContextOpen); +#endif + #if defined(CHIP_EXCHANGE_CONTEXT_DETAIL_LOGGING) ChipLogDetail(ExchangeManager, "ec++ id: " ChipLogFormatExchange, ChipLogValueExchange(this)); #endif @@ -362,6 +344,10 @@ ExchangeContext::~ExchangeContext() // VerifyOrDie(mFlags.Has(Flags::kFlagClosed)); +#if CHIP_CONFIG_ENABLE_ICD_SERVER + app::ICDNotifier::GetInstance().BroadcastActiveRequestWithdrawal(app::ICDListener::KeepActiveFlags::kExchangeContextOpen); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER + // Ideally, in this scenario, the retransmit table should // be clear of any outstanding messages for this context and // the boolean parameter passed to DoClose() should not matter. @@ -478,18 +464,6 @@ void ExchangeContext::HandleResponseTimeout(System::Layer * aSystemLayer, void * void ExchangeContext::NotifyResponseTimeout(bool aCloseIfNeeded) { -#if CONFIG_DEVICE_LAYER && CHIP_CONFIG_ENABLE_ICD_SERVER - DeviceLayer::ChipDeviceEvent event; - event.Type = DeviceLayer::DeviceEventType::kChipMsgRxEventHandled; - event.RxEventContext.wasReceived = false; - event.RxEventContext.clearsExpectedResponse = true; - CHIP_ERROR status = DeviceLayer::PlatformMgr().PostEvent(&event); - if (status != CHIP_NO_ERROR) - { - ChipLogError(DeviceLayer, "Failed to post Message Response Timeout event %" CHIP_ERROR_FORMAT, status.Format()); - } -#endif // CONFIG_DEVICE_LAYER && CHIP_CONFIG_ENABLE_ICD_SERVER - // Grab the value of WaitingForResponseOrAck() before we mess with our state. bool gotMRPAck = !WaitingForResponseOrAck(); @@ -611,17 +585,10 @@ CHIP_ERROR ExchangeContext::HandleMessage(uint32_t messageCounter, const Payload return CHIP_ERROR_INCORRECT_STATE; } -#if CONFIG_DEVICE_LAYER && CHIP_CONFIG_ENABLE_ICD_SERVER - DeviceLayer::ChipDeviceEvent event; - event.Type = DeviceLayer::DeviceEventType::kChipMsgRxEventHandled; - event.RxEventContext.wasReceived = true; - event.RxEventContext.clearsExpectedResponse = IsResponseExpected(); - CHIP_ERROR status = DeviceLayer::PlatformMgr().PostEvent(&event); - if (status != CHIP_NO_ERROR) - { - ChipLogError(DeviceLayer, "Failed to post Message received event %" CHIP_ERROR_FORMAT, status.Format()); - } -#endif // CONFIG_DEVICE_LAYER && CHIP_CONFIG_ENABLE_ICD_SERVER +#if CHIP_CONFIG_ENABLE_ICD_SERVER + // message received + app::ICDNotifier::GetInstance().BroadcastNetworkActivityNotification(); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER if (IsResponseExpected()) { diff --git a/src/messaging/ReliableMessageContext.cpp b/src/messaging/ReliableMessageContext.cpp index 4d3139d6945d2a..ece92063ffff69 100644 --- a/src/messaging/ReliableMessageContext.cpp +++ b/src/messaging/ReliableMessageContext.cpp @@ -58,17 +58,6 @@ ReliableMessageMgr * ReliableMessageContext::GetReliableMessageMgr() void ReliableMessageContext::SetWaitingForAck(bool waitingForAck) { mFlags.Set(Flags::kFlagWaitingForAck, waitingForAck); - -#if CONFIG_DEVICE_LAYER && CHIP_CONFIG_ENABLE_ICD_SERVER - DeviceLayer::ChipDeviceEvent event; - event.Type = DeviceLayer::DeviceEventType::kICDMsgAckSyncEvent; - event.AckSync.awaitingAck = waitingForAck; - CHIP_ERROR status = DeviceLayer::PlatformMgr().PostEvent(&event); - if (status != CHIP_NO_ERROR) - { - ChipLogError(DeviceLayer, "Failed to post AckSync event %" CHIP_ERROR_FORMAT, status.Format()); - } -#endif } CHIP_ERROR ReliableMessageContext::FlushAcks() diff --git a/src/messaging/ReliableMessageMgr.cpp b/src/messaging/ReliableMessageMgr.cpp index 16f0f6e642c90b..f0ee9f380092fe 100644 --- a/src/messaging/ReliableMessageMgr.cpp +++ b/src/messaging/ReliableMessageMgr.cpp @@ -38,7 +38,8 @@ #include #if CHIP_CONFIG_ENABLE_ICD_SERVER -#include // nogncheck +#include // nogncheck +#include // nogncheck #endif using namespace chip::System::Clock::Literals; @@ -320,19 +321,9 @@ CHIP_ERROR ReliableMessageMgr::SendFromRetransTable(RetransTableEntry * entry) if (err == CHIP_NO_ERROR) { -#if CONFIG_DEVICE_LAYER && CHIP_CONFIG_ENABLE_ICD_SERVER - DeviceLayer::ChipDeviceEvent event; - // Here always set ExpectResponse to false. - // The Initial message sent from the Exchange Context will have set ExpectResponse to the correct value. - // If we are expecting a Response, the ICD will already be in a state waiting for the response (or timeout). - event.Type = DeviceLayer::DeviceEventType::kChipMsgSentEvent; - event.MessageSent.ExpectResponse = false; - CHIP_ERROR status = DeviceLayer::PlatformMgr().PostEvent(&event); - if (status != CHIP_NO_ERROR) - { - ChipLogError(DeviceLayer, "Failed to post retransmit message sent event %" CHIP_ERROR_FORMAT, status.Format()); - } -#endif // CONFIG_DEVICE_LAYER +#if CHIP_CONFIG_ENABLE_ICD_SERVER + app::ICDNotifier::GetInstance().BroadcastNetworkActivityNotification(); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER #if CHIP_CONFIG_RESOLVE_PEER_ON_FIRST_TRANSMIT_FAILURE const ExchangeManager * exchangeMgr = entry->ec->GetExchangeMgr(); // TODO: investigate why in ReliableMessageMgr::CheckResendApplicationMessageWithPeerExchange unit test released exchange