diff --git a/.github/workflows/gdbus_proxy_L1_test.yml b/.github/workflows/gdbus_proxy_L1_test.yml
index 242ca6bd..16edc038 100644
--- a/.github/workflows/gdbus_proxy_L1_test.yml
+++ b/.github/workflows/gdbus_proxy_L1_test.yml
@@ -107,6 +107,11 @@ jobs:
&&
cmake --build build/ThunderInterfaces --target install -j8
+ - name: Install IPowerManager header
+ run: |
+ IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1)
+ cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IPowerManager.h "$IFACE_DIR/"
+
- name: Build networkmanager with Gnome GDBUS Proxy
run: >
cmake
diff --git a/.github/workflows/legacy_L1_L2_test.yml b/.github/workflows/legacy_L1_L2_test.yml
index b3645cce..03538a0a 100644
--- a/.github/workflows/legacy_L1_L2_test.yml
+++ b/.github/workflows/legacy_L1_L2_test.yml
@@ -110,6 +110,11 @@ jobs:
&&
cmake --build build/ThunderInterfaces --target install -j8
+ - name: Install IPowerManager header
+ run: |
+ IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1)
+ cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IPowerManager.h "$IFACE_DIR/"
+
- name: Generate IARM headers
run: |
touch install/usr/lib/libIARMBus.so
diff --git a/.github/workflows/libnm_proxy_L1_test.yml b/.github/workflows/libnm_proxy_L1_test.yml
index 80d945d5..e5f64def 100644
--- a/.github/workflows/libnm_proxy_L1_test.yml
+++ b/.github/workflows/libnm_proxy_L1_test.yml
@@ -12,7 +12,8 @@ env:
jobs:
L1-tests:
name: Build and run unit tests
- runs-on: ubuntu-22.04
+ # Note: Ubuntu 24.04 is required for libnm 1.46 which is needed for NetworkManagerPowerClient support
+ runs-on: ubuntu-24.04
steps:
# Set up Thunder cache
@@ -107,11 +108,16 @@ jobs:
&&
cmake --build build/ThunderInterfaces --target install -j8
+ - name: Install IPowerManager header
+ run: |
+ IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1)
+ cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IPowerManager.h "$IFACE_DIR/"
+
- name: Generate dependency files
run: |
sudo bash -c 'echo "ETHERNET_INTERFACE=eth0
WIFI_INTERFACE=wlan0
- DEVICE_NAME=rdk_test_device " > /etc/device.properties'
+ DEFAULT_HOSTNAME=rdk_test_device " > /etc/device.properties'
- name: Generate IARM headers
run: |
@@ -160,7 +166,7 @@ jobs:
- name: Generate coverage
run: |
- lcov -c -o coverage.info -d build/networkmanager_libnm/
+ lcov --rc geninfo_unexecuted_blocks=1 -c -o coverage.info -d build/networkmanager_libnm/ --ignore-errors mismatch
lcov -e coverage.info '*/networkmanager/plugin/gnome/*' -o filtered_coverage.info
- name: Generate the html report
diff --git a/.github/workflows/rdk_proxy_L1_L2_test.yml b/.github/workflows/rdk_proxy_L1_L2_test.yml
index 4fb464fe..fc89bc38 100644
--- a/.github/workflows/rdk_proxy_L1_L2_test.yml
+++ b/.github/workflows/rdk_proxy_L1_L2_test.yml
@@ -107,6 +107,11 @@ jobs:
&&
cmake --build build/ThunderInterfaces --target install -j8
+ - name: Install IPowerManager header
+ run: |
+ IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1)
+ cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IPowerManager.h "$IFACE_DIR/"
+
- name: Generate IARM headers
run: |
touch install/usr/lib/libIARMBus.so
diff --git a/.github/workflows/validate_pr_desc.yml b/.github/workflows/validate_pr_desc.yml
index b6249ff7..3264db93 100644
--- a/.github/workflows/validate_pr_desc.yml
+++ b/.github/workflows/validate_pr_desc.yml
@@ -15,7 +15,7 @@ jobs:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
# Define valid ticket IDs
- VALID_TICKET_IDS=("RDKEMW")
+ VALID_TICKET_IDS=("RDKEMW" "RDK")
# Function to validate ticket format and ID
validate_ticket() {
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 07c24818..d262c5e6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,25 @@ All notable changes to this RDK Service will be documented in this file.
* Changes in CHANGELOG should be updated when commits are added to the main or release branches. There should be one CHANGELOG entry per JIRA Ticket. This is not enforced on sprint branches since there could be multiple changes for the same JIRA ticket during development.
+## [3.3.0] - 2026-06-12
+### Fixed
+- Fixed the memory leak that caused by the persistent m_nmClient and m_nmContext.
+
+## [3.2.0] - 2026-06-09
+### Added
+- Implemented IP Caching logic to avoid multiple hard fetches from Gnome & invalidating the cache on change.
+
+## [3.1.0] - 2026-06-08
+### Changed
+- Handled Power State changes within NetworkManager as below,
+- Transition to DeepSleep will disconnect when NSM is OFF
+- Transition from DeepSleep will Renew IP when NSM is ON
+
+## [3.0.0] - 2026-05-28
+### Changed
+- The device hostname header that used to retrive has changed as "DEFAULT_HOSTNAME"
+- Updated the WiFiStartScan to scan for specific SSID and also updated to take array of freq band as input instead of single freq
+
## [2.3.0] - 2026-05-21
### Fixed
- Fixed the issue which leads Enabling and Disabling of interfaces are taking longer
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a04ad16b..6d4cc1db 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,7 +36,7 @@ if (NOT WPEFramework_FOUND AND NOT Thunder_FOUND)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
-set(VERSION_MAJOR 2)
+set(VERSION_MAJOR 3)
set(VERSION_MINOR 3)
set(VERSION_PATCH 0)
@@ -55,6 +55,21 @@ option(ENABLE_LEGACY_PLUGINS "Enable Legacy Plugins" ON)
option(USE_RDK_LOGGER "Enable RDK Logger for logging" OFF )
option(ENABLE_UNIT_TESTING "Enable unit tests" OFF)
option(USE_TELEMETRY "Enable Telemetry T2 support" OFF)
+option(ENABLE_ETHERNET_CONNECTION_HANDLING
+ "Enable pre-sleep Ethernet deactivation" OFF)
+
+if(ENABLE_ETHERNET_CONNECTION_HANDLING)
+ add_definitions(-DENABLE_ETHERNET_CONNECTION_HANDLING)
+ message(STATUS "Ethernet connection handling: enabled")
+endif()
+
+# Backend identity macros are consumed by shared headers; define them globally
+# so all targets (plugin, tests, tools) compile against the same API surface.
+if(ENABLE_GNOME_NETWORKMANAGER AND ENABLE_GNOME_GDBUS)
+ add_compile_definitions(NM_BACKEND_GDBUS=1)
+elseif(NOT ENABLE_GNOME_NETWORKMANAGER)
+ add_compile_definitions(NM_BACKEND_RDK=1)
+endif()
if (USE_TELEMETRY)
find_package(T2 REQUIRED)
diff --git a/definition/NetworkManager.json b/definition/NetworkManager.json
index 65fb6e6c..f79bd812 100644
--- a/definition/NetworkManager.json
+++ b/definition/NetworkManager.json
@@ -8,7 +8,7 @@
"status": "production",
"description": "A Unified `NetworkManager` plugin that allows you to manage Ethernet and Wifi interfaces on the device.",
"sourcelocation": "https://github.com/rdkcentral/networkmanager/blob/main/definition/NetworkManager.json",
- "version": "2.3.0"
+ "version": "3.3.0"
},
"definitions": {
"success": {
@@ -47,9 +47,9 @@
"example": 24
},
"ula": {
- "summary": "The IPv6 Unified Local Address",
+ "summary": "The IPv6 Unique Local Address",
"type": "string",
- "example": "d00:410:2016::"
+ "example": "fd00:410:2016::"
},
"gateway": {
"summary": "The gateway address",
@@ -846,17 +846,27 @@
}
},
"StartWiFiScan":{
- "summary": "Initiates WiFi scaning. This method supports scanning for specific range of frequency like 2.4GHz only or 5GHz only or 6GHz only or ALL. When no input passed about the frequency to be scanned, it scans for all. When list of SSIDs to be scanned specifically, it can be passed as input. It publishes 'onAvailableSSIDs' event upon completion.",
+ "summary": "Initiates WiFi scanning. This method supports scanning specific frequency bands (2.4GHz, 5GHz, 6GHz). When no input is passed for frequency, it scans all supported frequencies. When list of SSIDs to be scanned specifically, it can be passed as input. It publishes 'onAvailableSSIDs' event upon completion.",
"events": {
"onAvailableSSIDs" : "Triggered when list of SSIDs is available after the scan completes."
},
"params": {
"type": "object",
"properties": {
- "frequency": {
- "summary": "The frequency to scan. An empty or `null` value scans all frequencies.",
- "type": "string",
- "example": "5"
+ "frequencies": {
+ "summary": "Frequency bands to scan. Omit this field or pass \"ALL\" to scan all frequencies.",
+ "type": "array",
+ "items": {
+ "summary": "The frequency to scan.",
+ "type": "string",
+ "enum": [
+ "ALL",
+ "2.4",
+ "5",
+ "6"
+ ],
+ "example": "2.4"
+ }
},
"ssids": {
"summary": "The list of SSIDs to be scanned.",
@@ -1233,7 +1243,7 @@
}
},
"GetWiFiSignalQuality":{
- "summary": "Get WiFi signal quality of currently connected SSID. The signal quality is identifed based on the Signal to Noise ratio which is calculated as SNR = rssi - noise. The possible states are\n* 'Excellent' : More than 40 dBm\n* 'Good' : 40 dBm to 25 dBm\n* 'Fair' : 25 dBm to 18 dBm\n* 'Weak' : 18 dBm to 0 dBm\n* 'Disconnected' : 0 dBm\n",
+ "summary": "Get WiFi signal quality of currently connected SSID. The signal quality is identified based on the Signal to Noise ratio which is calculated as SNR = rssi - noise. The possible states are\n* 'Excellent' : More than 40 dBm\n* 'Good' : 40 dBm to 25 dBm\n* 'Fair' : 25 dBm to 18 dBm\n* 'Weak' : 18 dBm to 0 dBm\n* 'Disconnected' : 0 dBm\n",
"events":{
"onWiFiSignalQualityChange" : "Triggered when Wifi signal strength switches between Excellent, Good, Fair, Weak."
},
@@ -1294,7 +1304,7 @@
"example": 2
},
"EAP": {
- "summary": "Supports security mode WPA enterpise",
+ "summary": "Supports security mode WPA enterprise",
"type": "integer",
"example": 3
}
@@ -1341,7 +1351,7 @@
}
},
"SetHostname": {
- "summary": "To configure a custom DHCP hostname instead of the default (which is typically the device name).\n\nSetting host name will take effect upon reconnect; like, device reboot, wake-up from deepsleep, while connecting to new Wi-Fi connection, WiFi On/Off, or renewal of the DHCP lease.",
+ "summary": "To configure a custom DHCP hostname instead of the default (which is typically the default hostname).\n\nSetting host name will take effect upon reconnect; like, device reboot, wake-up from deepsleep, while connecting to new Wi-Fi connection, WiFi On/Off, or renewal of the DHCP lease.",
"params": {
"type": "object",
"properties": {
@@ -1455,7 +1465,7 @@
"type": "object",
"properties": {
"prevState":{
- "summary": "The privious internet connection state",
+ "summary": "The previous internet connection state",
"type": "integer",
"example": 1
},
diff --git a/docs/NetworkManagerPlugin.md b/docs/NetworkManagerPlugin.md
index 2c31542f..3d109f0a 100644
--- a/docs/NetworkManagerPlugin.md
+++ b/docs/NetworkManagerPlugin.md
@@ -2,7 +2,7 @@
# NetworkManager Plugin
-**Version: 2.3.0**
+**Version: 3.3.0**
**Status: :black_circle::black_circle::black_circle:**
@@ -23,7 +23,7 @@ org.rdk.NetworkManager interface for Thunder framework.
## Scope
-This document describes purpose and functionality of the org.rdk.NetworkManager interface (version 2.3.0). It includes detailed specification about its methods provided and notifications sent.
+This document describes purpose and functionality of the org.rdk.NetworkManager interface (version 3.3.0). It includes detailed specification about its methods provided and notifications sent.
## Case Sensitivity
@@ -89,7 +89,7 @@ NetworkManager interface methods:
| [GetPublicIP](#method.GetPublicIP) | Gets the internet/public IP Address of the device |
| [Ping](#method.Ping) | Pings the specified endpoint with the specified number of packets |
| [Trace](#method.Trace) | Traces the specified endpoint with the specified number of packets using `traceroute` |
-| [StartWiFiScan](#method.StartWiFiScan) | Initiates WiFi scaning |
+| [StartWiFiScan](#method.StartWiFiScan) | Initiates WiFi scanning |
| [StopWiFiScan](#method.StopWiFiScan) | Stops WiFi scanning |
| [GetKnownSSIDs](#method.GetKnownSSIDs) | Gets list of saved SSIDs |
| [AddToKnownSSIDs](#method.AddToKnownSSIDs) | Saves the SSID, passphrase, and security mode for upcoming and future sessions |
@@ -103,7 +103,7 @@ NetworkManager interface methods:
| [GetWiFiSignalQuality](#method.GetWiFiSignalQuality) | Get WiFi signal quality of currently connected SSID |
| [GetSupportedSecurityModes](#method.GetSupportedSecurityModes) | Returns the Wifi security modes that the device supports |
| [GetWifiState](#method.GetWifiState) | Returns the current Wifi State |
-| [SetHostname](#method.SetHostname) | To configure a custom DHCP hostname instead of the default (which is typically the device name) |
+| [SetHostname](#method.SetHostname) | To configure a custom DHCP hostname instead of the default (which is typically the default hostname) |
## *SetLogLevel [method](#head.Methods)*
@@ -418,7 +418,7 @@ Gets the IP setting for the given interface.
| result.ipaddress | string | The IP address |
| result.prefix | integer | The prefix number |
| result.gateway | string | The gateway address |
-| result.ula | string | The IPv6 Unified Local Address |
+| result.ula | string | The IPv6 Unique Local Address |
| result.primarydns | string | The primary DNS address |
| result.secondarydns | string | The secondary DNS address |
| result.success | boolean | Whether the request succeeded |
@@ -453,7 +453,7 @@ Gets the IP setting for the given interface.
"ipaddress": "192.168.1.101",
"prefix": 24,
"gateway": "192.168.1.1",
- "ula": "d00:410:2016::",
+ "ula": "fd00:410:2016::",
"primarydns": "192.168.1.1",
"secondarydns": "192.168.1.2",
"success": true
@@ -1006,7 +1006,7 @@ Traces the specified endpoint with the specified number of packets using `tracer
## *StartWiFiScan [method](#head.Methods)*
-Initiates WiFi scaning. This method supports scanning for specific range of frequency like 2.4GHz only or 5GHz only or 6GHz only or ALL. When no input passed about the frequency to be scanned, it scans for all. When list of SSIDs to be scanned specifically, it can be passed as input. It publishes 'onAvailableSSIDs' event upon completion.
+Initiates WiFi scanning. This method supports scanning specific frequency bands (2.4GHz, 5GHz, 6GHz). When no input is passed for frequency, it scans all supported frequencies. When list of SSIDs to be scanned specifically, it can be passed as input. It publishes 'onAvailableSSIDs' event upon completion.
Also see: [onAvailableSSIDs](#event.onAvailableSSIDs)
@@ -1015,7 +1015,8 @@ Also see: [onAvailableSSIDs](#event.onAvailableSSIDs)
| Name | Type | Description |
| :-------- | :-------- | :-------- |
| params | object | |
-| params?.frequency | string | *(optional)* The frequency to scan. An empty or `null` value scans all frequencies |
+| params?.frequencies | array | *(optional)* Frequency bands to scan. Omit this field or pass "ALL" to scan all frequencies |
+| params?.frequencies[#] | string | *(optional)* The frequency to scan |
| params?.ssids | array | *(optional)* The list of SSIDs to be scanned |
| params?.ssids[#] | string | *(optional)* The SSID to scan |
@@ -1036,7 +1037,9 @@ Also see: [onAvailableSSIDs](#event.onAvailableSSIDs)
"id": 42,
"method": "org.rdk.NetworkManager.1.StartWiFiScan",
"params": {
- "frequency": "5",
+ "frequencies": [
+ "2.4"
+ ],
"ssids": [
"Xfinity Mobile"
]
@@ -1558,7 +1561,7 @@ This method takes no parameters.
## *GetWiFiSignalQuality [method](#head.Methods)*
-Get WiFi signal quality of currently connected SSID. The signal quality is identifed based on the Signal to Noise ratio which is calculated as SNR = rssi - noise. The possible states are
+Get WiFi signal quality of currently connected SSID. The signal quality is identified based on the Signal to Noise ratio which is calculated as SNR = rssi - noise. The possible states are
* 'Excellent' : More than 40 dBm
* 'Good' : 40 dBm to 25 dBm
* 'Fair' : 25 dBm to 18 dBm
@@ -1631,7 +1634,7 @@ This method takes no parameters.
| result.security.NONE | integer | Security mode for open network |
| result.security.WPA_PSK | integer | Supports security mode WPA,WPA-PSK,WPA2-PSK, WPA3-Personal-Transition |
| result.security.SAE | integer | Supports security mode WPA3-Personal |
-| result.security.EAP | integer | Supports security mode WPA enterpise |
+| result.security.EAP | integer | Supports security mode WPA enterprise |
| result.success | boolean | Whether the request succeeded |
### Example
@@ -1726,7 +1729,7 @@ This method takes no parameters.
## *SetHostname [method](#head.Methods)*
-To configure a custom DHCP hostname instead of the default (which is typically the device name).
+To configure a custom DHCP hostname instead of the default (which is typically the default hostname).
Setting host name will take effect upon reconnect; like, device reboot, wake-up from deepsleep, while connecting to new Wi-Fi connection, WiFi On/Off, or renewal of the DHCP lease.
@@ -1891,7 +1894,7 @@ Triggered when internet connection state changed.The possible internet connectio
| Name | Type | Description |
| :-------- | :-------- | :-------- |
| params | object | |
-| params.prevState | integer | The privious internet connection state |
+| params.prevState | integer | The previous internet connection state |
| params.prevStatus | string | The previous internet connection status |
| params.state | integer | The internet connection state |
| params.status | string | The internet connection status |
diff --git a/interface/INetworkManager.h b/interface/INetworkManager.h
index 15c739cf..ce8cb509 100644
--- a/interface/INetworkManager.h
+++ b/interface/INetworkManager.h
@@ -105,7 +105,7 @@ namespace WPEFramework
enum WIFIFrequency : uint8_t
{
- WIFI_FREQUENCY_NONE /* @text: NONE */,
+ WIFI_FREQUENCY_ALL /* @text: ALL */,
WIFI_FREQUENCY_2_4_GHZ /* @text: 2.4GHz */,
WIFI_FREQUENCY_5_GHZ /* @text: 5GHz */,
WIFI_FREQUENCY_6_GHZ /* @text: 6GHz */,
@@ -248,7 +248,7 @@ namespace WPEFramework
// WiFi Specific Methods
/* @brief Initiate a WIFI Scan; This is Async method and returns the scan results as Event */
- virtual uint32_t StartWiFiScan(const string& frequency /* @in */, IStringIterator* const ssids/* @in */) = 0;
+ virtual uint32_t StartWiFiScan(IStringIterator* const frequencies /* @in */, IStringIterator* const ssids/* @in */) = 0;
virtual uint32_t StopWiFiScan(void) = 0;
virtual uint32_t GetKnownSSIDs(IStringIterator*& ssids /* @out */) = 0;
diff --git a/legacy/LegacyWiFiManagerAPIs.cpp b/legacy/LegacyWiFiManagerAPIs.cpp
index 9132ed09..9a6573ff 100644
--- a/legacy/LegacyWiFiManagerAPIs.cpp
+++ b/legacy/LegacyWiFiManagerAPIs.cpp
@@ -634,12 +634,27 @@ namespace WPEFramework
{
LOG_INPARAM();
uint32_t rc = Core::ERROR_GENERAL;
- string frequency{};
+ Exchange::INetworkManager::IStringIterator* frequencies = nullptr;
Exchange::INetworkManager::IStringIterator* ssids = NULL;
-
if (parameters.HasLabel("frequency"))
- frequency = parameters["frequency"].String();
+ {
+ std::vector frequencyList;
+ if (Core::JSON::Variant::type::STRING == parameters["frequency"].Content())
+ {
+ frequencyList.push_back(parameters["frequency"].String());
+ }
+ else
+ {
+ NMLOG_ERROR("Unexpected variant type in frequency parameter.");
+ returnJson(rc);
+ }
+
+ frequencies = (Core::Service::Create(frequencyList));
+ if (frequencies == nullptr) {
+ returnJson(rc);
+ }
+ }
if (parameters.HasLabel("ssid"))
{
@@ -654,6 +669,8 @@ namespace WPEFramework
ssids = (Core::Service::Create(inputSSIDlist));
if (ssids == nullptr) {
+ if (frequencies)
+ frequencies->Release();
returnJson(rc);
}
}
@@ -661,12 +678,15 @@ namespace WPEFramework
auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN);
if (_nwmgr)
{
- rc = _nwmgr->StartWiFiScan(frequency, ssids);
+ rc = _nwmgr->StartWiFiScan(frequencies, ssids);
_nwmgr->Release();
}
else
rc = Core::ERROR_UNAVAILABLE;
+ if (frequencies)
+ frequencies->Release();
+
if (ssids)
ssids->Release();
diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt
index a5b12d94..6090b8f1 100644
--- a/plugin/CMakeLists.txt
+++ b/plugin/CMakeLists.txt
@@ -81,6 +81,7 @@ add_library(${MODULE_IMPL_NAME} SHARED
NetworkManagerConnectivity.cpp
NetworkManagerStunClient.cpp
NetworkManagerLogger.cpp
+ NetworkManagerPowerClient.cpp
Module.cpp)
if(ENABLE_GNOME_NETWORKMANAGER)
diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp
index b4b5bca6..b10b375b 100644
--- a/plugin/NetworkManagerImplementation.cpp
+++ b/plugin/NetworkManagerImplementation.cpp
@@ -19,6 +19,9 @@
#include
#include
+#include
+#include
+#include
#include "NetworkManagerImplementation.h"
#if USE_TELEMETRY
@@ -54,6 +57,8 @@ namespace WPEFramework
m_wlanConnected.store(false);
m_ethEnabled.store(false);
m_wlanEnabled.store(false);
+ m_ethDisconnectedForSleep.store(false);
+ m_wlanDisconnectedForSleep.store(false);
/* Set NetworkManager Out-Process name to be NWMgrPlugin */
Core::ProcessInfo().Name("NWMgrPlugin");
@@ -71,6 +76,7 @@ namespace WPEFramework
NetworkManagerImplementation::~NetworkManagerImplementation()
{
NMLOG_INFO("NetworkManager Out-Of-Process Shutdown/Cleanup");
+ m_powerClient.reset();
connectivityMonitor.stopConnectivityMonitor();
_instance = nullptr;
platform_deinit();
@@ -199,6 +205,7 @@ namespace WPEFramework
NetworkManagerImplementation::platform_init();
/* change gnome networkmanager or netsrvmgr logg level */
NetworkManagerImplementation::platform_logging(static_cast (config.loglevel.Value()));
+ m_powerClient.reset(new NetworkManagerPowerClient(*this));
return(Core::ERROR_NONE);
}
@@ -590,7 +597,6 @@ namespace WPEFramework
return;
}
-
void NetworkManagerImplementation::filterScanResults(JsonArray &ssids)
{
JsonArray result;
@@ -598,27 +604,41 @@ namespace WPEFramework
std::unordered_set scanForSsidsSet(m_filterSsidslist.begin(), m_filterSsidslist.end());
// If neither SSID list nor frequency is provided, exit
- if (m_filterSsidslist.empty() && m_filterfrequency.empty())
+ if (m_filterSsidslist.empty() && m_filterFrequencies.empty())
{
NMLOG_DEBUG("Neither SSID nor Frequency is provided. Exiting function.");
return;
}
- if (!m_filterfrequency.empty())
- {
- filterFreq = std::stod(m_filterfrequency);
- }
-
for (int i = 0; i < ssids.Length(); i++)
{
JsonObject object = ssids[i].Object();
string ssid = object["ssid"].String();
string frequency = object["frequency"].String();
-
double frequencyValue = std::stod(frequency);
bool ssidMatches = scanForSsidsSet.empty() || scanForSsidsSet.find(ssid) != scanForSsidsSet.end();
- bool freqMatches = m_filterfrequency.empty() || (filterFreq == frequencyValue);
+ bool freqMatches = m_filterFrequencies.empty();
+ if (!freqMatches)
+ {
+ for (const auto& selectedFrequency : m_filterFrequencies)
+ {
+ if (selectedFrequency == "ALL")
+ {
+ freqMatches = true;
+ break;
+ }
+ else
+ {
+ filterFreq = std::stod(selectedFrequency);
+ if (filterFreq == frequencyValue)
+ {
+ freqMatches = true;
+ break;
+ }
+ }
+ }
+ }
if (ssidMatches && freqMatches)
result.Add(object);
@@ -654,8 +674,10 @@ namespace WPEFramework
{
if(interface == "eth0")
{
+#if defined(NM_BACKEND_GDBUS) || defined(NM_BACKEND_RDK)
m_ethIPv4Address = {};
m_ethIPv6Address = {};
+#endif
m_ethConnected.store(false);
setDefaultInterface("wlan0"); // If WiFi is connected, make it the default interface
// As default interface is changed to wlan0, switch connectivity monitor to initial check
@@ -663,8 +685,10 @@ namespace WPEFramework
}
else if(interface == "wlan0")
{
+#if defined(NM_BACKEND_GDBUS) || defined(NM_BACKEND_RDK)
m_wlanIPv4Address = {};
m_wlanIPv6Address = {};
+#endif
m_wlanConnected.store(false);
bool triggerConnectivityCheck;
if(m_ethConnected.load())
@@ -775,7 +799,9 @@ namespace WPEFramework
}
_notificationLock.Lock();
- NMLOG_INFO("Posting onIPAddressChange %s - %s", ipaddress.c_str(), interface.c_str());
+ NMLOG_INFO("Posting onIPAddressChange %s: %s %s %s",
+ (Exchange::INetworkManager::IP_ACQUIRED == status) ? "IP acquired" : "IP lost",
+ interface.c_str(), ipversion.c_str(), ipaddress.c_str());
for (const auto callback : _notificationCallbacks) {
callback->onIPAddressChange(interface, ipversion, ipaddress, status);
}
@@ -1184,5 +1210,272 @@ namespace WPEFramework
}
#endif
}
+
+ void NetworkManagerImplementation::OnPowerModePreChange(
+ const Exchange::IPowerManager::PowerState currentState,
+ const Exchange::IPowerManager::PowerState newState,
+ std::function sendAck)
+ {
+ // Called from NetworkManagerPowerClient's power thread.
+ NMLOG_DEBUG("OnPowerModePreChange: current=%d new=%d",
+ static_cast(currentState), static_cast(newState));
+
+ using PowerState = Exchange::IPowerManager::PowerState;
+
+ if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP)
+ {
+ if (m_wlanEnabled.load() && m_wlanConnected.load())
+ {
+ NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — disconnecting WiFi");
+
+ uint32_t rcWifiDown = WiFiDisconnect();
+ if (rcWifiDown == Core::ERROR_NONE)
+ {
+ m_wlanDisconnectedForSleep.store(true);
+ }
+ else
+ {
+ NMLOG_ERROR("OnPowerModePreChange: WiFiDisconnect failed (rc=%u), will not reconnect on wakeup", rcWifiDown);
+ }
+ }
+ else
+ {
+ NMLOG_DEBUG("OnPowerModePreChange: going to DeepSleep — WiFi not connected, skipping disconnect");
+ }
+#ifdef ENABLE_ETHERNET_CONNECTION_HANDLING
+ if (m_ethEnabled.load() && m_ethConnected.load())
+ {
+ NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — deactivating Ethernet");
+
+ uint32_t rcEthDown = EthernetDeactivate();
+ if (rcEthDown == Core::ERROR_NONE)
+ {
+ m_ethDisconnectedForSleep.store(true);
+ }
+ else
+ {
+ NMLOG_ERROR("OnPowerModePreChange: EthernetDeactivate failed (rc=%u), will not activate on wakeup", rcEthDown);
+ }
+ }
+ else
+ {
+ NMLOG_DEBUG("OnPowerModePreChange: going to DeepSleep — Ethernet not activated, skipping deactivate");
+ }
+#endif
+ }
+ else if (currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP)
+ {
+ if (m_wlanDisconnectedForSleep.load())
+ {
+ if (!m_lastConnectedSSID.empty())
+ {
+ NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — reconnecting to '%s'",
+ m_lastConnectedSSID.c_str());
+ uint32_t rcWifiUp = ConnectToKnownSSID(m_lastConnectedSSID);
+ if (rcWifiUp == Core::ERROR_NONE)
+ {
+ m_wlanDisconnectedForSleep.store(false);
+ }
+ else
+ {
+ NMLOG_ERROR("OnPowerModePreChange: ConnectToKnownSSID failed (rc=%u)", rcWifiUp);
+ }
+ }
+ else
+ {
+ NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — no last SSID, skipping reconnect");
+ }
+ }
+ else
+ {
+ NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — WiFi was not connected or was already down before sleep, skipping reconnect");
+ }
+ }
+ 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));
+ if (currentState == Exchange::IPowerManager::PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) {
+
+ if (m_wlanEnabled.load() && m_wlanConnected.load())
+ {
+ // 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");
+ if (StartWiFiScan(nullptr, nullptr) != Core::ERROR_NONE)
+ {
+ NMLOG_ERROR("OnPowerModeChanged: StartWiFiScan failed");
+ }
+
+ NMLOG_INFO("OnPowerModeChanged: waking from DeepSleep, requesting DHCP lease on wlan0");
+ if (ReacquireDHCPLease("wlan0") != Core::ERROR_NONE)
+ {
+ NMLOG_ERROR("OnPowerModeChanged: ReacquireDHCPLease(wlan0) failed");
+ }
+ }
+ if (m_ethEnabled.load() && m_ethConnected.load())
+ {
+ NMLOG_INFO("OnPowerModeChanged: waking from DeepSleep, requesting DHCP lease on eth0");
+ if (ReacquireDHCPLease("eth0") != Core::ERROR_NONE)
+ {
+ NMLOG_ERROR("OnPowerModeChanged: ReacquireDHCPLease(eth0) failed");
+ }
+ }
+ }
+ }
+
+ bool isIPv4LinkLocal(const std::string& addr)
+ {
+ struct in_addr sa{};
+ return inet_pton(AF_INET, addr.c_str(), &sa) == 1 &&
+ (ntohl(sa.s_addr) & 0xffff0000u) == 0xa9fe0000u;
+ }
+
+ bool isIPv6LinkLocal(const std::string& addr)
+ {
+ struct in6_addr sa6{};
+ return inet_pton(AF_INET6, addr.c_str(), &sa6) == 1 &&
+ sa6.s6_addr[0] == 0xfe && (sa6.s6_addr[1] & 0xc0) == 0x80;
+ }
+
+ bool isIPv6ULA(const std::string& addr)
+ {
+ struct in6_addr sa6{};
+ return inet_pton(AF_INET6, addr.c_str(), &sa6) == 1 &&
+ (sa6.s6_addr[0] & 0xfe) == 0xfc;
+ }
+
+ /* Parse a MAC string into 6 bytes. Accepts both "AA:BB:CC:DD:EE:FF" and "aabbccddeeff". */
+ static bool parseMac(const std::string& mac, uint8_t out[6])
+ {
+ unsigned int b[6];
+ if (mac.size() >= 17 &&
+ sscanf(mac.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x",
+ &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]) == 6)
+ {
+ for (int i = 0; i < 6; ++i) out[i] = static_cast(b[i]);
+ return true;
+ }
+ if (mac.size() >= 12 &&
+ sscanf(mac.c_str(), "%02x%02x%02x%02x%02x%02x",
+ &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]) == 6)
+ {
+ for (int i = 0; i < 6; ++i) out[i] = static_cast(b[i]);
+ return true;
+ }
+ return false;
+ }
+
+ bool isIPv6MacBased(const std::string& ipv6Addr, const std::string& macAddr)
+ {
+ struct in6_addr sa6{};
+ uint8_t mac[6];
+ if (inet_pton(AF_INET6, ipv6Addr.c_str(), &sa6) != 1 || !parseMac(macAddr, mac))
+ return false;
+
+ /* Build 8-byte EUI-64 identifier from 6-byte MAC:
+ mac[0..2] | ff:fe | mac[3..5], then flip the U/L bit (bit 1) of byte 0. */
+ uint8_t eui64[8];
+ eui64[0] = mac[0] ^ 0x02; // flip Universal/Local bit
+ eui64[1] = mac[1];
+ eui64[2] = mac[2];
+ eui64[3] = 0xff;
+ eui64[4] = 0xfe;
+ eui64[5] = mac[3];
+ eui64[6] = mac[4];
+ eui64[7] = mac[5];
+
+ /* Compare against the interface-ID (last 8 bytes) of the IPv6 address. */
+ if (memcmp(&sa6.s6_addr[8], eui64, 8) == 0)
+ {
+ NMLOG_DEBUG("MAC %s based global v6 address %s", macAddr.c_str(), ipv6Addr.c_str());
+ return true;
+ }
+ return false;
+ }
+
+ bool NetworkManagerImplementation::lookupIpCache(
+ const std::string& iface, const std::string& ipFamily,
+ Exchange::INetworkManager::IPAddress& out) const
+ {
+ std::lock_guard lock(m_ipCacheMutex);
+ auto it = m_ipCacheMap.find({iface, ipFamily});
+ if (it != m_ipCacheMap.end() && it->second.valid) {
+ out = it->second.toIPAddress();
+ out.ipversion = ipFamily;
+ return true;
+ }
+ return false;
+ }
+
+ std::set NetworkManagerImplementation::swapIpCache(
+ const std::string& iface, const std::string& ipFamily,
+ IpFamilyCache newCache)
+ {
+ std::set oldKeys;
+ std::lock_guard lock(m_ipCacheMutex);
+ IpFamilyCache& cache = m_ipCacheMap[{iface, ipFamily}];
+ for (const auto& kv : cache.globalAddresses)
+ oldKeys.insert(kv.first);
+ cache = std::move(newCache);
+ return oldKeys;
+ }
+
+ Exchange::INetworkManager::IPAddress IpFamilyCache::toIPAddress() const
+ {
+ Exchange::INetworkManager::IPAddress addr{};
+ /* Detect IP version from any available address. */
+ bool isIPv6 = false;
+ {
+ const std::string* sample = nullptr;
+ if (!globalAddresses.empty())
+ sample = &globalAddresses.begin()->first;
+ else if (!uniqueLocalAddresses.empty())
+ sample = &(*uniqueLocalAddresses.begin());
+ else if (!linkLocalAddresses.empty())
+ sample = &(*linkLocalAddresses.begin());
+ if (sample) {
+ struct in6_addr sa6{};
+ isIPv6 = (inet_pton(AF_INET6, sample->c_str(), &sa6) == 1);
+ }
+ }
+ addr.ipversion = isIPv6 ? "IPv6" : "IPv4";
+ addr.autoconfig = autoconfig;
+ addr.dhcpserver = dhcpserver;
+ addr.ula = uniqueLocalAddresses.empty() ? "" : *uniqueLocalAddresses.begin();
+ addr.gateway = gateway;
+ addr.primarydns = primarydns;
+ addr.secondarydns = secondarydns;
+ /* Prefer non-MAC-based global; fall back to MAC-based if all are MAC-based. */
+ const std::string* bestGlobal = nullptr;
+ uint32_t bestPrefix = 0;
+ const std::string* fallbackMac = nullptr;
+ uint32_t fallbackMacPrefix = 0;
+ for (const auto& kv : globalAddresses) {
+ if (kv.second.type == ADDR_GLOBAL) {
+ bestGlobal = &kv.first;
+ bestPrefix = kv.second.prefix;
+ break;
+ }
+ if (!fallbackMac) {
+ fallbackMac = &kv.first;
+ fallbackMacPrefix = kv.second.prefix;
+ }
+ }
+ if (bestGlobal) {
+ addr.ipaddress = *bestGlobal;
+ addr.prefix = bestPrefix;
+ } else if (fallbackMac) {
+ addr.ipaddress = *fallbackMac;
+ addr.prefix = fallbackMacPrefix;
+ }
+ return addr;
+ }
}
}
diff --git a/plugin/NetworkManagerImplementation.h b/plugin/NetworkManagerImplementation.h
index f5bd49b1..65baa0b1 100644
--- a/plugin/NetworkManagerImplementation.h
+++ b/plugin/NetworkManagerImplementation.h
@@ -26,7 +26,10 @@
#include
#include
#include
+#include