diff options
Diffstat (limited to 'src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp')
-rw-r--r-- | src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp new file mode 100644 index 0000000000..c055555cac --- /dev/null +++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp @@ -0,0 +1,220 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qnetworkmanagerservice.h" +#include "qnetworkmanagernetworkinformationbackend.h" + +#include <QObject> +#include <QList> +#include <QtDBus/QDBusConnection> +#include <QtDBus/QDBusError> +#include <QtDBus/QDBusInterface> +#include <QtDBus/QDBusMessage> +#include <QtDBus/QDBusReply> +#include <QtDBus/QDBusPendingCallWatcher> +#include <QtDBus/QDBusObjectPath> +#include <QtDBus/QDBusPendingCall> + +#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"_L1 + +#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager" +#define NM_DBUS_SERVICE NM_DBUS_INTERFACE ""_L1 + +#define NM_DBUS_PATH "/org/freedesktop/NetworkManager"_L1 +#define NM_CONNECTION_DBUS_INTERFACE NM_DBUS_SERVICE ".Connection.Active"_L1 +#define NM_DEVICE_DBUS_INTERFACE NM_DBUS_SERVICE ".Device"_L1 + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +namespace { +constexpr QLatin1StringView propertiesChangedKey = "PropertiesChanged"_L1; +const QString &stateKey() +{ + static auto key = u"State"_s; + return key; +} +const QString &connectivityKey() +{ + static auto key = u"Connectivity"_s; + return key; +} +const QString &primaryConnectionKey() +{ + static auto key = u"PrimaryConnection"_s; + return key; +} +} + +QNetworkManagerInterfaceBase::QNetworkManagerInterfaceBase(QObject *parent) + : QDBusAbstractInterface(NM_DBUS_SERVICE, NM_DBUS_PATH, + NM_DBUS_INTERFACE, QDBusConnection::systemBus(), parent) +{ +} + +bool QNetworkManagerInterfaceBase::networkManagerAvailable() +{ + return QNetworkManagerInterfaceBase().isValid(); +} + +QNetworkManagerInterface::QNetworkManagerInterface(QObject *parent) + : QNetworkManagerInterfaceBase(parent) +{ + if (!QDBusAbstractInterface::isValid()) + return; + + PropertiesDBusInterface managerPropertiesInterface( + NM_DBUS_SERVICE, NM_DBUS_PATH, DBUS_PROPERTIES_INTERFACE, + QDBusConnection::systemBus()); + QList<QVariant> argumentList; + argumentList << NM_DBUS_SERVICE; + QDBusPendingReply<QVariantMap> propsReply = managerPropertiesInterface.callWithArgumentList( + QDBus::Block, "GetAll"_L1, argumentList); + if (propsReply.isError()) { + validDBusConnection = false; + if (auto error = propsReply.error(); error.type() != QDBusError::AccessDenied) + qWarning() << "Failed to query NetworkManager properties:" << error.message(); + return; + } + propertyMap = propsReply.value(); + + validDBusConnection = QDBusConnection::systemBus().connect(NM_DBUS_SERVICE, NM_DBUS_PATH, + DBUS_PROPERTIES_INTERFACE, propertiesChangedKey, this, + SLOT(setProperties(QString,QMap<QString,QVariant>,QList<QString>))); +} + +QNetworkManagerInterface::~QNetworkManagerInterface() +{ + QDBusConnection::systemBus().disconnect(NM_DBUS_SERVICE, NM_DBUS_PATH, + DBUS_PROPERTIES_INTERFACE, propertiesChangedKey, this, + SLOT(setProperties(QString,QMap<QString,QVariant>,QList<QString>))); +} + +QNetworkManagerInterface::NMState QNetworkManagerInterface::state() const +{ + auto it = propertyMap.constFind(stateKey()); + if (it != propertyMap.cend()) + return static_cast<QNetworkManagerInterface::NMState>(it->toUInt()); + return QNetworkManagerInterface::NM_STATE_UNKNOWN; +} + +QNetworkManagerInterface::NMConnectivityState QNetworkManagerInterface::connectivityState() const +{ + auto it = propertyMap.constFind(connectivityKey()); + if (it != propertyMap.cend()) + return static_cast<NMConnectivityState>(it->toUInt()); + return QNetworkManagerInterface::NM_CONNECTIVITY_UNKNOWN; +} + +static std::optional<QDBusInterface> getPrimaryDevice(const QDBusObjectPath &devicePath) +{ + const QDBusInterface connection(NM_DBUS_SERVICE, devicePath.path(), + NM_CONNECTION_DBUS_INTERFACE, QDBusConnection::systemBus()); + if (!connection.isValid()) + return std::nullopt; + + const auto devicePaths = connection.property("Devices").value<QList<QDBusObjectPath>>(); + if (devicePaths.isEmpty()) + return std::nullopt; + + const QDBusObjectPath primaryDevicePath = devicePaths.front(); + return std::make_optional<QDBusInterface>(NM_DBUS_SERVICE, primaryDevicePath.path(), + NM_DEVICE_DBUS_INTERFACE, + QDBusConnection::systemBus()); +} + +std::optional<QDBusObjectPath> QNetworkManagerInterface::primaryConnectionDevicePath() const +{ + auto it = propertyMap.constFind(primaryConnectionKey()); + if (it != propertyMap.cend()) + return it->value<QDBusObjectPath>(); + return std::nullopt; +} + +auto QNetworkManagerInterface::deviceType() const -> NMDeviceType +{ + if (const auto path = primaryConnectionDevicePath()) + return extractDeviceType(*path); + return NM_DEVICE_TYPE_UNKNOWN; +} + +auto QNetworkManagerInterface::meteredState() const -> NMMetered +{ + if (const auto path = primaryConnectionDevicePath()) + return extractDeviceMetered(*path); + return NM_METERED_UNKNOWN; +} + +auto QNetworkManagerInterface::extractDeviceType(const QDBusObjectPath &devicePath) const + -> NMDeviceType +{ + const auto primaryDevice = getPrimaryDevice(devicePath); + if (!primaryDevice) + return NM_DEVICE_TYPE_UNKNOWN; + if (!primaryDevice->isValid()) + return NM_DEVICE_TYPE_UNKNOWN; + const QVariant deviceType = primaryDevice->property("DeviceType"); + if (!deviceType.isValid()) + return NM_DEVICE_TYPE_UNKNOWN; + return static_cast<NMDeviceType>(deviceType.toUInt()); +} + +auto QNetworkManagerInterface::extractDeviceMetered(const QDBusObjectPath &devicePath) const + -> NMMetered +{ + const auto primaryDevice = getPrimaryDevice(devicePath); + if (!primaryDevice) + return NM_METERED_UNKNOWN; + if (!primaryDevice->isValid()) + return NM_METERED_UNKNOWN; + const QVariant metered = primaryDevice->property("Metered"); + if (!metered.isValid()) + return NM_METERED_UNKNOWN; + return static_cast<NMMetered>(metered.toUInt()); +} + +void QNetworkManagerInterface::setBackend(QNetworkManagerNetworkInformationBackend *ourBackend) +{ + backend = ourBackend; +} + +void QNetworkManagerInterface::setProperties(const QString &interfaceName, + const QMap<QString, QVariant> &map, + const QStringList &invalidatedProperties) +{ + Q_UNUSED(interfaceName); + Q_UNUSED(invalidatedProperties); + + for (auto i = map.cbegin(), end = map.cend(); i != end; ++i) { + bool valueChanged = true; + + auto it = propertyMap.lowerBound(i.key()); + if (it != propertyMap.end() && it.key() == i.key()) { + valueChanged = (it.value() != i.value()); + *it = *i; + } else { + propertyMap.insert(it, i.key(), i.value()); + } + + if (valueChanged) { + if (i.key() == stateKey()) { + quint32 state = i.value().toUInt(); + backend->onStateChanged(static_cast<NMState>(state)); + } else if (i.key() == connectivityKey()) { + quint32 state = i.value().toUInt(); + backend->onConnectivityChanged(static_cast<NMConnectivityState>(state)); + } else if (i.key() == primaryConnectionKey()) { + const QDBusObjectPath devicePath = i->value<QDBusObjectPath>(); + backend->onDeviceTypeChanged(extractDeviceType(devicePath)); + backend->onMeteredChanged(extractDeviceMetered(devicePath)); + } else if (i.key() == "Metered"_L1) { + backend->onMeteredChanged(static_cast<NMMetered>(i->toUInt())); + } + } + } +} + +QT_END_NAMESPACE + +#include "moc_qnetworkmanagerservice.cpp" |