1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qnetworkmanagerservice.h" |
5 | |
6 | #include <QObject> |
7 | #include <QList> |
8 | #include <QtDBus/QDBusConnection> |
9 | #include <QtDBus/QDBusError> |
10 | #include <QtDBus/QDBusInterface> |
11 | #include <QtDBus/QDBusMessage> |
12 | #include <QtDBus/QDBusReply> |
13 | #include <QtDBus/QDBusPendingCallWatcher> |
14 | #include <QtDBus/QDBusObjectPath> |
15 | #include <QtDBus/QDBusPendingCall> |
16 | |
17 | #define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" |
18 | |
19 | #define NM_DBUS_SERVICE "org.freedesktop.NetworkManager" |
20 | |
21 | #define NM_DBUS_PATH "/org/freedesktop/NetworkManager" |
22 | #define NM_DBUS_INTERFACE NM_DBUS_SERVICE |
23 | #define NM_CONNECTION_DBUS_INTERFACE NM_DBUS_SERVICE ".Connection.Active" |
24 | #define NM_DEVICE_DBUS_INTERFACE NM_DBUS_SERVICE ".Device" |
25 | |
26 | QT_BEGIN_NAMESPACE |
27 | |
28 | using namespace Qt::StringLiterals; |
29 | |
30 | QNetworkManagerInterfaceBase::QNetworkManagerInterfaceBase(QObject *parent) |
31 | : QDBusAbstractInterface(NM_DBUS_SERVICE ""_L1 , NM_DBUS_PATH ""_L1 , |
32 | NM_DBUS_INTERFACE, QDBusConnection::systemBus(), parent) |
33 | { |
34 | } |
35 | |
36 | bool QNetworkManagerInterfaceBase::networkManagerAvailable() |
37 | { |
38 | return QNetworkManagerInterfaceBase().isValid(); |
39 | } |
40 | |
41 | QNetworkManagerInterface::QNetworkManagerInterface(QObject *parent) |
42 | : QNetworkManagerInterfaceBase(parent) |
43 | { |
44 | if (!isValid()) |
45 | return; |
46 | |
47 | PropertiesDBusInterface managerPropertiesInterface( |
48 | NM_DBUS_SERVICE ""_L1 , NM_DBUS_PATH ""_L1 , DBUS_PROPERTIES_INTERFACE, |
49 | QDBusConnection::systemBus()); |
50 | QList<QVariant> argumentList; |
51 | argumentList << NM_DBUS_INTERFACE ""_L1 ; |
52 | QDBusPendingReply<QVariantMap> propsReply = managerPropertiesInterface.callWithArgumentList( |
53 | mode: QDBus::Block, method: "GetAll"_L1 , args: argumentList); |
54 | if (!propsReply.isError()) { |
55 | propertyMap = propsReply.value(); |
56 | } else { |
57 | qWarning() << "propsReply" << propsReply.error().message(); |
58 | } |
59 | |
60 | QDBusConnection::systemBus().connect(NM_DBUS_SERVICE ""_L1 , NM_DBUS_PATH ""_L1 , |
61 | DBUS_PROPERTIES_INTERFACE""_L1 , name: "PropertiesChanged"_L1 , receiver: this, |
62 | SLOT(setProperties(QString,QMap<QString,QVariant>,QList<QString>))); |
63 | } |
64 | |
65 | QNetworkManagerInterface::~QNetworkManagerInterface() |
66 | { |
67 | QDBusConnection::systemBus().disconnect(NM_DBUS_SERVICE ""_L1 , NM_DBUS_PATH ""_L1 , |
68 | DBUS_PROPERTIES_INTERFACE ""_L1 , name: "PropertiesChanged"_L1 , receiver: this, |
69 | SLOT(setProperties(QString,QMap<QString,QVariant>,QList<QString>))); |
70 | } |
71 | |
72 | QNetworkManagerInterface::NMState QNetworkManagerInterface::state() const |
73 | { |
74 | if (propertyMap.contains(key: "State" )) |
75 | return static_cast<QNetworkManagerInterface::NMState>(propertyMap.value(key: "State" ).toUInt()); |
76 | return QNetworkManagerInterface::NM_STATE_UNKNOWN; |
77 | } |
78 | |
79 | QNetworkManagerInterface::NMConnectivityState QNetworkManagerInterface::connectivityState() const |
80 | { |
81 | if (propertyMap.contains(key: "Connectivity" )) |
82 | return static_cast<NMConnectivityState>(propertyMap.value(key: "Connectivity" ).toUInt()); |
83 | return QNetworkManagerInterface::NM_CONNECTIVITY_UNKNOWN; |
84 | } |
85 | |
86 | static std::optional<QDBusInterface> getPrimaryDevice(const QDBusObjectPath &devicePath) |
87 | { |
88 | const QDBusInterface connection(NM_DBUS_SERVICE, devicePath.path(), |
89 | NM_CONNECTION_DBUS_INTERFACE, QDBusConnection::systemBus()); |
90 | if (!connection.isValid()) |
91 | return std::nullopt; |
92 | |
93 | const auto devicePaths = connection.property(name: "Devices" ).value<QList<QDBusObjectPath>>(); |
94 | if (devicePaths.isEmpty()) |
95 | return std::nullopt; |
96 | |
97 | const QDBusObjectPath primaryDevicePath = devicePaths.front(); |
98 | return std::make_optional<QDBusInterface>(NM_DBUS_SERVICE, args: primaryDevicePath.path(), |
99 | NM_DEVICE_DBUS_INTERFACE, |
100 | args: QDBusConnection::systemBus()); |
101 | } |
102 | |
103 | std::optional<QDBusObjectPath> QNetworkManagerInterface::primaryConnectionDevicePath() const |
104 | { |
105 | auto it = propertyMap.constFind(key: u"PrimaryConnection"_s ); |
106 | if (it != propertyMap.cend()) |
107 | return it->value<QDBusObjectPath>(); |
108 | return std::nullopt; |
109 | } |
110 | |
111 | auto QNetworkManagerInterface::deviceType() const -> NMDeviceType |
112 | { |
113 | if (const auto path = primaryConnectionDevicePath()) |
114 | return extractDeviceType(devicePath: *path); |
115 | return NM_DEVICE_TYPE_UNKNOWN; |
116 | } |
117 | |
118 | auto QNetworkManagerInterface::meteredState() const -> NMMetered |
119 | { |
120 | if (const auto path = primaryConnectionDevicePath()) |
121 | return extractDeviceMetered(devicePath: *path); |
122 | return NM_METERED_UNKNOWN; |
123 | } |
124 | |
125 | auto QNetworkManagerInterface::(const QDBusObjectPath &devicePath) const |
126 | -> NMDeviceType |
127 | { |
128 | const auto primaryDevice = getPrimaryDevice(devicePath); |
129 | if (!primaryDevice) |
130 | return NM_DEVICE_TYPE_UNKNOWN; |
131 | if (!primaryDevice->isValid()) |
132 | return NM_DEVICE_TYPE_UNKNOWN; |
133 | const QVariant deviceType = primaryDevice->property(name: "DeviceType" ); |
134 | if (!deviceType.isValid()) |
135 | return NM_DEVICE_TYPE_UNKNOWN; |
136 | return static_cast<NMDeviceType>(deviceType.toUInt()); |
137 | } |
138 | |
139 | auto QNetworkManagerInterface::(const QDBusObjectPath &devicePath) const |
140 | -> NMMetered |
141 | { |
142 | const auto primaryDevice = getPrimaryDevice(devicePath); |
143 | if (!primaryDevice) |
144 | return NM_METERED_UNKNOWN; |
145 | if (!primaryDevice->isValid()) |
146 | return NM_METERED_UNKNOWN; |
147 | const QVariant metered = primaryDevice->property(name: "Metered" ); |
148 | if (!metered.isValid()) |
149 | return NM_METERED_UNKNOWN; |
150 | return static_cast<NMMetered>(metered.toUInt()); |
151 | } |
152 | |
153 | void QNetworkManagerInterface::setProperties(const QString &interfaceName, |
154 | const QMap<QString, QVariant> &map, |
155 | const QStringList &invalidatedProperties) |
156 | { |
157 | Q_UNUSED(interfaceName); |
158 | Q_UNUSED(invalidatedProperties); |
159 | |
160 | for (auto i = map.cbegin(), end = map.cend(); i != end; ++i) { |
161 | bool valueChanged = true; |
162 | |
163 | auto it = propertyMap.lowerBound(key: i.key()); |
164 | if (it != propertyMap.end() && it.key() == i.key()) { |
165 | valueChanged = (it.value() != i.value()); |
166 | *it = *i; |
167 | } else { |
168 | propertyMap.insert(pos: it, key: i.key(), value: i.value()); |
169 | } |
170 | |
171 | if (valueChanged) { |
172 | if (i.key() == "State"_L1 ) { |
173 | quint32 state = i.value().toUInt(); |
174 | Q_EMIT stateChanged(static_cast<NMState>(state)); |
175 | } else if (i.key() == "Connectivity"_L1 ) { |
176 | quint32 state = i.value().toUInt(); |
177 | Q_EMIT connectivityChanged(static_cast<NMConnectivityState>(state)); |
178 | } else if (i.key() == "PrimaryConnection"_L1 ) { |
179 | const QDBusObjectPath devicePath = i->value<QDBusObjectPath>(); |
180 | Q_EMIT deviceTypeChanged(extractDeviceType(devicePath)); |
181 | Q_EMIT meteredChanged(extractDeviceMetered(devicePath)); |
182 | } else if (i.key() == "Metered"_L1 ) { |
183 | Q_EMIT meteredChanged(static_cast<NMMetered>(i->toUInt())); |
184 | } |
185 | } |
186 | } |
187 | } |
188 | |
189 | QT_END_NAMESPACE |
190 | |
191 | #include "moc_qnetworkmanagerservice.cpp" |
192 | |