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

166 lines
7.3 KiB
C++

// SPDX-FileCopyrightText: 2026 Reion Wong <reionwong@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <chrono>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "base/dbus.h"
#include "base/signal.h"
#include "base/timer.h"
#include "networkmanager/types.h"
namespace cutefish::networkmanager {
// NetworkManager as one GetManagedObjects and the signals since describe it,
// shared by everything in the process that shows or changes networks. Nothing
// here blocks: every call is asynchronous, and the cache is only ever read.
class Client
{
public:
using Variant = base::dbus::Variant;
using VariantMap = base::dbus::VariantMap;
using Completion = std::function<void(const base::dbus::Reply& reply)>;
explicit Client(base::dbus::Connection* bus);
~Client();
Client(const Client&) = delete;
Client& operator=(const Client&) = delete;
// The system bus's client of the calling thread's loop, made on first use
// and gone with the loop.
static Client* instance();
// Makes instance() a client of this bus from now on, for tests that serve
// NetworkManager on a bus of their own; the client made so far goes.
static void setInstanceBus(base::dbus::Connection* bus);
base::dbus::Connection* bus() const { return m_bus; }
// NetworkManager's unique bus name; empty while it is not running.
const std::string& owner() const { return m_owner; }
// The objects and the settings of every profile have arrived.
bool ready() const { return m_ready; }
bool has(const std::string& path, const std::string& interface) const;
VariantMap properties(const std::string& path, const std::string& interface) const;
Variant property(const std::string& path, const std::string& interface, const std::string& name) const;
Variant manager(const std::string& name) const;
std::vector<std::string> devices() const;
uint32_t deviceType(const std::string& device) const;
uint32_t deviceState(const std::string& device) const;
std::string activeConnection(const std::string& device) const;
std::vector<std::string> activeConnections() const;
std::vector<std::string> accessPoints(const std::string& device) const;
// Access points the device no longer lists only because nothing scanned
// for a while; their properties stay readable through property().
std::vector<std::string> rememberedAccessPoints(const std::string& device) const;
bool remembers(const std::string& accessPoint) const;
// The Wi-Fi device that lists the access point, or listed a remembered one.
std::string accessPointDevice(const std::string& accessPoint) const;
// `accessPoint` if NetworkManager lists it; for a remembered one, the same
// network's access point once a scan lists it again, empty until then.
std::string scannedAccessPoint(const std::string& accessPoint) const;
// What `device` lists for the access point with these properties: the
// same BSSID, else the strongest with the same name and security.
std::string listedAccessPoint(const std::string& device, const VariantMap& accessPoint) const;
// The profiles this user may read.
std::vector<std::string> connections() const;
bool readable(const std::string& connection) const;
// As NetworkManager sent them, so an Update hands back the D-Bus types it was given.
Settings settings(const std::string& connection) const;
Variant setting(const std::string& connection, const std::string& setting, const std::string& key) const;
// The profile that shares this machine's connection over Wi-Fi.
std::string hotspotConnection() const;
std::string permission(const std::string& name) const;
// New profiles are for every user when this user may change system
// profiles without an administrator password; otherwise a
// non-administrator could not join at all.
bool sharesNewConnections() const;
// This user's entry in a profile's connection.permissions.
static std::string userPermission();
// For calls that may wait on an administrator password: they may prompt
// for it and wait that long for the answer.
static constexpr std::chrono::milliseconds authorizedTimeout{2 * 60 * 1000};
// The reply reaches `completion` unless `context` is gone by then.
void call(const std::string& path, const std::string& interface, const std::string& method,
Variant::Items arguments = {}, const CallContext* context = nullptr, Completion completion = {},
std::chrono::milliseconds timeout = {});
void setProperty(const std::string& path, const std::string& interface, const std::string& name,
const Variant& value, const CallContext* context = nullptr, Completion completion = {});
base::Signal<> readyChanged;
// Once per batch of applied changes.
base::Signal<> changed;
base::Signal<const std::string&, uint32_t, uint32_t> activeStateChanged;
base::Signal<const std::string&> objectRemoved;
private:
struct RememberedAccessPoint
{
std::string device;
VariantMap properties;
int missedScans = 0;
};
void refresh();
void reset();
void load(const std::string& owner);
void subscribe(const std::string& owner);
void addInterfaces(const std::string& path, const std::map<std::string, VariantMap>& interfaces);
void interfacesAdded(const base::dbus::Message& message);
void interfacesRemoved(const base::dbus::Message& message);
void propertiesChanged(const base::dbus::Message& message);
void connectionUpdated(const base::dbus::Message& message);
void activeStateSignal(const base::dbus::Message& message);
void fetchSettings(const std::string& path);
void fetchPermissions();
void scheduleChanged();
void updateReady();
bool keepsAccessPoints(const std::string& device) const;
void noteAccessPoints(const std::string& device);
void rememberAccessPoint(const std::string& path, const VariantMap& properties);
void scanFinished(const std::string& device);
void pruneRemembered();
base::dbus::Connection* m_bus;
CallContext m_context;
std::string m_owner;
uint64_t m_generation = 0;
bool m_loading = false;
bool m_loaded = false;
bool m_ready = false;
std::map<std::string, std::map<std::string, VariantMap>> m_objects;
std::map<std::string, Settings> m_settings;
// The newest GetSettings per profile; an older reply is stale.
std::map<std::string, uint64_t> m_settingsRequests;
uint64_t m_permissionsRequest = 0;
uint64_t m_nextRequest = 0;
std::map<std::string, std::string> m_permissions;
std::map<std::string, std::string> m_accessPointDevices;
// wpa_supplicant drops an access point 250 s after a scan last saw it,
// and while connected NetworkManager hardly scans, so a list would open
// with only the network in use. These stay until two later scans miss
// them, as NetworkManager drops its own.
std::map<std::string, RememberedAccessPoint> m_remembered;
std::unordered_map<uint64_t, base::dbus::PendingCall> m_pending;
uint64_t m_nextPending = 0;
std::vector<base::dbus::Match> m_matches;
base::dbus::NameWatch m_ownerWatch;
base::Timer m_changedTimer;
base::Timer m_retryTimer;
};
} // namespace cutefish::networkmanager