From bc5da5276dc56481eec124d5df1b8a6ee7b30aef Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Sun, 31 Oct 2021 23:52:08 +0100 Subject: [PATCH] NetworkManager: Always prefer system APs as intended. If we had a local prefered AP with a higher RSSI, we attempted to associate with it over wpa_supplicant being already attempting to associate with its own preferred AP. That... failed horribly. Also adapt to the new lj-wpaclient API, fixing a few other edge-cases, and making the whole thing slightly faster because we no longer uselessly sleep. And more reliable because we now actually wait for replies to our requests. Bump base https://github.com/koreader/koreader-base/pull/1424 --- base | 2 +- frontend/ui/network/manager.lua | 49 +++++++++++++---- frontend/ui/network/wpa_supplicant.lua | 75 ++++++++++++++++++-------- 3 files changed, 92 insertions(+), 34 deletions(-) diff --git a/base b/base index 5c8fa477f..e0643c89f 160000 --- a/base +++ b/base @@ -1 +1 @@ -Subproject commit 5c8fa477f8abca437950420e08efa3d2ef0da1bf +Subproject commit e0643c89f924718cf04b8e56502e68e172a355dd diff --git a/frontend/ui/network/manager.lua b/frontend/ui/network/manager.lua index 67c7cddd9..a83b3a543 100644 --- a/frontend/ui/network/manager.lua +++ b/frontend/ui/network/manager.lua @@ -599,6 +599,7 @@ function NetworkMgr:reconnectOrShowNetworkMenu(complete_callback) -- rescan if the first scan appeared to yield an empty list. --- @fixme This *might* be an issue better handled in lj-wpaclient... if #network_list == 0 then + logger.warn("Initial Wi-Fi scan yielded no results, rescanning") network_list, err = self:getNetworkList() if network_list == nil then UIManager:show(InfoMessage:new{text = err}) @@ -613,27 +614,53 @@ function NetworkMgr:reconnectOrShowNetworkMenu(complete_callback) if self.wifi_toggle_long_press then self.wifi_toggle_long_press = nil else + local ssid + -- We need to do two passes, as we may have *both* an already connected network (from the global wpa config), + -- *and* preferred networks, and if the prferred networks have a better signal quality, + -- they'll be sorted *earlier*, which would cause us to try to associate to a different AP than + -- what wpa_supplicant is already trying to do... for dummy, network in ipairs(network_list) do if network.connected then -- On platforms where we use wpa_supplicant (if we're calling this, we are), -- the invocation will check its global config, and if an AP configured there is reachable, -- it'll already have connected to it on its own. success = true - elseif network.password then - success = NetworkMgr:authenticateNetwork(network) + ssid = network.ssid + break end - if success then - NetworkMgr:obtainIP() - if complete_callback then - complete_callback() + end + + -- Next, look for our own prferred networks... + local err_msg = _("Connection failed") + if not success then + for dummy, network in ipairs(network_list) do + if network.password then + -- If we hit a preferred network and we're not already connected, + -- attempt to connect to said preferred network.... + success, err_msg = NetworkMgr:authenticateNetwork(network) + if success then + ssid = network.ssid + break + end end - UIManager:show(InfoMessage:new{ - text = T(_("Connected to network %1"), BD.wrap(network.ssid)), - timeout = 3, - }) - break end end + + if success then + NetworkMgr:obtainIP() + if complete_callback then + complete_callback() + end + UIManager:show(InfoMessage:new{ + text = T(_("Connected to network %1"), BD.wrap(ssid)), + timeout = 3, + }) + else + UIManager:show(InfoMessage:new{ + text = err_msg, + timeout = 3, + }) + end end if not success then -- NOTE: Also supports a disconnect_callback, should we use it for something? diff --git a/frontend/ui/network/wpa_supplicant.lua b/frontend/ui/network/wpa_supplicant.lua index 86a0843e3..bf183b53a 100644 --- a/frontend/ui/network/wpa_supplicant.lua +++ b/frontend/ui/network/wpa_supplicant.lua @@ -4,7 +4,7 @@ WPA client helper for Kobo. local FFIUtil = require("ffi/util") local InfoMessage = require("ui/widget/infomessage") -local WpaClient = require('lj-wpaclient/wpaclient') +local WpaClient = require("lj-wpaclient/wpaclient") local UIManager = require("ui/uimanager") local _ = require("gettext") local T = FFIUtil.template @@ -20,13 +20,17 @@ function WpaSupplicant:getNetworkList() return nil, T(CLIENT_INIT_ERR_MSG, err) end - local list = wcli:scanThenGetResults() + local list + list, err = wcli:scanThenGetResults() wcli:close() + if list == nil then + return nil, T("An error occurred while scanning: %1.", err) + end local saved_networks = self:getAllSavedNetworks() local curr_network = self:getCurrentNetwork() - for _,network in ipairs(list) do + for _, network in ipairs(list) do network.signal_quality = network:getSignalQuality() local saved_nw = saved_networks:readSetting(network.ssid) if saved_nw then @@ -58,27 +62,30 @@ end --- Authenticates network. function WpaSupplicant:authenticateNetwork(network) - local err, wcli, nw_id + local wcli, reply, err wcli, err = WpaClient.new(self.wpa_supplicant.ctrl_interface) if not wcli then return false, T(CLIENT_INIT_ERR_MSG, err) end - nw_id, err = wcli:addNetwork() - if err then return false, err end + reply, err = wcli:addNetwork() + if reply == nil then + return false, err + end + local nw_id = reply - local re = wcli:setNetwork(nw_id, "ssid", string.format("\"%s\"", network.ssid)) - if re == "FAIL" then + reply, err = wcli:setNetwork(nw_id, "ssid", string.format("\"%s\"", network.ssid)) + if reply == nil or reply == "FAIL" then wcli:removeNetwork(nw_id) - return false, _("An error occurred while selecting network.") + return false, T("An error occurred while selecting network: %1.", err) end -- if password is empty it’s an open AP if network.password and #network.password == 0 then -- Open AP - re = wcli:setNetwork(nw_id, "key_mgmt", "NONE") - if re == "FAIL" then + reply, err = wcli:setNetwork(nw_id, "key_mgmt", "NONE") + if reply == nil or reply == "FAIL" then wcli:removeNetwork(nw_id) - return false, _("An error occurred while setting passwordless mode.") + return false, T("An error occurred while setting passwordless mode: %1.", err) end -- else it’s a WPA AP else @@ -86,10 +93,10 @@ function WpaSupplicant:authenticateNetwork(network) network.psk = calculatePsk(network.ssid, network.password) self:saveNetwork(network) end - re = wcli:setNetwork(nw_id, "psk", network.psk) - if re == "FAIL" then + reply, err = wcli:setNetwork(nw_id, "psk", network.psk) + if reply == nil or reply == "FAIL" then wcli:removeNetwork(nw_id) - return false, _("An error occurred while setting password.") + return false, T("An error occurred while setting password: %1.", err) end end wcli:enableNetworkByID(nw_id) @@ -99,10 +106,32 @@ function WpaSupplicant:authenticateNetwork(network) local failure_cnt = 0 local max_retry = 30 local info = InfoMessage:new{text = _("Authenticating…")} - local msg + local success = false + local msg = _("Authenticated") UIManager:show(info) UIManager:forceRePaint() while cnt < max_retry do + -- Start by checking if we're not actually connected already... + -- NOTE: This is mainly to catch corner-cases where our preferred network list differs from the system's, + -- and ours happened to be sorted earlier because of a better signal quality... + local connected, state = wcli:getConnectedNetwork() + if connected then + network.wpa_supplicant_id = connected.id + network.ssid = connected.ssid + success = true + break + else + if state then + UIManager:close(info) + -- Make the state prettier + local first, rest = state:sub(1, 1), state:sub(2) + info = InfoMessage:new{text = string.upper(first) .. string.lower(rest) .. "…"} + UIManager:show(info) + UIManager:forceRePaint() + end + end + + -- Otherwise, poke at the wpa_supplicant socket for a bit... local ev = wcli:readEvent() if ev ~= nil then if not ev:isScanEvent() then @@ -113,28 +142,30 @@ function WpaSupplicant:authenticateNetwork(network) end if ev:isAuthSuccessful() then network.wpa_supplicant_id = nw_id - re = true + success = true break elseif ev:isAuthFailed() then failure_cnt = failure_cnt + 1 if failure_cnt > 3 then - re, msg = false, _("Failed to authenticate") + success, msg = false, _("Failed to authenticate") break end end else - FFIUtil.sleep(1) + wcli:waitForEvent(1 * 1000) cnt = cnt + 1 end end - if re ~= true then wcli:removeNetwork(nw_id) end + if success ~= true then + wcli:removeNetwork(nw_id) + end wcli:close() UIManager:close(info) UIManager:forceRePaint() if cnt >= max_retry then - re, msg = false, _("Timed out") + success, msg = false, _("Timed out") end - return re, msg + return success, msg end function WpaSupplicant:disconnectNetwork(network)