You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koreader/frontend/ui/network/networklistener.lua

217 lines
7.8 KiB
Lua

local BD = require("ui/bidi")
local Device = require("device")
local Event = require("ui/event")
local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer")
local NetworkMgr = require("ui/network/manager")
local UIManager = require("ui/uimanager")
local logger = require("logger")
local _ = require("gettext")
local T = require("ffi/util").template
local NetworkListener = InputContainer:new{}
function NetworkListener:onToggleWifi()
if not NetworkMgr:isOnline() then
UIManager:show(InfoMessage:new{
text = _("Turning on Wi-Fi…"),
timeout = 1,
})
-- NB Normal widgets should use NetworkMgr:promptWifiOn()
-- (or, better yet, the NetworkMgr:beforeWifiAction wrappers: NetworkMgr:runWhenOnline() & co.)
-- This is specifically the toggle Wi-Fi action, so consent is implied.
local complete_callback = function()
UIManager:broadcastEvent(Event:new("NetworkConnected"))
end
NetworkMgr:turnOnWifi(complete_callback)
else
local complete_callback = function()
UIManager:broadcastEvent(Event:new("NetworkDisconnected"))
end
NetworkMgr:turnOffWifi(complete_callback)
UIManager:show(InfoMessage:new{
text = _("Wi-Fi off."),
timeout = 1,
})
end
end
function NetworkListener:onInfoWifiOff()
-- That's the end goal
local complete_callback = function()
UIManager:broadcastEvent(Event:new("NetworkDisconnected"))
end
NetworkMgr:turnOffWifi(complete_callback)
UIManager:show(InfoMessage:new{
text = _("Wi-Fi off."),
timeout = 1,
})
end
function NetworkListener:onInfoWifiOn()
if not NetworkMgr:isOnline() then
UIManager:show(InfoMessage:new{
text = _("Enabling wifi…"),
timeout = 1,
})
-- NB Normal widgets should use NetworkMgr:promptWifiOn()
-- (or, better yet, the NetworkMgr:beforeWifiAction wrappers: NetworkMgr:runWhenOnline() & co.)
-- This is specifically the toggle Wi-Fi action, so consent is implied.
local complete_callback = function()
UIManager:broadcastEvent(Event:new("NetworkConnected"))
end
NetworkMgr:turnOnWifi(complete_callback)
else
local info_text
local current_network = NetworkMgr:getCurrentNetwork()
-- this method is only available for some implementations
if current_network and current_network.ssid then
info_text = T(_("Already connected to network %1."), BD.wrap(current_network.ssid))
else
info_text = _("Already connected.")
end
UIManager:show(InfoMessage:new{
text = info_text,
timeout = 1,
})
end
end
-- Everything below is to handle auto_disable_wifi ;).
local default_network_timeout_seconds = 5*60
local max_network_timeout_seconds = 30*60
-- This should be more than enough to catch actual activity vs. noise spread over 5 minutes.
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())
-- allows us to get away with a Linux-only solution.
function NetworkListener:_getTxPackets()
-- read tx_packets stats from sysfs (for the right network if)
local file = io.open("/sys/class/net/" .. NetworkMgr:getNetworkInterfaceName() .. "/statistics/tx_packets", "rb")
-- file exists only when Wi-Fi module is loaded.
if not file then return nil end
local out = file:read("*all")
file:close()
-- strip NaN from file read (i.e.,: line endings, error messages)
local tx_packets
if type(out) ~= "number" then
tx_packets = tonumber(out)
else
tx_packets = out
end
-- finally return it
if type(tx_packets) == "number" then
return tx_packets
else
return nil
end
end
function NetworkListener:_unscheduleActivityCheck()
logger.dbg("NetworkListener: unschedule network activity check")
if self._activity_check_scheduled then
UIManager:unschedule(self._scheduleActivityCheck)
self._activity_check_scheduled = nil
logger.dbg("NetworkListener: network activity check unscheduled")
end
-- We also need to reset the stats, otherwise we'll be comparing apples vs. oranges... (i.e., two different network sessions)
if self._last_tx_packets then
self._last_tx_packets = nil
end
if self._activity_check_delay then
self._activity_check_delay = nil
end
end
function NetworkListener:_scheduleActivityCheck()
logger.dbg("NetworkListener: network activity check")
local keep_checking = true
local tx_packets = NetworkListener:_getTxPackets()
if self._last_tx_packets then
-- Compute noise margin based on the current delay
local delay = self._activity_check_delay or default_network_timeout_seconds
local noise = delay / default_network_timeout_seconds * network_activity_noise_margin
-- If there was no meaningful activity (+/- a couple packets), kill the Wi-Fi
if math.max(0, tx_packets - noise) <= self._last_tx_packets then
logger.dbg("NetworkListener: No meaningful network activity ( then:", self._last_tx_packets, "vs. now:", tx_packets, "), disabling Wi-Fi")
keep_checking = false
local complete_callback = function()
UIManager:broadcastEvent(Event:new("NetworkDisconnected"))
end
NetworkMgr:turnOffWifi(complete_callback)
-- NOTE: We leave wifi_was_on as-is on purpose, we wouldn't want to break auto_restore_wifi workflows on the next start...
end
end
-- If we've just killed Wi-Fi, onNetworkDisconnected will take care of unscheduling us
if keep_checking then
-- Update tracker for next iter
self._last_tx_packets = tx_packets
-- If it's already been scheduled, increase the delay until we hit the ceiling
if self._activity_check_delay then
self._activity_check_delay = self._activity_check_delay + default_network_timeout_seconds
if self._activity_check_delay > max_network_timeout_seconds then
self._activity_check_delay = max_network_timeout_seconds
end
else
self._activity_check_delay = default_network_timeout_seconds
end
UIManager:scheduleIn(self._activity_check_delay, self._scheduleActivityCheck, self)
self._activity_check_scheduled = true
logger.dbg("NetworkListener: network activity check scheduled in", self._activity_check_delay, "seconds")
end
end
function NetworkListener:onNetworkConnected()
if not (Device:hasWifiManager() and not Device:isEmulator()) then
return
end
if not G_reader_settings:isTrue("auto_disable_wifi") then
return
end
-- If the activity check has already been scheduled for some reason, unschedule it first.
NetworkListener:_unscheduleActivityCheck()
NetworkListener:_scheduleActivityCheck()
end
function NetworkListener:onNetworkDisconnected()
if not (Device:hasWifiManager() and not Device:isEmulator()) then
return
end
if not G_reader_settings:isTrue("auto_disable_wifi") then
return
end
NetworkListener:_unscheduleActivityCheck()
-- Reset NetworkMgr's beforeWifiAction marker
NetworkMgr:clearBeforeActionFlag()
end
-- Also unschedule on suspend (and we happen to also kill Wi-Fi to do so, so resetting the stats is also relevant here)
function NetworkListener:onSuspend()
self:onNetworkDisconnected()
end
return NetworkListener