Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ 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.7.0] - 2026-08-21
### Changed
- Implemented a caching logic about the status of the interface & update based on events

## [3.6.0] - 2026-08-11
### Fixed
- Fixed the issue with connecting to a SSID that is not present in scan list
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ endif()

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
set(VERSION_MAJOR 3)
set(VERSION_MINOR 6)
set(VERSION_MINOR 7)
set(VERSION_PATCH 0)

add_compile_definitions(NETWORKMANAGER_MAJOR_VERSION=${VERSION_MAJOR})
Expand Down
2 changes: 1 addition & 1 deletion definition/NetworkManager.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "3.6.0"
"version": "3.7.0"
},
"definitions": {
"success": {
Expand Down
4 changes: 2 additions & 2 deletions docs/NetworkManagerPlugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<a name="head.NetworkManager_Plugin"></a>
# NetworkManager Plugin

**Version: 3.6.0**
**Version: 3.7.0**

**Status: :black_circle::black_circle::black_circle:**

Expand All @@ -23,7 +23,7 @@ org.rdk.NetworkManager interface for Thunder framework.
<a name="head.Scope"></a>
## Scope

This document describes purpose and functionality of the org.rdk.NetworkManager interface (version 3.6.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.7.0). It includes detailed specification about its methods provided and notifications sent.

<a name="head.Case_Sensitivity"></a>
## Case Sensitivity
Expand Down
22 changes: 22 additions & 0 deletions plugin/NetworkManagerJsonRpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "INetworkManager.h"
#include "NetworkManagerJsonEnum.h"

#include <chrono>

#define LOG_INPARAM() { string json; parameters.ToString(json); NMLOG_INFO("params=%s", json.c_str() ); }
#define LOG_OUTPARAM() { string json; response.ToString(json); NMLOG_INFO("response=%s", json.c_str() ); }

Expand Down Expand Up @@ -164,11 +166,19 @@ namespace WPEFramework

Exchange::INetworkManager::IInterfaceDetailsIterator* _interfaces{};

const auto tStart = std::chrono::steady_clock::now();

if (_networkManager)
rc = _networkManager->GetAvailableInterfaces(_interfaces);
else
rc = Core::ERROR_UNAVAILABLE;

const auto tAfterComRpc = std::chrono::steady_clock::now();
const long long comRpcUs = static_cast<long long>(std::chrono::duration_cast<std::chrono::microseconds>(tAfterComRpc - tStart).count());
NMLOG_DEBUG("[PERF] GetAvailableInterfaces COM-RPC call took %lld us", comRpcUs);
if (comRpcUs >= 1000000)
NMLOG_WARNING("[PERF] GetAvailableInterfaces COM-RPC call took %lld us (>= 1s)", comRpcUs);

if (Core::ERROR_NONE == rc)
{
if (_interfaces != nullptr)
Expand All @@ -193,6 +203,11 @@ namespace WPEFramework
}
}

const auto tEnd = std::chrono::steady_clock::now();
NMLOG_DEBUG("[PERF] GetAvailableInterfaces iterator drain+serialize took %lld us, total %lld us",
static_cast<long long>(std::chrono::duration_cast<std::chrono::microseconds>(tEnd - tAfterComRpc).count()),
static_cast<long long>(std::chrono::duration_cast<std::chrono::microseconds>(tEnd - tStart).count()));

returnJson(rc);
}

Expand Down Expand Up @@ -241,6 +256,8 @@ namespace WPEFramework
LOG_INPARAM();
uint32_t rc = Core::ERROR_GENERAL;

const auto tStart = std::chrono::steady_clock::now();

if (parameters.HasLabel("interface"))
{
const string interface = parameters["interface"].String();
Expand All @@ -259,6 +276,11 @@ namespace WPEFramework
else
rc = Core::ERROR_BAD_REQUEST;

const long long comRpcUs = static_cast<long long>(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - tStart).count());
NMLOG_DEBUG("[PERF] GetInterfaceState COM-RPC call took %lld us", comRpcUs);
if (comRpcUs >= 1000000)
NMLOG_WARNING("[PERF] GetInterfaceState COM-RPC call took %lld us (>= 1s)", comRpcUs);

returnJson(rc);
}

