From 577304a5b6e2d4f4914d47d2121ba1e37dc931af Mon Sep 17 00:00:00 2001 From: Anand Date: Sat, 2 May 2026 10:55:28 +0000 Subject: [PATCH 1/9] RDK-61440: Implementation of handling PowerMode Change in NM plugin Reason for change: Ctrl ntwrk state based on PowerMode transitions Test procedure: Change the PowerMode state and verify the behavior Risks: low Priority: P1 Signed-off-by: Anand N --- plugin/CMakeLists.txt | 12 ++ plugin/NetworkManagerImplementation.cpp | 61 ++++++++ plugin/NetworkManagerImplementation.h | 18 +++ plugin/NetworkManagerPowerClient.cpp | 187 ++++++++++++++++++++++++ plugin/NetworkManagerPowerClient.h | 155 ++++++++++++++++++++ 5 files changed, 433 insertions(+) create mode 100644 plugin/NetworkManagerPowerClient.cpp create mode 100644 plugin/NetworkManagerPowerClient.h diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt index d834861d..18b4dad5 100644 --- a/plugin/CMakeLists.txt +++ b/plugin/CMakeLists.txt @@ -26,6 +26,13 @@ find_package(${NAMESPACE}Core REQUIRED) find_package(${NAMESPACE}Plugins REQUIRED) find_package(CURL) +option(ENABLE_POWERMANAGER "Enable PowerManager COMRPC integration for DeepSleep WiFi management" ON) +if(ENABLE_POWERMANAGER) + find_package(${NAMESPACE}Definitions REQUIRED) + add_definitions(-DENABLE_POWERMANAGER) + message("NetworkManager: PowerManager integration enabled") +endif() + if (USE_RDK_LOGGER) find_package(rdklogger REQUIRED) add_definitions(-DUSE_RDK_LOGGER) @@ -83,6 +90,11 @@ add_library(${MODULE_IMPL_NAME} SHARED NetworkManagerLogger.cpp Module.cpp) +if(ENABLE_POWERMANAGER) + target_sources(${MODULE_IMPL_NAME} PRIVATE NetworkManagerPowerClient.cpp) + target_link_libraries(${MODULE_IMPL_NAME} PRIVATE ${NAMESPACE}Definitions::${NAMESPACE}Definitions) +endif() + if(ENABLE_GNOME_NETWORKMANAGER) if(ENABLE_GNOME_GDBUS) message("networkmanager building with gdbus") diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp index b4b5bca6..5185d26d 100644 --- a/plugin/NetworkManagerImplementation.cpp +++ b/plugin/NetworkManagerImplementation.cpp @@ -20,6 +20,9 @@ #include #include #include "NetworkManagerImplementation.h" +#ifdef ENABLE_POWERMANAGER +#include "NetworkManagerPowerClient.h" +#endif #if USE_TELEMETRY #include "NetworkManagerJsonEnum.h" @@ -71,6 +74,9 @@ namespace WPEFramework NetworkManagerImplementation::~NetworkManagerImplementation() { NMLOG_INFO("NetworkManager Out-Of-Process Shutdown/Cleanup"); +#ifdef ENABLE_POWERMANAGER + _powerClient.reset(); +#endif connectivityMonitor.stopConnectivityMonitor(); _instance = nullptr; platform_deinit(); @@ -199,6 +205,9 @@ namespace WPEFramework NetworkManagerImplementation::platform_init(); /* change gnome networkmanager or netsrvmgr logg level */ NetworkManagerImplementation::platform_logging(static_cast (config.loglevel.Value())); +#ifdef ENABLE_POWERMANAGER + _powerClient.reset(new NetworkManagerPowerClient(*this)); +#endif return(Core::ERROR_NONE); } @@ -1184,5 +1193,57 @@ namespace WPEFramework } #endif } + +#ifdef ENABLE_POWERMANAGER + void NetworkManagerImplementation::OnPowerModePreChange( + const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState, + std::function sendAck) + { + NMLOG_INFO("OnPowerModePreChange: current=%d new=%d", + static_cast(currentState), static_cast(newState)); + + using PowerState = Exchange::IPowerManager::PowerState; + + if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { + // Transitioning TO DeepSleep + bool standbyMode = _powerClient ? _powerClient->getNetworkStandbyMode() : false; + if (!standbyMode) { + NMLOG_INFO("OnPowerModePreChange: going to DeepSleep, Network Standby OFF — disconnecting WiFi"); + // WiFiDisconnect(); + } else { + NMLOG_INFO("OnPowerModePreChange: going to DeepSleep, Network Standby ON — WiFi left connected"); + } + sendAck(); + } else if (currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { + // Waking FROM DeepSleep + bool standbyMode = _powerClient ? _powerClient->getNetworkStandbyMode() : false; + if (!standbyMode) { + if (!m_lastConnectedSSID.empty()) { + NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep, Network Standby OFF — reconnecting to '%s'", + m_lastConnectedSSID.c_str()); + // ConnectToKnownSSID(m_lastConnectedSSID); // fire-and-forget + } else { + NMLOG_WARNING("OnPowerModePreChange: waking from DeepSleep, Network Standby OFF — no last SSID, skipping reconnect"); + } + } else { + NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep, Network Standby ON — no reconnect needed"); + } + sendAck(); + } else { + // All other transitions: fast-path ack + sendAck(); + } + } + + void NetworkManagerImplementation::OnPowerModeChanged( + const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState) + { + NMLOG_INFO("OnPowerModeChanged: current=%d new=%d", + static_cast(currentState), static_cast(newState)); + // Reserved for future use (e.g., suppress connectivity checks during deep sleep) + } +#endif // ENABLE_POWERMANAGER } } diff --git a/plugin/NetworkManagerImplementation.h b/plugin/NetworkManagerImplementation.h index f5bd49b1..15d6159f 100644 --- a/plugin/NetworkManagerImplementation.h +++ b/plugin/NetworkManagerImplementation.h @@ -34,6 +34,9 @@ using namespace std; #include "NetworkManagerLogger.h" #include "NetworkManagerConnectivity.h" #include "NetworkManagerStunClient.h" +#ifdef ENABLE_POWERMANAGER +#include "NetworkManagerPowerClient.h" +#endif /* Forward declarations to avoid pulling GLib/libnm headers into this header */ typedef struct _NMClient NMClient; @@ -63,6 +66,9 @@ namespace WPEFramework namespace Plugin { class NetworkManagerImplementation : public Exchange::INetworkManager +#ifdef ENABLE_POWERMANAGER + , public INetworkPowerCallback +#endif { enum NetworkEvents { @@ -277,6 +283,15 @@ namespace WPEFramework void ReportWiFiSignalQualityChange(const string ssid, const int strength, const int noise, const int snr, const Exchange::INetworkManager::WiFiSignalQuality quality); void logTelemetry(const std::string& eventName, const std::string& message); +#ifdef ENABLE_POWERMANAGER + // INetworkPowerCallback overrides + void OnPowerModePreChange(const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState, + std::function sendAck) override; + void OnPowerModeChanged(const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState) override; +#endif + private: void platform_init(void); void platform_deinit(void); @@ -314,6 +329,9 @@ namespace WPEFramework std::atomic m_stopThread{false}; std::mutex m_condVariableMutex; std::condition_variable m_condVariable; +#ifdef ENABLE_POWERMANAGER + std::unique_ptr _powerClient; +#endif public: IPAddress m_ethIPv4Address; IPAddress m_wlanIPv4Address; diff --git a/plugin/NetworkManagerPowerClient.cpp b/plugin/NetworkManagerPowerClient.cpp new file mode 100644 index 00000000..c9d5efc8 --- /dev/null +++ b/plugin/NetworkManagerPowerClient.cpp @@ -0,0 +1,187 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* 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. +**/ + +#ifdef ENABLE_POWERMANAGER + +#include "NetworkManagerPowerClient.h" +#include "NetworkManagerLogger.h" +#include + +using namespace WPEFramework; +using namespace WPEFramework::Exchange; +using namespace WPEFramework::Plugin; + +// --------------------------------------------------------------------------- +// NetworkManagerPowerClient +// --------------------------------------------------------------------------- + +NetworkManagerPowerClient::NetworkManagerPowerClient(INetworkPowerCallback& callback) + : mPreChangeNotification(callback, *this) + , mChangedNotification(callback) +{ + NMLOG_INFO("NetworkManagerPowerClient: connecting to PowerManager"); + if (auto r = Open(RPC::CommunicationTimeOut, Connector(), "org.rdk.PowerManager"); r == Core::ERROR_NONE) { + // Connected; Operational() will be called by the framework when the proxy is ready + } else { + NMLOG_ERROR("NetworkManagerPowerClient: failed to open link to PowerManager (error %u)", r); + } +} + +NetworkManagerPowerClient::~NetworkManagerPowerClient() +{ + NMLOG_INFO("NetworkManagerPowerClient: shutting down"); + Close(Core::infinite); + unregisterEventsAndDeactivate(); +} + +bool NetworkManagerPowerClient::IsValid() const +{ + return mPowerManager != nullptr; +} + +bool NetworkManagerPowerClient::getNetworkStandbyMode() const +{ + bool standbyMode = false; + if (IsValid()) { + if (auto r = mPowerManager->GetNetworkStandbyMode(standbyMode); r != Core::ERROR_NONE) { + NMLOG_ERROR("NetworkManagerPowerClient: GetNetworkStandbyMode failed (%u)", r); + } + } + return standbyMode; +} + +void NetworkManagerPowerClient::sendPowerModePreChangeComplete(int transactionId) +{ + if (IsValid()) { + // Return value intentionally ignored; a stale transactionId is harmless + mPowerManager->PowerModePreChangeComplete(mClientId, transactionId); + } +} + +void NetworkManagerPowerClient::sendDelayPowerModeChange(int transactionId, int seconds) +{ + if (IsValid()) { + if (auto r = mPowerManager->DelayPowerModeChangeBy(mClientId, transactionId, seconds); r != Core::ERROR_NONE) { + NMLOG_ERROR("NetworkManagerPowerClient: DelayPowerModeChangeBy failed (%u)", r); + } + } +} + +void NetworkManagerPowerClient::Operational(bool upAndRunning) +{ + NMLOG_INFO("NetworkManagerPowerClient::Operational(%s)", upAndRunning ? "true" : "false"); + if (upAndRunning) { + if (!IsValid()) { + mPowerManager = Interface(); + registerEvents(); + } + } else { + unregisterEventsAndDeactivate(); + } +} + +void NetworkManagerPowerClient::registerEvents() +{ + NMLOG_INFO("NetworkManagerPowerClient: registering events"); + if (!IsValid()) { + NMLOG_ERROR("NetworkManagerPowerClient: not in valid state, skipping event registration"); + return; + } + if (auto r = mPowerManager->AddPowerModePreChangeClient("org.rdk.NetworkManager", mClientId); r != Core::ERROR_NONE) { + NMLOG_ERROR("NetworkManagerPowerClient: AddPowerModePreChangeClient failed (%u)", r); + } + if (auto r = mPowerManager->Register(&mPreChangeNotification); r != Core::ERROR_NONE) { + NMLOG_ERROR("NetworkManagerPowerClient: Register(preChange) failed (%u)", r); + } + if (auto r = mPowerManager->Register(&mChangedNotification); r != Core::ERROR_NONE) { + NMLOG_ERROR("NetworkManagerPowerClient: Register(changed) failed (%u)", r); + } +} + +void NetworkManagerPowerClient::unregisterEvents() +{ + NMLOG_INFO("NetworkManagerPowerClient: unregistering events"); + if (!IsValid()) { + NMLOG_ERROR("NetworkManagerPowerClient: not in valid state, skipping event unregistration"); + return; + } + // NOTE: RemovePowerModePreChangeClient MUST be called before Unregister(IModePreChangeNotification) + // per the IPowerManager API contract. + if (auto r = mPowerManager->RemovePowerModePreChangeClient(mClientId); r != Core::ERROR_NONE) { + NMLOG_ERROR("NetworkManagerPowerClient: RemovePowerModePreChangeClient failed (%u)", r); + } + if (auto r = mPowerManager->Unregister(&mPreChangeNotification); r != Core::ERROR_NONE) { + NMLOG_ERROR("NetworkManagerPowerClient: Unregister(preChange) failed (%u)", r); + } + if (auto r = mPowerManager->Unregister(&mChangedNotification); r != Core::ERROR_NONE) { + NMLOG_ERROR("NetworkManagerPowerClient: Unregister(changed) failed (%u)", r); + } +} + +void NetworkManagerPowerClient::unregisterEventsAndDeactivate() +{ + if (IsValid()) { + unregisterEvents(); + mPowerManager->Release(); + mPowerManager = nullptr; + } +} + +// --------------------------------------------------------------------------- +// PreChangeNotification +// --------------------------------------------------------------------------- + +void NetworkManagerPowerClient::PreChangeNotification::OnPowerModePreChange( + const PowerState currentState, const PowerState newState, + const int transactionId, const int stateChangeAfter) +{ + NMLOG_INFO("NetworkManagerPowerClient::OnPowerModePreChange current=%d new=%d txId=%d after=%ds", + static_cast(currentState), static_cast(newState), transactionId, stateChangeAfter); + + auto sendAck = [transactionId, this]() { + mClient.sendPowerModePreChangeComplete(transactionId); + }; + + if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { + // Transitioning TO DeepSleep: request a 5-second window so WiFiDisconnect + // can complete before PowerManager proceeds. + mClient.sendDelayPowerModeChange(transactionId, 5); + mCallback.OnPowerModePreChange(currentState, newState, sendAck); + } else if (currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { + // Waking FROM DeepSleep: reconnect is fire-and-forget; no delay needed. + mCallback.OnPowerModePreChange(currentState, newState, sendAck); + } else { + // All other transitions: fast-path ack, no network action needed. + sendAck(); + } +} + +// --------------------------------------------------------------------------- +// ChangedNotification +// --------------------------------------------------------------------------- + +void NetworkManagerPowerClient::ChangedNotification::OnPowerModeChanged( + const PowerState currentState, const PowerState newState) +{ + NMLOG_INFO("NetworkManagerPowerClient::OnPowerModeChanged current=%d new=%d", + static_cast(currentState), static_cast(newState)); + mCallback.OnPowerModeChanged(currentState, newState); +} + +#endif // ENABLE_POWERMANAGER diff --git a/plugin/NetworkManagerPowerClient.h b/plugin/NetworkManagerPowerClient.h new file mode 100644 index 00000000..7fa995d6 --- /dev/null +++ b/plugin/NetworkManagerPowerClient.h @@ -0,0 +1,155 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* 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 + +#ifdef ENABLE_POWERMANAGER + +#include "Module.h" +#include +#include + +namespace WPEFramework { +namespace Plugin { + +/** + * Callback interface that decouples NetworkManagerPowerClient from + * NetworkManagerImplementation. The implementation receives power state + * transitions and must call sendAck() exactly once per OnPowerModePreChange + * invocation to release the PowerManager pre-change lock. + */ +struct INetworkPowerCallback { + virtual ~INetworkPowerCallback() = default; + + /** + * Called when a power mode pre-change event arrives that involves + * POWER_STATE_STANDBY_DEEP_SLEEP (either as the new state or the current + * state). The implementation MUST call sendAck() exactly once. + */ + virtual void OnPowerModePreChange(const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState, + std::function sendAck) = 0; + + /** + * Called when a power mode changed event arrives (informational only; + * no ack required). + */ + virtual void OnPowerModeChanged(const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState) = 0; +}; + +/** + * Thunder COMRPC client to the PowerManager plugin. + * + * Follows the same pattern as Mediarite's PowerManagerPluginClient: + * - Inherits SmartInterfaceType for automatic + * reconnect / Operational() lifecycle callbacks. + * - Registers as an AddPowerModePreChangeClient so it participates + * in the pre-change ack protocol. + * - Delegates DeepSleep transitions to INetworkPowerCallback. + * - Sends a fast-path PowerModePreChangeComplete for all other transitions. + * + * Lifecycle: + * Construction → Open() connects to PowerManager (async). + * Operational(true) → registers events; IsValid() returns true. + * Operational(false) → unregisters events and releases proxy. + * Destruction → Close() then unregisterEventsAndDeactivate(). + */ +class NetworkManagerPowerClient : protected RPC::SmartInterfaceType { +public: + using PowerState = Exchange::IPowerManager::PowerState; + + explicit NetworkManagerPowerClient(INetworkPowerCallback& callback); + ~NetworkManagerPowerClient() override; + + NetworkManagerPowerClient(const NetworkManagerPowerClient&) = delete; + NetworkManagerPowerClient& operator=(const NetworkManagerPowerClient&) = delete; + + /** Returns true when the PowerManager COMRPC proxy is available. */ + bool IsValid() const; + + /** Queries the current Network Standby mode from PowerManager. */ + bool getNetworkStandbyMode() const; + + /** Sends PowerModePreChangeComplete to PowerManager; ignores return value. */ + void sendPowerModePreChangeComplete(int transactionId); + + /** Requests a delay window extension via DelayPowerModeChangeBy. */ + void sendDelayPowerModeChange(int transactionId, int seconds); + +private: + // ----------------------------------------------------------------------- + // IModePreChangeNotification sink + // ----------------------------------------------------------------------- + class PreChangeNotification : public Exchange::IPowerManager::IModePreChangeNotification { + public: + PreChangeNotification(INetworkPowerCallback& callback, NetworkManagerPowerClient& client) + : mCallback(callback), mClient(client) {} + + void OnPowerModePreChange(const PowerState currentState, const PowerState newState, + const int transactionId, const int stateChangeAfter) override; + + BEGIN_INTERFACE_MAP(PreChangeNotification) + INTERFACE_ENTRY(Exchange::IPowerManager::IModePreChangeNotification) + END_INTERFACE_MAP + + private: + INetworkPowerCallback& mCallback; + NetworkManagerPowerClient& mClient; + }; + + // ----------------------------------------------------------------------- + // IModeChangedNotification sink + // ----------------------------------------------------------------------- + class ChangedNotification : public Exchange::IPowerManager::IModeChangedNotification { + public: + explicit ChangedNotification(INetworkPowerCallback& callback) : mCallback(callback) {} + + void OnPowerModeChanged(const PowerState currentState, const PowerState newState) override; + + BEGIN_INTERFACE_MAP(ChangedNotification) + INTERFACE_ENTRY(Exchange::IPowerManager::IModeChangedNotification) + END_INTERFACE_MAP + + private: + INetworkPowerCallback& mCallback; + }; + + // ----------------------------------------------------------------------- + // SmartInterfaceType lifecycle callback + // ----------------------------------------------------------------------- + void Operational(bool upAndRunning) override; + + void registerEvents(); + void unregisterEvents(); + void unregisterEventsAndDeactivate(); + + // ----------------------------------------------------------------------- + // Members + // ----------------------------------------------------------------------- + Exchange::IPowerManager* mPowerManager{nullptr}; + Core::Sink mPreChangeNotification; + Core::Sink mChangedNotification; + uint32_t mClientId{0}; +}; + +} // namespace Plugin +} // namespace WPEFramework + +#endif // ENABLE_POWERMANAGER From dcac682494d2478f69352bea53c6f675e73222ca Mon Sep 17 00:00:00 2001 From: Anand Date: Mon, 4 May 2026 05:44:14 +0000 Subject: [PATCH 2/9] RDK-61440: Implementation of handling PowerMode Change in NM plugin Reason for change: Add log to know cliendId Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N --- plugin/NetworkManagerPowerClient.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugin/NetworkManagerPowerClient.cpp b/plugin/NetworkManagerPowerClient.cpp index c9d5efc8..773d3274 100644 --- a/plugin/NetworkManagerPowerClient.cpp +++ b/plugin/NetworkManagerPowerClient.cpp @@ -105,6 +105,8 @@ void NetworkManagerPowerClient::registerEvents() } if (auto r = mPowerManager->AddPowerModePreChangeClient("org.rdk.NetworkManager", mClientId); r != Core::ERROR_NONE) { NMLOG_ERROR("NetworkManagerPowerClient: AddPowerModePreChangeClient failed (%u)", r); + } else { + NMLOG_INFO("NetworkManagerPowerClient: registered as pre-change client, mClientId=%u, sink=%p", mClientId, static_cast(&mPreChangeNotification)); } if (auto r = mPowerManager->Register(&mPreChangeNotification); r != Core::ERROR_NONE) { NMLOG_ERROR("NetworkManagerPowerClient: Register(preChange) failed (%u)", r); From 5750f47d2ea97cec15cecfd5330e60b93c526e0a Mon Sep 17 00:00:00 2001 From: Anand Date: Tue, 5 May 2026 04:53:38 +0000 Subject: [PATCH 3/9] RDK-61440: Implementation of handling PowerMode Change in NM plugin Reason for change: fix crash from power worker thread Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N --- plugin/NetworkManagerImplementation.cpp | 19 ++++++++++++------- plugin/NetworkManagerPowerClient.cpp | 2 +- plugin/gnome/NetworkManagerGnomeWIFI.cpp | 20 +++++++++++++++++++- plugin/gnome/NetworkManagerGnomeWIFI.h | 1 + 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp index 5185d26d..af48fe23 100644 --- a/plugin/NetworkManagerImplementation.cpp +++ b/plugin/NetworkManagerImplementation.cpp @@ -1204,25 +1204,33 @@ namespace WPEFramework static_cast(currentState), static_cast(newState)); using PowerState = Exchange::IPowerManager::PowerState; + bool standbyMode = _powerClient ? _powerClient->getNetworkStandbyMode() : false; + + + if (newState != PowerState::POWER_STATE_STANDBY_DEEP_SLEEP && + currentState != PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { + // Fast path for non-deep-sleep transitions + NMLOG_INFO("OnPowerModePreChange: non-deep-sleep transition, fast-path ack"); + sendAck(); + return; + } if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { // Transitioning TO DeepSleep - bool standbyMode = _powerClient ? _powerClient->getNetworkStandbyMode() : false; if (!standbyMode) { NMLOG_INFO("OnPowerModePreChange: going to DeepSleep, Network Standby OFF — disconnecting WiFi"); - // WiFiDisconnect(); + WiFiDisconnect(); } else { NMLOG_INFO("OnPowerModePreChange: going to DeepSleep, Network Standby ON — WiFi left connected"); } sendAck(); } else if (currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { // Waking FROM DeepSleep - bool standbyMode = _powerClient ? _powerClient->getNetworkStandbyMode() : false; if (!standbyMode) { if (!m_lastConnectedSSID.empty()) { NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep, Network Standby OFF — reconnecting to '%s'", m_lastConnectedSSID.c_str()); - // ConnectToKnownSSID(m_lastConnectedSSID); // fire-and-forget + // ConnectToKnownSSID(m_lastConnectedSSID); // fire-and-forget } else { NMLOG_WARNING("OnPowerModePreChange: waking from DeepSleep, Network Standby OFF — no last SSID, skipping reconnect"); } @@ -1230,9 +1238,6 @@ namespace WPEFramework NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep, Network Standby ON — no reconnect needed"); } sendAck(); - } else { - // All other transitions: fast-path ack - sendAck(); } } diff --git a/plugin/NetworkManagerPowerClient.cpp b/plugin/NetworkManagerPowerClient.cpp index 773d3274..74761b56 100644 --- a/plugin/NetworkManagerPowerClient.cpp +++ b/plugin/NetworkManagerPowerClient.cpp @@ -106,7 +106,7 @@ void NetworkManagerPowerClient::registerEvents() if (auto r = mPowerManager->AddPowerModePreChangeClient("org.rdk.NetworkManager", mClientId); r != Core::ERROR_NONE) { NMLOG_ERROR("NetworkManagerPowerClient: AddPowerModePreChangeClient failed (%u)", r); } else { - NMLOG_INFO("NetworkManagerPowerClient: registered as pre-change client, mClientId=%u, sink=%p", mClientId, static_cast(&mPreChangeNotification)); + NMLOG_INFO("NetworkManagerPowerClient: registered as pre-change client, mClientId=%u", mClientId); } if (auto r = mPowerManager->Register(&mPreChangeNotification); r != Core::ERROR_NONE) { NMLOG_ERROR("NetworkManagerPowerClient: Register(preChange) failed (%u)", r); diff --git a/plugin/gnome/NetworkManagerGnomeWIFI.cpp b/plugin/gnome/NetworkManagerGnomeWIFI.cpp index 2348124d..9c804fc6 100644 --- a/plugin/gnome/NetworkManagerGnomeWIFI.cpp +++ b/plugin/gnome/NetworkManagerGnomeWIFI.cpp @@ -45,7 +45,11 @@ namespace WPEFramework wifiManager::wifiManager() : m_client(nullptr), m_loop(nullptr), m_createNewConnection(false), m_objectPath(nullptr), m_wifidevice(nullptr), m_source(nullptr), m_cancellable(nullptr){ NMLOG_INFO("wifiManager"); m_nmContext = g_main_context_new(); - g_main_context_push_thread_default(m_nmContext); + // g_main_context_push_thread_default(m_nmContext); + // Do NOT push m_nmContext here. Pushing here permanently locks ownership + // to the constructor thread (owner_count stays at 1, never released). + // All callers — including power-event threads — must push/pop around + // each createClientNewConnection()/deleteClientConnection() pair instead. m_loop = g_main_loop_new(m_nmContext, FALSE); } @@ -53,12 +57,21 @@ namespace WPEFramework { GError *error = NULL; + // Serialize concurrent wifi operations from different threads + m_opMutex.lock(); + // Push our private context as thread-default so nm_client_new (and any + // internal g_dbus_proxy_new_sync it calls) uses m_nmContext instead of + // the global default context, which is owned by nm_event_thrd. + g_main_context_push_thread_default(m_nmContext); + m_client = nm_client_new(NULL, &error); if (!m_client || !m_loop) { if (error) { NMLOG_ERROR("Could not connect to NetworkManager: %s.", error->message); g_error_free(error); } + g_main_context_pop_thread_default(m_nmContext); + m_opMutex.unlock(); return false; } @@ -102,6 +115,11 @@ namespace WPEFramework g_free(m_objectPath); m_objectPath = NULL; } + + // Pop the context pushed in createClientNewConnection() + g_main_context_pop_thread_default(m_nmContext); + // Release operation lock acquired in createClientNewConnection() + m_opMutex.unlock(); } bool wifiManager::quit(NMDevice *wifiNMDevice) diff --git a/plugin/gnome/NetworkManagerGnomeWIFI.h b/plugin/gnome/NetworkManagerGnomeWIFI.h index 51164aa5..72782e19 100644 --- a/plugin/gnome/NetworkManagerGnomeWIFI.h +++ b/plugin/gnome/NetworkManagerGnomeWIFI.h @@ -106,6 +106,7 @@ namespace WPEFramework GSource *m_source; GCancellable *m_cancellable; std::mutex m_cancellableMutex; + std::mutex m_opMutex; // serializes concurrent wifi operations from different threads bool m_isSuccess = false; SecretAgent m_secretAgent; }; From d0af89b07b9f6f2b647a8f21b2f9a9f0e399414a Mon Sep 17 00:00:00 2001 From: Anand Date: Wed, 6 May 2026 06:24:09 +0000 Subject: [PATCH 4/9] RDK-61440: Implementation of handling PowerMode Change in NM plugin Reason for change: offload prechange WiFi disc to dedicated powerthrd Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N --- plugin/NetworkManagerImplementation.cpp | 40 +++------ plugin/NetworkManagerPowerClient.cpp | 104 ++++++++++++++++++++---- plugin/NetworkManagerPowerClient.h | 30 ++++++- 3 files changed, 128 insertions(+), 46 deletions(-) diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp index af48fe23..155f017c 100644 --- a/plugin/NetworkManagerImplementation.cpp +++ b/plugin/NetworkManagerImplementation.cpp @@ -1200,45 +1200,27 @@ namespace WPEFramework const Exchange::IPowerManager::PowerState newState, std::function sendAck) { + // Called from NetworkManagerPowerClient's power thread. + // Policy (standbyMode check, DeepSleep filter) has already been + // applied by the power thread before calling here. NMLOG_INFO("OnPowerModePreChange: current=%d new=%d", static_cast(currentState), static_cast(newState)); using PowerState = Exchange::IPowerManager::PowerState; - bool standbyMode = _powerClient ? _powerClient->getNetworkStandbyMode() : false; - - - if (newState != PowerState::POWER_STATE_STANDBY_DEEP_SLEEP && - currentState != PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { - // Fast path for non-deep-sleep transitions - NMLOG_INFO("OnPowerModePreChange: non-deep-sleep transition, fast-path ack"); - sendAck(); - return; - } if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { - // Transitioning TO DeepSleep - if (!standbyMode) { - NMLOG_INFO("OnPowerModePreChange: going to DeepSleep, Network Standby OFF — disconnecting WiFi"); - WiFiDisconnect(); - } else { - NMLOG_INFO("OnPowerModePreChange: going to DeepSleep, Network Standby ON — WiFi left connected"); - } - sendAck(); + NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — disconnecting WiFi"); + WiFiDisconnect(); } else if (currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { - // Waking FROM DeepSleep - if (!standbyMode) { - if (!m_lastConnectedSSID.empty()) { - NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep, Network Standby OFF — reconnecting to '%s'", - m_lastConnectedSSID.c_str()); - // ConnectToKnownSSID(m_lastConnectedSSID); // fire-and-forget - } else { - NMLOG_WARNING("OnPowerModePreChange: waking from DeepSleep, Network Standby OFF — no last SSID, skipping reconnect"); - } + if (!m_lastConnectedSSID.empty()) { + NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — reconnecting to '%s'", + m_lastConnectedSSID.c_str()); + // ConnectToKnownSSID(m_lastConnectedSSID); // fire-and-forget } else { - NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep, Network Standby ON — no reconnect needed"); + NMLOG_WARNING("OnPowerModePreChange: waking from DeepSleep — no last SSID, skipping reconnect"); } - sendAck(); } + sendAck(); } void NetworkManagerImplementation::OnPowerModeChanged( diff --git a/plugin/NetworkManagerPowerClient.cpp b/plugin/NetworkManagerPowerClient.cpp index 74761b56..a5f00f38 100644 --- a/plugin/NetworkManagerPowerClient.cpp +++ b/plugin/NetworkManagerPowerClient.cpp @@ -32,7 +32,8 @@ using namespace WPEFramework::Plugin; // --------------------------------------------------------------------------- NetworkManagerPowerClient::NetworkManagerPowerClient(INetworkPowerCallback& callback) - : mPreChangeNotification(callback, *this) + : mCallback(callback) + , mPreChangeNotification(*this) , mChangedNotification(callback) { NMLOG_INFO("NetworkManagerPowerClient: connecting to PowerManager"); @@ -46,6 +47,13 @@ NetworkManagerPowerClient::NetworkManagerPowerClient(INetworkPowerCallback& call NetworkManagerPowerClient::~NetworkManagerPowerClient() { NMLOG_INFO("NetworkManagerPowerClient: shutting down"); + // Stop the power-event thread first so any in-flight work completes + // before we release the COM-RPC proxy. + mStopThread = true; + mQueueCv.notify_one(); + if (mPowerThread.joinable()) { + mPowerThread.join(); + } Close(Core::infinite); unregisterEventsAndDeactivate(); } @@ -90,8 +98,19 @@ void NetworkManagerPowerClient::Operational(bool upAndRunning) if (!IsValid()) { mPowerManager = Interface(); registerEvents(); + // Start the dedicated power-event thread after registration so it + // is ready to handle events as soon as they can arrive. + mStopThread = false; + mPowerThread = std::thread(&NetworkManagerPowerClient::powerThreadLoop, this); } } else { + // Stop the power-event thread before unregistering so any in-flight + // event that was already enqueued is drained with a fast ack. + mStopThread = true; + mQueueCv.notify_one(); + if (mPowerThread.joinable()) { + mPowerThread.join(); + } unregisterEventsAndDeactivate(); } } @@ -145,6 +164,63 @@ void NetworkManagerPowerClient::unregisterEventsAndDeactivate() } } +// --------------------------------------------------------------------------- +// Power event thread +// --------------------------------------------------------------------------- + +void NetworkManagerPowerClient::powerThreadLoop() +{ + NMLOG_INFO("NetworkManagerPowerClient: power event thread started"); + while (true) { + PowerEvent event{}; + { + std::unique_lock lock(mQueueMutex); + mQueueCv.wait(lock, [this]{ return !mEventQueue.empty() || mStopThread.load(); }); + + if (mStopThread) { + // Drain remaining events with fast acks before exiting so + // PowerManager is never left waiting on a stale transaction. + std::vector pending; + while (!mEventQueue.empty()) { + pending.push_back(mEventQueue.front().transactionId); + mEventQueue.pop(); + } + lock.unlock(); + for (int txId : pending) { + sendPowerModePreChangeComplete(txId); + } + break; + } + + event = mEventQueue.front(); + mEventQueue.pop(); + } + // Lock released — process event on this thread (blocking is fine here) + + auto sendAck = [transactionId = event.transactionId, this]() { + sendPowerModePreChangeComplete(transactionId); + }; + + const bool toDeepSleep = (event.newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP); + const bool fromDeepSleep = (event.currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP); + + if ((toDeepSleep || fromDeepSleep) && !event.standbyMode) { + // Deep-sleep transition with Network Standby OFF: delegate to + // NetworkManagerImplementation (WiFiDisconnect / reconnect) which + // will call sendAck() when done. + NMLOG_INFO("NetworkManagerPowerClient: power thread — %s DeepSleep standby OFF", + toDeepSleep ? "to" : "from"); + mCallback.OnPowerModePreChange(event.currentState, event.newState, sendAck); + } else { + // standby ON or non-DeepSleep: no WiFi action needed, ack immediately. + NMLOG_INFO("NetworkManagerPowerClient: power thread — fast-path ack (standbyMode=%d toDeepSleep=%d fromDeepSleep=%d)", + event.standbyMode, toDeepSleep, fromDeepSleep); + sendAck(); + } + } + NMLOG_INFO("NetworkManagerPowerClient: power event thread stopped"); +} + // --------------------------------------------------------------------------- // PreChangeNotification // --------------------------------------------------------------------------- @@ -156,22 +232,22 @@ void NetworkManagerPowerClient::PreChangeNotification::OnPowerModePreChange( NMLOG_INFO("NetworkManagerPowerClient::OnPowerModePreChange current=%d new=%d txId=%d after=%ds", static_cast(currentState), static_cast(newState), transactionId, stateChangeAfter); - auto sendAck = [transactionId, this]() { - mClient.sendPowerModePreChangeComplete(transactionId); - }; + // Query standby mode inline on the COM-RPC dispatcher thread — it is a + // COM-RPC call and must not be made from the power thread. + const bool standbyMode = mClient.getNetworkStandbyMode(); - if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { - // Transitioning TO DeepSleep: request a 5-second window so WiFiDisconnect - // can complete before PowerManager proceeds. + // Reserve a delay window now (before returning) so PowerManager knows to + // wait at least 5 s. Only needed when we will actually disconnect WiFi. + if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP && !standbyMode) { mClient.sendDelayPowerModeChange(transactionId, 5); - mCallback.OnPowerModePreChange(currentState, newState, sendAck); - } else if (currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { - // Waking FROM DeepSleep: reconnect is fire-and-forget; no delay needed. - mCallback.OnPowerModePreChange(currentState, newState, sendAck); - } else { - // All other transitions: fast-path ack, no network action needed. - sendAck(); } + + // Enqueue and return immediately — the power thread does the real work. + { + std::lock_guard lock(mClient.mQueueMutex); + mClient.mEventQueue.push(PowerEvent{currentState, newState, standbyMode, transactionId}); + } + mClient.mQueueCv.notify_one(); } // --------------------------------------------------------------------------- diff --git a/plugin/NetworkManagerPowerClient.h b/plugin/NetworkManagerPowerClient.h index 7fa995d6..b3a45fe2 100644 --- a/plugin/NetworkManagerPowerClient.h +++ b/plugin/NetworkManagerPowerClient.h @@ -23,7 +23,13 @@ #include "Module.h" #include +#include +#include #include +#include +#include +#include +#include namespace WPEFramework { namespace Plugin { @@ -99,8 +105,8 @@ class NetworkManagerPowerClient : protected RPC::SmartInterfaceType mPreChangeNotification; Core::Sink mChangedNotification; uint32_t mClientId{0}; + + // Power-event thread: receives events enqueued by the COM-RPC dispatcher + // thread and processes them (WiFiDisconnect etc.) without blocking the + // dispatcher. + struct PowerEvent { + PowerState currentState; + PowerState newState; + bool standbyMode; + int transactionId; + }; + + std::thread mPowerThread; + std::queue mEventQueue; + std::mutex mQueueMutex; + std::condition_variable mQueueCv; + std::atomic mStopThread{false}; + + void powerThreadLoop(); }; } // namespace Plugin From ea391ad2ca1f5274f030c8def80d17f6917cc9ea Mon Sep 17 00:00:00 2001 From: Anand Date: Wed, 6 May 2026 13:52:32 +0000 Subject: [PATCH 5/9] RDK-61440: Implementation of handling PowerMode Change in NM plugin Reason for change: enabled ConnectToKnownSSID() Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N --- plugin/NetworkManagerImplementation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp index 155f017c..2ca158ea 100644 --- a/plugin/NetworkManagerImplementation.cpp +++ b/plugin/NetworkManagerImplementation.cpp @@ -1215,7 +1215,7 @@ namespace WPEFramework if (!m_lastConnectedSSID.empty()) { NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — reconnecting to '%s'", m_lastConnectedSSID.c_str()); - // ConnectToKnownSSID(m_lastConnectedSSID); // fire-and-forget + ConnectToKnownSSID(m_lastConnectedSSID); // fire-and-forget } else { NMLOG_WARNING("OnPowerModePreChange: waking from DeepSleep — no last SSID, skipping reconnect"); } From afbdab58f6fb30649f46c6dd963e19ac89ad7fe1 Mon Sep 17 00:00:00 2001 From: Anand Date: Mon, 11 May 2026 03:59:41 +0000 Subject: [PATCH 6/9] RDK-61440: Implementation of handling PowerMode Change in NM plugin Reason for change: trigger normal WiFi scan on DeepSleep wakeup with Network Standby ON to handle AP channel changes Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N --- plugin/NetworkManagerImplementation.cpp | 8 ++++- plugin/NetworkManagerPowerClient.cpp | 45 +++++++++++++++++++++---- plugin/NetworkManagerPowerClient.h | 13 ++++--- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp index 2ca158ea..9ba38fe8 100644 --- a/plugin/NetworkManagerImplementation.cpp +++ b/plugin/NetworkManagerImplementation.cpp @@ -1229,7 +1229,13 @@ namespace WPEFramework { NMLOG_INFO("OnPowerModeChanged: current=%d new=%d", static_cast(currentState), static_cast(newState)); - // Reserved for future use (e.g., suppress connectivity checks during deep sleep) + if (currentState == Exchange::IPowerManager::POWER_STATE_STANDBY_DEEP_SLEEP) { + // Waking from DeepSleep with Network Standby ON: the AP may have + // changed channel while the device slept (802.11 CSA). Trigger an + // active scan so the driver discovers the AP on its new channel. + NMLOG_INFO("OnPowerModeChanged: waking from DeepSleep, triggering active WiFi scan"); + StartWiFiScan("", nullptr); + } } #endif // ENABLE_POWERMANAGER } diff --git a/plugin/NetworkManagerPowerClient.cpp b/plugin/NetworkManagerPowerClient.cpp index a5f00f38..92c3be06 100644 --- a/plugin/NetworkManagerPowerClient.cpp +++ b/plugin/NetworkManagerPowerClient.cpp @@ -34,7 +34,7 @@ using namespace WPEFramework::Plugin; NetworkManagerPowerClient::NetworkManagerPowerClient(INetworkPowerCallback& callback) : mCallback(callback) , mPreChangeNotification(*this) - , mChangedNotification(callback) + , mChangedNotification(*this) { NMLOG_INFO("NetworkManagerPowerClient: connecting to PowerManager"); if (auto r = Open(RPC::CommunicationTimeOut, Connector(), "org.rdk.PowerManager"); r == Core::ERROR_NONE) { @@ -180,14 +180,17 @@ void NetworkManagerPowerClient::powerThreadLoop() if (mStopThread) { // Drain remaining events with fast acks before exiting so // PowerManager is never left waiting on a stale transaction. - std::vector pending; + // CHANGED events have no ack protocol — skip them. + std::vector pending; while (!mEventQueue.empty()) { - pending.push_back(mEventQueue.front().transactionId); + pending.push_back(mEventQueue.front()); mEventQueue.pop(); } lock.unlock(); - for (int txId : pending) { - sendPowerModePreChangeComplete(txId); + for (const auto& e : pending) { + if (e.type == PowerEvent::EventType::PRE_CHANGE) { + sendPowerModePreChangeComplete(e.transactionId); + } } break; } @@ -197,6 +200,20 @@ void NetworkManagerPowerClient::powerThreadLoop() } // Lock released — process event on this thread (blocking is fine here) + if (event.type == PowerEvent::EventType::CHANGED) { + // Wakeup notification — no ack required. + const bool fromDeepSleep = (event.currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP); + if (fromDeepSleep && event.standbyMode) { + NMLOG_INFO("NetworkManagerPowerClient: power thread — wakeup from DeepSleep standby ON, triggering WiFi scan"); + mCallback.OnPowerModeChanged(event.currentState, event.newState); + } else { + NMLOG_INFO("NetworkManagerPowerClient: power thread — CHANGED event, no action (fromDeepSleep=%d standbyMode=%d)", + fromDeepSleep, event.standbyMode); + } + continue; + } + + // PRE_CHANGE event processing below auto sendAck = [transactionId = event.transactionId, this]() { sendPowerModePreChangeComplete(transactionId); }; @@ -236,6 +253,9 @@ void NetworkManagerPowerClient::PreChangeNotification::OnPowerModePreChange( // COM-RPC call and must not be made from the power thread. const bool standbyMode = mClient.getNetworkStandbyMode(); + // Cache for use by ChangedNotification + mClient.mLastChangeStandbyMode = standbyMode; + // Reserve a delay window now (before returning) so PowerManager knows to // wait at least 5 s. Only needed when we will actually disconnect WiFi. if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP && !standbyMode) { @@ -245,7 +265,8 @@ void NetworkManagerPowerClient::PreChangeNotification::OnPowerModePreChange( // Enqueue and return immediately — the power thread does the real work. { std::lock_guard lock(mClient.mQueueMutex); - mClient.mEventQueue.push(PowerEvent{currentState, newState, standbyMode, transactionId}); + mClient.mEventQueue.push(PowerEvent{PowerEvent::EventType::PRE_CHANGE, + currentState, newState, standbyMode, transactionId}); } mClient.mQueueCv.notify_one(); } @@ -259,7 +280,17 @@ void NetworkManagerPowerClient::ChangedNotification::OnPowerModeChanged( { NMLOG_INFO("NetworkManagerPowerClient::OnPowerModeChanged current=%d new=%d", static_cast(currentState), static_cast(newState)); - mCallback.OnPowerModeChanged(currentState, newState); + + // Use the standby mode cached + const bool standbyMode = mClient.mLastChangeStandbyMode; + + // Enqueue and return immediately so the COM-RPC dispatcher thread is freed. + { + std::lock_guard lock(mClient.mQueueMutex); + mClient.mEventQueue.push(PowerEvent{PowerEvent::EventType::CHANGED, + currentState, newState, standbyMode, 0}); + } + mClient.mQueueCv.notify_one(); } #endif // ENABLE_POWERMANAGER diff --git a/plugin/NetworkManagerPowerClient.h b/plugin/NetworkManagerPowerClient.h index b3a45fe2..14009200 100644 --- a/plugin/NetworkManagerPowerClient.h +++ b/plugin/NetworkManagerPowerClient.h @@ -37,8 +37,7 @@ namespace Plugin { /** * Callback interface that decouples NetworkManagerPowerClient from * NetworkManagerImplementation. The implementation receives power state - * transitions and must call sendAck() exactly once per OnPowerModePreChange - * invocation to release the PowerManager pre-change lock. + * transitions and must call sendAck() exactly once per OnPowerModePreChange. */ struct INetworkPowerCallback { virtual ~INetworkPowerCallback() = default; @@ -124,7 +123,7 @@ class NetworkManagerPowerClient : protected RPC::SmartInterfaceType mStopThread{false}; + // Cached standby mode from the last PRE_CHANGE event. + bool mLastChangeStandbyMode{false}; + void powerThreadLoop(); }; From 7bfe781e0355eb63d4aa3f11f1251181c4afa75f Mon Sep 17 00:00:00 2001 From: Anand Date: Mon, 11 May 2026 16:32:29 +0000 Subject: [PATCH 7/9] RDK-61440: Implementation of handling PowerMode Change in NM plugin Reason for change: check before initiating wifi connect and disconnect Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N --- plugin/NetworkManagerImplementation.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp index 9ba38fe8..9ba61290 100644 --- a/plugin/NetworkManagerImplementation.cpp +++ b/plugin/NetworkManagerImplementation.cpp @@ -1209,10 +1209,16 @@ namespace WPEFramework using PowerState = Exchange::IPowerManager::PowerState; if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { - NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — disconnecting WiFi"); - WiFiDisconnect(); + if (m_wlanEnabled.load() && m_wlanConnected.load()) { + NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — disconnecting WiFi"); + WiFiDisconnect(); + } + else + { + NMLOG_WARNING("OnPowerModePreChange: going to DeepSleep — WiFi not connected, skipping disconnect"); + } } else if (currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { - if (!m_lastConnectedSSID.empty()) { + if (m_wlanEnabled.load() && !m_lastConnectedSSID.empty()) { NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — reconnecting to '%s'", m_lastConnectedSSID.c_str()); ConnectToKnownSSID(m_lastConnectedSSID); // fire-and-forget From eab2f57c6f85f91f55c0460b23db5e38b72d3e93 Mon Sep 17 00:00:00 2001 From: Anand Date: Wed, 13 May 2026 01:42:09 +0000 Subject: [PATCH 8/9] RDK-61440: Implementation of handling PowerMode Change in NM plugin Reason for change: Disconnect ethernet on DeepSleep entry and reconnect on wakeup OnPowerModePreChange Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N --- plugin/NetworkManagerImplementation.cpp | 22 ++++++++++++++++-- plugin/NetworkManagerImplementation.h | 2 ++ plugin/gnome/NetworkManagerGnomeProxy.cpp | 16 +++++++++++++ plugin/gnome/NetworkManagerGnomeWIFI.cpp | 28 +++++++++++++++++++++++ plugin/gnome/NetworkManagerGnomeWIFI.h | 1 + 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp index 9ba61290..42538f86 100644 --- a/plugin/NetworkManagerImplementation.cpp +++ b/plugin/NetworkManagerImplementation.cpp @@ -1211,20 +1211,38 @@ namespace WPEFramework if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { if (m_wlanEnabled.load() && m_wlanConnected.load()) { NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — disconnecting WiFi"); - WiFiDisconnect(); + uint32_t rcWifiDown = WiFiDisconnect(); + if (rcWifiDown != Core::ERROR_NONE) + NMLOG_WARNING("OnPowerModePreChange: WiFiDisconnect failed (rc=%u)", rcWifiDown); } else { NMLOG_WARNING("OnPowerModePreChange: going to DeepSleep — WiFi not connected, skipping disconnect"); } + if (m_ethEnabled.load() && m_ethConnected.load()) { + NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — disconnecting Ethernet"); + uint32_t rcEthDown = EthernetDisconnect(); + if (rcEthDown != Core::ERROR_NONE) + NMLOG_WARNING("OnPowerModePreChange: EthernetDisconnect failed (rc=%u)", rcEthDown); + } else { + NMLOG_WARNING("OnPowerModePreChange: going to DeepSleep — Ethernet not connected, skipping disconnect"); + } } else if (currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { if (m_wlanEnabled.load() && !m_lastConnectedSSID.empty()) { NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — reconnecting to '%s'", m_lastConnectedSSID.c_str()); - ConnectToKnownSSID(m_lastConnectedSSID); // fire-and-forget + uint32_t rcWifiUp = ConnectToKnownSSID(m_lastConnectedSSID); + if (rcWifiUp != Core::ERROR_NONE) + NMLOG_WARNING("OnPowerModePreChange: ConnectToKnownSSID failed (rc=%u)", rcWifiUp); } else { NMLOG_WARNING("OnPowerModePreChange: waking from DeepSleep — no last SSID, skipping reconnect"); } + if (m_ethEnabled.load()) { + NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — reconnecting Ethernet"); + uint32_t rcEthUp = EthernetConnect(); + if (rcEthUp != Core::ERROR_NONE) + NMLOG_WARNING("OnPowerModePreChange: EthernetConnect failed (rc=%u)", rcEthUp); + } } sendAck(); } diff --git a/plugin/NetworkManagerImplementation.h b/plugin/NetworkManagerImplementation.h index 15d6159f..86f3e9e0 100644 --- a/plugin/NetworkManagerImplementation.h +++ b/plugin/NetworkManagerImplementation.h @@ -232,6 +232,8 @@ namespace WPEFramework uint32_t WiFiConnect(const WiFiConnectTo& ssid /* @in */) override; uint32_t WiFiDisconnect(void) override; + uint32_t EthernetDisconnect(void); + uint32_t EthernetConnect(void); uint32_t GetConnectedSSID(WiFiSSIDInfo& ssidInfo /* @out */) override; uint32_t StartWPS(const WiFiWPS& method /* @in */, const string& wps_pin /* @in */) override; diff --git a/plugin/gnome/NetworkManagerGnomeProxy.cpp b/plugin/gnome/NetworkManagerGnomeProxy.cpp index b661a8de..a1a91de0 100644 --- a/plugin/gnome/NetworkManagerGnomeProxy.cpp +++ b/plugin/gnome/NetworkManagerGnomeProxy.cpp @@ -1118,6 +1118,22 @@ namespace WPEFramework return rc; } + uint32_t NetworkManagerImplementation::EthernetDisconnect(void) + { + uint32_t rc = Core::ERROR_GENERAL; + if(wifi->ethernetDisconnect()) + rc = Core::ERROR_NONE; + return rc; + } + + uint32_t NetworkManagerImplementation::EthernetConnect(void) + { + uint32_t rc = Core::ERROR_GENERAL; + if(wifi->activateKnownConnection(nmUtils::ethIface(), "Wired connection 1")) + rc = Core::ERROR_NONE; + return rc; + } + uint32_t NetworkManagerImplementation::GetConnectedSSID(WiFiSSIDInfo& ssidInfo /* @out */) { uint32_t rc = Core::ERROR_RPC_CALL_FAILED; diff --git a/plugin/gnome/NetworkManagerGnomeWIFI.cpp b/plugin/gnome/NetworkManagerGnomeWIFI.cpp index 9c804fc6..5364cece 100644 --- a/plugin/gnome/NetworkManagerGnomeWIFI.cpp +++ b/plugin/gnome/NetworkManagerGnomeWIFI.cpp @@ -425,6 +425,34 @@ namespace WPEFramework return m_isSuccess; } + bool wifiManager::ethernetDisconnect() + { + NMDeviceState deviceState = NM_DEVICE_STATE_UNKNOWN; + if(!createClientNewConnection()) + return false; + + NMDevice *ethDevice = nm_client_get_device_by_iface(m_client, nmUtils::ethIface()); + if(ethDevice == NULL) { + NMLOG_WARNING("ethernet device not found !"); + deleteClientConnection(); + return true; + } + + deviceState = nm_device_get_state(ethDevice); + NMLOG_DEBUG("ethernet device current state is %d !", deviceState); + if (deviceState <= NM_DEVICE_STATE_DISCONNECTED || deviceState == NM_DEVICE_STATE_FAILED || deviceState == NM_DEVICE_STATE_DEACTIVATING) + { + NMLOG_WARNING("ethernet already disconnected !"); + deleteClientConnection(); + return true; + } + + nm_device_disconnect_async(ethDevice, m_cancellable, disconnectCb, this); + wait(m_loop); + deleteClientConnection(); + return m_isSuccess; + } + static NMAccessPoint* findMatchingSSID(const GPtrArray* ApList, Exchange::INetworkManager::WiFiConnectTo& ssidInfo) { NMAccessPoint *AccessPoint = nullptr; diff --git a/plugin/gnome/NetworkManagerGnomeWIFI.h b/plugin/gnome/NetworkManagerGnomeWIFI.h index 72782e19..1548d8d5 100644 --- a/plugin/gnome/NetworkManagerGnomeWIFI.h +++ b/plugin/gnome/NetworkManagerGnomeWIFI.h @@ -51,6 +51,7 @@ namespace WPEFramework bool getWifiState(Exchange::INetworkManager::WiFiState& state); bool wifiDisconnect(); + bool ethernetDisconnect(); bool activateKnownConnection(std::string iface, std::string knowConnectionID=""); bool wifiConnectedSSIDInfo(Exchange::INetworkManager::WiFiSSIDInfo &ssidinfo); bool wifiConnect(const Exchange::INetworkManager::WiFiConnectTo &ssidInfo); From 93339e601ebbc48bf86dee4123baa74f3e62bd23 Mon Sep 17 00:00:00 2001 From: Anand Date: Wed, 13 May 2026 07:01:57 +0000 Subject: [PATCH 9/9] RDK-61440: Implementation of handling PowerMode Change in NM plugin Reason for change: guard ethernet wakeup reconnect with m_ethDisconnectedForSleep Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N --- plugin/NetworkManagerImplementation.cpp | 13 ++++++++++--- plugin/NetworkManagerImplementation.h | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp index 42538f86..ab29e938 100644 --- a/plugin/NetworkManagerImplementation.cpp +++ b/plugin/NetworkManagerImplementation.cpp @@ -57,6 +57,7 @@ namespace WPEFramework m_wlanConnected.store(false); m_ethEnabled.store(false); m_wlanEnabled.store(false); + m_ethDisconnectedForSleep.store(false); /* Set NetworkManager Out-Process name to be NWMgrPlugin */ Core::ProcessInfo().Name("NWMgrPlugin"); @@ -1222,8 +1223,11 @@ namespace WPEFramework if (m_ethEnabled.load() && m_ethConnected.load()) { NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — disconnecting Ethernet"); uint32_t rcEthDown = EthernetDisconnect(); - if (rcEthDown != Core::ERROR_NONE) - NMLOG_WARNING("OnPowerModePreChange: EthernetDisconnect failed (rc=%u)", rcEthDown); + if (rcEthDown == Core::ERROR_NONE) { + m_ethDisconnectedForSleep.store(true); + } else { + NMLOG_WARNING("OnPowerModePreChange: EthernetDisconnect failed (rc=%u), will not reconnect on wakeup", rcEthDown); + } } else { NMLOG_WARNING("OnPowerModePreChange: going to DeepSleep — Ethernet not connected, skipping disconnect"); } @@ -1237,11 +1241,14 @@ namespace WPEFramework } else { NMLOG_WARNING("OnPowerModePreChange: waking from DeepSleep — no last SSID, skipping reconnect"); } - if (m_ethEnabled.load()) { + if (m_ethDisconnectedForSleep.load()) { + m_ethDisconnectedForSleep.store(false); NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — reconnecting Ethernet"); uint32_t rcEthUp = EthernetConnect(); if (rcEthUp != Core::ERROR_NONE) NMLOG_WARNING("OnPowerModePreChange: EthernetConnect failed (rc=%u)", rcEthUp); + } else { + NMLOG_WARNING("OnPowerModePreChange: waking from DeepSleep — Ethernet was not disconnected for sleep, skipping reconnect"); } } sendAck(); diff --git a/plugin/NetworkManagerImplementation.h b/plugin/NetworkManagerImplementation.h index 86f3e9e0..919526b9 100644 --- a/plugin/NetworkManagerImplementation.h +++ b/plugin/NetworkManagerImplementation.h @@ -343,6 +343,7 @@ namespace WPEFramework std::atomic m_wlanConnected; std::atomic m_ethEnabled; std::atomic m_wlanEnabled; + std::atomic m_ethDisconnectedForSleep; std::string m_lastConnectedSSID; NMClient *m_nmClient{nullptr}; /* proxy NMClient — bound to m_nmContext */ GMainContext *m_nmContext{nullptr}; /* isolated context, not the global default */