diff --git a/CHANGELOG.md b/CHANGELOG.md
index a904e2f7..7fa5294b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,19 @@ 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.
+## [2.2.0] - 2026-04-30
+### Added
+- Added new method to connection to specific known SSID
+- Extended WiFiConnect method to support BSSID and specific Band
+- Defaulted to use RDKLogger and avoided redundant logging for few methods
+- Added Minimal Ethernet Connection Profile for migration handling
+- General improvements on RPC methods & crash resilience
+
+## [2.1.0] - 2026-03-20
+### Added
+- Added T2 eventing from NetworkManager
+- Updated documentation of the plugin
+
## [2.0.0] - 2026-02-11
### Changed
- The onAvailableSSIDs event signature changed to report strength, noise & frequency as Number
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 50e77090..99ffdb2d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,7 +37,7 @@ endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
set(VERSION_MAJOR 2)
-set(VERSION_MINOR 0)
+set(VERSION_MINOR 2)
set(VERSION_PATCH 0)
add_compile_definitions(NETWORKMANAGER_MAJOR_VERSION=${VERSION_MAJOR})
diff --git a/definition/NetworkManager.json b/definition/NetworkManager.json
index 3dff5571..2252aed0 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.0.0"
+ "version": "2.2.0"
},
"definitions": {
"success": {
diff --git a/docs/NetworkManagerPlugin.md b/docs/NetworkManagerPlugin.md
index a041f624..9e4d969c 100644
--- a/docs/NetworkManagerPlugin.md
+++ b/docs/NetworkManagerPlugin.md
@@ -2,7 +2,7 @@
# NetworkManager Plugin
-**Version: 2.0.0**
+**Version: 2.2.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.0.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 2.2.0). It includes detailed specification about its methods provided and notifications sent.
## Case Sensitivity
diff --git a/legacy/LegacyNetworkAPIs.cpp b/legacy/LegacyNetworkAPIs.cpp
index d5924ee5..071466cd 100644
--- a/legacy/LegacyNetworkAPIs.cpp
+++ b/legacy/LegacyNetworkAPIs.cpp
@@ -783,6 +783,9 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN+1] = {
}
}
endpointsIter = (Core::Service::Create(endpoints));
+ if (endpointsIter == nullptr) {
+ returnJson(rc);
+ }
auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN);
if (_nwmgr)
diff --git a/legacy/LegacyWiFiManagerAPIs.cpp b/legacy/LegacyWiFiManagerAPIs.cpp
index 12e91b0a..9132ed09 100644
--- a/legacy/LegacyWiFiManagerAPIs.cpp
+++ b/legacy/LegacyWiFiManagerAPIs.cpp
@@ -653,6 +653,9 @@ namespace WPEFramework
}
ssids = (Core::Service::Create(inputSSIDlist));
+ if (ssids == nullptr) {
+ returnJson(rc);
+ }
}
auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN);
diff --git a/plugin/NetworkManagerConnectivity.cpp b/plugin/NetworkManagerConnectivity.cpp
index 3d9b8892..93825d51 100644
--- a/plugin/NetworkManagerConnectivity.cpp
+++ b/plugin/NetworkManagerConnectivity.cpp
@@ -575,7 +575,7 @@ namespace WPEFramework
NMCONNECTIVITY_CURL_HEAD_REQUEST, ipversionLocal, interface);
if (interface.empty())
- interface = _instance->m_defaultInterface;
+ interface = _instance->getDefaultInterface();
return testInternet.getInternetState();
}
@@ -634,7 +634,9 @@ namespace WPEFramework
return false;
}
- if(_instance->m_defaultInterface.empty())
+ string defaultIface = _instance->getDefaultInterface();
+
+ if(defaultIface.empty())
{
NMLOG_WARNING("default interface not set");
return false;
@@ -646,7 +648,7 @@ namespace WPEFramework
m_cmCv.notify_one();
NMLOG_INFO("switching to initial check - eth %s - wlan %s - default interface %s",
- _instance->m_ethConnected.load()? "up":"down", _instance->m_wlanConnected.load()? "up":"down", _instance->m_defaultInterface.c_str());
+ _instance->m_ethConnected.load()? "up":"down", _instance->m_wlanConnected.load()? "up":"down", defaultIface.c_str());
return true;
}
@@ -658,7 +660,8 @@ namespace WPEFramework
{
NMLOG_INFO("notifying internet state %s", getInternetStateString(newInternetState));
Exchange::INetworkManager::InternetStatus newState = newInternetState;
- _instance->ReportInternetStatusChange(oldState , newState, _instance->m_defaultInterface);
+ string defaultIface = _instance->getDefaultInterface();
+ _instance->ReportInternetStatusChange(oldState, newState, defaultIface);
m_InternetState = newInternetState;
oldState = newState; // 'm_InternetState' not exactly previous state, it may change to unknow when interface changed
}
@@ -694,68 +697,73 @@ namespace WPEFramework
m_notify = true;
InitialRetryCount = 1;
}
- else if(_instance->m_defaultInterface.empty())
- {
- NMLOG_WARNING("default interface not set");
- if (InitialRetryCount == 0)
- m_notify = true;
- InitialRetryCount = 1;
- }
- else if (m_switchToInitial)
+ else
{
- if (InitialRetryCount == 0)
- m_notify = true;
- NMLOG_INFO("Initial connectivity check - index:%d, current state:%s, interface:%s", InitialRetryCount, getInternetStateString(currentInternetState), _instance->m_defaultInterface.c_str());
- timeoutInSec = NMCONNECTIVITY_MONITOR_MIN_INTERVAL;
- TestConnectivity testInternet(m_endpoint(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS,
- NMCONNECTIVITY_CURL_HEAD_REQUEST, 2, _instance->m_defaultInterface);
- currentInternetState = testInternet.getInternetState();
+ string defaultIface = _instance->getDefaultInterface();
- if (currentInternetState == INTERNET_NOT_AVAILABLE) {
- NMLOG_DEBUG("interface connected but no internet");
- InitialRetryCount = 1; // continue same check for 5 sec
- }
- else {
- if(currentInternetState == INTERNET_CAPTIVE_PORTAL)
- m_captiveURI = testInternet.getCaptivePortal();
-
- if (currentInternetState != m_InternetState) {
- NMLOG_DEBUG("initial connectivity state change from %s to %s", getInternetStateString(m_InternetState), getInternetStateString(currentInternetState));
- m_InternetState = currentInternetState;
- InitialRetryCount = 1; // reset retry count to get continuous 3 same state
+ if(defaultIface.empty())
+ {
+ NMLOG_WARNING("default interface not set");
+ if (InitialRetryCount == 0)
m_notify = true;
- }
- InitialRetryCount++;
+ InitialRetryCount = 1;
}
-
- if (InitialRetryCount > NM_CONNECTIVITY_MONITOR_RETRY_COUNT) {
- m_switchToInitial = false;
- m_notify = true;
- NMLOG_INFO("switching to ideal ccm check interface: %s", _instance->m_defaultInterface.c_str());
- }
- }
- else
- {
- // ideal case check every 30 sec happenses when captive portal or limited internet
- timeoutInSec = NMCONNECTIVITY_MONITOR_RETRY_INTERVAL;
- InitialRetryCount = 0;
-
- if(m_InternetState != INTERNET_FULLY_CONNECTED)
+ else if (m_switchToInitial)
{
+ if (InitialRetryCount == 0)
+ m_notify = true;
+ NMLOG_INFO("Initial connectivity check - index:%d, current state:%s, interface:%s", InitialRetryCount, getInternetStateString(currentInternetState), defaultIface.c_str());
+ timeoutInSec = NMCONNECTIVITY_MONITOR_MIN_INTERVAL;
TestConnectivity testInternet(m_endpoint(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS,
- NMCONNECTIVITY_CURL_HEAD_REQUEST, 2, _instance->m_defaultInterface); // check both IP versions
+ NMCONNECTIVITY_CURL_HEAD_REQUEST, 2, defaultIface);
currentInternetState = testInternet.getInternetState();
- if (currentInternetState == INTERNET_CAPTIVE_PORTAL) // if captive portal found copy the URL
- m_captiveURI = testInternet.getCaptivePortal();
+ if (currentInternetState == INTERNET_NOT_AVAILABLE) {
+ NMLOG_DEBUG("interface connected but no internet");
+ InitialRetryCount = 1; // continue same check for 5 sec
+ }
+ else {
+ if(currentInternetState == INTERNET_CAPTIVE_PORTAL)
+ m_captiveURI = testInternet.getCaptivePortal();
+
+ if (currentInternetState != m_InternetState) {
+ NMLOG_DEBUG("initial connectivity state change from %s to %s", getInternetStateString(m_InternetState), getInternetStateString(currentInternetState));
+ m_InternetState = currentInternetState;
+ InitialRetryCount = 1; // reset retry count to get continuous 3 same state
+ m_notify = true;
+ }
+ InitialRetryCount++;
+ }
- if (currentInternetState != m_InternetState)
- {
- NMLOG_INFO("ideal connectivity state change from %s to %s", getInternetStateString(m_InternetState), getInternetStateString(currentInternetState));
- m_switchToInitial = true;
+ if (InitialRetryCount > NM_CONNECTIVITY_MONITOR_RETRY_COUNT) {
+ m_switchToInitial = false;
m_notify = true;
- InitialRetryCount = 1;
- timeoutInSec = NMCONNECTIVITY_MONITOR_MIN_INTERVAL; // retry in 5 sec
+ NMLOG_INFO("switching to ideal ccm check interface: %s", defaultIface.c_str());
+ }
+ }
+ else
+ {
+ // ideal case check every 30 sec happenses when captive portal or limited internet
+ timeoutInSec = NMCONNECTIVITY_MONITOR_RETRY_INTERVAL;
+ InitialRetryCount = 0;
+
+ if(m_InternetState != INTERNET_FULLY_CONNECTED)
+ {
+ TestConnectivity testInternet(m_endpoint(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS,
+ NMCONNECTIVITY_CURL_HEAD_REQUEST, 2, defaultIface); // check both IP versions
+ currentInternetState = testInternet.getInternetState();
+
+ if (currentInternetState == INTERNET_CAPTIVE_PORTAL) // if captive portal found copy the URL
+ m_captiveURI = testInternet.getCaptivePortal();
+
+ if (currentInternetState != m_InternetState)
+ {
+ NMLOG_INFO("ideal connectivity state change from %s to %s", getInternetStateString(m_InternetState), getInternetStateString(currentInternetState));
+ m_switchToInitial = true;
+ m_notify = true;
+ InitialRetryCount = 1;
+ timeoutInSec = NMCONNECTIVITY_MONITOR_MIN_INTERVAL; // retry in 5 sec
+ }
}
}
}
diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp
index 56cedd93..b4b5bca6 100644
--- a/plugin/NetworkManagerImplementation.cpp
+++ b/plugin/NetworkManagerImplementation.cpp
@@ -73,6 +73,7 @@ namespace WPEFramework
NMLOG_INFO("NetworkManager Out-Of-Process Shutdown/Cleanup");
connectivityMonitor.stopConnectivityMonitor();
_instance = nullptr;
+ platform_deinit();
if(m_registrationThread.joinable())
{
m_registrationThread.join();
@@ -250,6 +251,9 @@ namespace WPEFramework
LOG_ENTRY_FUNCTION();
std::vector tmpEndpoints = connectivityMonitor.getConnectivityMonitorEndpoints();
endpoints = (Core::Service::Create(tmpEndpoints));
+ if(endpoints == nullptr) {
+ return Core::ERROR_GENERAL;
+ }
return Core::ERROR_NONE;
}
@@ -311,7 +315,7 @@ namespace WPEFramework
ipversion = "IPv4";
if(interface.empty())
- interface = m_defaultInterface;
+ interface = getDefaultInterface();
return Core::ERROR_NONE;
}
@@ -337,7 +341,7 @@ namespace WPEFramework
NMLOG_DEBUG("Primary interface: %s, eth0: [enabled=%d, connected=%d], wlan0: [enabled=%d, connected=%d]",
interface.c_str(), m_ethEnabled.load(), m_ethConnected.load(), m_wlanEnabled.load(), m_wlanConnected.load());
- m_defaultInterface = interface;
+ setDefaultInterface(interface);
return Core::ERROR_NONE;
}
@@ -364,7 +368,7 @@ namespace WPEFramework
ipversion = "IPv4";
if (interface.empty())
- interface = m_defaultInterface;
+ interface = getDefaultInterface();
ipaddress = result.public_ip;
#if USE_TELEMETRY
@@ -637,7 +641,9 @@ namespace WPEFramework
using Implementation = RPC::IteratorType;
security = Core::Service::Create(modeInfo);
-
+ if (security == nullptr) {
+ return Core::ERROR_GENERAL;
+ }
return Core::ERROR_NONE;
}
@@ -651,7 +657,7 @@ namespace WPEFramework
m_ethIPv4Address = {};
m_ethIPv6Address = {};
m_ethConnected.store(false);
- m_defaultInterface = "wlan0"; // If WiFi is connected, make it the default interface
+ setDefaultInterface("wlan0"); // If WiFi is connected, make it the default interface
// As default interface is changed to wlan0, switch connectivity monitor to initial check
connectivityMonitor.switchToInitialCheck();
}
@@ -660,10 +666,11 @@ namespace WPEFramework
m_wlanIPv4Address = {};
m_wlanIPv6Address = {};
m_wlanConnected.store(false);
+ bool triggerConnectivityCheck;
if(m_ethConnected.load())
- m_defaultInterface = "eth0"; // If Ethernet is connected, make it the default interface
-
- if(m_defaultInterface == interface)
+ setDefaultInterface("eth0"); // If Ethernet is connected, make it the default interface
+ triggerConnectivityCheck = (getDefaultInterface() == interface);
+ if(triggerConnectivityCheck)
{
// When WiFi is disconnected while Ethernet is connected, we don't need to trigger connectivity monitor.
// For WiFi-only state and WiFi disconnected, we should trigger connectivity monitor.
@@ -752,12 +759,14 @@ namespace WPEFramework
}
// FIXME : Availability of ip address for a given interface does not mean that its the default interface. This hardcoding will work for RDKProxy but not for Gnome.
+ bool isDefaultIface;
if (m_ethConnected.load() && m_wlanConnected.load())
- m_defaultInterface = "eth0";
+ setDefaultInterface("eth0");
else
- m_defaultInterface = interface;
+ setDefaultInterface(interface);
+ isDefaultIface = (getDefaultInterface() == interface);
- if(m_defaultInterface == interface) {
+ if(isDefaultIface) {
// As default interface is connected, switch connectivity monitor to initial check any way
connectivityMonitor.switchToInitialCheck();
}
diff --git a/plugin/NetworkManagerImplementation.h b/plugin/NetworkManagerImplementation.h
index 8a0c33d0..f5bd49b1 100644
--- a/plugin/NetworkManagerImplementation.h
+++ b/plugin/NetworkManagerImplementation.h
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
using namespace std;
@@ -34,6 +35,10 @@ using namespace std;
#include "NetworkManagerConnectivity.h"
#include "NetworkManagerStunClient.h"
+/* Forward declarations to avoid pulling GLib/libnm headers into this header */
+typedef struct _NMClient NMClient;
+typedef struct _GMainContext GMainContext;
+
/*
* Receiver thermal noise + BW factor + assumed noise figure (NF) (dB)
* for a 20MHz channel,
@@ -274,6 +279,7 @@ namespace WPEFramework
private:
void platform_init(void);
+ void platform_deinit(void);
void platform_logging(const NetworkManagerLogger::LogLevel& level);
void getInitialConnectionState(void);
void executeExternally(NetworkEvents event, const string commandToExecute, string& response);
@@ -317,9 +323,26 @@ namespace WPEFramework
std::atomic m_wlanConnected;
std::atomic m_ethEnabled;
std::atomic m_wlanEnabled;
- string m_defaultInterface;
std::string m_lastConnectedSSID;
+ NMClient *m_nmClient{nullptr}; /* proxy NMClient — bound to m_nmContext */
+ GMainContext *m_nmContext{nullptr}; /* isolated context, not the global default */
mutable ConnectivityMonitor connectivityMonitor;
+
+ string getDefaultInterface() const
+ {
+ std::lock_guard lock(m_defaultInterfaceMutex);
+ return m_defaultInterface;
+ }
+
+ void setDefaultInterface(const string& iface)
+ {
+ std::lock_guard lock(m_defaultInterfaceMutex);
+ m_defaultInterface = iface;
+ }
+
+ private:
+ string m_defaultInterface;
+ mutable std::mutex m_defaultInterfaceMutex;
};
}
}
diff --git a/plugin/NetworkManagerJsonRpc.cpp b/plugin/NetworkManagerJsonRpc.cpp
index 22af0cdb..5ed0b60f 100644
--- a/plugin/NetworkManagerJsonRpc.cpp
+++ b/plugin/NetworkManagerJsonRpc.cpp
@@ -429,6 +429,9 @@ namespace WPEFramework
}
}
endpointsIter = (Core::Service::Create(endpoints));
+ if(endpointsIter == nullptr){
+ returnJson(rc);
+ }
if (_networkManager)
rc = _networkManager->SetConnectivityTestEndpoints(endpointsIter);
@@ -664,6 +667,9 @@ namespace WPEFramework
}
}
ssids = (Core::Service::Create(ssidslist));
+ if(ssids == nullptr){
+ returnJson(rc);
+ }
}
if (_networkManager)
diff --git a/plugin/gnome/NetworkManagerGnomeProxy.cpp b/plugin/gnome/NetworkManagerGnomeProxy.cpp
index a36a28c1..b661a8de 100644
--- a/plugin/gnome/NetworkManagerGnomeProxy.cpp
+++ b/plugin/gnome/NetworkManagerGnomeProxy.cpp
@@ -25,7 +25,6 @@
#include
#define IN_IS_ADDR_LINKLOCAL(a) ((((uint32_t)ntohl(a)) & 0xffff0000U) == 0xa9fe0000U)
-static NMClient *client = NULL;
using namespace WPEFramework;
using namespace WPEFramework::Plugin;
using namespace std;
@@ -196,7 +195,7 @@ namespace WPEFramework
const GPtrArray *connections = NULL;
NMConnection *connection = NULL;
- if (client == nullptr) {
+ if (m_nmClient == nullptr) {
NMLOG_ERROR("NMClient is NULL");
return Core::ERROR_GENERAL;
}
@@ -207,7 +206,7 @@ namespace WPEFramework
return Core::ERROR_BAD_REQUEST;
}
- connections = nm_client_get_connections(client);
+ connections = nm_client_get_connections(m_nmClient);
if (connections == NULL || connections->len == 0)
{
NMLOG_ERROR("Could not get nm connections");
@@ -250,6 +249,12 @@ namespace WPEFramework
return Core::ERROR_NONE;
}
+ void NetworkManagerImplementation::platform_deinit()
+ {
+ if(m_nmClient) { g_object_unref(m_nmClient); m_nmClient = nullptr; }
+ if(m_nmContext) { g_main_context_unref(m_nmContext); m_nmContext = nullptr; }
+ }
+
void NetworkManagerImplementation::platform_logging(const NetworkManagerLogger::LogLevel& level)
{
/* set networkmanager daemon log level based on current plugin log level */
@@ -264,24 +269,35 @@ namespace WPEFramework
GError *error = NULL;
// initialize the NMClient object
- client = nm_client_new(NULL, &error);
- if (client == NULL) {
+ // Create an isolated GMainContext so this m_nmClient's D-Bus socket is NOT a
+ // source on the global default context. The event thread runs the default
+ // context via g_main_loop_run(); without isolation it would own and mutate
+ // this m_nmClient's GObjects concurrently with the RPC thread.
+ m_nmContext = g_main_context_new();
+ g_main_context_push_thread_default(m_nmContext);
+ m_nmClient = nm_client_new(NULL, &error);
+ g_main_context_pop_thread_default(m_nmContext);
+ if (m_nmClient == NULL) {
if (error) {
NMLOG_FATAL("Error initializing NMClient: %s", error->message);
g_error_free(error);
}
+ if (m_nmContext) {
+ g_main_context_unref(m_nmContext);
+ m_nmContext = nullptr;
+ }
return;
}
nmUtils::getDeviceProperties(); // get interface name form '/etc/device.proprties'
- modifyDefaultConnConfig(client);
- NMDeviceState ethState = ifaceState(client, nmUtils::ethIface());
+ modifyDefaultConnConfig(m_nmClient);
+ NMDeviceState ethState = ifaceState(m_nmClient, nmUtils::ethIface());
if(ethState > NM_DEVICE_STATE_DISCONNECTED && ethState < NM_DEVICE_STATE_DEACTIVATING)
- m_defaultInterface = nmUtils::ethIface();
+ setDefaultInterface(nmUtils::ethIface());
else
- m_defaultInterface = nmUtils::wlanIface();
+ setDefaultInterface(nmUtils::wlanIface());
- NMLOG_INFO("default interface is %s", m_defaultInterface.c_str());
+ NMLOG_INFO("default interface is %s", getDefaultInterface().c_str());
// getInitialConnectionState function not called here, as event monitor will report the initial state
nmEvent = GnomeNetworkManagerEvents::getInstance();
nmEvent->startNetworkMangerEventMonitor();
@@ -294,12 +310,17 @@ namespace WPEFramework
std::vector interfaceList;
std::string wifiname = nmUtils::wlanIface(), ethname = nmUtils::ethIface();
- if(client == nullptr) {
- NMLOG_FATAL("client connection null:");
+ if(m_nmClient == nullptr) {
+ NMLOG_FATAL("NMClient is null");
return Core::ERROR_GENERAL;
}
- GPtrArray *devices = const_cast(nm_client_get_devices(client));
+ if (m_nmContext) {
+ for (int i = 0; i < 100 && g_main_context_iteration(m_nmContext, FALSE); ++i){
+ // Intentional empty body: just flushing the event queue
+ }
+ }
+ GPtrArray *devices = const_cast(nm_client_get_devices(m_nmClient));
if (devices == NULL) {
NMLOG_ERROR("Failed to get device list.");
return Core::ERROR_GENERAL;
@@ -350,6 +371,9 @@ namespace WPEFramework
using Implementation = RPC::IteratorType;
interfacesItr = Core::Service::Create(interfaceList);
+ if(interfacesItr == nullptr) {
+ return Core::ERROR_GENERAL;
+ }
return rc;
}
#if 0
@@ -422,12 +446,13 @@ namespace WPEFramework
return rc;
}
#endif
+
uint32_t NetworkManagerImplementation::SetInterfaceState(const string& interface/* @in */, const bool enabled /* @in */)
{
- if(client == nullptr)
+ if(m_nmClient == nullptr)
{
- NMLOG_WARNING("client connection null:");
+ NMLOG_WARNING("NMClient is null");
return Core::ERROR_RPC_CALL_FAILED;
}
@@ -437,33 +462,127 @@ namespace WPEFramework
return Core::ERROR_GENERAL;
}
- if(!wifi->setInterfaceState(interface, enabled))
+ // For ethernet enable: run BOOT_MIGRATION cleanup first, then setInterfaceState
+ if(enabled && interface == nmUtils::ethIface())
{
- NMLOG_ERROR("interface state change failed");
- return Core::ERROR_GENERAL;
- }
+ // Check boot type and delete all ethernet NM connections if BOOT_MIGRATION
+ {
+ const char* bootFile = "/tmp/bootType";
+ std::ifstream file(bootFile);
- NMLOG_INFO("interface %s state: %s", interface.c_str(), enabled ? "enabled" : "disabled");
- // update the interface global cache state
- if(interface == nmUtils::wlanIface() && _instance != NULL)
- _instance->m_wlanEnabled.store(enabled);
- else if(interface == nmUtils::ethIface() && _instance != NULL)
- _instance->m_ethEnabled.store(enabled);
+ if(file.is_open())
+ {
+ std::string line, bootTypeValue;
+ while(std::getline(file, line))
+ {
+ const std::string key = "BOOT_TYPE=";
+ auto pos = line.find(key);
+ if(pos != std::string::npos)
+ {
+ bootTypeValue = line.substr(pos + key.size());
+ break;
+ }
+ }
+
+ if(bootTypeValue == "BOOT_MIGRATION")
+ {
+ NMLOG_INFO("BOOT_MIGRATION detected, deleting all wired NM connections");
+
+ // Bring down the ethernet interface before wiping its connections
+ // so NM doesn't immediately re-activate them during deletion.
+ NMDevice *ethDev = nm_client_get_device_by_iface(m_nmClient, interface.c_str());
+ if(ethDev)
+ {
+ GError *discError = nullptr;
+ if(!nm_device_disconnect(ethDev, nullptr, &discError))
+ {
+ NMLOG_WARNING("Failed to disconnect %s before migration cleanup: %s",
+ interface.c_str(),
+ discError ? discError->message : "unknown error");
+ if(discError) g_error_free(discError);
+ }
+ }
+
+ const GPtrArray *connections = nm_client_get_connections(m_nmClient);
+ if(connections && connections->len > 0)
+ {
+ /* Snapshot the list before iterating: nm_client_get_connections()
+ * returns an internal array that can be mutated as connections
+ * are removed, so we must not iterate it while deleting. */
+ GPtrArray *snapshot = g_ptr_array_new_full(connections->len, g_object_unref);
+ for(guint i = 0; i < connections->len; ++i)
+ {
+ NMRemoteConnection *conn = NM_REMOTE_CONNECTION(connections->pdata[i]);
+ if(!conn) continue;
+ NMSettingConnection *sCon = nm_connection_get_setting_connection(NM_CONNECTION(conn));
+ if(!sCon) continue;
+ const char *connType = nm_setting_connection_get_connection_type(sCon);
+ if(g_strcmp0(connType, NM_SETTING_WIRED_SETTING_NAME) != 0)
+ {
+ NMLOG_DEBUG("Skipping non-wired connection type: %s", connType ? connType : "null");
+ continue;
+ }
+ g_ptr_array_add(snapshot, g_object_ref(conn));
+ }
+
+ for(guint i = 0; i < snapshot->len; ++i)
+ {
+ NMRemoteConnection *conn = NM_REMOTE_CONNECTION(snapshot->pdata[i]);
+ GError *error = nullptr;
+ if(!nm_remote_connection_delete(conn, nullptr, &error))
+ {
+ const char *connId = nm_connection_get_id(NM_CONNECTION(conn));
+ NMLOG_ERROR("Failed to delete connection %s: %s",
+ connId ? connId : "",
+ error ? error->message : "unknown error");
+ if(error) g_error_free(error);
+ }
+ }
+ g_ptr_array_unref(snapshot);
+ }
+ }
+ }
+ }
+
+ NMLOG_INFO("Adding minimal ethernet connection profile ...");
+ wifi->addMinimalEthernetConnection(nmUtils::ethIface());
+
+ if(!wifi->setInterfaceState(interface, enabled))
+ {
+ NMLOG_ERROR("interface state change failed");
+ return Core::ERROR_GENERAL;
+ }
+
+ NMLOG_INFO("interface %s state: %s", interface.c_str(), enabled ? "enabled" : "disabled");
+ if(_instance != NULL)
+ _instance->m_ethEnabled.store(enabled);
- if(enabled)
- {
sleep(1); // wait for 1 sec to change the device state
+ NMLOG_INFO("Activating connection 'Wired connection 1' ...");
+ // default wired connection name is 'Wired connection 1'
+ wifi->activateKnownConnection(nmUtils::ethIface(), "Wired connection 1");
+ }
+ else
+ {
+ if(!wifi->setInterfaceState(interface, enabled))
+ {
+ NMLOG_ERROR("interface state change failed");
+ return Core::ERROR_GENERAL;
+ }
+
+ NMLOG_INFO("interface %s state: %s", interface.c_str(), enabled ? "enabled" : "disabled");
+ // update the interface global cache state
if(interface == nmUtils::wlanIface() && _instance != NULL)
+ _instance->m_wlanEnabled.store(enabled);
+ else if(interface == nmUtils::ethIface() && _instance != NULL)
+ _instance->m_ethEnabled.store(enabled);
+
+ if(enabled && interface == nmUtils::wlanIface() && _instance != NULL)
{
+ sleep(1); // wait for 1 sec to change the device state
NMLOG_INFO("Activating connection '%s' ...", _instance->m_lastConnectedSSID.c_str());
wifi->activateKnownConnection(nmUtils::wlanIface(), _instance->m_lastConnectedSSID);
}
- else if(interface == nmUtils::ethIface())
- {
- NMLOG_INFO("Activating connection 'Wired connection 1' ...");
- // default wired connection name is 'Wired connection 1'
- wifi->activateKnownConnection(nmUtils::ethIface(), "Wired connection 1");
- }
}
return Core::ERROR_NONE;
@@ -475,19 +594,26 @@ namespace WPEFramework
bool isIfaceFound = false;
std::string wifiname = nmUtils::wlanIface(), ethname = nmUtils::ethIface();
- if(client == nullptr)
- {
- NMLOG_WARNING("client connection null:");
- return Core::ERROR_RPC_CALL_FAILED;
- }
-
if(interface.empty() || (wifiname != interface && ethname != interface))
{
NMLOG_ERROR("interface: %s; not valied", interface.c_str()!=nullptr? interface.c_str():"empty");
return Core::ERROR_GENERAL;
}
- GPtrArray *devices = const_cast(nm_client_get_devices(client));
+ if(m_nmClient == nullptr)
+ {
+ NMLOG_WARNING("NMClient is null");
+ return Core::ERROR_RPC_CALL_FAILED;
+ }
+
+ if (m_nmContext) {
+ for (int i = 0; i < 100 && g_main_context_iteration(m_nmContext, FALSE); ++i){
+ // Intentional empty body: just flushing the event queue
+ }
+ }
+
+ GPtrArray *devices = const_cast(nm_client_get_devices(m_nmClient));
+
if (devices == NULL) {
NMLOG_ERROR("Failed to get device list.");
return Core::ERROR_GENERAL;
@@ -559,12 +685,6 @@ namespace WPEFramework
std::string wifiname = nmUtils::wlanIface(), ethname = nmUtils::ethIface();
- if(client == nullptr)
- {
- NMLOG_WARNING("client connection null:");
- return Core::ERROR_RPC_CALL_FAILED;
- }
-
if(interface.empty())
{
if(Core::ERROR_NONE != GetPrimaryInterface(interface))
@@ -626,7 +746,24 @@ namespace WPEFramework
}
}
- device = nm_client_get_device_by_iface(client, interface.c_str());
+ if(m_nmClient == nullptr)
+ {
+ NMLOG_WARNING("NMClient is null");
+ return Core::ERROR_RPC_CALL_FAILED;
+ }
+
+ /* Drain any pending D-Bus property-change events queued on m_nmContext
+ * before reading libnm GObject state. Because m_nmContext is isolated
+ * from the event thread, nobody else can run it — so this loop is
+ * single-threaded and safe. It ensures m_nmClient reflects the latest state
+ * from NetworkManager before we start iterating connections/addresses. */
+ if (m_nmContext) {
+ for (int i = 0; i < 100 && g_main_context_iteration(m_nmContext, FALSE); ++i){
+ // Intentional empty body: just flushing the event queue
+ }
+ }
+
+ device = nm_client_get_device_by_iface(m_nmClient, interface.c_str());
if (device == NULL)
{
NMLOG_FATAL("libnm doesn't have device corresponding to %s", interface.c_str());
@@ -644,7 +781,7 @@ namespace WPEFramework
// if(ipversion.empty())
// NMLOG_DEBUG("ipversion is empty default value IPv4");
- const GPtrArray *connections = nm_client_get_active_connections(client);
+ const GPtrArray *connections = nm_client_get_active_connections(m_nmClient);
if(connections == NULL)
{
NMLOG_WARNING("no active connection; ip is not assigned to interface");
@@ -701,9 +838,14 @@ namespace WPEFramework
{
for (guint i = 0; i < ipByte->len; i++)
{
+ ipStr.clear();
ipAddr = static_cast(ipByte->pdata[i]);
if(ipAddr)
- ipStr = nm_ip_address_get_address(ipAddr);
+ {
+ const char* addr = nm_ip_address_get_address(ipAddr);
+ if(addr)
+ ipStr = addr;
+ }
if(!ipStr.empty())
{
// Skip link-local IPv4 addresses (169.254.x.x)
@@ -713,7 +855,7 @@ namespace WPEFramework
NMLOG_DEBUG("Skipping link-local IPv4 address: %s", ipStr.c_str());
continue;
}
- result.ipaddress = nm_ip_address_get_address(ipAddr);
+ result.ipaddress = ipStr;
result.prefix = nm_ip_address_get_prefix(ipAddr);
NMLOG_DEBUG("IPv4 addr: %s/%d", result.ipaddress.c_str(), result.prefix);
}
@@ -772,9 +914,14 @@ namespace WPEFramework
{
for (guint i = 0; i < ipArray->len; i++)
{
+ ipStr.clear();
ipAddr = static_cast(ipArray->pdata[i]);
if(ipAddr)
- ipStr = nm_ip_address_get_address(ipAddr);
+ {
+ const char* addr = nm_ip_address_get_address(ipAddr);
+ if(addr)
+ ipStr = addr;
+ }
if(!ipStr.empty())
{
if (ipStr.compare(0, 5, "fe80:") == 0 || ipStr.compare(0, 6, "fe80::") == 0)
@@ -888,6 +1035,9 @@ namespace WPEFramework
if (!ssidList.empty())
{
ssids = Core::Service::Create(ssidList);
+ if(ssids == nullptr) {
+ return Core::ERROR_GENERAL;
+ }
rc = Core::ERROR_NONE;
}
else
diff --git a/plugin/gnome/NetworkManagerGnomeWIFI.cpp b/plugin/gnome/NetworkManagerGnomeWIFI.cpp
index e0a6abae..2348124d 100644
--- a/plugin/gnome/NetworkManagerGnomeWIFI.cpp
+++ b/plugin/gnome/NetworkManagerGnomeWIFI.cpp
@@ -655,6 +655,66 @@ namespace WPEFramework
return connection;
}
+ static void addMinimalEthernetConnectionCb(GObject *client, GAsyncResult *result, gpointer user_data)
+ {
+ GError *error = NULL;
+ wifiManager *_wifiManager = static_cast(user_data);
+ NMRemoteConnection *remoteConn = nm_client_add_connection2_finish(NM_CLIENT(client), result, NULL, &error);
+ if (error) {
+ if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ NMLOG_DEBUG("addMinimalEthernetConnection operation was cancelled");
+ g_error_free(error);
+ if (remoteConn)
+ g_object_unref(remoteConn);
+ if (_wifiManager->m_loop && g_main_loop_is_running(_wifiManager->m_loop)) {
+ g_main_loop_quit(_wifiManager->m_loop);
+ }
+ return; // do not alter m_isSuccess on cancellation
+ }
+ NMLOG_ERROR("addMinimalEthernetConnection error: %s", error->message);
+ _wifiManager->m_isSuccess = false;
+ g_error_free(error);
+ }
+ else if (!remoteConn) {
+ NMLOG_ERROR("addMinimalEthernetConnection failed");
+ _wifiManager->m_isSuccess = false;
+ }
+ else {
+ NMLOG_INFO("addMinimalEthernetConnection success");
+ _wifiManager->m_isSuccess = true;
+ g_object_unref(remoteConn);
+ }
+ g_main_loop_quit(_wifiManager->m_loop);
+ }
+
+ bool wifiManager::addMinimalEthernetConnection(std::string iface)
+ {
+ if (!createClientNewConnection())
+ return false;
+
+ NMConnection *ethConn = createMinimalEthernetConnection(iface);
+ if (ethConn == NULL)
+ {
+ NMLOG_ERROR("Failed to create minimal ethernet connection");
+ deleteClientConnection();
+ return false;
+ }
+
+ GVariant *connSettings = nm_connection_to_dbus(ethConn, NM_CONNECTION_SERIALIZE_ALL);
+ g_object_unref(ethConn);
+
+ m_isSuccess = false;
+ nm_client_add_connection2(m_client,
+ connSettings,
+ NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK,
+ NULL, TRUE, m_cancellable,
+ addMinimalEthernetConnectionCb, this);
+ g_variant_unref(connSettings);
+ wait(m_loop);
+ deleteClientConnection();
+ return m_isSuccess;
+ }
+
static bool connectionBuilder(const Exchange::INetworkManager::WiFiConnectTo& ssidinfo, NMConnection *m_connection, bool iswpsAP = false)
{
if(ssidinfo.ssid.empty() || ssidinfo.ssid.length() > 32)
@@ -1819,7 +1879,11 @@ namespace WPEFramework
error = NULL;
}
else
- NMLOG_ERROR("NetworkManager cleint create failed");
+ NMLOG_ERROR("NetworkManager client create failed");
+ g_main_context_pop_thread_default(wpsContext);
+ g_main_context_release(wpsContext);
+ g_main_context_unref(wpsContext);
+ wpsContext = NULL;
break;
}
@@ -1827,6 +1891,10 @@ namespace WPEFramework
if(wifidevice == NULL)
{
NMLOG_ERROR("Failed to get device list.");
+ g_main_context_pop_thread_default(wpsContext);
+ g_main_context_release(wpsContext);
+ g_main_context_unref(wpsContext);
+ wpsContext = NULL;
break;
}
@@ -1866,6 +1934,10 @@ namespace WPEFramework
if(ApList == NULL)
{
NMLOG_ERROR("Aplist Error !");
+ g_main_context_pop_thread_default(wpsContext);
+ g_main_context_release(wpsContext);
+ g_main_context_unref(wpsContext);
+ wpsContext = NULL;
break;
}
@@ -1958,6 +2030,11 @@ namespace WPEFramework
{
/* if wps action not triggerd do a scanning request */
nm_device_wifi_request_scan(NM_DEVICE_WIFI(wifidevice), NULL, &error);
+ if(error) {
+ NMLOG_WARNING("WiFi scan request failed: %s", error->message);
+ g_error_free(error);
+ error = NULL;
+ }
}
g_main_context_pop_thread_default(wpsContext);
@@ -2122,24 +2199,27 @@ namespace WPEFramework
// that can cause networking issues.
nm_device_disconnect_async(device, nullptr, disconnectCb, this);
wait(m_loop);
- // Wait until device is truly disconnected
+
+ // Identify the correct context
+ GMainContext *device_context = g_main_loop_get_context(m_loop);
int retry = 24; // 12 seconds
NMDeviceState oldDevState = NM_DEVICE_STATE_UNKNOWN;
while (retry-- > 0) {
- /* Force glib event processing to update state
- * This below line will create an uncertain time wait. We are taking a fixed time interval of 12 seconds.
- */
- // while (g_main_context_iteration(NULL, FALSE));
- g_usleep(500 * 1000); // give some time to NM to process the request
+ // 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));
+
+ // Fetch the updated state
deviceState = nm_device_get_state(device);
- if(oldDevState != deviceState)
- {
+ if(oldDevState != deviceState) {
oldDevState = deviceState;
- NMLOG_WARNING("Device state: %d", deviceState);
+ NMLOG_WARNING("Device state: %d Retry: %d", deviceState, retry);
}
-
- if (deviceState <= NM_DEVICE_STATE_DISCONNECTED)
+ if (deviceState <= NM_DEVICE_STATE_DISCONNECTED) {
break;
+ }
+ g_usleep(500 * 1000); // give some time to NM to process the request
}
}
}
diff --git a/plugin/gnome/NetworkManagerGnomeWIFI.h b/plugin/gnome/NetworkManagerGnomeWIFI.h
index 69799506..51164aa5 100644
--- a/plugin/gnome/NetworkManagerGnomeWIFI.h
+++ b/plugin/gnome/NetworkManagerGnomeWIFI.h
@@ -67,6 +67,7 @@ namespace WPEFramework
bool setInterfaceState(std::string interface, bool enabled);
bool setIpSettings(const string interface, const Exchange::INetworkManager::IPAddress &address);
bool setPrimaryInterface(const string interface);
+ bool addMinimalEthernetConnection(std::string iface);
private:
NMDevice *getWifiDevice();
diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusProxy.cpp b/plugin/gnome/gdbus/NetworkManagerGdbusProxy.cpp
index 6e848836..73aa49d9 100644
--- a/plugin/gnome/gdbus/NetworkManagerGdbusProxy.cpp
+++ b/plugin/gnome/gdbus/NetworkManagerGdbusProxy.cpp
@@ -90,6 +90,11 @@ namespace WPEFramework
nmUtils::setNetworkManagerlogLevelToTrace();
}
+ void NetworkManagerImplementation::platform_deinit()
+ {
+ return;
+ }
+
void NetworkManagerImplementation::platform_init()
{
::_instance = this;
@@ -104,14 +109,14 @@ namespace WPEFramework
if(_nmGdbusClient->getDeviceInfo(GnomeUtils::getEthIfname(), ethDevInfo))
{
if(ethDevInfo.state > NM_DEVICE_STATE_DISCONNECTED && ethDevInfo.state < NM_DEVICE_STATE_DEACTIVATING)
- m_defaultInterface = GnomeUtils::getEthIfname();
+ setDefaultInterface(GnomeUtils::getEthIfname());
else
- m_defaultInterface = GnomeUtils::getWifiIfname();
+ setDefaultInterface(GnomeUtils::getWifiIfname());
}
else
- m_defaultInterface = GnomeUtils::getWifiIfname();
+ setDefaultInterface(GnomeUtils::getWifiIfname());
- NMLOG_INFO("default interface is %s", m_defaultInterface.c_str());
+ NMLOG_INFO("default interface is %s", getDefaultInterface().c_str());
// Start event monitoring
_nmGdbusEvents->startNetworkMangerEventMonitor();
@@ -136,6 +141,9 @@ namespace WPEFramework
NMLOG_ERROR("GetAvailableInterfaces failed");
using Implementation = RPC::IteratorType;
interfacesItr = Core::Service::Create(interfaceList);
+ if(interfacesItr == nullptr){
+ return Core::ERROR_GENERAL;
+ }
return rc;
}
@@ -148,7 +156,7 @@ namespace WPEFramework
{
if(_nmGdbusClient->getDefaultInterface(interface))
{
- _instance->m_defaultInterface = interface;
+ _instance->setDefaultInterface(interface);
return Core::ERROR_NONE;
}
else
@@ -234,6 +242,9 @@ namespace WPEFramework
if(_nmGdbusClient->getKnownSSIDs(ssidList) && !ssidList.empty())
{
ssids = Core::Service::Create(ssidList);
+ if(ssids == nullptr) {
+ return Core::ERROR_GENERAL;
+ }
rc = Core::ERROR_NONE;
}
else
diff --git a/plugin/rdk/NetworkManagerRDKProxy.cpp b/plugin/rdk/NetworkManagerRDKProxy.cpp
index 670b0a6f..f218d61c 100644
--- a/plugin/rdk/NetworkManagerRDKProxy.cpp
+++ b/plugin/rdk/NetworkManagerRDKProxy.cpp
@@ -399,8 +399,8 @@ namespace WPEFramework
if(iface.connected)
{
NMLOG_INFO("'%s' interface is connected", iface.name.c_str());
- if(m_defaultInterface != iface.name)
- ReportActiveInterfaceChange(m_defaultInterface, iface.name);
+ if(getDefaultInterface() != iface.name)
+ ReportActiveInterfaceChange(getDefaultInterface(), iface.name);
Exchange::INetworkManager::IPAddress addrv4;
Exchange::INetworkManager::IPAddress addrv6;
std::string ipversion = "IPv4";
@@ -444,6 +444,11 @@ namespace WPEFramework
return Core::ERROR_NONE;
}
+ void NetworkManagerImplementation::platform_deinit()
+ {
+ return;
+ }
+
void NetworkManagerImplementation::platform_init()
{
LOG_ENTRY_FUNCTION();
@@ -562,6 +567,9 @@ namespace WPEFramework
}
using Implementation = RPC::IteratorType;
interfacesItr = Core::Service::Create(interfaceList);
+ if(interfacesItr == nullptr) {
+ return Core::ERROR_GENERAL;
+ }
rc = Core::ERROR_NONE;
}
@@ -583,7 +591,8 @@ namespace WPEFramework
if (IARM_RESULT_SUCCESS == IARM_Bus_Call(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_getDefaultInterface, (void*)&defaultRoute, sizeof(defaultRoute)))
{
NMLOG_INFO ("Call to %s for %s returned interface = %s, gateway = %s", IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_getDefaultInterface, defaultRoute.interface, defaultRoute.gateway);
- interface = m_defaultInterface = defaultRoute.interface;
+ interface = defaultRoute.interface;
+ setDefaultInterface(defaultRoute.interface);
rc = Core::ERROR_NONE;
}
else
@@ -699,7 +708,7 @@ namespace WPEFramework
if(interface.empty())
{
- interface = m_defaultInterface;
+ interface = getDefaultInterface();
}
if(ipversion.empty())
{
@@ -1037,6 +1046,9 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN+1] = {
NMLOG_INFO ("GetKnownSSIDs Success");
ssids = Core::Service::Create(ssidList);
+ if(ssids == nullptr) {
+ return Core::ERROR_GENERAL;
+ }
rc = Core::ERROR_NONE;
}
else
diff --git a/tests/l2Test/libnm/l2_test_libnmproxy.cpp b/tests/l2Test/libnm/l2_test_libnmproxy.cpp
index bd633dd7..e4f9b1ec 100644
--- a/tests/l2Test/libnm/l2_test_libnmproxy.cpp
+++ b/tests/l2Test/libnm/l2_test_libnmproxy.cpp
@@ -770,7 +770,6 @@ TEST_F(NetworkManagerTest, GetIPSettings_ipv4_config_valid)
.WillOnce(::testing::Return("192.168.1.0"));
EXPECT_CALL(*p_libnmWrapsImplMock, nm_ip_address_get_address(::testing::_))
- .WillOnce(::testing::Return("192.168.1.2"))
.WillOnce(::testing::Return("192.168.1.2"));
EXPECT_CALL(*p_libnmWrapsImplMock, nm_ip_config_get_addresses(::testing::_))
@@ -1371,4 +1370,4 @@ TEST_F(NetworkManagerTest, SetIPSettings_static_wlan0_autoConffalse)
g_object_unref(dummyActiveConn);
g_object_unref(dummyRemoteConn);
-}
\ No newline at end of file
+}