diff --git a/CHANGELOG.md b/CHANGELOG.md index 851e39f7..fe546edd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 724fb37e..f02326d7 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 3) -set(VERSION_MINOR 6) +set(VERSION_MINOR 7) set(VERSION_PATCH 0) add_compile_definitions(NETWORKMANAGER_MAJOR_VERSION=${VERSION_MAJOR}) diff --git a/definition/NetworkManager.json b/definition/NetworkManager.json index 2922b983..014a649f 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": "3.6.0" + "version": "3.7.0" }, "definitions": { "success": { diff --git a/docs/NetworkManagerPlugin.md b/docs/NetworkManagerPlugin.md index fd6d97dc..74341a26 100644 --- a/docs/NetworkManagerPlugin.md +++ b/docs/NetworkManagerPlugin.md @@ -2,7 +2,7 @@ # NetworkManager Plugin -**Version: 3.6.0** +**Version: 3.7.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 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. ## Case Sensitivity diff --git a/plugin/NetworkManagerJsonRpc.cpp b/plugin/NetworkManagerJsonRpc.cpp index d49e6c5d..053ba8f7 100644 --- a/plugin/NetworkManagerJsonRpc.cpp +++ b/plugin/NetworkManagerJsonRpc.cpp @@ -21,6 +21,8 @@ #include "INetworkManager.h" #include "NetworkManagerJsonEnum.h" +#include + #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() ); } @@ -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(std::chrono::duration_cast(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) @@ -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(std::chrono::duration_cast(tEnd - tAfterComRpc).count()), + static_cast(std::chrono::duration_cast(tEnd - tStart).count())); + returnJson(rc); } @@ -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(); @@ -259,6 +276,11 @@ namespace WPEFramework else rc = Core::ERROR_BAD_REQUEST; + const long long comRpcUs = static_cast(std::chrono::duration_cast(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); } diff --git a/plugin/NetworkManagerLogger.cpp b/plugin/NetworkManagerLogger.cpp index 6a58a1fe..99aa51bd 100644 --- a/plugin/NetworkManagerLogger.cpp +++ b/plugin/NetworkManagerLogger.cpp @@ -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}; @@ -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); diff --git a/plugin/gnome/NetworkManagerGnomeEvents.cpp b/plugin/gnome/NetworkManagerGnomeEvents.cpp index 7a87cc20..35793d79 100644 --- a/plugin/gnome/NetworkManagerGnomeEvents.cpp +++ b/plugin/gnome/NetworkManagerGnomeEvents.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "Module.h" #include "NetworkManagerGnomeEvents.h" @@ -44,6 +45,49 @@ namespace WPEFramework 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 _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 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 lock(_ifaceStateCacheMutex); + _ifaceStateCache.erase(iface); + } + + bool GnomeNetworkManagerEvents::getInterfaceStateCache(const std::string& iface, InterfaceStateInfo& out) + { + std::lock_guard 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; @@ -276,8 +320,11 @@ namespace WPEFramework 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)) { @@ -469,7 +516,9 @@ namespace WPEFramework { 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()); @@ -482,6 +531,7 @@ namespace WPEFramework /* 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); @@ -529,7 +579,9 @@ namespace WPEFramework { 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()); @@ -542,6 +594,10 @@ namespace WPEFramework 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); @@ -637,10 +693,13 @@ namespace WPEFramework 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) { diff --git a/plugin/gnome/NetworkManagerGnomeEvents.h b/plugin/gnome/NetworkManagerGnomeEvents.h index bf282de7..2bc31f9b 100644 --- a/plugin/gnome/NetworkManagerGnomeEvents.h +++ b/plugin/gnome/NetworkManagerGnomeEvents.h @@ -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(); diff --git a/plugin/gnome/NetworkManagerGnomeProxy.cpp b/plugin/gnome/NetworkManagerGnomeProxy.cpp index 30d065e8..65b2fc4b 100644 --- a/plugin/gnome/NetworkManagerGnomeProxy.cpp +++ b/plugin/gnome/NetworkManagerGnomeProxy.cpp @@ -22,6 +22,7 @@ #include "NetworkManagerGnomeUtils.h" #include #include +#include using namespace WPEFramework; using namespace WPEFramework::Plugin; using namespace std; @@ -347,77 +348,37 @@ namespace WPEFramework std::vector interfaceList; std::string wifiname = nmUtils::wlanIface(), ethname = nmUtils::ethIface(); - if(m_nmContext == nullptr) { - NMLOG_FATAL("NMContext is null"); - return Core::ERROR_GENERAL; - } - - NMClient *client = createProxyClient(m_nmContext); - if (client == nullptr) { - NMLOG_FATAL("Failed to create NMClient for GetAvailableInterfaces"); - return Core::ERROR_GENERAL; - } + const auto tEntry = std::chrono::steady_clock::now(); - GPtrArray *devices = const_cast(nm_client_get_devices(client)); - if (devices == NULL) { - NMLOG_ERROR("Failed to get device list."); - deleteProxyClient(client); - return rc; - } - - for (guint j = 0; j < devices->len; j++) + /* Serve from the event-maintained cache; no per-call NMClient. An interface + * absent from the cache (not yet reported by NM) is simply omitted; an empty + * list is a valid result (ground reality), not an error. */ + for (int i = 0; i < 2; ++i) { - NMDevice *device = NM_DEVICE(devices->pdata[j]); - if(device != NULL) - { - const char* ifacePtr = nm_device_get_iface(device); - if(ifacePtr == nullptr) - continue; - std::string ifaceStr = ifacePtr; - if(ifaceStr == wifiname || ifaceStr == ethname) // only wifi and ethenet taking - { - NMDeviceState deviceState = NM_DEVICE_STATE_UNKNOWN; - Exchange::INetworkManager::InterfaceDetails interface{}; - const char* macAddr = nm_device_get_hw_address(device); - if(macAddr != nullptr) { - interface.mac = macAddr; - } - deviceState = nm_device_get_state(device); - interface.enabled = (deviceState >= NM_DEVICE_STATE_UNAVAILABLE)? true : false; - if(deviceState > NM_DEVICE_STATE_DISCONNECTED && deviceState < NM_DEVICE_STATE_DEACTIVATING) - interface.connected = true; - else - interface.connected = false; + const std::string& ifname = (i == 0) ? ethname : wifiname; + GnomeNetworkManagerEvents::InterfaceStateInfo info; + if (!GnomeNetworkManagerEvents::getInterfaceStateCache(ifname, info)) + continue; - if(ifaceStr == wifiname) { - interface.type = INTERFACE_TYPE_WIFI; - interface.name = wifiname; - m_wlanConnected.store(interface.connected); - m_wlanEnabled.store(interface.enabled); - } - else if(ifaceStr == ethname) { - interface.type = INTERFACE_TYPE_ETHERNET; - interface.name = ethname; - m_ethConnected.store(interface.connected); - m_ethEnabled.store(interface.enabled); - } + Exchange::INetworkManager::InterfaceDetails interface{}; + interface.type = (i == 0) ? INTERFACE_TYPE_ETHERNET : INTERFACE_TYPE_WIFI; + interface.name = ifname; + interface.mac = info.mac; + interface.enabled = GnomeNetworkManagerEvents::isInterfaceStateEnabled(info.state); + interface.connected = GnomeNetworkManagerEvents::isInterfaceStateConnected(info.state); - interfaceList.push_back(interface); - rc = Core::ERROR_NONE; - } - } + interfaceList.push_back(interface); } - deleteProxyClient(client); - - if (rc != Core::ERROR_NONE) - return rc; - using Implementation = RPC::IteratorType; interfacesItr = Core::Service::Create(interfaceList); if(interfacesItr == nullptr) { return Core::ERROR_GENERAL; } + rc = Core::ERROR_NONE; + + NMLOG_DEBUG("[PERF] GetAvailableInterfaces (impl) total %lld us", + static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - tEntry).count())); return rc; } #if 0 @@ -640,65 +601,29 @@ namespace WPEFramework uint32_t NetworkManagerImplementation::GetInterfaceState(const string& interface/* @in */, bool& isEnabled /* @out */) { isEnabled = false; - bool isIfaceFound = false; std::string wifiname = nmUtils::wlanIface(), ethname = nmUtils::ethIface(); + const auto tEntry = std::chrono::steady_clock::now(); + 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; } - if(m_nmContext == nullptr) + /* Serve from the event-maintained cache; no per-call NMClient. */ + GnomeNetworkManagerEvents::InterfaceStateInfo info; + if (!GnomeNetworkManagerEvents::getInterfaceStateCache(interface, info)) { - NMLOG_WARNING("NMContext is null"); - return Core::ERROR_RPC_CALL_FAILED; - } - - NMClient *client = createProxyClient(m_nmContext); - if (client == nullptr) { - NMLOG_ERROR("Failed to create NMClient for GetInterfaceState"); - return Core::ERROR_RPC_CALL_FAILED; - } - - GPtrArray *devices = const_cast(nm_client_get_devices(client)); - - if (devices == NULL) { - NMLOG_ERROR("Failed to get device list."); - deleteProxyClient(client); + NMLOG_WARNING("%s : state not known yet", interface.c_str()); return Core::ERROR_GENERAL; } - for (guint j = 0; j < devices->len; j++) - { - NMDevice *device = NM_DEVICE(devices->pdata[j]); - if(device != NULL) - { - const char* iface = nm_device_get_iface(device); - if(iface != NULL) - { - std::string ifaceStr; - ifaceStr.assign(iface); - NMDeviceState deviceState = NM_DEVICE_STATE_UNKNOWN; - if(ifaceStr == interface) - { - isIfaceFound = true; - deviceState = nm_device_get_state(device); - isEnabled = (deviceState > NM_DEVICE_STATE_UNAVAILABLE) ? true : false; - NMLOG_INFO("%s : %s", ifaceStr.c_str(), isEnabled?"enabled":"disabled"); - break; - } - } - } - } - - deleteProxyClient(client); - - if(isIfaceFound) - return Core::ERROR_NONE; - else - NMLOG_ERROR("%s : not found", interface.c_str()); - return Core::ERROR_GENERAL; + isEnabled = GnomeNetworkManagerEvents::isInterfaceStateEnabled(info.state); + NMLOG_INFO("%s : %s", interface.c_str(), isEnabled?"enabled":"disabled"); + NMLOG_DEBUG("[PERF] GetInterfaceState (impl) total %lld us", + static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - tEntry).count())); + return Core::ERROR_NONE; } /* @brief Get IP Address Of the Interface */ diff --git a/tests/l2Test/libnm/l2_test_libnmproxy.cpp b/tests/l2Test/libnm/l2_test_libnmproxy.cpp index 0a3dad69..a102c7ea 100644 --- a/tests/l2Test/libnm/l2_test_libnmproxy.cpp +++ b/tests/l2Test/libnm/l2_test_libnmproxy.cpp @@ -35,6 +35,7 @@ #include "NetworkManagerImplementation.h" #include "NetworkManagerLogger.h" #include "NetworkManager.h" +#include "NetworkManagerGnomeEvents.h" #include using namespace WPEFramework; @@ -150,6 +151,11 @@ class NetworkManagerTest : public ::testing::Test { std::cerr << "Failed to create /etc/device.properties file." << std::endl; } } + + /* Interface-state cache is a process-global static; reset the known + interfaces so each test starts from a deterministic empty state. */ + Plugin::GnomeNetworkManagerEvents::removeInterfaceStateCache("eth0"); + Plugin::GnomeNetworkManagerEvents::removeInterfaceStateCache("wlan0"); } virtual ~NetworkManagerTest() override @@ -353,59 +359,37 @@ TEST_F(NetworkManagerTest, GetPrimaryInterface_empty) TEST_F(NetworkManagerTest, GetInterfaceState_Failed) { - EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) - .WillRepeatedly(::testing::Return(nullptr)); - + /* wlan0 not present in the event cache -> state unknown -> failure */ EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), _T("{\"interface\":\"wlan0\"}"), response)); EXPECT_EQ(response, _T("{\"success\":false}")); } TEST_F(NetworkManagerTest, GetInterfaceState_WifiEth) { - GPtrArray* fakeDevices = g_ptr_array_new(); - - NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); - g_ptr_array_add(fakeDevices, deviceDummy); - - EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) - .WillRepeatedly(::testing::Return(fakeDevices)); - - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) - .WillOnce(::testing::Return("wlan0")) - .WillOnce(::testing::Return("eth0")); - - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) - .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) - .WillOnce(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); // disabled + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("wlan0", NM_DEVICE_STATE_ACTIVATED, "66:77:88:99:AA:BB"); + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("eth0", NM_DEVICE_STATE_UNMANAGED, "00:11:22:33:44:55"); // disabled EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), _T("{\"interface\":\"wlan0\"}"), response)); EXPECT_EQ(response, _T("{\"enabled\":true,\"success\":true}")); EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), _T("{\"interface\":\"eth0\"}"), response)); EXPECT_EQ(response, _T("{\"enabled\":false,\"success\":true}")); - - g_object_unref(deviceDummy); - g_ptr_array_free(fakeDevices, TRUE); } TEST_F(NetworkManagerTest, GetInterfaceState_WifiEthNotFound) { - GPtrArray* fakeDevices = g_ptr_array_new(); - - NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); - g_ptr_array_add(fakeDevices, deviceDummy); - - EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) - .WillRepeatedly(::testing::Return(fakeDevices)); - - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) - .WillOnce(::testing::Return("wlan1")); + /* Only eth0 known to the cache; querying wlan0 -> not found -> failure */ + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("eth0", NM_DEVICE_STATE_ACTIVATED, "00:11:22:33:44:55"); EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), _T("{\"interface\":\"wlan0\"}"), response)); EXPECT_EQ(response, _T("{\"success\":false}")); +} - g_object_unref(deviceDummy); - g_ptr_array_free(fakeDevices, TRUE); +TEST_F(NetworkManagerTest, GetInterfaceState_emptyInterface) +{ + /* Empty interface name is rejected before any cache lookup */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), _T("{\"interface\":\"\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); } TEST_F(NetworkManagerTest, GetInterfaceState_unknown) @@ -421,38 +405,19 @@ TEST_F(NetworkManagerTest, GetInterfaceState_unknown) EXPECT_EQ(isEnabled, false); } -TEST_F(NetworkManagerTest, GetAvailableInterfaces_DevicesNull) +TEST_F(NetworkManagerTest, GetAvailableInterfaces_emptyCache) { - EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) - .WillOnce(::testing::Return(nullptr)); + /* No interfaces reported by NM yet -> empty list is a valid (successful) result */ EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T(""), response)); - EXPECT_TRUE(response.find("\"success\":false") != std::string::npos); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("eth0") == std::string::npos); + EXPECT_TRUE(response.find("wlan0") == std::string::npos); } TEST_F(NetworkManagerTest, GetAvailableInterfaces_Enabled) { - GPtrArray* fakeDevices = g_ptr_array_new(); - NMDevice *ethDevice = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); - NMDevice* wifiDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); - g_ptr_array_add(fakeDevices, wifiDevice); - g_ptr_array_add(fakeDevices, ethDevice); - - EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) - .WillOnce(::testing::Return(fakeDevices)); - - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(ethDevice)) - .WillOnce(::testing::Return("eth0")); - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(wifiDevice)) - .WillOnce(::testing::Return("wlan0")); - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_hw_address(ethDevice)) - .WillOnce(::testing::Return("00:11:22:33:44:55")); - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_hw_address(wifiDevice)) - .WillOnce(::testing::Return("66:77:88:99:AA:BB")); - - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(ethDevice)) - .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(wifiDevice)) - .WillOnce(::testing::Return(NM_DEVICE_STATE_UNAVAILABLE)); + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("eth0", NM_DEVICE_STATE_ACTIVATED, "00:11:22:33:44:55"); + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("wlan0", NM_DEVICE_STATE_UNAVAILABLE, "66:77:88:99:AA:BB"); EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T(""), response)); EXPECT_TRUE(response.find("wlan0") != std::string::npos); @@ -460,40 +425,16 @@ TEST_F(NetworkManagerTest, GetAvailableInterfaces_Enabled) std::string expectedResponse = _T("{\"interfaces\":[") - _T("{\"type\":\"WIFI\",\"name\":\"wlan0\",\"mac\":\"66:77:88:99:AA:BB\",\"enabled\":true,\"connected\":false},") - _T("{\"type\":\"ETHERNET\",\"name\":\"eth0\",\"mac\":\"00:11:22:33:44:55\",\"enabled\":true,\"connected\":true}") + _T("{\"type\":\"ETHERNET\",\"name\":\"eth0\",\"mac\":\"00:11:22:33:44:55\",\"enabled\":true,\"connected\":true},") + _T("{\"type\":\"WIFI\",\"name\":\"wlan0\",\"mac\":\"66:77:88:99:AA:BB\",\"enabled\":true,\"connected\":false}") _T("],\"success\":true}"); EXPECT_EQ(response, expectedResponse); - - g_object_unref(ethDevice); - g_object_unref(wifiDevice); - g_ptr_array_free(fakeDevices, TRUE); } TEST_F(NetworkManagerTest, GetAvailableInterfaces_disabled) { - GPtrArray* fakeDevices = g_ptr_array_new(); - NMDevice *ethDevice = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); - NMDevice* wifiDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); - g_ptr_array_add(fakeDevices, wifiDevice); - g_ptr_array_add(fakeDevices, ethDevice); - - EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) - .WillOnce(::testing::Return(fakeDevices)); - - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(ethDevice)) - .WillOnce(::testing::Return("eth0")); - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(wifiDevice)) - .WillOnce(::testing::Return("wlan0")); - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_hw_address(ethDevice)) - .WillOnce(::testing::Return("00:11:22:33:44:55")); - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_hw_address(wifiDevice)) - .WillOnce(::testing::Return("66:77:88:99:AA:BB")); - - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(ethDevice)) - .WillOnce(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); - EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(wifiDevice)) - .WillOnce(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("eth0", NM_DEVICE_STATE_UNMANAGED, "00:11:22:33:44:55"); + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("wlan0", NM_DEVICE_STATE_UNMANAGED, "66:77:88:99:AA:BB"); EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T(""), response)); EXPECT_TRUE(response.find("wlan0") != std::string::npos); @@ -501,14 +442,25 @@ TEST_F(NetworkManagerTest, GetAvailableInterfaces_disabled) std::string expectedResponse = _T("{\"interfaces\":[") - _T("{\"type\":\"WIFI\",\"name\":\"wlan0\",\"mac\":\"66:77:88:99:AA:BB\",\"enabled\":false,\"connected\":false},") - _T("{\"type\":\"ETHERNET\",\"name\":\"eth0\",\"mac\":\"00:11:22:33:44:55\",\"enabled\":false,\"connected\":false}") + _T("{\"type\":\"ETHERNET\",\"name\":\"eth0\",\"mac\":\"00:11:22:33:44:55\",\"enabled\":false,\"connected\":false},") + _T("{\"type\":\"WIFI\",\"name\":\"wlan0\",\"mac\":\"66:77:88:99:AA:BB\",\"enabled\":false,\"connected\":false}") _T("],\"success\":true}"); EXPECT_EQ(response, expectedResponse); +} - g_object_unref(ethDevice); - g_object_unref(wifiDevice); - g_ptr_array_free(fakeDevices, TRUE); +TEST_F(NetworkManagerTest, GetAvailableInterfaces_onlyEthernet) +{ + /* Only eth0 reported by NM; wlan0 (absent from cache) is omitted from the list */ + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("eth0", NM_DEVICE_STATE_ACTIVATED, "00:11:22:33:44:55"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T(""), response)); + EXPECT_TRUE(response.find("wlan0") == std::string::npos); + + std::string expectedResponse = + _T("{\"interfaces\":[") + _T("{\"type\":\"ETHERNET\",\"name\":\"eth0\",\"mac\":\"00:11:22:33:44:55\",\"enabled\":true,\"connected\":true}") + _T("],\"success\":true}"); + EXPECT_EQ(response, expectedResponse); } TEST_F(NetworkManagerTest, GetIPSettings_unknown_iface) diff --git a/tests/l2Test/libnm/l2_test_libnmproxyInit.cpp b/tests/l2Test/libnm/l2_test_libnmproxyInit.cpp index b550b3d6..52d2a139 100644 --- a/tests/l2Test/libnm/l2_test_libnmproxyInit.cpp +++ b/tests/l2Test/libnm/l2_test_libnmproxyInit.cpp @@ -33,6 +33,7 @@ #include "NetworkManagerImplementation.h" #include "NetworkManagerLogger.h" #include "NetworkManager.h" +#include "NetworkManagerGnomeEvents.h" #include using namespace WPEFramework; @@ -123,6 +124,10 @@ class NetworkManagerInitTest : public ::testing::Test { virtual void SetUp() override { + /* Interface-state cache is a process-global static shared across suites; + clear the known interfaces so platform_init failure yields an empty cache. */ + Plugin::GnomeNetworkManagerEvents::removeInterfaceStateCache("eth0"); + Plugin::GnomeNetworkManagerEvents::removeInterfaceStateCache("wlan0"); } virtual ~NetworkManagerInitTest() override @@ -149,7 +154,10 @@ TEST_F(NetworkManagerInitTest, platformInit) NetworkManagerLogger::SetLevel(static_cast(NetworkManagerLogger::DEBUG_LEVEL)); EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T(""), response)); - EXPECT_TRUE(response.find("\"success\":false") != std::string::npos); + /* platform_init failed (no NMClient) -> event cache never populated -> empty list, still success */ + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("eth0") == std::string::npos); + EXPECT_TRUE(response.find("wlan0") == std::string::npos); EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetPrimaryInterface"), _T(""), response)); EXPECT_EQ(response, _T("{\"interface\":\"\",\"success\":true}"));