network: convert D-Bus refresh calls to async

- Convert refreshAccessPoints, refreshVpnConnections, and
  refreshSavedConnections from synchronous to async using
  callMethodAsync/uponReplyInvoke with shared_ptr lifetime management
- Fix activateVpnConnection to use callMethodAsync to avoid blocking
  the main event loop
- Fix proxy lifetimes: wrap locally-created sdbus::IProxy objects in
  shared_ptr and capture in reply lambdas so they outlive the call
- Fix GetAll calls to use org.freedesktop.DBus.Properties interface
  with the target interface as an argument
- Add activating-state detection so VPN button updates immediately
- Fix -Wconversion and -Wshadow compiler warnings
This commit is contained in:
Mathew-D
2026-05-09 21:54:06 -04:00
parent f15d226e73
commit 2bea2e0bfc
2 changed files with 407 additions and 194 deletions
+403 -191
View File
@@ -97,6 +97,15 @@ namespace {
return {}; return {};
} }
// Tracks in-flight async refresh operations so we only emit state changes after all complete.
struct PendingRefresh {
std::vector<AccessPointInfo> capturedAps;
std::vector<VpnConnectionInfo> capturedVpns;
std::vector<std::string> capturedSaved;
std::atomic<int> pendingOps{0};
std::function<void()> onAllComplete;
};
} // namespace } // namespace
const char* NetworkService::glyphForState(const NetworkState& state) noexcept { const char* NetworkService::glyphForState(const NetworkState& state) noexcept {
@@ -201,24 +210,37 @@ NetworkService::~NetworkService() = default;
void NetworkService::setChangeCallback(ChangeCallback callback) { m_changeCallback = std::move(callback); } void NetworkService::setChangeCallback(ChangeCallback callback) { m_changeCallback = std::move(callback); }
void NetworkService::refresh() { void NetworkService::refresh() {
const auto previousAps = m_accessPoints; auto pending = std::make_shared<PendingRefresh>();
const auto previousVpns = m_vpnConnections; pending->capturedAps = m_accessPoints;
const auto previousSaved = m_savedSsids; pending->capturedVpns = m_vpnConnections;
refreshAccessPoints(); pending->capturedSaved = m_savedSsids;
refreshVpnConnections(); pending->pendingOps = 3;
refreshSavedConnections();
NetworkState next = readState(); pending->onAllComplete = [this, pending]() {
const bool apsChanged = previousAps != m_accessPoints; NetworkState next = readState();
const bool vpnsChanged = previousVpns != m_vpnConnections; const bool apsChanged = pending->capturedAps != m_accessPoints;
const bool savedChanged = previousSaved != m_savedSsids; const bool vpnsChanged = pending->capturedVpns != m_vpnConnections;
const bool stateChanged = next != m_state; const bool savedChanged = pending->capturedSaved != m_savedSsids;
const bool wirelessEnabledChanged = next.wirelessEnabled != m_state.wirelessEnabled; const bool stateChanged = next != m_state;
const NetworkChangeOrigin origin = const bool wirelessEnabledChanged = next.wirelessEnabled != m_state.wirelessEnabled;
wirelessEnabledChanged ? consumeWirelessEnabledChangeOrigin(next.wirelessEnabled) : NetworkChangeOrigin::External; const NetworkChangeOrigin origin =
m_state = std::move(next); wirelessEnabledChanged ? consumeWirelessEnabledChangeOrigin(next.wirelessEnabled)
if ((stateChanged || apsChanged || vpnsChanged || savedChanged) && m_changeCallback) { : NetworkChangeOrigin::External;
m_changeCallback(m_state, origin); m_state = std::move(next);
} if ((stateChanged || apsChanged || vpnsChanged || savedChanged) && m_changeCallback) {
m_changeCallback(m_state, origin);
}
};
auto onOpComplete = [pending]() {
if (--pending->pendingOps == 0) {
pending->onAllComplete();
}
};
refreshAccessPoints(onOpComplete);
refreshVpnConnections(onOpComplete);
refreshSavedConnections(onOpComplete);
} }
void NetworkService::requestScan() { void NetworkService::requestScan() {
@@ -558,138 +580,220 @@ bool NetworkService::hasSavedConnection(const std::string& ssid) const {
return std::find(m_savedSsids.begin(), m_savedSsids.end(), ssid) != m_savedSsids.end(); return std::find(m_savedSsids.begin(), m_savedSsids.end(), ssid) != m_savedSsids.end();
} }
void NetworkService::refreshSavedConnections() { void NetworkService::refreshSavedConnections(std::function<void()> onComplete) {
std::vector<std::string> next;
try { try {
auto settings = sdbus::createProxy(m_bus.connection(), k_nmBusName, k_nmSettingsObjectPath); auto settings =
std::vector<sdbus::ObjectPath> connectionPaths; std::shared_ptr<sdbus::IProxy>(sdbus::createProxy(m_bus.connection(), k_nmBusName, k_nmSettingsObjectPath));
settings->callMethod("ListConnections").onInterface(k_nmSettingsInterface).storeResultsTo(connectionPaths); settings->callMethodAsync("ListConnections")
for (const auto& connectionPath : connectionPaths) { .onInterface(k_nmSettingsInterface)
try { .uponReplyInvoke([this, settings, onComplete](std::optional<sdbus::Error> err,
auto connection = sdbus::createProxy(m_bus.connection(), k_nmBusName, connectionPath); std::vector<sdbus::ObjectPath> connectionPaths) {
std::map<std::string, std::map<std::string, sdbus::Variant>> cfg; if (err.has_value()) {
connection->callMethod("GetSettings").onInterface(k_nmSettingsConnectionInterface).storeResultsTo(cfg); kLog.debug("refreshSavedConnections ListConnections failed: {}", err->what());
auto wifiIt = cfg.find("802-11-wireless"); if (onComplete)
if (wifiIt == cfg.end()) { onComplete();
continue; return;
}
auto ssidIt = wifiIt->second.find("ssid");
if (ssidIt == wifiIt->second.end()) {
continue;
}
try {
const auto bytes = ssidIt->second.get<std::vector<std::uint8_t>>();
std::string ssid(bytes.begin(), bytes.end());
if (!ssid.empty()) {
next.push_back(std::move(ssid));
} }
} catch (const sdbus::Error&) {
}
} catch (const sdbus::Error&) {
}
}
} catch (const sdbus::Error& e) {
kLog.debug("refreshSavedConnections: {}", e.what());
}
std::ranges::sort(next);
next.erase(std::unique(next.begin(), next.end()), next.end());
m_savedSsids = std::move(next);
}
void NetworkService::refreshVpnConnections() { if (connectionPaths.empty()) {
std::vector<VpnConnectionInfo> next; m_savedSsids.clear();
std::set<std::string> vpnProfilePaths; if (onComplete)
try { onComplete();
auto settings = sdbus::createProxy(m_bus.connection(), k_nmBusName, k_nmSettingsObjectPath); return;
std::vector<sdbus::ObjectPath> connectionPaths;
settings->callMethod("ListConnections").onInterface(k_nmSettingsInterface).storeResultsTo(connectionPaths);
for (const auto& connectionPath : connectionPaths) {
try {
auto connection = sdbus::createProxy(m_bus.connection(), k_nmBusName, connectionPath);
std::map<std::string, std::map<std::string, sdbus::Variant>> cfg;
connection->callMethod("GetSettings").onInterface(k_nmSettingsConnectionInterface).storeResultsTo(cfg);
auto connIt = cfg.find("connection");
if (connIt == cfg.end()) {
continue;
}
auto typeIt = connIt->second.find("type");
if (typeIt == connIt->second.end()) {
continue;
}
std::string type;
try {
type = typeIt->second.get<std::string>();
} catch (const sdbus::Error&) {
continue;
}
const bool hasVpnSection = cfg.contains("vpn");
const bool vpnLikeType = type == "vpn" || type == "wireguard";
if (!vpnLikeType && !hasVpnSection) {
continue;
}
VpnConnectionInfo info;
info.path = std::string(connectionPath);
auto idIt = connIt->second.find("id");
if (idIt != connIt->second.end()) {
try {
info.name = idIt->second.get<std::string>();
} catch (const sdbus::Error&) {
} }
}
if (info.name.empty()) {
info.name = info.path;
}
info.active = false;
vpnProfilePaths.insert(info.path);
next.push_back(std::move(info));
} catch (const sdbus::Error&) {
}
}
} catch (const sdbus::Error& e) {
kLog.debug("refreshVpnConnections: {}", e.what());
}
if (!next.empty()) { auto opState = std::make_shared<std::pair<std::vector<std::string>, std::atomic<int>>>(
try { std::make_pair(std::vector<std::string>{}, static_cast<int>(connectionPaths.size())));
std::vector<sdbus::ObjectPath> activeConnections;
const sdbus::Variant activeVar = m_nm->getProperty("ActiveConnections").onInterface(k_nmInterface); for (const auto& connectionPath : connectionPaths) {
activeConnections = activeVar.get<std::vector<sdbus::ObjectPath>>(); try {
for (const auto& activePath : activeConnections) { auto connection = std::shared_ptr<sdbus::IProxy>(
try { sdbus::createProxy(m_bus.connection(), k_nmBusName, connectionPath));
auto active = sdbus::createProxy(m_bus.connection(), k_nmBusName, activePath); connection->callMethodAsync("GetSettings")
const auto state = getPropertyOr<std::uint32_t>(*active, k_nmActiveConnectionInterface, "State", 0U); .onInterface(k_nmSettingsConnectionInterface)
if (state != k_nmActiveConnectionStateActivating && state != k_nmActiveConnectionStateActivated) { .uponReplyInvoke([this, connection, opState, onComplete](
continue; std::optional<sdbus::Error> settingsErr,
} std::map<std::string, std::map<std::string, sdbus::Variant>> cfg) {
const auto profilePath = getPropertyOr<sdbus::ObjectPath>(*active, k_nmActiveConnectionInterface, if (!settingsErr.has_value()) {
"Connection", sdbus::ObjectPath{}); auto wifiIt = cfg.find("802-11-wireless");
const std::string profilePathStr = std::string(profilePath); if (wifiIt != cfg.end()) {
if (!vpnProfilePaths.contains(profilePathStr)) { auto ssidIt = wifiIt->second.find("ssid");
continue; if (ssidIt != wifiIt->second.end()) {
} try {
for (auto& vpn : next) { const auto bytes = ssidIt->second.get<std::vector<std::uint8_t>>();
if (vpn.path == profilePathStr) { std::string ssid(bytes.begin(), bytes.end());
vpn.active = true; if (!ssid.empty()) {
break; opState->first.push_back(std::move(ssid));
}
} catch (const sdbus::Error&) {
}
}
}
}
if (--opState->second == 0) {
std::ranges::sort(opState->first);
opState->first.erase(std::unique(opState->first.begin(), opState->first.end()),
opState->first.end());
m_savedSsids = std::move(opState->first);
if (onComplete)
onComplete();
}
});
} catch (const sdbus::Error&) {
if (--opState->second == 0) {
std::ranges::sort(opState->first);
opState->first.erase(std::unique(opState->first.begin(), opState->first.end()),
opState->first.end());
m_savedSsids = std::move(opState->first);
if (onComplete)
onComplete();
}
} }
} }
} catch (const sdbus::Error&) { });
} } catch (const sdbus::Error& e) {
} kLog.debug("refreshSavedConnections: {}", e.what());
} catch (const sdbus::Error& e) { if (onComplete)
kLog.debug("refreshVpnConnections active list: {}", e.what()); onComplete();
}
} }
}
std::ranges::sort(next, [](const VpnConnectionInfo& a, const VpnConnectionInfo& b) { void NetworkService::refreshVpnConnections(std::function<void()> onComplete) {
if (a.active != b.active) { try {
return a.active; auto settings =
} std::shared_ptr<sdbus::IProxy>(sdbus::createProxy(m_bus.connection(), k_nmBusName, k_nmSettingsObjectPath));
return a.name < b.name; settings->callMethodAsync("ListConnections")
}); .onInterface(k_nmSettingsInterface)
m_vpnConnections = std::move(next); .uponReplyInvoke([this, settings, onComplete](std::optional<sdbus::Error> err,
std::vector<sdbus::ObjectPath> connectionPaths) {
if (err.has_value()) {
kLog.debug("refreshVpnConnections ListConnections failed: {}", err->what());
if (onComplete)
onComplete();
return;
}
if (connectionPaths.empty()) {
m_vpnConnections.clear();
if (onComplete)
onComplete();
return;
}
auto opState = std::make_shared<std::pair<
std::pair<std::vector<VpnConnectionInfo>, std::set<std::string>>,
std::atomic<int>>>(std::make_pair(
std::make_pair(std::vector<VpnConnectionInfo>{}, std::set<std::string>{}),
static_cast<int>(connectionPaths.size())));
for (const auto& connectionPath : connectionPaths) {
try {
auto connection = std::shared_ptr<sdbus::IProxy>(
sdbus::createProxy(m_bus.connection(), k_nmBusName, connectionPath));
connection->callMethodAsync("GetSettings")
.onInterface(k_nmSettingsConnectionInterface)
.uponReplyInvoke([this, connection, opState, connectionPath, onComplete](
std::optional<sdbus::Error> getErr,
std::map<std::string, std::map<std::string, sdbus::Variant>> cfg) {
if (!getErr.has_value()) {
auto connIt = cfg.find("connection");
if (connIt != cfg.end()) {
auto typeIt = connIt->second.find("type");
if (typeIt != connIt->second.end()) {
try {
const auto type = typeIt->second.get<std::string>();
const bool hasVpnSection = cfg.contains("vpn");
const bool vpnLikeType = type == "vpn" || type == "wireguard";
if (vpnLikeType || hasVpnSection) {
VpnConnectionInfo info;
info.path = std::string(connectionPath);
auto idIt = connIt->second.find("id");
if (idIt != connIt->second.end()) {
try {
info.name = idIt->second.get<std::string>();
} catch (const sdbus::Error&) {
}
}
if (info.name.empty()) {
info.name = info.path;
}
info.active = false;
opState->first.second.insert(info.path);
opState->first.first.push_back(std::move(info));
}
} catch (const sdbus::Error&) {
}
}
}
}
if (--opState->second == 0) {
// Mark active VPNs
try {
std::vector<sdbus::ObjectPath> activeConnections;
const sdbus::Variant activeVar =
m_nm->getProperty("ActiveConnections").onInterface(k_nmInterface);
activeConnections = activeVar.get<std::vector<sdbus::ObjectPath>>();
for (const auto& activePath : activeConnections) {
try {
auto active = sdbus::createProxy(m_bus.connection(), k_nmBusName, activePath);
const auto state =
getPropertyOr<std::uint32_t>(*active, k_nmActiveConnectionInterface, "State", 0U);
if (state != k_nmActiveConnectionStateActivating &&
state != k_nmActiveConnectionStateActivated) {
continue;
}
const auto profilePath =
getPropertyOr<sdbus::ObjectPath>(*active, k_nmActiveConnectionInterface,
"Connection", sdbus::ObjectPath{});
const std::string profilePathStr = std::string(profilePath);
if (!opState->first.second.contains(profilePathStr)) {
continue;
}
for (auto& vpn : opState->first.first) {
if (vpn.path == profilePathStr) {
vpn.active = true;
break;
}
}
} catch (const sdbus::Error&) {
}
}
} catch (const sdbus::Error& e) {
kLog.debug("refreshVpnConnections active list: {}", e.what());
}
std::ranges::sort(opState->first.first,
[](const VpnConnectionInfo& a, const VpnConnectionInfo& b) {
if (a.active != b.active) {
return a.active;
}
return a.name < b.name;
});
m_vpnConnections = std::move(opState->first.first);
if (onComplete)
onComplete();
}
});
} catch (const sdbus::Error&) {
if (--opState->second == 0) {
std::ranges::sort(opState->first.first,
[](const VpnConnectionInfo& a, const VpnConnectionInfo& b) {
if (a.active != b.active) {
return a.active;
}
return a.name < b.name;
});
m_vpnConnections = std::move(opState->first.first);
if (onComplete)
onComplete();
}
}
}
});
} catch (const sdbus::Error& e) {
kLog.debug("refreshVpnConnections: {}", e.what());
if (onComplete)
onComplete();
}
} }
void NetworkService::ensureWifiDeviceSubscribed(const std::string& devicePath) { void NetworkService::ensureWifiDeviceSubscribed(const std::string& devicePath) {
@@ -727,64 +831,170 @@ void NetworkService::ensureWifiDeviceSubscribed(const std::string& devicePath) {
} }
} }
void NetworkService::refreshAccessPoints() { void NetworkService::refreshAccessPoints(std::function<void()> onComplete) {
std::vector<AccessPointInfo> next;
try { try {
std::vector<sdbus::ObjectPath> devices; m_nm->callMethodAsync("GetDevices")
m_nm->callMethod("GetDevices").onInterface(k_nmInterface).storeResultsTo(devices); .onInterface(k_nmInterface)
for (const auto& devicePath : devices) { .uponReplyInvoke([this, onComplete](std::optional<sdbus::Error> err,
try { std::vector<sdbus::ObjectPath> devices) {
auto device = sdbus::createProxy(m_bus.connection(), k_nmBusName, devicePath); if (err.has_value()) {
const auto deviceType = getPropertyOr<std::uint32_t>(*device, k_nmDeviceInterface, "DeviceType", 0U); kLog.debug("refreshAccessPoints GetDevices failed: {}", err->what());
if (deviceType != k_nmDeviceTypeWifi) { if (onComplete)
continue; onComplete();
} return;
ensureWifiDeviceSubscribed(devicePath);
std::string activeApPath;
try {
const sdbus::Variant value =
device->getProperty("ActiveAccessPoint").onInterface(k_nmDeviceWirelessInterface);
activeApPath = value.get<sdbus::ObjectPath>();
} catch (const sdbus::Error&) {
}
const sdbus::Variant apListVar = device->getProperty("AccessPoints").onInterface(k_nmDeviceWirelessInterface);
const auto apPaths = apListVar.get<std::vector<sdbus::ObjectPath>>();
for (const auto& apPath : apPaths) {
try {
auto ap = sdbus::createProxy(m_bus.connection(), k_nmBusName, apPath);
AccessPointInfo info;
info.path = apPath;
info.devicePath = devicePath;
info.active = !activeApPath.empty() && apPath == activeApPath;
try {
const sdbus::Variant ssidVar = ap->getProperty("Ssid").onInterface(k_nmAccessPointInterface);
const auto ssidBytes = ssidVar.get<std::vector<std::uint8_t>>();
info.ssid.assign(ssidBytes.begin(), ssidBytes.end());
} catch (const sdbus::Error&) {
}
info.strength = getPropertyOr<std::uint8_t>(*ap, k_nmAccessPointInterface, "Strength", std::uint8_t{0});
const auto wpaFlags = getPropertyOr<std::uint32_t>(*ap, k_nmAccessPointInterface, "WpaFlags", 0U);
const auto rsnFlags = getPropertyOr<std::uint32_t>(*ap, k_nmAccessPointInterface, "RsnFlags", 0U);
info.secured = (wpaFlags != k_nm80211ApSecNone) || (rsnFlags != k_nm80211ApSecNone);
if (info.ssid.empty()) {
continue; // skip hidden networks for now
}
next.push_back(std::move(info));
} catch (const sdbus::Error&) {
} }
}
} catch (const sdbus::Error&) { if (devices.empty()) {
} m_accessPoints.clear();
} if (onComplete)
onComplete();
return;
}
// One slot per device; non-WiFi devices decrement immediately without contributing APs.
const int totalDevices = static_cast<int>(devices.size());
auto deviceState =
std::make_shared<std::pair<std::vector<AccessPointInfo>, std::atomic<int>>>(
std::make_pair(std::vector<AccessPointInfo>{}, totalDevices));
for (const auto& devicePath : devices) {
try {
auto device = std::shared_ptr<sdbus::IProxy>(
sdbus::createProxy(m_bus.connection(), k_nmBusName, devicePath));
// GetAll on DBus.Properties with the wireless interface arg: succeeds only for
// WiFi devices and also gives us ActiveAccessPoint — no sync reads needed.
device->callMethodAsync("GetAll")
.onInterface(k_propertiesInterface)
.withArguments(k_nmDeviceWirelessInterface)
.uponReplyInvoke([this, device, deviceState, devicePath, onComplete](
std::optional<sdbus::Error> wifiErr,
std::map<std::string, sdbus::Variant> wifiProps) {
if (wifiErr.has_value()) {
// Not a WiFi device — just decrement and possibly finish.
if (--deviceState->second == 0) {
finishRefreshAccessPoints(deviceState->first, onComplete);
}
return;
}
// WiFi device confirmed. Subscribe for scan/state signals.
ensureWifiDeviceSubscribed(devicePath);
std::string activeApPath;
if (auto it = wifiProps.find("ActiveAccessPoint"); it != wifiProps.end()) {
try {
activeApPath = it->second.get<sdbus::ObjectPath>();
} catch (const sdbus::Error&) {
}
}
device->callMethodAsync("GetAccessPoints")
.onInterface(k_nmDeviceWirelessInterface)
.uponReplyInvoke([this, device, deviceState, devicePath, activeApPath, onComplete](
std::optional<sdbus::Error> apErr,
std::vector<sdbus::ObjectPath> apPaths) {
if (apErr.has_value() || apPaths.empty()) {
if (--deviceState->second == 0) {
finishRefreshAccessPoints(deviceState->first, onComplete);
}
return;
}
const int pendingAps = static_cast<int>(apPaths.size());
auto apState =
std::make_shared<std::pair<std::vector<AccessPointInfo>, std::atomic<int>>>(
std::make_pair(std::vector<AccessPointInfo>{}, pendingAps));
for (const auto& apPath : apPaths) {
try {
auto ap = std::shared_ptr<sdbus::IProxy>(
sdbus::createProxy(m_bus.connection(), k_nmBusName, apPath));
ap->callMethodAsync("GetAll")
.onInterface(k_propertiesInterface)
.withArguments(k_nmAccessPointInterface)
.uponReplyInvoke(
[this, ap, deviceState, apState, devicePath, activeApPath, apPath,
onComplete](std::optional<sdbus::Error> propErr,
std::map<std::string, sdbus::Variant> properties) {
if (!propErr.has_value()) {
AccessPointInfo info;
info.path = apPath;
info.devicePath = devicePath;
info.active =
!activeApPath.empty() && apPath == activeApPath;
try {
const auto ssidBytes =
properties.at("Ssid").get<std::vector<std::uint8_t>>();
info.ssid.assign(ssidBytes.begin(), ssidBytes.end());
} catch (const sdbus::Error&) {
}
try {
info.strength =
properties.at("Strength").get<std::uint8_t>();
} catch (const sdbus::Error&) {
}
const auto wpaFlags = [&properties]() {
try {
return properties.at("WpaFlags").get<std::uint32_t>();
} catch (const sdbus::Error&) {
return 0U;
}
}();
const auto rsnFlags = [&properties]() {
try {
return properties.at("RsnFlags").get<std::uint32_t>();
} catch (const sdbus::Error&) {
return 0U;
}
}();
info.secured = (wpaFlags != k_nm80211ApSecNone) ||
(rsnFlags != k_nm80211ApSecNone);
if (!info.ssid.empty()) {
apState->first.push_back(std::move(info));
}
}
if (--apState->second == 0) {
for (auto& apInfo : apState->first) {
deviceState->first.push_back(std::move(apInfo));
}
if (--deviceState->second == 0) {
finishRefreshAccessPoints(deviceState->first, onComplete);
}
}
});
} catch (const sdbus::Error&) {
if (--apState->second == 0) {
for (auto& apInfo : apState->first) {
deviceState->first.push_back(std::move(apInfo));
}
if (--deviceState->second == 0) {
finishRefreshAccessPoints(deviceState->first, onComplete);
}
}
}
}
});
});
} catch (const sdbus::Error&) {
if (--deviceState->second == 0) {
finishRefreshAccessPoints(deviceState->first, onComplete);
}
}
}
});
} catch (const sdbus::Error& e) { } catch (const sdbus::Error& e) {
kLog.debug("refreshAccessPoints: {}", e.what()); kLog.debug("refreshAccessPoints: {}", e.what());
if (onComplete)
onComplete();
} }
}
void NetworkService::finishRefreshAccessPoints(std::vector<AccessPointInfo>& aps,
std::function<void()> onComplete) {
// Deduplicate by SSID, keeping the strongest (and marking active if any entry is active). // Deduplicate by SSID, keeping the strongest (and marking active if any entry is active).
std::vector<AccessPointInfo> deduped; std::vector<AccessPointInfo> deduped;
deduped.reserve(next.size()); deduped.reserve(aps.size());
for (auto& ap : next) { for (auto& ap : aps) {
auto it = std::find_if(deduped.begin(), deduped.end(), auto it = std::find_if(deduped.begin(), deduped.end(),
[&](const AccessPointInfo& other) { return other.ssid == ap.ssid; }); [&](const AccessPointInfo& other) { return other.ssid == ap.ssid; });
if (it == deduped.end()) { if (it == deduped.end()) {
@@ -809,6 +1019,8 @@ void NetworkService::refreshAccessPoints() {
}); });
m_accessPoints = std::move(deduped); m_accessPoints = std::move(deduped);
if (onComplete)
onComplete();
} }
void NetworkService::rebindActiveConnection() { void NetworkService::rebindActiveConnection() {
+4 -3
View File
@@ -106,9 +106,10 @@ public:
[[nodiscard]] bool hasSavedConnection(const std::string& ssid) const; [[nodiscard]] bool hasSavedConnection(const std::string& ssid) const;
private: private:
void refreshAccessPoints(); void refreshAccessPoints(std::function<void()> onComplete);
void refreshSavedConnections(); void refreshSavedConnections(std::function<void()> onComplete);
void refreshVpnConnections(); void refreshVpnConnections(std::function<void()> onComplete);
void finishRefreshAccessPoints(std::vector<AccessPointInfo>& aps, std::function<void()> onComplete);
void rebindActiveConnection(); void rebindActiveConnection();
void rebindActiveDevice(const std::string& devicePath); void rebindActiveDevice(const std::string& devicePath);
void rebindActiveAccessPoint(const std::string& apPath); void rebindActiveAccessPoint(const std::string& apPath);