Various Wi-Fi QoL improvements (#6424)

* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421 
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
reviewable/pr6438/r1
NiLuJe 4 years ago committed by GitHub
parent e23f68e5f7
commit 37a01100b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -149,20 +149,17 @@ function CloudStorage:openCloudServer(url)
local tbl
local NetworkMgr = require("ui/network/manager")
if self.type == "dropbox" then
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
if NetworkMgr:willRerunWhenOnline(function() self:openCloudServer(url) end) then
return
end
tbl = DropBox:run(url, self.password, self.choose_folder_mode)
elseif self.type == "ftp" then
if not NetworkMgr:isConnected() then
NetworkMgr:promptWifiOn()
if NetworkMgr:willRerunWhenConnected(function() self:openCloudServer(url) end) then
return
end
tbl = Ftp:run(self.address, self.username, self.password, url)
elseif self.type == "webdav" then
if not NetworkMgr:isConnected() then
NetworkMgr:promptWifiOn()
if NetworkMgr:willRerunWhenConnected(function() self:openCloudServer(url) end) then
return
end
tbl = WebDav:run(self.address, self.username, self.password, url)

@ -836,11 +836,10 @@ function ReaderDictionary:showDownload(downloadable_dicts)
for dummy, dict in ipairs(downloadable_dicts) do
table.insert(kv_pairs, {dict.name, "",
callback = function()
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
return
local connect_callback = function()
self:downloadDictionaryPrep(dict)
end
self:downloadDictionaryPrep(dict)
NetworkMgr:runWhenOnline(connect_callback)
end})
local lang
if dict.lang_in == dict.lang_out then

@ -57,7 +57,7 @@ local symbol_prefix = {
frontlight = C_("FooterLetterPrefix", "L:"),
-- @translators This is the footer letter prefix for memory usage.
mem_usage = C_("FooterLetterPrefix", "M:"),
-- @translators This is the footer letter prefix for wifi status.
-- @translators This is the footer letter prefix for Wi-Fi status.
wifi_status = C_("FooterLetterPrefix", "W:"),
},
icons = {
@ -1835,7 +1835,7 @@ function ReaderFooter:applyFooterMode(mode)
-- 7 for from statistics chapter time to read
-- 8 for front light level
-- 9 for memory usage
-- 10 for wifi status
-- 10 for Wi-Fi status
-- 11 for book title
-- 12 for current chapter

@ -384,10 +384,11 @@ function ReaderWikipedia:onLookupWikipedia(word, box, get_fullpage, forced_lang)
end
function ReaderWikipedia:lookupWikipedia(word, box, get_fullpage, forced_lang)
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
if NetworkMgr:willRerunWhenOnline(function() self:lookupWikipedia(word, box, get_fullpage, forced_lang) end) then
-- Not online yet, nothing more to do here, NetworkMgr will forward the callback and run it once connected!
return
end
-- word is the text to query. If get_fullpage is true, it is the
-- exact wikipedia page title we want the full page of.
self:initLanguages(word)
@ -525,11 +526,10 @@ function ReaderWikipedia:onSaveSettings()
end
function ReaderWikipedia:onShowWikipediaLookup()
if NetworkMgr:isOnline() then
local connect_callback = function()
self:lookupInput()
else
NetworkMgr:promptWifiOn()
end
NetworkMgr:runWhenOnline(connect_callback)
return true
end

@ -17,7 +17,7 @@ local function isConnected()
-- read carrier state from sysfs (for eth0)
local file = io.open("/sys/class/net/eth0/carrier", "rb")
-- file exists while wifi module is loaded.
-- file exists while Wi-Fi module is loaded.
if not file then return 0 end
-- 0 means not connected, 1 connected
@ -224,7 +224,7 @@ end
-- wireless
function Cervantes:initNetworkManager(NetworkMgr)
function NetworkMgr:turnOffWifi(complete_callback)
logger.info("Cervantes: disabling WiFi")
logger.info("Cervantes: disabling Wi-Fi")
self:releaseIP()
os.execute("./disable-wifi.sh")
if complete_callback then
@ -232,10 +232,13 @@ function Cervantes:initNetworkManager(NetworkMgr)
end
end
function NetworkMgr:turnOnWifi(complete_callback)
logger.info("Cervantes: enabling WiFi")
logger.info("Cervantes: enabling Wi-Fi")
os.execute("./enable-wifi.sh")
self:reconnectOrShowNetworkMenu(complete_callback)
end
function NetworkMgr:getNetworkInterfaceName()
return "eth0"
end
NetworkMgr:setWirelessBackend("wpa_supplicant", {ctrl_interface = "/var/run/wpa_supplicant/eth0"})
function NetworkMgr:obtainIP()
os.execute("./obtain-ip.sh")

@ -282,12 +282,12 @@ function Device:onPowerEvent(ev)
self.screen_saver_mode = true
UIManager:scheduleIn(0.1, function()
local network_manager = require("ui/network/manager")
-- NOTE: wifi_was_on does not necessarily mean that WiFi is *currently* on! It means *we* enabled it.
-- NOTE: wifi_was_on does not necessarily mean that Wi-Fi is *currently* on! It means *we* enabled it.
-- This is critical on Kobos (c.f., #3936), where it might still be on from KSM or Nickel,
-- without us being aware of it (i.e., wifi_was_on still unset or false),
-- because suspend will at best fail, and at worst deadlock the system if WiFi is on,
-- because suspend will at best fail, and at worst deadlock the system if Wi-Fi is on,
-- regardless of who enabled it!
if network_manager.wifi_was_on or network_manager:isWifiOn() then
if network_manager:isWifiOn() then
network_manager:releaseIP()
network_manager:turnOffWifi()
end

@ -12,7 +12,7 @@ local function kindleEnableWifi(toggle)
end
if lipc_handle then
-- Be extremely thorough... c.f., #6019
-- NOTE: I *assume* this'll also ensure we prefer WiFi over 3G/4G, which is a plus in my book...
-- NOTE: I *assume* this'll also ensure we prefer Wi-Fi over 3G/4G, which is a plus in my book...
if toggle == 1 then
lipc_handle:set_int_property("com.lab126.cmd", "wirelessEnable", 1)
lipc_handle:set_int_property("com.lab126.wifid", "enable", 1)
@ -103,20 +103,19 @@ function Kindle:initNetworkManager(NetworkMgr)
function NetworkMgr:turnOnWifi(complete_callback)
kindleEnableWifi(1)
-- NOTE: As we defer the actual work to lipc,
-- we have no guarantee the WiFi state will have changed by the time kindleEnableWifi returns,
-- so, delay the callback a bit...
-- we have no guarantee the Wi-Fi state will have changed by the time kindleEnableWifi returns,
-- so, delay the callback until we at least can ensure isConnect is true.
if complete_callback then
local UIManager = require("ui/uimanager")
UIManager:scheduleIn(1, complete_callback)
NetworkMgr:scheduleConnectivityCheck(complete_callback)
end
end
function NetworkMgr:turnOffWifi(complete_callback)
kindleEnableWifi(0)
-- NOTE: Same here...
-- NOTE: Same here, except disconnect is simpler, so a dumb delay will do...
if complete_callback then
local UIManager = require("ui/uimanager")
UIManager:scheduleIn(1, complete_callback)
UIManager:scheduleIn(2, complete_callback)
end
end
@ -374,7 +373,7 @@ local KindleOasis = Kindle:new{
hasGSensor = yes,
display_dpi = 300,
--[[
-- NOTE: Points to event3 on WiFi devices, event4 on 3G devices...
-- NOTE: Points to event3 on Wi-Fi devices, event4 on 3G devices...
-- 3G devices apparently have an extra SX9500 Proximity/Capacitive controller for mysterious purposes...
-- This evidently screws with the ordering, so, use the udev by-path path instead to avoid hackier workarounds.
-- cf. #2181
@ -405,7 +404,7 @@ local KindlePaperWhite4 = Kindle:new{
display_dpi = 300,
-- NOTE: LTE devices once again have a mysterious extra SX9310 proximity sensor...
-- Except this time, we can't rely on by-path, because there's no entry for the TS :/.
-- Should be event2 on WiFi, event3 on LTE, we'll fix it in init.
-- Should be event2 on Wi-Fi, event3 on LTE, we'll fix it in init.
touch_dev = "/dev/input/event2",
}

@ -10,11 +10,11 @@ local function yes() return true end
local function no() return false end
local function koboEnableWifi(toggle)
if toggle == 1 then
logger.info("Kobo WiFi: enabling WiFi")
if toggle == true then
logger.info("Kobo Wi-Fi: enabling Wi-Fi")
os.execute("./enable-wifi.sh")
else
logger.info("Kobo WiFi: disabling WiFi")
logger.info("Kobo Wi-Fi: disabling Wi-Fi")
os.execute("./disable-wifi.sh")
end
end
@ -414,14 +414,15 @@ end
function Kobo:initNetworkManager(NetworkMgr)
function NetworkMgr:turnOffWifi(complete_callback)
koboEnableWifi(0)
self:releaseIP()
koboEnableWifi(false)
if complete_callback then
complete_callback()
end
end
function NetworkMgr:turnOnWifi(complete_callback)
koboEnableWifi(1)
koboEnableWifi(true)
self:showNetworkMenu(complete_callback)
end
@ -429,6 +430,9 @@ function Kobo:initNetworkManager(NetworkMgr)
if not net_if then
net_if = "eth0"
end
function NetworkMgr:getNetworkInterfaceName()
return net_if
end
NetworkMgr:setWirelessBackend(
"wpa_supplicant", {ctrl_interface = "/var/run/wpa_supplicant/" .. net_if})
@ -444,14 +448,14 @@ function Kobo:initNetworkManager(NetworkMgr)
os.execute("./restore-wifi-async.sh")
end
-- NOTE: Cheap-ass way of checking if WiFi seems to be enabled...
-- 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.
function NetworkMgr:isWifiOn()
local fd = io.open("/proc/modules", "r")
if fd then
local lsmod = fd:read("*all")
fd:close()
-- lsmod is usually empty, unless WiFi or USB is enabled
-- lsmod 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.

@ -158,6 +158,10 @@ function SonyPRSTUX:initNetworkManager(NetworkMgr)
self:showNetworkMenu(complete_callback)
end
function NetworkMgr:getNetworkInterfaceName()
return "wlan0"
end
NetworkMgr:setWirelessBackend("wpa_supplicant", {ctrl_interface = "/var/run/wpa_supplicant/wlan0"})
function NetworkMgr:obtainIP()

@ -62,9 +62,11 @@ local order = {
network = {
"network_wifi",
"network_proxy",
"network_powersave",
"network_restore",
"network_info",
"network_before_wifi_action",
"network_after_wifi_action",
"network_dismiss_scan",
"----------------------------",
"ssh",

@ -83,9 +83,11 @@ local order = {
network = {
"network_wifi",
"network_proxy",
"network_powersave",
"network_restore",
"network_info",
"network_before_wifi_action",
"network_after_wifi_action",
"network_dismiss_scan",
"----------------------------",
"ssh",

@ -2,6 +2,7 @@ local BD = require("ui/bidi")
local ConfirmBox = require("ui/widget/confirmbox")
local DataStorage = require("datastorage")
local Device = require("device")
local Event = require("ui/event")
local InfoMessage = require("ui/widget/infomessage")
local LuaSettings = require("luasettings")
local UIManager = require("ui/uimanager")
@ -16,38 +17,80 @@ function NetworkMgr:readNWSettings()
self.nw_settings = LuaSettings:open(DataStorage:getSettingsDir().."/network.lua")
end
-- Used after restoreWifiAsync() to make sure we eventually send a NetworkConnected event, as a few things rely on it (KOSync, c.f. #5109).
function NetworkMgr:connectivityCheck(iter)
-- Give up after a while...
if iter > 6 then
-- Used after restoreWifiAsync() and the turn_on beforeWifiAction to make sure we eventually send a NetworkConnected event,
-- as quite a few things rely on it (KOSync, c.f. #5109; the network activity check, c.f., #6424).
function NetworkMgr:connectivityCheck(iter, callback, widget)
-- Give up after a while (restoreWifiAsync can take over 45s, so, try to cover that)...
if iter > 25 then
logger.info("Failed to restore Wi-Fi (after", iter, "iterations)!")
self.wifi_was_on = false
G_reader_settings:saveSetting("wifi_was_on", false)
-- If we abort, murder Wi-Fi and the async script first...
if Device:hasWifiManager() and not Device:isEmulator() then
os.execute("pkill -TERM restore-wifi-async.sh 2>/dev/null")
end
NetworkMgr:turnOffWifi()
-- Handle the UI warning if it's from a beforeWifiAction...
if widget then
UIManager:close(widget)
UIManager:show(InfoMessage:new{ text = _("Error connecting to the network") })
end
return
end
if NetworkMgr:isWifiOn() and NetworkMgr:isConnected() then
local Event = require("ui/event")
self.wifi_was_on = true
G_reader_settings:saveSetting("wifi_was_on", true)
UIManager:broadcastEvent(Event:new("NetworkConnected"))
logger.info("WiFi successfully restored!")
logger.info("Wi-Fi successfully restored (after", iter, "iterations)!")
-- Handle the UI & callback if it's from a beforeWifiAction...
if widget then
UIManager:close(widget)
end
if callback then
callback()
else
-- If this trickled down from a turn_onbeforeWifiAction and there is no callback,
-- mention that the action needs to be retried manually.
if widget then
UIManager:show(InfoMessage:new{
text = _("You can now retry the action that required network access"),
timeout = 3,
})
end
end
else
UIManager:scheduleIn(2, function() NetworkMgr:connectivityCheck(iter + 1) end)
UIManager:scheduleIn(2, function() NetworkMgr:connectivityCheck(iter + 1, callback, widget) end)
end
end
function NetworkMgr:scheduleConnectivityCheck()
UIManager:scheduleIn(2, function() NetworkMgr:connectivityCheck(1) end)
function NetworkMgr:scheduleConnectivityCheck(callback, widget)
UIManager:scheduleIn(2, function() NetworkMgr:connectivityCheck(1, callback, widget) end)
end
function NetworkMgr:init()
-- On Kobo, kill WiFi if NetworkMgr:isWifiOn() and NOT NetworkMgr:isConnected()
-- (i.e., if the launcher left the WiFi in an inconsistent state: modules loaded, but no route to gateway).
-- On Kobo, kill Wi-Fi if NetworkMgr:isWifiOn() and NOT NetworkMgr:isConnected()
-- (i.e., if the launcher left the Wi-Fi in an inconsistent state: modules loaded, but no route to gateway).
if Device:isKobo() and self:isWifiOn() and not self:isConnected() then
logger.info("Kobo WiFi: Left in an inconsistent state by launcher!")
logger.info("Kobo Wi-Fi: Left in an inconsistent state by launcher!")
self:turnOffWifi()
end
self.wifi_was_on = G_reader_settings:isTrue("wifi_was_on")
if self.wifi_was_on and G_reader_settings:isTrue("auto_restore_wifi") then
self:restoreWifiAsync()
-- Don't bother if WiFi is already up...
if not (self:isWifiOn() and self:isConnected()) then
self:restoreWifiAsync()
end
self:scheduleConnectivityCheck()
else
-- Trigger an initial NetworkConnected event if WiFi was already up when we were launched
if NetworkMgr:isWifiOn() and NetworkMgr:isConnected() then
-- NOTE: This needs to be delayed because NetworkListener is initialized slightly later by the FM/Reader app...
UIManager:scheduleIn(2, function() UIManager:broadcastEvent(Event:new("NetworkConnected")) end)
end
end
end
@ -57,6 +100,7 @@ end
function NetworkMgr:turnOnWifi() end
function NetworkMgr:turnOffWifi() end
function NetworkMgr:isWifiOn() end
function NetworkMgr:getNetworkInterfaceName() end
function NetworkMgr:getNetworkList() end
function NetworkMgr:getCurrentNetwork() end
function NetworkMgr:authenticateNetwork() end
@ -92,32 +136,69 @@ function NetworkMgr:promptWifiOff(complete_callback)
end
function NetworkMgr:turnOnWifiAndWaitForConnection(callback)
NetworkMgr:turnOnWifi()
local timeout = 30
local retry_count = 0
local info = InfoMessage:new{ text = T(_("Enabling Wi-Fi. Waiting for Internet connection…\nTimeout %1 seconds."), timeout)}
local info = InfoMessage:new{ text = _("Connecting to Wi-Fi…") }
UIManager:show(info)
UIManager:forceRePaint()
while not NetworkMgr:isOnline() and retry_count < timeout do
ffiutil.sleep(1)
retry_count = retry_count + 1
end
UIManager:close(info)
if retry_count == timeout then
UIManager:show(InfoMessage:new{ text = _("Error connecting to the network") })
return
-- Don't bother if WiFi is already up...
if not (self:isWifiOn() and self:isConnected()) then
self:turnOnWifi()
end
if callback then callback() end
-- This will handle sending the proper Event, manage wifi_was_on, as well as tearing down Wi-Fi in case of failures,
-- (i.e., much like getWifiToggleMenuTable).
self:scheduleConnectivityCheck(callback, info)
end
--- This quirky internal flag is used for the rare beforeWifiAction -> afterWifiAction brackets.
function NetworkMgr:clearBeforeActionFlag()
self._before_action_tripped = nil
end
function NetworkMgr:setBeforeActionFlag()
self._before_action_tripped = true
end
function NetworkMgr:getBeforeActionFlag()
return self._before_action_tripped
end
--- @note: The callback will only run *after* a *succesful* network connection.
--- The only guarantee it provides is isConnected (i.e., an IP & a local gateway),
--- *NOT* isOnline (i.e., WAN), se be careful with recursive callbacks!
function NetworkMgr:beforeWifiAction(callback)
-- Remember that we ran, for afterWifiAction...
self:setBeforeActionFlag()
local wifi_enable_action = G_reader_settings:readSetting("wifi_enable_action")
if wifi_enable_action == "turn_on" then
NetworkMgr:turnOnWifiAndWaitForConnection(callback)
else
NetworkMgr:promptWifiOn(callback)
end
end
end
-- NOTE: This is actually used very sparingly (newsdownloader/send2ebook),
-- because bracketing a single action in a connect/disconnect session doesn't necessarily make much sense...
function NetworkMgr:afterWifiAction(callback)
-- Don't do anything if beforeWifiAction never actually ran...
if not self:getBeforeActionFlag() then
return
end
self:clearBeforeActionFlag()
local wifi_disable_action = G_reader_settings:readSetting("wifi_disable_action")
if wifi_disable_action == "leave_on" then
-- NOP :)
if callback then
callback()
end
elseif wifi_disable_action == "turn_off" then
NetworkMgr:turnOffWifi(callback)
else
NetworkMgr:promptWifiOff(callback)
end
end
function NetworkMgr:isConnected()
if Device:isAndroid() or Device:isCervantes() or Device:isPocketBook() or Device:isEmulator() then
@ -174,6 +255,68 @@ function NetworkMgr:setHTTPProxy(proxy)
end
end
-- Helper functions to hide the quirks of using beforeWifiAction properly ;).
-- Run callback *now* if you're currently online (ie., isOnline),
-- or attempt to go online and run it *ASAP* without any more user interaction.
-- NOTE: If you're currently connected but without Internet access (i.e., isConnected and not isOnline),
-- it will just attempt to re-connect, *without* running the callback.
-- c.f., ReaderWikipedia:onShowWikipediaLookup @ frontend/apps/reader/modules/readerwikipedia.lua
function NetworkMgr:runWhenOnline(callback)
if self:isOnline() then
callback()
else
--- @note: Avoid infinite recursion, beforeWifiAction only guarantees isConnected, not isOnline.
if not self:isConnected() then
self:beforeWifiAction(callback)
else
self:beforeWifiAction()
end
end
end
-- This one is for callbacks that only require isConnected, and since that's guaranteed by beforeWifiAction,
-- you also have a guarantee that the callback *will* run.
function NetworkMgr:runWhenConnected(callback)
if self:isConnected() then
callback()
else
self:beforeWifiAction(callback)
end
end
-- Mild variants that are used for recursive calls at the beginning of a complex function call.
-- Returns true when not yet online, in which case you should *abort* (i.e., return) the initial call,
-- and otherwise, go-on as planned.
-- NOTE: If you're currently connected but without Internet access (i.e., isConnected and not isOnline),
-- it will just attempt to re-connect, *without* running the callback.
-- c.f., ReaderWikipedia:lookupWikipedia @ frontend/apps/reader/modules/readerwikipedia.lua
function NetworkMgr:willRerunWhenOnline(callback)
if not self:isOnline() then
--- @note: Avoid infinite recursion, beforeWifiAction only guarantees isConnected, not isOnline.
if not self:isConnected() then
self:beforeWifiAction(callback)
else
self:beforeWifiAction()
end
return true
end
return false
end
-- This one is for callbacks that only require isConnected, and since that's guaranteed by beforeWifiAction,
-- you also have a guarantee that the callback *will* run.
function NetworkMgr:willRerunWhenConnected(callback)
if not self:isConnected() then
self:beforeWifiAction(callback)
return true
end
return false
end
function NetworkMgr:getWifiMenuTable()
if Device:isAndroid() then
return {
@ -196,7 +339,6 @@ function NetworkMgr:getWifiToggleMenuTable()
local complete_callback = function()
-- notify touch menu to update item check state
touchmenu_instance:updateItems()
local Event = require("ui/event")
-- if wifi was on, this callback will only be executed when the network has been
-- disconnected.
if wifi_status then
@ -208,7 +350,7 @@ function NetworkMgr:getWifiToggleMenuTable()
if NetworkMgr:isWifiOn() and NetworkMgr:isConnected() then
UIManager:broadcastEvent(Event:new("NetworkConnected"))
elseif NetworkMgr:isWifiOn() and not NetworkMgr:isConnected() then
-- Don't leave WiFi in an inconsistent state if the connection failed.
-- Don't leave Wi-Fi in an inconsistent state if the connection failed.
self.wifi_was_on = false
G_reader_settings:saveSetting("wifi_was_on", false)
-- NOTE: We're limiting this to only a few platforms, as it might be actually harmful on some devices.
@ -219,8 +361,8 @@ function NetworkMgr:getWifiToggleMenuTable()
-- Kobo: Yes, please.
-- Cervantes: Loads/unloads module, probably could use it like Kobo.
-- Kindle: Probably could use it, if only because leaving Wireless on is generally a terrible idea on Kindle,
-- except that we defer to lipc, which makes WiFi handling asynchronous, and the callback is simply delayed by 1s,
-- so we can't be sure the system will actually have finished bringing WiFi up by then...
-- except that we defer to lipc, which makes Wi-Fi handling asynchronous, and the callback is simply delayed by 1s,
-- so we can't be sure the system will actually have finished bringing Wi-Fi up by then...
NetworkMgr:turnOffWifi()
touchmenu_instance:updateItems()
end
@ -276,9 +418,26 @@ function NetworkMgr:getProxyMenuTable()
}
end
function NetworkMgr:getPowersaveMenuTable()
return {
text = _("Kill 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,
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...
UIManager:show(InfoMessage:new{
text = _("This will take effect on next restart."),
})
end,
}
end
function NetworkMgr:getRestoreMenuTable()
return {
text = _("Automatically restore Wi-Fi connection after resume"),
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,
callback = function() G_reader_settings:flipNilOrFalse("auto_restore_wifi") end,
@ -306,34 +465,67 @@ function NetworkMgr:getInfoMenuTable()
end
function NetworkMgr:getBeforeWifiActionMenuTable()
local wifi_enable_action_setting = G_reader_settings:readSetting("wifi_enable_action") or "prompt"
local wifi_enable_actions = {
turn_on = {_("turn on"), _("Turn on (experimental)")},
prompt = {_("prompt"), _("Prompt")},
}
local action_table = function(wifi_enable_action)
return {
text = wifi_enable_actions[wifi_enable_action][2],
checked_func = function()
return wifi_enable_action_setting == wifi_enable_action
end,
callback = function()
wifi_enable_action_setting = wifi_enable_action
G_reader_settings:saveSetting("wifi_enable_action", wifi_enable_action)
end,
}
end
return {
text_func = function()
return T(_("Action when Wi-Fi is off: %1"),
wifi_enable_actions[wifi_enable_action_setting][1]
)
end,
sub_item_table = {
action_table("turn_on"),
action_table("prompt"),
}
}
local wifi_enable_action_setting = G_reader_settings:readSetting("wifi_enable_action") or "prompt"
local wifi_enable_actions = {
turn_on = {_("turn on"), _("Turn on")},
prompt = {_("prompt"), _("Prompt")},
}
local action_table = function(wifi_enable_action)
return {
text = wifi_enable_actions[wifi_enable_action][2],
checked_func = function()
return wifi_enable_action_setting == wifi_enable_action
end,
callback = function()
wifi_enable_action_setting = wifi_enable_action
G_reader_settings:saveSetting("wifi_enable_action", wifi_enable_action)
end,
}
end
return {
text_func = function()
return T(_("Action when Wi-Fi is off: %1"),
wifi_enable_actions[wifi_enable_action_setting][1]
)
end,
sub_item_table = {
action_table("turn_on"),
action_table("prompt"),
}
}
end
function NetworkMgr:getAfterWifiActionMenuTable()
local wifi_disable_action_setting = G_reader_settings:readSetting("wifi_disable_action") or "prompt"
local wifi_disable_actions = {
leave_on = {_("leave on"), _("Leave on")},
turn_off = {_("turn off"), _("Turn off")},
prompt = {_("prompt"), _("Prompt")},
}
local action_table = function(wifi_disable_action)
return {
text = wifi_disable_actions[wifi_disable_action][2],
checked_func = function()
return wifi_disable_action_setting == wifi_disable_action
end,
callback = function()
wifi_disable_action_setting = wifi_disable_action
G_reader_settings:saveSetting("wifi_disable_action", wifi_disable_action)
end,
}
end
return {
text_func = function()
return T(_("Action when done with Wi-Fi: %1"),
wifi_disable_actions[wifi_disable_action_setting][1]
)
end,
sub_item_table = {
action_table("leave_on"),
action_table("turn_off"),
action_table("prompt"),
}
}
end
function NetworkMgr:getDismissScanMenuTable()
@ -354,9 +546,11 @@ function NetworkMgr:getMenuTable(common_settings)
common_settings.network_info = self:getInfoMenuTable()
if Device:hasWifiManager() then
common_settings.network_powersave = self:getPowersaveMenuTable()
common_settings.network_restore = self:getRestoreMenuTable()
common_settings.network_dismiss_scan = self:getDismissScanMenuTable()
common_settings.network_before_wifi_action = self:getBeforeWifiActionMenuTable()
common_settings.network_after_wifi_action = self:getAfterWifiActionMenuTable()
end
end
@ -458,6 +652,7 @@ if NETWORK_PROXY then
NetworkMgr:setHTTPProxy(NETWORK_PROXY)
end
Device:initNetworkManager(NetworkMgr)
NetworkMgr:init()

@ -1,8 +1,11 @@
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
@ -16,10 +19,17 @@ function NetworkListener:onToggleWifi()
})
-- NB Normal widgets should use NetworkMgr:promptWifiOn()
-- This is specifically the toggle wifi action, so consent is implied.
NetworkMgr:turnOnWifi()
-- (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
NetworkMgr:turnOffWifi()
local complete_callback = function()
UIManager:broadcastEvent(Event:new("NetworkDisconnected"))
end
NetworkMgr:turnOffWifi(complete_callback)
UIManager:show(InfoMessage:new{
text = _("Wi-Fi off."),
@ -29,8 +39,11 @@ function NetworkListener:onToggleWifi()
end
function NetworkListener:onInfoWifiOff()
-- can't hurt
NetworkMgr:turnOffWifi()
-- 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."),
@ -46,8 +59,12 @@ function NetworkListener:onInfoWifiOn()
})
-- 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.
NetworkMgr:turnOnWifi()
local complete_callback = function()
UIManager:broadcastEvent(Event:new("NetworkConnected"))
end
NetworkMgr:turnOnWifi(complete_callback)
else
local info_text
local current_network = NetworkMgr:getCurrentNetwork()
@ -64,4 +81,136 @@ function NetworkListener:onInfoWifiOn()
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

@ -455,21 +455,19 @@ function OTAManager:getOTAMenuTable()
return {
text = _("Update"),
hold_callback = function()
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
else
local connect_callback = function()
OTAManager:fetchAndProcessUpdate()
end
NetworkMgr:runWhenOnline(connect_callback)
end,
sub_item_table = {
{
text = _("Check for update"),
callback = function()
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
else
local connect_callback = function()
OTAManager:fetchAndProcessUpdate()
end
NetworkMgr:runWhenOnline(connect_callback)
end
},
{

@ -416,10 +416,10 @@ Show translated text in TextViewer, with alternate translations
--]]
function Translator:showTranslation(text, target_lang, source_lang)
local NetworkMgr = require("ui/network/manager")
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
if NetworkMgr:willRerunWhenOnline(function() self:showTranslation(text, target_lang, source_lang) end) then
return
end
-- Wrap next function with Trapper to be able to interrupt
-- translation service query.
local Trapper = require("ui/trapper")

@ -22,9 +22,12 @@ Example:
UIManager:show(require("ui/widget/networksetting"):new{
network_list = network_list,
connect_callback = function()
-- connect_callback will be called when an connect/disconnect
-- attempt has been made. you can update UI widgets in the
-- callback.
-- connect_callback will be called when a *connect* (NOT disconnect)
-- attempt has been successful.
-- You can update UI widgets in the callback.
end,
disconnect_callback = function()
-- This one will fire unconditionally after a disconnect attempt.
end,
})
@ -224,7 +227,8 @@ function NetworkItem:connect()
text = err_msg
end
if self.setting_ui.connect_callback then
-- Do what it says on the tin, and only trigger the connect_callback on a *successful* connect.
if success and self.setting_ui.connect_callback then
self.setting_ui.connect_callback()
end
@ -244,8 +248,8 @@ function NetworkItem:disconnect()
self.info.connected = nil
self:refresh()
self.setting_ui:setConnectedItem(nil)
if self.setting_ui.connect_callback then
self.setting_ui.connect_callback()
if self.setting_ui.disconnect_callback then
self.setting_ui.disconnect_callback()
end
end
@ -378,6 +382,7 @@ local NetworkSetting = InputContainer:new{
-- }
network_list = nil,
connect_callback = nil,
disconnect_callback = nil,
}
function NetworkSetting:init()

@ -377,10 +377,7 @@ end
function OPDSBrowser:getCatalog(item_url, username, password)
local ok, catalog = pcall(self.parseFeed, self, item_url, username, password)
if not ok and catalog and not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
return
elseif not ok and catalog then
if not ok and catalog then
logger.info("cannot get catalog info from", item_url, catalog)
UIManager:show(InfoMessage:new{
text = T(_("Cannot get catalog info from %1"), (BD.url(item_url) or "")),
@ -724,11 +721,17 @@ function OPDSBrowser:onMenuSelect(item)
self:showDownloads(item)
-- navigation
else
local connect_callback
if item.searchable then
self:browseSearchable(item.url, item.username, item.password)
connect_callback = function()
self:browseSearchable(item.url, item.username, item.password)
end
else
self:browse(item.url, item.username, item.password)
connect_callback = function()
self:browse(item.url, item.username, item.password)
end
end
NetworkMgr:runWhenConnected(connect_callback)
end
return true
end

@ -21,11 +21,11 @@ EOF
}
RestoreWifi() {
echo "[$(date)] restore-wifi-async.sh: Restarting WiFi"
echo "[$(date)] restore-wifi-async.sh: Restarting Wi-Fi"
./enable-wifi.sh
RunWpaCli
./obtain-ip.sh
echo "[$(date)] restore-wifi-async.sh: Restarted WiFi"
echo "[$(date)] restore-wifi-async.sh: Restarted Wi-Fi"
}
RestoreWifi &

@ -1,8 +1,38 @@
#!/bin/sh
# Disable wifi, and remove all modules.
# NOTE: Save our resolv.conf to avoid ending up with an empty one, in case the DHCP client wipes it on release (#6424).
cp -a "/etc/resolv.conf" "/tmp/resolv.ko"
old_hash="$(md5sum "/etc/resolv.conf" | cut -f1 -d' ')"
killall udhcpc default.script wpa_supplicant 2>/dev/null
if [ -x "/sbin/dhcpcd" ]; then
env -u LD_LIBRARY_PATH dhcpcd -d -k "${INTERFACE}"
killall -q -TERM udhcpc default.script
else
killall -q -TERM udhcpc default.script dhcpcd
fi
# NOTE: dhcpcd -k waits for the signalled process to die, but busybox's killall doesn't have a -w, --wait flag,
# so we have to wait for udhcpc to die ourselves...
kill_timeout=0
while pkill -0 udhcpc; do
# Stop waiting after 5s
if [ ${kill_timeout} -ge 20 ]; then
break
fi
usleep 250000
kill_timeout=$((kill_timeout + 1))
done
new_hash="$(md5sum "/etc/resolv.conf" | cut -f1 -d' ')"
# Restore our network-specific resolv.conf if the DHCP client wiped it when releasing the lease...
if [ "${new_hash}" != "${old_hash}" ]; then
mv -f "/tmp/resolv.ko" "/etc/resolv.conf"
else
rm -f "/tmp/resolv.ko"
fi
wpa_cli terminate
[ "${WIFI_MODULE}" != "8189fs" ] && [ "${WIFI_MODULE}" != "8192es" ] && wlarm_le -i "${INTERFACE}" down
ifconfig "${INTERFACE}" down

@ -1,7 +1,6 @@
#!/bin/sh
# Load wifi modules and enable wifi.
lsmod | grep -q sdio_wifi_pwr || insmod "/drivers/${PLATFORM}/wifi/sdio_wifi_pwr.ko"
# Moar sleep!
usleep 250000
@ -13,6 +12,6 @@ sleep 1
ifconfig "${INTERFACE}" up
[ "${WIFI_MODULE}" != "8189fs" ] && [ "${WIFI_MODULE}" != "8192es" ] && wlarm_le -i "${INTERFACE}" up
pidof wpa_supplicant >/dev/null ||
pkill -0 wpa_supplicant ||
env -u LD_LIBRARY_PATH \
wpa_supplicant -D wext -s -i "${INTERFACE}" -O /var/run/wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -B
wpa_supplicant -D wext -s -i "${INTERFACE}" -c /etc/wpa_supplicant/wpa_supplicant.conf -O /var/run/wpa_supplicant -B

@ -110,7 +110,11 @@ if [ "${VIA_NICKEL}" = "true" ]; then
sync
# And we can now stop the full Kobo software stack
# NOTE: We don't need to kill KFMon, it's smart enough not to allow running anything else while we're up
killall -TERM nickel hindenburg sickel fickel adobehost fmon 2>/dev/null
# NOTE: We kill Nickel's master dhcpcd daemon on purpose,
# as we want to be able to use our own per-if processes w/ custom args later on.
# A SIGTERM does not break anything, it'll just prevent automatic lease renewal until the time
# KOReader actually sets the if up itself (i.e., it'll do)...
killall -q -TERM nickel hindenburg sickel fickel adobehost dhcpcd-dbus dhcpcd fmon
fi
# fallback for old fmon, KFMon and advboot users (-> if no args were passed to the script, start the FM)
@ -131,7 +135,7 @@ if [ -z "${PRODUCT}" ]; then
export PRODUCT
fi
# PLATFORM is used in koreader for the path to the WiFi drivers (as well as when restarting nickel)
# PLATFORM is used in koreader for the path to the Wi-Fi drivers (as well as when restarting nickel)
if [ -z "${PLATFORM}" ]; then
# shellcheck disable=SC2046
export $(grep -s -e '^PLATFORM=' "/proc/$(pidof -s udevd)/environ")
@ -209,6 +213,16 @@ ko_do_fbdepth() {
fi
}
# Ensure we start with a valid nameserver in resolv.conf, otherwise we're stuck with broken name resolution (#6421, #6424).
# Fun fact: this wouldn't be necessary if Kobo were using a non-prehistoric glibc... (it was fixed in glibc 2.26).
ko_do_dns() {
# If there aren't any servers listed, append CloudFlare's
if not grep -q '^nameserver' "/etc/resolv.conf"; then
echo "# Added by KOReader because your setup is broken" >>"/etc/resolv.conf"
echo "nameserver 1.1.1.1" >>"/etc/resolv.conf"
fi
}
# Remount the SD card RW if it's inserted and currently RO
if awk '$4~/(^|,)ro($|,)/' /proc/mounts | grep ' /mnt/sd '; then
mount -o remount,rw /mnt/sd
@ -232,6 +246,8 @@ while [ ${RETURN_VALUE} -ne 0 ]; do
ko_update_check
# Do or double-check the fb depth switch, or restore original bitdepth if requested
ko_do_fbdepth
# Make sure we have a sane resolv.conf
ko_do_dns
fi
./reader.lua "${args}" >>crash.log 2>&1
@ -273,11 +289,11 @@ while [ ${RETURN_VALUE} -ne 0 ]; do
fi
# U+1F4A3, the hard way, because we can't use \u or \U escape sequences...
# shellcheck disable=SC2039
./fbink -q -b -O -m -t regular=./fonts/freefont/FreeSerif.ttf,px=${bombHeight},top=${bombMargin} $'\xf0\x9f\x92\xa3'
./fbink -q -b -O -m -t regular=./fonts/freefont/FreeSerif.ttf,px=${bombHeight},top=${bombMargin} -- $'\xf0\x9f\x92\xa3'
# And then print the tail end of the log on the bottom of the screen...
crashLog="$(tail -n 25 crash.log | sed -e 's/\t/ /g')"
# The idea for the margins being to leave enough room for an fbink -Z bar, small horizontal margins, and a font size based on what 6pt looked like @ 265dpi
./fbink -q -b -O -t regular=./fonts/droid/DroidSansMono.ttf,top=$((viewHeight / 2 + FONTH * 2 + FONTH / 2)),left=$((viewWidth / 60)),right=$((viewWidth / 60)),px=$((viewHeight / 64)) "${crashLog}"
./fbink -q -b -O -t regular=./fonts/droid/DroidSansMono.ttf,top=$((viewHeight / 2 + FONTH * 2 + FONTH / 2)),left=$((viewWidth / 60)),right=$((viewWidth / 60)),px=$((viewHeight / 64)) -- "${crashLog}"
# So far, we hadn't triggered an actual screen refresh, do that now, to make sure everything is bundled in a single flashing refresh.
./fbink -q -f -s
# Cue a lemming's faceplant sound effect!

@ -21,10 +21,38 @@ unset KO_NO_CBB
/etc/init.d/on-animator.sh
) &
# Make sure we kill the WiFi first, because nickel apparently doesn't like it if it's up... (cf. #1520)
# Make sure we kill the Wi-Fi first, because nickel apparently doesn't like it if it's up... (cf. #1520)
# NOTE: That check is possibly wrong on PLATFORM == freescale (because I don't know if the sdio_wifi_pwr module exists there), but we don't terribly care about that.
if lsmod | grep -q sdio_wifi_pwr; then
killall restore-wifi-async.sh enable-wifi.sh obtain-ip.sh udhcpc default.script wpa_supplicant 2>/dev/null
killall -q -TERM restore-wifi-async.sh enable-wifi.sh obtain-ip.sh
cp -a "/etc/resolv.conf" "/tmp/resolv.ko"
old_hash="$(md5sum "/etc/resolv.conf" | cut -f1 -d' ')"
if [ -x "/sbin/dhcpcd" ]; then
env -u LD_LIBRARY_PATH dhcpcd -d -k "${INTERFACE}"
killall -q -TERM udhcpc default.script
else
killall -q -TERM udhcpc default.script dhcpcd
fi
# NOTE: dhcpcd -k waits for the signalled process to die, but busybox's killall doesn't have a -w, --wait flag,
# so we have to wait for udhcpc to die ourselves...
kill_timeout=0
while pkill -0 udhcpc; do
# Stop waiting after 5s
if [ ${kill_timeout} -ge 20 ]; then
break
fi
usleep 250000
kill_timeout=$((kill_timeout + 1))
done
new_hash="$(md5sum "/etc/resolv.conf" | cut -f1 -d' ')"
# Restore our network-specific resolv.conf if the DHCP client wiped it when releasing the lease...
if [ "${new_hash}" != "${old_hash}" ]; then
mv -f "/tmp/resolv.ko" "/etc/resolv.conf"
else
rm -f "/tmp/resolv.ko"
fi
wpa_cli terminate
[ "${WIFI_MODULE}" != "8189fs" ] && [ "${WIFI_MODULE}" != "8192es" ] && wlarm_le -i "${INTERFACE}" down
ifconfig "${INTERFACE}" down
# NOTE: Kobo's busybox build is weird. rmmod appears to be modprobe in disguise, defaulting to the -r flag...

@ -2,5 +2,10 @@
./release-ip.sh
# Use udhcpc to obtain IP.
env -u LD_LIBRARY_PATH udhcpc -S -i "${INTERFACE}" -s /etc/udhcpc.d/default.script -t15 -T10 -A3 -b -q
# NOTE: Prefer dhcpcd over udhcpc if available. That's what Nickel uses,
# and udhcpc appears to trip some insanely wonky corner cases on current FW (#6421)
if [ -x "/sbin/dhcpcd" ]; then
env -u LD_LIBRARY_PATH dhcpcd -d -t 30 -w "${INTERFACE}"
else
env -u LD_LIBRARY_PATH udhcpc -S -i "${INTERFACE}" -s /etc/udhcpc.d/default.script -b -q
fi

@ -1,8 +1,34 @@
#!/bin/sh
# PATH export is only needed if you run this script manually from a shell
export PATH="${PATH}:/sbin"
# Release IP and shutdown udhcpc.
pkill -9 -f '/bin/sh /etc/udhcpc.d/default.script'
ifconfig "${INTERFACE}" 0.0.0.0
# NOTE: Save our resolv.conf to avoid ending up with an empty one, in case the DHCP client wipes it on release (#6424).
cp -a "/etc/resolv.conf" "/tmp/resolv.ko"
old_hash="$(md5sum "/etc/resolv.conf" | cut -f1 -d' ')"
if [ -x "/sbin/dhcpcd" ]; then
env -u LD_LIBRARY_PATH dhcpcd -d -k "${INTERFACE}"
killall -q -TERM udhcpc default.script
else
killall -q -TERM udhcpc default.script dhcpcd
ifconfig "${INTERFACE}" 0.0.0.0
fi
# NOTE: dhcpcd -k waits for the signalled process to die, but busybox's killall doesn't have a -w, --wait flag,
# so we have to wait for udhcpc to die ourselves...
kill_timeout=0
while pkill -0 udhcpc; do
# Stop waiting after 5s
if [ ${kill_timeout} -ge 20 ]; then
break
fi
usleep 250000
kill_timeout=$((kill_timeout + 1))
done
new_hash="$(md5sum "/etc/resolv.conf" | cut -f1 -d' ')"
# Restore our network-specific resolv.conf if the DHCP client wiped it when releasing the lease...
if [ "${new_hash}" != "${old_hash}" ]; then
mv -f "/tmp/resolv.ko" "/etc/resolv.conf"
else
rm -f "/tmp/resolv.ko"
fi

@ -1,7 +1,7 @@
#!/bin/sh
RestoreWifi() {
echo "[$(date)] restore-wifi-async.sh: Restarting WiFi"
echo "[$(date)] restore-wifi-async.sh: Restarting Wi-Fi"
./enable-wifi.sh
@ -9,8 +9,8 @@ RestoreWifi() {
# Pilfered from https://github.com/shermp/Kobo-UNCaGED/pull/21 ;)
wpac_timeout=0
while ! wpa_cli status | grep -q "wpa_state=COMPLETED"; do
# If wpa_supplicant hasn't connected within 10 seconds, assume it never will, and tear down WiFi
if [ ${wpac_timeout} -ge 40 ]; then
# If wpa_supplicant hasn't connected within 15 seconds, assume it never will, and tear down Wi-Fi
if [ ${wpac_timeout} -ge 60 ]; then
echo "[$(date)] restore-wifi-async.sh: Failed to connect to preferred AP!"
./disable-wifi.sh
return 1
@ -21,7 +21,7 @@ RestoreWifi() {
./obtain-ip.sh
echo "[$(date)] restore-wifi-async.sh: Restarted WiFi"
echo "[$(date)] restore-wifi-async.sh: Restarted Wi-Fi"
}
RestoreWifi &

@ -1,6 +1,6 @@
# General
When connected to WiFi you can find the IP address and root password for your
When connected to Wi-Fi you can find the IP address and root password for your
reMarkable in Settings -> About, then scroll down the GPLv3 compliance on the
right (finger drag scroll, not the page forward/back buttons). This should also
work for the USB network you get if you connect the reMarkable to your computer

@ -178,11 +178,11 @@ while [ ${RETURN_VALUE} -ne 0 ]; do
fi
# U+1F4A3, the hard way, because we can't use \u or \U escape sequences...
# shellcheck disable=SC2039
./fbink -q -b -O -m -t regular=./fonts/freefont/FreeSerif.ttf,px=${bombHeight},top=${bombMargin} $'\xf0\x9f\x92\xa3'
./fbink -q -b -O -m -t regular=./fonts/freefont/FreeSerif.ttf,px=${bombHeight},top=${bombMargin} -- $'\xf0\x9f\x92\xa3'
# And then print the tail end of the log on the bottom of the screen...
crashLog="$(tail -n 25 crash.log | sed -e 's/\t/ /g')"
# The idea for the margins being to leave enough room for an fbink -Z bar, small horizontal margins, and a font size based on what 6pt looked like @ 265dpi
./fbink -q -b -O -t regular=./fonts/droid/DroidSansMono.ttf,top=$((viewHeight / 2 + FONTH * 2 + FONTH / 2)),left=$((viewWidth / 60)),right=$((viewWidth / 60)),px=$((viewHeight / 64)) "${crashLog}"
./fbink -q -b -O -t regular=./fonts/droid/DroidSansMono.ttf,top=$((viewHeight / 2 + FONTH * 2 + FONTH / 2)),left=$((viewWidth / 60)),right=$((viewWidth / 60)),px=$((viewHeight / 64)) -- "${crashLog}"
# So far, we hadn't triggered an actual screen refresh, do that now, to make sure everything is bundled in a single flashing refresh.
./fbink -q -f -s
# Cue a lemming's faceplant sound effect!

@ -5,9 +5,9 @@ if [ "$1" = "on" ]; then
wmiconfig -i wlan0 --wlan enable
wmiconfig -i wlan0 --setreassocmode 0
wmiconfig -i wlan0 --power maxperf
echo "WiFi Enabled"
echo "Wi-Fi Enabled"
else
wmiconfig -i wlan0 --abortscan
wmiconfig -i wlan0 --wlan disable
echo "Wifi Disabled"
echo "Wi-Fi Disabled"
fi

@ -2,7 +2,7 @@
set -x
# disable WiFi
# disable Wi-Fi
./set-wifi.sh off
# enter sleep, disabling all devices except CPU

@ -203,6 +203,10 @@ Do you want to continue? ]]), driver),
end
function CalibreWireless:connect()
if NetworkMgr:willRerunWhenConnected(function() self:connect() end) then
return
end
self.connect_message = false
local host, port
if G_reader_settings:hasNot("calibre_wireless_url") then
@ -231,8 +235,6 @@ function CalibreWireless:connect()
else
self:setInboxDir(host, port)
end
elseif not NetworkMgr:isConnected() then
NetworkMgr:promptWifiOn()
else
logger.info("cannot connect to calibre server")
UIManager:show(InfoMessage:new{

@ -347,10 +347,10 @@ For more information, please visit https://github.com/koreader/koreader/wiki/Eve
end
function EvernoteExporter:login()
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
if NetworkMgr:willRerunWhenOnline(function() self:login() end) then
return
end
self.login_dialog = LoginDialog:new{
title = self.login_title,
username = self.evernote_username or "",
@ -410,7 +410,7 @@ function EvernoteExporter:doLogin(username, password)
}
self.evernote_username = username
local ok, token = pcall(oauth.getToken, oauth)
-- prompt users to turn on Wifi if network is unreachable
-- prompt users to turn on Wi-Fi if network is unreachable
if not ok and token then
UIManager:show(InfoMessage:new{
text = _("An error occurred while logging in:") .. "\n" .. token,
@ -425,7 +425,8 @@ function EvernoteExporter:doLogin(username, password)
local guid
ok, guid = pcall(self.getExportNotebook, self, client)
if not ok and guid and guid:find("Transport not open") then
NetworkMgr:promptWifiOn()
--- @note: No recursive callback because it feels fishy here...
NetworkMgr:beforeWifiAction()
return
elseif not ok and guid then
UIManager:show(InfoMessage:new{
@ -589,7 +590,7 @@ function EvernoteExporter:exportClippings(clippings)
end
-- check if booknotes are exported in this notebook
-- so that booknotes will still be exported after switching user account
--Don't respect exported_stamp on txt export since it isn't possible to delete(update) prior clippings.
-- Don't respect exported_stamp on txt export since it isn't possible to delete(update) prior clippings.
if booknotes.exported[exported_stamp] ~= true or self.txt_export or self.json_export then
local ok, err
if self.html_export then
@ -603,9 +604,10 @@ function EvernoteExporter:exportClippings(clippings)
else
ok, err = pcall(self.exportBooknotesToEvernote, self, client, title, booknotes)
end
-- error reporting
-- Error reporting
if not ok and err and err:find("Transport not open") then
NetworkMgr:promptWifiOn()
--- @note: No recursive callback because it feels fishy here...
NetworkMgr:beforeWifiAction()
return
elseif not ok and err then
logger.dbg("Error while exporting book", title, err)

@ -170,16 +170,16 @@ end
-- search_type = author - serch book by author
-- search_type = title - search book by title
function Goodreads:search(search_type)
if NetworkMgr:willRerunWhenOnline(function() self:search(search_type) end) then
return
end
local title_header
local hint
local search_input
local text_input
local info
local result
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
return
end
if search_type == "all" then
title_header = _("Search all books in Goodreads")
hint = _("Title, author or ISBN")

@ -295,10 +295,10 @@ function KOSync:setWhisperBackward(strategy)
end
function KOSync:login()
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
if NetworkMgr:willRerunWhenOnline(function() self:login() end) then
return
end
self.login_dialog = LoginDialog:new{
title = self.title,
username = self.kosync_username or "",

@ -22,7 +22,6 @@ local NewsDownloader = WidgetContainer:new{
}
local initialized = false
local wifi_enabled_before_action = true
local feed_config_file_name = "feed_config.lua"
local news_downloader_config_file = "news_downloader_settings.lua"
local news_downloader_settings
@ -59,13 +58,6 @@ local function getFeedLink(possible_link)
end
end
--- @todo Implement as NetworkMgr:afterWifiAction with configuration options.
function NewsDownloader:afterWifiAction()
if not wifi_enabled_before_action then
NetworkMgr:promptWifiOff()
end
end
function NewsDownloader:init()
self.ui.menu:registerToMainMenu(self)
end
@ -79,12 +71,7 @@ function NewsDownloader:addToMainMenu(menu_items)
text = _("Download news"),
keep_menu_open = true,
callback = function()
if not NetworkMgr:isOnline() then
wifi_enabled_before_action = false
NetworkMgr:beforeWifiAction(self.loadConfigAndProcessFeedsWithUI)
else
self:loadConfigAndProcessFeedsWithUI()
end
NetworkMgr:runWhenOnline(function() self:loadConfigAndProcessFeedsWithUI() end)
end,
},
{
@ -222,7 +209,7 @@ function NewsDownloader:loadConfigAndProcessFeeds()
end
UI:info(T(_("Downloading news finished. Could not process some feeds. Unsupported format in: %1"), unsupported_urls))
end
NewsDownloader:afterWifiAction()
NetworkMgr:afterWifiAction()
end
function NewsDownloader:loadConfigAndProcessFeedsWithUI()

@ -21,7 +21,6 @@ local Send2Ebook = WidgetContainer:new{
}
local initialized = false
local wifi_enabled_before_action = true
local send2ebook_config_file = "send2ebook_settings.lua"
local config_key_custom_dl_dir = "custom_dl_dir";
local default_download_dir_name = "send2ebook"
@ -45,13 +44,6 @@ function Send2Ebook:downloadFileAndRemove(connection_url, remote_path, local_dow
end
end
--- @todo Implement as NetworkMgr:afterWifiAction with configuration options.
function Send2Ebook:afterWifiAction()
if not wifi_enabled_before_action then
NetworkMgr:promptWifiOff()
end
end
function Send2Ebook:init()
self.ui.menu:registerToMainMenu(self)
end
@ -65,12 +57,10 @@ function Send2Ebook:addToMainMenu(menu_items)
text = _("Download and remove from server"),
keep_menu_open = true,
callback = function()
if not NetworkMgr:isOnline() then
wifi_enabled_before_action = false
NetworkMgr:beforeWifiAction(self.process)
else
self:process()
end
local connect_callback = function()
self:process()
end
NetworkMgr:runWhenOnline(connect_callback)
end,
},
{
@ -171,7 +161,7 @@ function Send2Ebook:process()
end
end
UIManager:show(info)
Send2Ebook:afterWifiAction()
NetworkMgr:afterWifiAction()
end
function Send2Ebook:removeReadActicles()

@ -34,7 +34,7 @@ local function currentTime()
return _("Time synchronized.")
end
local function execute()
local function syncNTP()
local info = InfoMessage:new{
text = _("Synchronizing time. This may take several seconds.")
}
@ -58,11 +58,7 @@ local menuItem = {
text = _("Synchronize time"),
keep_menu_open = true,
callback = function()
if NetworkMgr:isOnline() then
execute()
else
NetworkMgr:promptWifiOn()
end
NetworkMgr:runWhenOnline(function() syncNTP() end)
end
}

@ -112,15 +112,14 @@ function Wallabag:addToMainMenu(menu_items)
{
text = _("Delete finished articles remotely"),
callback = function()
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
return
local connect_callback = function()
local num_deleted = self:processLocalFiles("manual")
UIManager:show(InfoMessage:new{
text = T(_("Articles processed.\nDeleted: %1"), num_deleted)
})
self:refreshCurrentDirIfNeeded()
end
local num_deleted = self:processLocalFiles("manual")
UIManager:show(InfoMessage:new{
text = T(_("Articles processed.\nDeleted: %1"), num_deleted)
})
self:refreshCurrentDirIfNeeded()
NetworkMgr:runWhenOnline(connect_callback)
end,
enabled_func = function()
return self.is_delete_finished or self.is_delete_read
@ -1078,12 +1077,11 @@ function Wallabag:onAddWallabagArticle(article_url)
end
function Wallabag:onSynchronizeWallabag()
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
return
local connect_callback = function()
self:synchronize()
self:refreshCurrentDirIfNeeded()
end
self:synchronize()
self:refreshCurrentDirIfNeeded()
NetworkMgr:runWhenOnline(connect_callback)
-- stop propagation
return true

@ -41,15 +41,14 @@ function ZSync:addToMainMenu(menu_items)
end,
callback = function()
local NetworkMgr = require("ui/network/manager")
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
return
end
if not self.filemq_server then
self:publish()
else
self:unpublish()
local connect_callback = function()
if not self.filemq_server then
self:publish()
else
self:unpublish()
end
end
NetworkMgr:runWhenOnline(connect_callback)
end
},
{
@ -63,15 +62,14 @@ function ZSync:addToMainMenu(menu_items)
end,
callback = function()
local NetworkMgr = require("ui/network/manager")
if not NetworkMgr:isOnline() then
NetworkMgr:promptWifiOn()
return
end
if not self.filemq_client then
self:subscribe()
else
self:unsubscribe()
local connect_callback = function()
if not self.filemq_client then
self:subscribe()
else
self:unsubscribe()
end
end
NetworkMgr:runWhenOnline(connect_callback)
end
}
}

@ -12,7 +12,7 @@ describe("NetworkSetting module", function()
assert.is.falsy(ns.connected_item)
end)
it("should call connect_callback after disconnect", function()
it("should NOT call connect_callback after disconnect", function()
stub(NetworkMgr, "disconnectNetwork")
stub(NetworkMgr, "releaseIP")
@ -33,6 +33,33 @@ describe("NetworkSetting module", function()
connect_callback = function() called = true end
}
ns.connected_item:disconnect()
assert.falsy(called)
NetworkMgr.disconnectNetwork:revert()
NetworkMgr.releaseIP:revert()
end)
it("should call disconnect_callback after disconnect", function()
stub(NetworkMgr, "disconnectNetwork")
stub(NetworkMgr, "releaseIP")
UIManager:quit()
local called = false
local network_list = {
{
ssid = "foo",
signal_level = -58,
flags = "[WPA2-PSK-CCMP][ESS]",
signal_quality = 84,
password = "123abc",
connected = true,
},
}
local ns = NetworkSetting:new{
network_list = network_list,
disconnect_callback = function() called = true end
}
ns.connected_item:disconnect()
assert.truthy(called)
NetworkMgr.disconnectNetwork:revert()

Loading…
Cancel
Save