Expand Down
14 changes: 10 additions & 4 deletions plugin/NetworkManagerLogger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ namespace NetworkManagerLogger {

void logPrint(LogLevel level, const char* file, const char* func, int line, const char* format, ...)
{
// Gate on level before formatting so disabled logs incur no vsnprintf cost.
#ifdef USE_RDK_LOGGER
if (!rdk_logger_is_logLevel_enabled(RDKLOGGER_MODULE_NAME, mapTordkLogLevel(level)))
return;
#else
if (gDefaultLogLevel < level)
return;
#endif

size_t n = 0;
const short kFormatMessageSize = 1024;
char formattedLog[kFormatMessageSize] = {0};
Expand All @@ -102,16 +111,13 @@ namespace NetworkManagerLogger {
}
formattedLog[kFormatMessageSize - 1] = '\0';
#ifdef USE_RDK_LOGGER
RDK_LOG(mapTordkLogLevel(level), RDKLOGGER_MODULE_NAME, "[%s +%d] %s\n", trimPath(file), line, formattedLog);
RDK_LOG(mapTordkLogLevel(level), RDKLOGGER_MODULE_NAME, "[%s +%d] %s : %s\n", trimPath(file), line, func, formattedLog);
#else
const char* levelMap[] = {"Fatal", "Error", "Warn", "Info", "Debug"};
struct timeval tv;
struct tm* lt;
const char* fileName = trimPath(file);

if (gDefaultLogLevel < level)
return;

gettimeofday(&tv, NULL);
lt = localtime(&tv.tv_sec);

Expand Down
67 changes: 63 additions & 4 deletions plugin/gnome/NetworkManagerGnomeEvents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <thread>
#include <string>
#include <map>
#include <mutex>
#include <NetworkManager.h>
#include "Module.h"
#include "NetworkManagerGnomeEvents.h"
Expand All @@ -44,6 +45,49 @@
extern NetworkManagerImplementation* _instance;
static GnomeNetworkManagerEvents *_nmEventInstance = nullptr;

/* Gnome-owned interface-state cache (raw NMDeviceState + MAC per interface).
Written only by the event monitor; read by the proxy's pure readers. */
static std::map<std::string, GnomeNetworkManagerEvents::InterfaceStateInfo> _ifaceStateCache;
static std::mutex _ifaceStateCacheMutex;

bool GnomeNetworkManagerEvents::isInterfaceStateEnabled(NMDeviceState state)
{
return (state >= NM_DEVICE_STATE_UNAVAILABLE);
}

bool GnomeNetworkManagerEvents::isInterfaceStateConnected(NMDeviceState state)
{
return (state > NM_DEVICE_STATE_DISCONNECTED && state < NM_DEVICE_STATE_DEACTIVATING);
}

void GnomeNetworkManagerEvents::updateInterfaceStateCache(const std::string& iface, NMDeviceState state, const char* mac)
{
if (iface.empty())
return;
std::lock_guard<std::mutex> lock(_ifaceStateCacheMutex);
InterfaceStateInfo& info = _ifaceStateCache[iface];
info.state = state;
info.present = true;
if (mac != nullptr && mac[0] != '\0')
info.mac = mac; // keep in sync when NM reports a new/randomized HW address
}

void GnomeNetworkManagerEvents::removeInterfaceStateCache(const std::string& iface)
{
std::lock_guard<std::mutex> lock(_ifaceStateCacheMutex);
_ifaceStateCache.erase(iface);
}

bool GnomeNetworkManagerEvents::getInterfaceStateCache(const std::string& iface, InterfaceStateInfo& out)
{
std::lock_guard<std::mutex> lock(_ifaceStateCacheMutex);
auto it = _ifaceStateCache.find(iface);
if (it == _ifaceStateCache.end() || !it->second.present)
return false;
out = it->second;
return true;
}

static void primaryConnectionCb(NMClient *client, GParamSpec *param, NMEvents *nmEvents)
{
NMActiveConnection *primaryConn;
Expand Down Expand Up @@ -143,7 +187,7 @@
uint32_t prefix = nm_ip_address_get_prefix(addr);
if (isIPv6) {
if (isIPv6LinkLocal(addrString)) {
newCache.linkLocalAddresses.insert(addrString);

Check notice

Code scanning / Coverity

Variable copied when it could be moved Low

Variable copied when it could be moved in refreshIpFamilyCache
} else if (isIPv6ULA(addrString)) {
newCache.uniqueLocalAddresses.insert(addrString);
} else {
Expand Down Expand Up @@ -276,8 +320,11 @@
return;
NMDeviceState deviceState;
deviceState = nm_device_get_state(device);
std::string ifname = nm_device_get_iface(device);
const char* iface = nm_device_get_iface(device);
if (!iface) return;
std::string ifname = iface;
NMDeviceStateReason reason = nm_device_get_state_reason(device);
updateInterfaceStateCache(ifname, deviceState, nm_device_get_hw_address(device));
if(ifname == nmUtils::wlanIface())
{
if(!NM_IS_DEVICE_WIFI(device)) {
Expand Down Expand Up @@ -469,7 +516,9 @@
{
if( ((device != NULL) && NM_IS_DEVICE(device)) )
{
std::string ifname = nm_device_get_iface(device);
const char* iface = nm_device_get_iface(device);
if (!iface) return;
std::string ifname = iface;
if(ifname == nmUtils::wlanIface()) {
GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, nmUtils::wlanIface());
NMLOG_INFO("WIFI device added: %s", ifname.c_str());
Expand All @@ -482,6 +531,7 @@
/* ip events added only for eth0 and wlan0 */
if(ifname == nmUtils::ethIface() || ifname == nmUtils::wlanIface())
{
GnomeNetworkManagerEvents::updateInterfaceStateCache(ifname, nm_device_get_state(device), nm_device_get_hw_address(device));
g_signal_connect(device, "notify::" NM_DEVICE_STATE, G_CALLBACK(GnomeNetworkManagerEvents::deviceStateChangeCb), nmEvents);
g_signal_connect(device, "notify::ip4-config", G_CALLBACK(ip4ConfigChangedCb), nmEvents);
g_signal_connect(device, "notify::ip6-config", G_CALLBACK(ip6ConfigChangedCb), nmEvents);
Expand Down Expand Up @@ -529,7 +579,9 @@
{
if( ((device != NULL) && NM_IS_DEVICE(device)) )
{
std::string ifname = nm_device_get_iface(device);
const char* iface = nm_device_get_iface(device);
if (!iface) return;
std::string ifname = iface;
if(ifname == nmUtils::wlanIface()) {
GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, nmUtils::wlanIface());
NMLOG_INFO("WIFI device removed: %s", ifname.c_str());
Expand All @@ -542,6 +594,10 @@
return; // not a tracked interface
}

/* Device is gone: drop it from the cache so the reads omit it
(matches the original live-device-list behaviour). */
GnomeNetworkManagerEvents::removeInterfaceStateCache(ifname);

/* Disconnect all device-level signals (state, ip4/ip6-config changes). */
g_signal_handlers_disconnect_by_data(device, nmEvents);

Expand All @@ -565,7 +621,7 @@
if (_instance) {
for (const char* family : {"IPv4", "IPv6"}) {
IpFamilyCache empty;
std::set<std::string> oldKeys = _instance->swapIpCache(ifname, family, empty);

Check notice

Code scanning / Coverity

Variable copied when it could be moved Low

Variable copied when it could be moved in deviceRemovedCB
for (const auto& key : oldKeys) {
_instance->ReportIPAddressChange(ifname, family, key, Exchange::INetworkManager::IP_LOST);
}
Expand Down Expand Up @@ -637,10 +693,13 @@
NMDevice *device = NM_DEVICE(g_ptr_array_index(devices, count));
if( ((device != NULL) && NM_IS_DEVICE(device)) )
{
std::string ifname = nm_device_get_iface(device);
const char* iface = nm_device_get_iface(device);
if (!iface) continue;
std::string ifname = iface;
if((ifname == nmUtils::ethIface()) || (ifname == nmUtils::wlanIface()))
{
NMDeviceState devState = nm_device_get_state(device);
GnomeNetworkManagerEvents::updateInterfaceStateCache(ifname, devState, nm_device_get_hw_address(device));

if(devState > NM_DEVICE_STATE_DISCONNECTED && devState <= NM_DEVICE_STATE_ACTIVATED)
{
Expand Down
17 changes: 17 additions & 0 deletions plugin/gnome/NetworkManagerGnomeEvents.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,29 @@ namespace WPEFramework
{

public:
/* Gnome-owned cache of live NM device state, kept out of the
backend-agnostic NetworkManagerImplementation (holds a libnm type). */
struct InterfaceStateInfo {
NMDeviceState state = NM_DEVICE_STATE_UNKNOWN;
std::string mac;
bool present = false;
};

static void onInterfaceStateChangeCb(uint8_t newState, std::string iface); // ReportInterfaceStateChange
static void onActiveInterfaceChangeCb(std::string newInterface); // ReportActiveInterfaceChange
static void onAvailableSSIDsCb(NMDeviceWifi *wifiDevice, GParamSpec *pspec, gpointer userData); // ReportAvailableSSIDs
static void onWIFIStateChanged(uint8_t state); // ReportWiFiStateChange
static void deviceStateChangeCb(NMDevice *device, GParamSpec *pspec, NMEvents *nmEvents);

/* Interface-state cache: sole writer is the event monitor; readers
(GetAvailableInterfaces / GetInterfaceState) are pure. */
static void updateInterfaceStateCache(const std::string& iface, NMDeviceState state, const char* mac);
static void removeInterfaceStateCache(const std::string& iface);
static bool getInterfaceStateCache(const std::string& iface, InterfaceStateInfo& out);
/* Canonical enabled/connected definitions shared by both read APIs. */
static bool isInterfaceStateEnabled(NMDeviceState state);
static bool isInterfaceStateConnected(NMDeviceState state);

public:
static GnomeNetworkManagerEvents* getInstance();
bool startNetworkMangerEventMonitor();
Expand Down
Loading
Loading