diff --git a/frontend/device/android/device.lua b/frontend/device/android/device.lua index e2e69bf91..61a26e5e2 100644 --- a/frontend/device/android/device.lua +++ b/frontend/device/android/device.lua @@ -292,12 +292,13 @@ function Device:initNetworkManager(NetworkMgr) android.openWifiSettings() end - function NetworkMgr:isWifiOn() + function NetworkMgr:isConnected() local ok = android.getNetworkInfo() ok = tonumber(ok) if not ok then return false end return ok == 1 end + NetworkMgr.isWifiOn = NetworkMgr.isConnected end function Device:performHapticFeedback(type) diff --git a/frontend/device/cervantes/device.lua b/frontend/device/cervantes/device.lua index 860cacce3..8e3a08951 100644 --- a/frontend/device/cervantes/device.lua +++ b/frontend/device/cervantes/device.lua @@ -12,20 +12,6 @@ local function getProductId() return product_id end -local function isConnected() - -- read carrier state from sysfs (for eth0) - local file = io.open("/sys/class/net/eth0/carrier", "rb") - - -- file exists while Wi-Fi module is loaded. - if not file then return 0 end - - -- 0 means not connected, 1 connected - local out = file:read("*number") - file:close() - - return out or 0 -end - local function isMassStorageSupported() -- we rely on 3rd party package for that. It should be installed as part of KOReader prerequisites, local safemode_version = io.open("/usr/share/safemode/version", "rb") @@ -190,9 +176,8 @@ function Cervantes:initNetworkManager(NetworkMgr) function NetworkMgr:restoreWifiAsync() os.execute("./restore-wifi-async.sh") end - function NetworkMgr:isWifiOn() - return 1 == isConnected() - end + NetworkMgr.isWifiOn = NetworkMgr.sysfsWifiOn + NetworkMgr.isConnected = NetworkMgr.sysfsCarrierConnected end -- power functions: suspend, resume, reboot, poweroff diff --git a/frontend/device/generic/device.lua b/frontend/device/generic/device.lua index 445f1e0cb..ee5b2953f 100644 --- a/frontend/device/generic/device.lua +++ b/frontend/device/generic/device.lua @@ -268,7 +268,7 @@ function Device:onPowerEvent(ev) logger.dbg("Resuming...") local UIManager = require("ui/uimanager") UIManager:unschedule(self.suspend) - if self:hasWifiManager() and not self:isEmulator() then + if self:hasWifiManager() then local network_manager = require("ui/network/manager") if network_manager.wifi_was_on and G_reader_settings:isTrue("auto_restore_wifi") then network_manager:restoreWifiAsync() diff --git a/frontend/device/kindle/device.lua b/frontend/device/kindle/device.lua index f54f94a2a..78b6e0945 100644 --- a/frontend/device/kindle/device.lua +++ b/frontend/device/kindle/device.lua @@ -69,24 +69,6 @@ local function isWifiUp() end --]] --- Faster lipc-less variant ;). -local function isWifiUp() - -- Read carrier state from sysfs (so far, all Kindles appear to use wlan0) - -- NOTE: We can afford to use CLOEXEC, as devices too old for it don't support Wi-Fi anyway ;). - local file = io.open("/sys/class/net/wlan0/carrier", "re") - - -- File only exists while Wi-Fi module is loaded. - if not file then - return false - end - - -- 0 means not connected, 1 connected - local out = file:read("*number") - file:close() - - return true, out == 1 -end - --[[ Test if a kindle device is flagged as a Special Offers device (i.e., ad supported) (FW >= 5.x) --]] @@ -200,7 +182,12 @@ function Kindle:initNetworkManager(NetworkMgr) end end - NetworkMgr.isWifiOn = isWifiUp + function NetworkMgr:getNetworkInterfaceName() + return "wlan0" -- so far, all Kindles appear to use wlan0 + end + + NetworkMgr.isWifiOn = NetworkMgr.sysfsWifiOn + NetworkMgr.isConnected = NetworkMgr.sysfsCarrierConnected end function Kindle:supportsScreensaver() diff --git a/frontend/device/kobo/device.lua b/frontend/device/kobo/device.lua index 881522cda..3dd74eb12 100644 --- a/frontend/device/kobo/device.lua +++ b/frontend/device/kobo/device.lua @@ -30,31 +30,6 @@ local function koboEnableWifi(toggle) end end --- NOTE: Cheap-ass way of checking if Wi-Fi seems to be enabled... --- Since the crux of the issues lies in race-y module unloading, this is perfectly fine for our usage. -local function koboIsWifiOn() - local needle = os.getenv("WIFI_MODULE") or "sdio_wifi_pwr" - local nlen = #needle - -- /proc/modules is usually empty, unless Wi-Fi or USB is enabled - -- We could alternatively check if lfs.attributes("/proc/sys/net/ipv4/conf/" .. os.getenv("INTERFACE"), "mode") == "directory" - -- c.f., also what Cervantes does via /sys/class/net/eth0/carrier to check if the interface is up. - -- That said, since we only care about whether *modules* are loaded, this does the job nicely. - local f = io.open("/proc/modules", "re") - if not f then - return false - end - - local found = false - for haystack in f:lines() do - if haystack:sub(1, nlen) == needle then - found = true - break - end - end - f:close() - return found -end - -- checks if standby is available on the device local function checkStandby() logger.dbg("Kobo: checking if standby is possible ...") @@ -876,7 +851,8 @@ function Kobo:initNetworkManager(NetworkMgr) os.execute("./restore-wifi-async.sh") end - NetworkMgr.isWifiOn = koboIsWifiOn + NetworkMgr.isWifiOn = NetworkMgr.sysfsWifiOn + NetworkMgr.isConnected = NetworkMgr.sysfsCarrierConnected end function Kobo:setTouchEventHandler() @@ -1059,7 +1035,7 @@ function Kobo:standby(max_duration) --[[ -- On most devices, attempting to PM with a Wi-Fi module loaded will horribly crash the kernel, so, don't? -- NOTE: Much like suspend, our caller should ensure this never happens, hence this being commented out ;). - if koboIsWifiOn() then + if NetworkMgr:isWifiOn() then -- AutoSuspend relies on NetworkMgr:getWifiState to prevent this, so, if we ever trip this, it's a bug ;). logger.err("Kobo standby: cannot standby with Wi-Fi modules loaded! (NetworkMgr is confused: this is a bug)") return diff --git a/frontend/device/pocketbook/device.lua b/frontend/device/pocketbook/device.lua index 49d13421a..0c8211b16 100644 --- a/frontend/device/pocketbook/device.lua +++ b/frontend/device/pocketbook/device.lua @@ -368,9 +368,10 @@ function PocketBook:initNetworkManager(NetworkMgr) end end - function NetworkMgr:isWifiOn() + function NetworkMgr:isConnected() return band(inkview.QueryNetwork(), C.NET_CONNECTED) ~= 0 end + NetworkMgr.isWifiOn = NetworkMgr.isConnected end function PocketBook:getSoftwareVersion() diff --git a/frontend/device/remarkable/device.lua b/frontend/device/remarkable/device.lua index 2bb149642..43d0b3178 100644 --- a/frontend/device/remarkable/device.lua +++ b/frontend/device/remarkable/device.lua @@ -194,9 +194,8 @@ function Remarkable:initNetworkManager(NetworkMgr) NetworkMgr:setWirelessBackend("wpa_supplicant", {ctrl_interface = "/var/run/wpa_supplicant/wlan0"}) - NetworkMgr.isWifiOn = function() - return NetworkMgr:isConnected() - end + NetworkMgr.isWifiOn = NetworkMgr.sysfsWifiOn + NetworkMgr.isConnected = NetworkMgr.sysfsCarrierConnected end function Remarkable:setDateTime(year, month, day, hour, min, sec) diff --git a/frontend/device/sdl/device.lua b/frontend/device/sdl/device.lua index e35b5eeec..dfeaf46c5 100644 --- a/frontend/device/sdl/device.lua +++ b/frontend/device/sdl/device.lua @@ -128,7 +128,6 @@ local Emulator = Device:extend{ hasNaturalLight = yes, hasNaturalLightApi = yes, hasWifiToggle = yes, - hasWifiManager = yes, -- Not really, Device:reboot & Device:powerOff are not implemented, so we just exit ;). canPowerOff = yes, canReboot = yes, @@ -367,6 +366,28 @@ function Device:setEventHandlers(UIManager) end end +function Device:initNetworkManager(NetworkMgr) + function NetworkMgr:isWifiOn() return true end + function NetworkMgr:isConnected() + -- Pull the default gateway first, so we don't even try to ping anything if there isn't one... + local default_gw, std_out + if isCommand("ip") then + std_out = io.popen([[ip r | grep default | tail -n 1 | cut -d ' ' -f 3]], "r") + else + std_out = io.popen([[route -n | awk '$4 == "UG" {print $2}' | tail -n 1]], "r") + end + + if std_out then + default_gw = std_out:read("*all") + std_out:close() + if not default_gw or default_gw == "" then + return false + end + end + return 0 == os.execute("ping -c1 -w2 " .. default_gw) + end +end + function Emulator:supportsScreensaver() return true end function Emulator:simulateSuspend() @@ -401,6 +422,7 @@ function Emulator:initNetworkManager(NetworkMgr) function NetworkMgr:isWifiOn() return G_reader_settings:nilOrTrue("emulator_fake_wifi_connected") end + NetworkMgr.isConnected = NetworkMgr.isWifiOn end io.write("Starting SDL in " .. SDL.getBasePath() .. "\n") diff --git a/frontend/device/sony-prstux/device.lua b/frontend/device/sony-prstux/device.lua index b25810ce4..6f55e7fa3 100644 --- a/frontend/device/sony-prstux/device.lua +++ b/frontend/device/sony-prstux/device.lua @@ -172,9 +172,13 @@ function SonyPRSTUX:initNetworkManager(NetworkMgr) -- os.execute("./restore-wifi-async.sh") end + --[[ function NetworkMgr:isWifiOn() return 0 == os.execute("wmiconfig -i wlan0 --wlan query | grep -q enabled") end + --]] + NetworkMgr.isWifiOn = NetworkMgr.sysfsWifiOn + NetworkMgr.isConnected = NetworkMgr.sysfsCarrierConnected end diff --git a/frontend/ui/network/manager.lua b/frontend/ui/network/manager.lua index 786ebd824..641e5f239 100644 --- a/frontend/ui/network/manager.lua +++ b/frontend/ui/network/manager.lua @@ -9,6 +9,7 @@ local MultiConfirmBox = require("ui/widget/multiconfirmbox") local UIManager = require("ui/uimanager") local ffiutil = require("ffi/util") local logger = require("logger") +local util = require("util") local _ = require("gettext") local T = ffiutil.template @@ -30,7 +31,7 @@ function NetworkMgr:connectivityCheck(iter, callback, widget) self.wifi_was_on = false G_reader_settings:makeFalse("wifi_was_on") -- If we abort, murder Wi-Fi and the async script first... - if Device:hasWifiManager() and not Device:isEmulator() then + if Device:hasWifiManager() then os.execute("pkill -TERM restore-wifi-async.sh 2>/dev/null") end self:turnOffWifi() @@ -105,7 +106,9 @@ end -- NetworkMgr:setWirelessBackend function NetworkMgr:turnOnWifi() end function NetworkMgr:turnOffWifi() end +-- This function returns status of the WiFi radio function NetworkMgr:isWifiOn() end +function NetworkMgr:isConnected() end function NetworkMgr:getNetworkInterfaceName() end function NetworkMgr:getNetworkList() end function NetworkMgr:getCurrentNetwork() end @@ -117,6 +120,30 @@ function NetworkMgr:releaseIP() end function NetworkMgr:restoreWifiAsync() end -- End of device specific methods +--Helper fuctions for devices that use the sysfs entry to check connectivity. +function NetworkMgr:sysfsWifiOn() + -- Network interface directory exists while the Wi-Fi module is loaded. + local net_if = self:getNetworkInterfaceName() + return util.pathExists("/sys/class/net/".. net_if) +end + +function NetworkMgr:sysfsCarrierConnected() + -- Read carrier state from sysfs. + -- NOTE: We can afford to use CLOEXEC, as devices too old for it don't support Wi-Fi anyway ;) + local out + local net_if = self:getNetworkInterfaceName() + local file = io.open("/sys/class/net/" .. net_if .. "/carrier", "re") + + -- File only exists while Wi-Fi module is loaded. + if file then + -- 0 means not connected, 1 connected + out = file:read("*number") + file:close() + end + + return out == 1 +end + function NetworkMgr:toggleWifiOn(complete_callback, long_press) local toggle_im = InfoMessage:new{ text = _("Turning on Wi-Fi…"), @@ -245,34 +272,6 @@ function NetworkMgr:afterWifiAction(callback) end end -function NetworkMgr:isConnected() - if Device:isAndroid() or Device:isCervantes() or Device:isPocketBook() or Device:isEmulator() then - return self:isWifiOn() - elseif Device:isKindle() then - local on, connected = self:isWifiOn() - return on and connected - else - -- Pull the default gateway first, so we don't even try to ping anything if there isn't one... - local default_gw - local std_out = io.popen([[/sbin/route -n | awk '$4 == "UG" {print $2}' | tail -n 1]], "r") - if std_out then - default_gw = std_out:read("*all") - std_out:close() - if not default_gw or default_gw == "" then - return false - end - end - - -- `-c1` try only once; `-w2` wait 2 seconds - -- NOTE: No -w flag available in the old busybox build used on Legacy Kindles... - if Device:isKindle() and Device:hasKeyboard() then - return 0 == os.execute("ping -c1 " .. default_gw) - else - return 0 == os.execute("ping -c1 -w2 " .. default_gw) - end - end -end - function NetworkMgr:isOnline() local socket = require("socket") -- Microsoft uses `dns.msftncsi.com` for Windows, see @@ -308,8 +307,7 @@ function NetworkMgr:isNetworkInfoAvailable() -- always available return true else - --- @todo also show network info when device is authenticated to router but offline - return self:isWifiOn() + return self:isConnected() end end @@ -499,7 +497,7 @@ function NetworkMgr:getPowersaveMenuTable() text = _("Disable Wi-Fi connection when inactive"), help_text = _([[This will automatically turn Wi-Fi off after a generous period of network inactivity, without disrupting workflows that require a network connection, so you can just keep reading without worrying about battery drain.]]), checked_func = function() return G_reader_settings:isTrue("auto_disable_wifi") end, - enabled_func = function() return Device:hasWifiManager() and not Device:isEmulator() end, + enabled_func = function() return Device:hasWifiManager() end, callback = function() G_reader_settings:flipNilOrFalse("auto_disable_wifi") -- NOTE: Well, not exactly, but the activity check wouldn't be (un)scheduled until the next Network(Dis)Connected event... @@ -513,7 +511,7 @@ function NetworkMgr:getRestoreMenuTable() text = _("Restore Wi-Fi connection on resume"), help_text = _([[This will attempt to automatically and silently re-connect to Wi-Fi on startup or on resume if Wi-Fi used to be enabled the last time you used KOReader.]]), checked_func = function() return G_reader_settings:isTrue("auto_restore_wifi") end, - enabled_func = function() return Device:hasWifiManager() and not Device:isEmulator() end, + enabled_func = function() return Device:hasWifiManager() end, callback = function() G_reader_settings:flipNilOrFalse("auto_restore_wifi") end, } end @@ -610,7 +608,7 @@ function NetworkMgr:getMenuTable(common_settings) common_settings.network_proxy = self:getProxyMenuTable() common_settings.network_info = self:getInfoMenuTable() - if Device:hasWifiManager() then + if Device:hasWifiManager() or Device:isEmulator() then common_settings.network_powersave = self:getPowersaveMenuTable() common_settings.network_restore = self:getRestoreMenuTable() common_settings.network_dismiss_scan = self:getDismissScanMenuTable() diff --git a/frontend/ui/network/networklistener.lua b/frontend/ui/network/networklistener.lua index 3cf99623e..73a9bb432 100644 --- a/frontend/ui/network/networklistener.lua +++ b/frontend/ui/network/networklistener.lua @@ -115,7 +115,7 @@ local network_activity_noise_margin = 12 -- unscaled_size_check: ignore -- Read the statistics/tx_packets sysfs entry for the current network interface. -- It *should* be the least noisy entry on an idle network... --- The fact that auto_disable_wifi is only available on (Device:hasWifiManager() and not Device:isEmulator()) +-- The fact that auto_disable_wifi is only available on Device:hasWifiManager() -- allows us to get away with a Linux-only solution. function NetworkListener:_getTxPackets() -- read tx_packets stats from sysfs (for the right network if) @@ -198,7 +198,7 @@ end function NetworkListener:onNetworkConnected() logger.dbg("NetworkListener: onNetworkConnected") - if not (Device:hasWifiManager() and not Device:isEmulator()) then + if not Device:hasWifiManager() then return end @@ -218,7 +218,7 @@ end function NetworkListener:onNetworkDisconnected() logger.dbg("NetworkListener: onNetworkDisconnected") - if not (Device:hasWifiManager() and not Device:isEmulator()) then + if not Device:hasWifiManager() then return end