RDK-61440: Implementation of handling PowerMode Change in NM plugin #308
RDK-61440: Implementation of handling PowerMode Change in NM plugin #308Anand73-n wants to merge 16 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces PowerManager integration to handle DeepSleep-related power mode transitions in the NetworkManager plugin, while also improving thread-safety around default-interface handling and making several robustness fixes (iterator null checks, GLib/libnm context isolation, and deinit hooks). It also bumps the plugin/interface version to 2.2.0 and updates documentation/changelog accordingly.
Changes:
- Add a new
NetworkManagerPowerClient(COMRPC client) and hook it intoNetworkManagerImplementationto act on DeepSleep pre-change/changed events. - Make default-interface access thread-safe (
getDefaultInterface/setDefaultInterface) and update call sites across proxies/connectivity monitor. - Improve crash resilience/robustness (null checks on iterator creation, platform deinit functions, GNOME wifi operation serialization, minimal ethernet profile support) and update versioning/docs.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/l2Test/libnm/l2_test_libnmproxy.cpp | Minor unit test expectation cleanup / formatting fix. |
| plugin/rdk/NetworkManagerRDKProxy.cpp | Use thread-safe default-interface getters/setters; add iterator null checks; add platform_deinit(). |
| plugin/NetworkManagerPowerClient.h | New PowerManager client interface + callback contract for DeepSleep handling. |
| plugin/NetworkManagerPowerClient.cpp | New COMRPC client implementation with an internal worker thread and pre-change ack handling. |
| plugin/NetworkManagerJsonRpc.cpp | Add null checks for iterator creation before invoking implementation APIs. |
| plugin/NetworkManagerImplementation.h | Introduce thread-safe default-interface accessors, PowerManager callback integration, and platform deinit declaration. |
| plugin/NetworkManagerImplementation.cpp | Instantiate PowerManager client; call platform deinit in destructor; use thread-safe default-interface; implement power callbacks. |
| plugin/NetworkManagerConnectivity.cpp | Use thread-safe default-interface access in connectivity monitoring logic. |
| plugin/gnome/NetworkManagerGnomeWIFI.h | Add ethernet-minimal-profile helper; add mutex to serialize wifi operations. |
| plugin/gnome/NetworkManagerGnomeWIFI.cpp | Context push/pop moved to per-operation; serialize operations; add minimal ethernet profile; improve cleanup/error handling. |
| plugin/gnome/NetworkManagerGnomeProxy.cpp | Replace global NMClient with per-instance client/context; add deinit; add migration cleanup + minimal ethernet profile on eth enable. |
| plugin/gnome/gdbus/NetworkManagerGdbusProxy.cpp | Use thread-safe default-interface and add platform_deinit(). |
| plugin/CMakeLists.txt | Add ENABLE_POWERMANAGER option, build/link new power client when enabled. |
| legacy/LegacyWiFiManagerAPIs.cpp | Add null check after iterator creation. |
| legacy/LegacyNetworkAPIs.cpp | Add null check after iterator creation. |
| docs/NetworkManagerPlugin.md | Bump documented version to 2.2.0. |
| definition/NetworkManager.json | Bump interface version to 2.2.0. |
| CMakeLists.txt | Bump project version minor to 2 (2.2.0). |
| CHANGELOG.md | Add entries for 2.2.0 and 2.1.0. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if(getDefaultInterface() != iface.name) | ||
| ReportActiveInterfaceChange(getDefaultInterface(), iface.name); |
| 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 there are multiple messages backed up, process a bounded number | ||
| // of pending iterations so this path cannot stall indefinitely if the | ||
| // context keeps receiving new work. | ||
| while (g_main_context_iteration(device_context, FALSE)); |
0963fb3 to
afbdab5
Compare
| 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 |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Comments suppressed due to low confidence (1)
plugin/NetworkManagerPowerClient.cpp:115
- Operational(false) stops and joins the power-event thread before unregistering from PowerManager. There is a race window where the COM-RPC dispatcher can still deliver an OnPowerModePreChange after the thread has exited, causing the notification handler to enqueue a PRE_CHANGE event that will never be processed/acked, potentially stalling the power-mode transition. Consider unregistering (or otherwise preventing further notifications / sending an immediate ack) before joining the thread, or making the notification path fast-ack when mStopThread is set.
} 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();
}
| if (m_ethEnabled.load() && m_ethConnected.load()) { | ||
| NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — disconnecting Ethernet"); | ||
| uint32_t rcEthDown = EthernetDisconnect(); | ||
| if (rcEthDown == Core::ERROR_NONE) { | ||
| m_ethDisconnectedForSleep.store(true); |
| 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() |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (2)
plugin/NetworkManagerImplementation.cpp:1264
m_wlanDisconnectedForSleepis cleared before attemptingConnectToKnownSSID(). If the reconnect fails, the flag remains false and subsequent wakeups (or retries) will be skipped even though WiFi was disconnected for sleep. Consider only clearing the flag after a successful reconnect, or restoring it whenConnectToKnownSSID()fails.
NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — reconnecting to '%s'",
m_lastConnectedSSID.c_str());
m_wlanDisconnectedForSleep.store(false);
uint32_t rcWifiUp = ConnectToKnownSSID(m_lastConnectedSSID);
if (rcWifiUp != Core::ERROR_NONE)
NMLOG_WARNING("OnPowerModePreChange: ConnectToKnownSSID failed (rc=%u)", rcWifiUp);
plugin/NetworkManagerImplementation.cpp:1282
m_ethDisconnectedForSleepis cleared before attemptingEthernetConnect(). If the reconnect fails, the flag remains false and later logic will assume Ethernet was not disconnected for sleep. Consider clearing this flag only after a successfulEthernetConnect(), or restoring it on failure.
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);
| // Pop the context pushed in createClientNewConnection() | ||
| g_main_context_pop_thread_default(m_nmContext); | ||
| // Release operation lock acquired in createClientNewConnection() | ||
| m_opMutex.unlock(); |
| uint32_t WiFiDisconnect(void) override; | ||
| uint32_t EthernetDisconnect(void); | ||
| uint32_t EthernetConnect(void); |
| if (m_ethEnabled.load() && m_ethConnected.load()) | ||
| { | ||
| NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — disconnecting Ethernet"); | ||
|
|
||
| uint32_t rcEthDown = EthernetDisconnect(); | ||
| if (rcEthDown == Core::ERROR_NONE) | ||
| { |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (4)
plugin/NetworkManagerImplementation.cpp:1272
- OnPowerModePreChange calls EthernetConnect(), but there is no corresponding implementation for the RDK/netsrvmgr backend. As-is, non-GNOME builds will fail to link. Either guard this reconnect logic behind the GNOME backend build flag or add a non-GNOME implementation (e.g., returning ERROR_UNAVAILABLE).
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);
plugin/NetworkManagerImplementation.cpp:1299
- OnPowerModeChanged calls ReapplyWifiSettings(), which is only implemented in the GNOME/libnm proxy today. This will break non-GNOME builds at link time. Please either guard this call behind the GNOME backend macro or provide an implementation/stub for other backends.
NMLOG_INFO("OnPowerModeChanged: waking from DeepSleep, triggering active WiFi scan");
StartWiFiScan("", nullptr);
NMLOG_INFO("OnPowerModeChanged: waking from DeepSleep, toggling auto-route-ext-gw on wlan0");
ReapplyWifiSettings();
}
plugin/gnome/NetworkManagerGnomeWIFI.cpp:538
- toggleAutoRouteExtGw passes nm_connection_to_dbus(conn, ...) directly into nm_remote_connection_update2 without unref-ing the returned GVariant. Elsewhere in this file (e.g., around line ~1455) the variant is stored and g_variant_unref()'d after the call, suggesting update2 does not take ownership. Please mirror that pattern here to avoid leaking the settings GVariant on each wakeup.
/* Save to disk */
m_isSuccess = false;
nm_remote_connection_update2(remoteConn,
nm_connection_to_dbus(conn, NM_CONNECTION_SERIALIZE_ALL),
NM_SETTINGS_UPDATE2_FLAG_TO_DISK,
NULL, m_cancellable,
onUpdate2Done, this);
plugin/gnome/NetworkManagerGnomeWIFI.cpp:538
- toggleAutoRouteExtGw intentionally toggles NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW and persists the change to disk (NM_SETTINGS_UPDATE2_FLAG_TO_DISK). Because this is called on every wake, the on-disk connection profile will flip between DEFAULT and TRUE across sleep cycles, which can permanently alter routing behavior and survive reboots. If the goal is only to force a runtime reapply, avoid TO_DISK and/or avoid changing the stored value (e.g., reapply current settings, update in-memory only, or restore the original value after reapply).
/* Read current value and toggle */
NMTernary currentVal = NM_TERNARY_DEFAULT;
g_object_get(s_ip4, NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, ¤tVal, NULL);
NMTernary newVal = (currentVal == NM_TERNARY_DEFAULT) ? NM_TERNARY_TRUE : NM_TERNARY_DEFAULT;
NMLOG_INFO("toggleAutoRouteExtGw: '%s' auto-route-ext-gw %d -> %d",
iface.c_str(), static_cast<int>(currentVal), static_cast<int>(newVal));
g_object_set(s_ip4, NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, newVal, NULL);
/* Save to disk */
m_isSuccess = false;
nm_remote_connection_update2(remoteConn,
nm_connection_to_dbus(conn, NM_CONNECTION_SERIALIZE_ALL),
NM_SETTINGS_UPDATE2_FLAG_TO_DISK,
NULL, m_cancellable,
onUpdate2Done, this);
| uint32_t WiFiDisconnect(void) override; | ||
| uint32_t EthernetDisconnect(void); | ||
| uint32_t EthernetConnect(void); | ||
| uint32_t ReapplyWifiSettings(void); |
| uint32_t rcEthDown = EthernetDisconnect(); | ||
| if (rcEthDown == Core::ERROR_NONE) | ||
| { | ||
| m_ethDisconnectedForSleep.store(true); | ||
| } |
| if(wifi->activateKnownConnection(nmUtils::ethIface(), "Wired connection 1")) | ||
| rc = Core::ERROR_NONE; |
| if (error) { | ||
| NMLOG_ERROR("Could not connect to NetworkManager: %s.", error->message); | ||
| g_error_free(error); | ||
| } |
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 <Anand_N@comcast.com>
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 <Anand_N@comcast.com>
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 <Anand_N@comcast.com>
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 <Anand_N@comcast.com>
Reason for change: enabled ConnectToKnownSSID() Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N <Anand_N@comcast.com>
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 <Anand_N@comcast.com>
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 <Anand_N@comcast.com>
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 <Anand_N@comcast.com>
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 <Anand_N@comcast.com>
Reason for change: guard wifi wakeup reconnect with m_wlanDisconnectedForSleep Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N <Anand_N@comcast.com>
Reason for change: Additional logging and code alignment Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N <Anand_N@comcast.com>
Reason for change: Fix for trigger Wi-Fi scan OnPowerMode Changed Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N <Anand_N@comcast.com>
Reason for change: code cleanup Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N <Anand_N@comcast.com>
Reason for change: code cleanup Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N <Anand_N@comcast.com>
Reason for change: reapply wlan0 on wakeup from DeepSleep with NSM ON Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N <Anand_N@comcast.com>
Reason for change: On wakeup from DeepSleep with Network Standby ON, request DHCP lease for both wlan0 and eth0. Test procedure: Change the PowerMode and verify the log Risks: low Priority: P1 Signed-off-by: Anand N <Anand_N@comcast.com>
5890365 to
16f394b
Compare
| 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 — must push/pop around | ||
| // each createClientNewConnection()/deleteClientConnection() pair instead. | ||
| m_loop = g_main_loop_new(m_nmContext, FALSE); |
| /* Save to disk */ | ||
| m_isSuccess = false; | ||
| nm_remote_connection_update2(remoteConn, | ||
| nm_connection_to_dbus(conn, NM_CONNECTION_SERIALIZE_ALL), | ||
| NM_SETTINGS_UPDATE2_FLAG_TO_DISK, | ||
| NULL, m_cancellable, | ||
| settingsSavedCb, this); |
| { | ||
| NMLOG_INFO("OnPowerModeChanged: current=%d new=%d", | ||
| static_cast<int>(currentState), static_cast<int>(newState)); | ||
| if (currentState == Exchange::IPowerManager::POWER_STATE_STANDBY_DEEP_SLEEP) { |
| std::atomic<bool> m_stopThread{false}; | ||
| std::mutex m_condVariableMutex; | ||
| std::condition_variable m_condVariable; | ||
| std::unique_ptr<NetworkManagerPowerClient> _powerClient; | ||
| public: |
| /* No-op on RDK platform */ | ||
| NMLOG_INFO("EthernetDeactivate: no-op on RDK platform"); | ||
| return Core::ERROR_NONE; | ||
| } | ||
|
|
||
| uint32_t NetworkManagerImplementation::EthernetActivate(void) | ||
| { | ||
| /* No-op on RDK platform */ | ||
| NMLOG_INFO("EthernetActivate: no-op on RDK platform"); | ||
| return Core::ERROR_NONE; | ||
| } | ||
|
|
||
| uint32_t NetworkManagerImplementation::RequestDHCPLease(const string& iface) | ||
| { | ||
| /* No-op on RDK platform */ | ||
| NMLOG_INFO("RequestDHCPLease: no-op on RDK platform (iface=%s)", iface.c_str()); | ||
| return Core::ERROR_NONE; |
Do not merge.