1/*
2 * BluezQt - Asynchronous Bluez wrapper library
3 *
4 * SPDX-FileCopyrightText: 2014-2015 David Rosca <nowrep@gmail.com>
5 *
6 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8
9#include "manager_p.h"
10#include "adapter.h"
11#include "adapter_p.h"
12#include "debug.h"
13#include "device.h"
14#include "device_p.h"
15#include "manager.h"
16#include "utils.h"
17
18#include <QDBusServiceWatcher>
19
20namespace BluezQt
21{
22ManagerPrivate::ManagerPrivate(Manager *parent)
23 : QObject(parent)
24 , q(parent)
25 , m_dbusObjectManager(nullptr)
26 , m_bluezAgentManager(nullptr)
27 , m_bluezProfileManager(nullptr)
28 , m_initialized(false)
29 , m_bluezRunning(false)
30 , m_loaded(false)
31 , m_adaptersLoaded(false)
32{
33 qDBusRegisterMetaType<DBusManagerStruct>();
34 qDBusRegisterMetaType<QVariantMapMap>();
35
36 m_rfkill = new Rfkill(this);
37 m_bluetoothBlocked = rfkillBlocked();
38 connect(m_rfkill, &Rfkill::stateChanged, this, &ManagerPrivate::rfkillStateChanged);
39
40 connect(q, &Manager::adapterRemoved, this, &ManagerPrivate::adapterRemoved);
41}
42
43void ManagerPrivate::init()
44{
45 // Keep an eye on org.bluez service
46 QDBusServiceWatcher *serviceWatcher = new QDBusServiceWatcher(Strings::orgBluez(),
47 DBusConnection::orgBluez(),
48 QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration,
49 this);
50
51 connect(serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &ManagerPrivate::serviceRegistered);
52 connect(serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &ManagerPrivate::serviceUnregistered);
53
54 // Update the current state of org.bluez service
55 if (!DBusConnection::orgBluez().isConnected()) {
56 Q_EMIT initError(QStringLiteral("DBus system bus is not connected!"));
57 return;
58 }
59
60 QDBusMessage call =
61 QDBusMessage::createMethodCall(Strings::orgFreedesktopDBus(), QStringLiteral("/"), Strings::orgFreedesktopDBus(), QStringLiteral("NameHasOwner"));
62
63 call << Strings::orgBluez();
64
65 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(DBusConnection::orgBluez().asyncCall(call));
66 connect(watcher, &QDBusPendingCallWatcher::finished, this, &ManagerPrivate::nameHasOwnerFinished);
67
68 DBusConnection::orgBluez().connect(Strings::orgBluez(),
69 QString(),
70 Strings::orgFreedesktopDBusProperties(),
71 QStringLiteral("PropertiesChanged"),
72 this,
73 SLOT(propertiesChanged(QString, QVariantMap, QStringList)));
74}
75
76void ManagerPrivate::nameHasOwnerFinished(QDBusPendingCallWatcher *watcher)
77{
78 const QDBusPendingReply<bool> &reply = *watcher;
79 watcher->deleteLater();
80
81 if (reply.isError()) {
82 Q_EMIT initError(reply.error().message());
83 return;
84 }
85
86 m_bluezRunning = reply.value();
87
88 if (m_bluezRunning) {
89 load();
90 } else {
91 m_initialized = true;
92 Q_EMIT initFinished();
93 }
94}
95
96void ManagerPrivate::load()
97{
98 if (!m_bluezRunning || m_loaded) {
99 return;
100 }
101
102 // Force QDBus to cache owner of org.bluez - this will be the only blocking call on system connection
103 DBusConnection::orgBluez().connect(Strings::orgBluez(), QStringLiteral("/"), Strings::orgFreedesktopDBus(), QStringLiteral("Dummy"), this, SLOT(dummy()));
104
105 m_dbusObjectManager = new DBusObjectManager(Strings::orgBluez(), QStringLiteral("/"), DBusConnection::orgBluez(), this);
106
107 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(m_dbusObjectManager->GetManagedObjects(), this);
108 connect(watcher, &QDBusPendingCallWatcher::finished, this, &ManagerPrivate::getManagedObjectsFinished);
109}
110
111void ManagerPrivate::getManagedObjectsFinished(QDBusPendingCallWatcher *watcher)
112{
113 const QDBusPendingReply<DBusManagerStruct> &reply = *watcher;
114 watcher->deleteLater();
115
116 if (reply.isError()) {
117 Q_EMIT initError(reply.error().message());
118 return;
119 }
120
121 DBusManagerStruct::const_iterator it;
122 const DBusManagerStruct &managedObjects = reply.value();
123
124 for (it = managedObjects.constBegin(); it != managedObjects.constEnd(); ++it) {
125 const QString &path = it.key().path();
126 const QVariantMapMap &interfaces = it.value();
127
128 interfacesAdded(it.key(), interfaces);
129
130 if (interfaces.contains(Strings::orgBluezAgentManager1())) {
131 m_bluezAgentManager = new BluezAgentManager(Strings::orgBluez(), path, DBusConnection::orgBluez(), this);
132 }
133 if (interfaces.contains(Strings::orgBluezProfileManager1())) {
134 m_bluezProfileManager = new BluezProfileManager(Strings::orgBluez(), path, DBusConnection::orgBluez(), this);
135 }
136 }
137
138 if (!m_bluezAgentManager) {
139 Q_EMIT initError(QStringLiteral("Cannot find org.bluez.AgentManager1 object!"));
140 return;
141 }
142
143 if (!m_bluezProfileManager) {
144 Q_EMIT initError(QStringLiteral("Cannot find org.bluez.ProfileManager1 object!"));
145 return;
146 }
147
148 connect(m_dbusObjectManager, &DBusObjectManager::InterfacesAdded, this, &ManagerPrivate::interfacesAdded);
149 connect(m_dbusObjectManager, &DBusObjectManager::InterfacesRemoved, this, &ManagerPrivate::interfacesRemoved);
150
151 m_loaded = true;
152 m_initialized = true;
153
154 Q_EMIT q->operationalChanged(true);
155
156 if (q->isBluetoothOperational()) {
157 Q_EMIT q->bluetoothOperationalChanged(true);
158 }
159
160 Q_EMIT initFinished();
161}
162
163void ManagerPrivate::clear()
164{
165 m_loaded = false;
166
167 // Delete all devices first
168 while (!m_devices.isEmpty()) {
169 DevicePtr device = m_devices.begin().value();
170 m_devices.remove(m_devices.begin().key());
171 device->adapter()->d->removeDevice(device);
172 }
173
174 // Delete all adapters
175 while (!m_adapters.isEmpty()) {
176 AdapterPtr adapter = m_adapters.begin().value();
177 m_adapters.remove(m_adapters.begin().key());
178 Q_EMIT adapter->adapterRemoved(adapter);
179
180 if (m_adapters.isEmpty()) {
181 Q_EMIT q->allAdaptersRemoved();
182 }
183 }
184
185 // Delete all other objects
186 m_usableAdapter.clear();
187
188 if (m_dbusObjectManager) {
189 m_dbusObjectManager->deleteLater();
190 m_dbusObjectManager = nullptr;
191 }
192
193 if (m_bluezAgentManager) {
194 m_bluezAgentManager->deleteLater();
195 m_bluezAgentManager = nullptr;
196 }
197}
198
199AdapterPtr ManagerPrivate::findUsableAdapter() const
200{
201 for (AdapterPtr adapter : std::as_const(m_adapters)) {
202 if (adapter->isPowered()) {
203 return adapter;
204 }
205 }
206 return AdapterPtr();
207}
208
209void ManagerPrivate::serviceRegistered()
210{
211 qCDebug(BLUEZQT) << "BlueZ service registered";
212 m_bluezRunning = true;
213
214 load();
215}
216
217void ManagerPrivate::serviceUnregistered()
218{
219 qCDebug(BLUEZQT) << "BlueZ service unregistered";
220
221 bool wasBtOperational = q->isBluetoothOperational();
222 m_bluezRunning = false;
223
224 if (wasBtOperational) {
225 Q_EMIT q->bluetoothOperationalChanged(false);
226 }
227
228 clear();
229 Q_EMIT q->operationalChanged(false);
230}
231
232void ManagerPrivate::interfacesAdded(const QDBusObjectPath &objectPath, const QVariantMapMap &interfaces)
233{
234 const QString &path = objectPath.path();
235 QVariantMapMap::const_iterator it;
236
237 for (it = interfaces.constBegin(); it != interfaces.constEnd(); ++it) {
238 if (it.key() == Strings::orgBluezAdapter1()) {
239 addAdapter(path, it.value());
240 } else if (it.key() == Strings::orgBluezDevice1()) {
241 addDevice(path, it.value());
242 }
243 }
244
245 for (auto it = m_adapters.cbegin(); it != m_adapters.cend(); ++it) {
246 if (path.startsWith(it.value()->ubi())) {
247 it.value()->d->interfacesAdded(path, interfaces);
248 break;
249 }
250 }
251
252 for (auto it = m_devices.cbegin(); it != m_devices.cend(); ++it) {
253 if (path.startsWith(it.value()->ubi())) {
254 it.value()->d->interfacesAdded(path, interfaces);
255 break;
256 }
257 }
258}
259
260void ManagerPrivate::interfacesRemoved(const QDBusObjectPath &objectPath, const QStringList &interfaces)
261{
262 const QString &path = objectPath.path();
263
264 for (const QString &interface : interfaces) {
265 if (interface == Strings::orgBluezAdapter1()) {
266 removeAdapter(path);
267 } else if (interface == Strings::orgBluezDevice1()) {
268 removeDevice(path);
269 }
270 }
271
272 for (auto it = m_adapters.cbegin(); it != m_adapters.cend(); ++it) {
273 if (path.startsWith(it.value()->ubi())) {
274 it.value()->d->interfacesRemoved(path, interfaces);
275 break;
276 }
277 }
278
279 for (auto it = m_devices.cbegin(); it != m_devices.cend(); ++it) {
280 if (path.startsWith(it.value()->ubi())) {
281 it.value()->d->interfacesRemoved(path, interfaces);
282 break;
283 }
284 }
285}
286
287void ManagerPrivate::adapterRemoved(const AdapterPtr &adapter)
288{
289 disconnect(adapter.data(), &Adapter::poweredChanged, this, &ManagerPrivate::adapterPoweredChanged);
290
291 // Current usable adapter was removed
292 if (adapter == m_usableAdapter) {
293 setUsableAdapter(findUsableAdapter());
294 }
295}
296
297void ManagerPrivate::adapterPoweredChanged(bool powered)
298{
299 Q_ASSERT(qobject_cast<Adapter *>(sender()));
300 AdapterPtr adapter = static_cast<Adapter *>(sender())->toSharedPtr();
301
302 // Current usable adapter was powered off
303 if (m_usableAdapter == adapter && !powered) {
304 setUsableAdapter(findUsableAdapter());
305 }
306
307 // Adapter was powered on, set it as usable
308 if (!m_usableAdapter && powered) {
309 setUsableAdapter(adapter);
310 }
311}
312
313void ManagerPrivate::rfkillStateChanged(Rfkill::State state)
314{
315 Q_UNUSED(state)
316
317 bool blocked = rfkillBlocked();
318 bool wasBtOperational = q->isBluetoothOperational();
319
320 if (m_bluetoothBlocked != blocked) {
321 m_bluetoothBlocked = blocked;
322 Q_EMIT q->bluetoothBlockedChanged(m_bluetoothBlocked);
323 if (wasBtOperational != q->isBluetoothOperational()) {
324 Q_EMIT q->bluetoothOperationalChanged(q->isBluetoothOperational());
325 }
326 }
327}
328
329void ManagerPrivate::addAdapter(const QString &adapterPath, const QVariantMap &properties)
330{
331 AdapterPtr adapter = AdapterPtr(new Adapter(adapterPath, properties));
332 adapter->d->q = adapter.toWeakRef();
333 m_adapters.insert(adapterPath, adapter);
334
335 Q_EMIT q->adapterAdded(adapter);
336
337 // Powered adapter was added, set it as usable
338 if (!m_usableAdapter && adapter->isPowered()) {
339 setUsableAdapter(adapter);
340 }
341
342 connect(adapter.data(), &Adapter::deviceAdded, q, &Manager::deviceAdded);
343 connect(adapter.data(), &Adapter::adapterRemoved, q, &Manager::adapterRemoved);
344 connect(adapter.data(), &Adapter::adapterChanged, q, &Manager::adapterChanged);
345 connect(adapter.data(), &Adapter::poweredChanged, this, &ManagerPrivate::adapterPoweredChanged);
346}
347
348void ManagerPrivate::addDevice(const QString &devicePath, const QVariantMap &properties)
349{
350 AdapterPtr adapter = m_adapters.value(properties.value(QStringLiteral("Adapter")).value<QDBusObjectPath>().path());
351 if (!adapter) {
352 return;
353 }
354
355 DevicePtr device = DevicePtr(new Device(devicePath, properties, adapter));
356 device->d->q = device.toWeakRef();
357 m_devices.insert(devicePath, device);
358 adapter->d->addDevice(device);
359
360 connect(device.data(), &Device::deviceRemoved, q, &Manager::deviceRemoved);
361 connect(device.data(), &Device::deviceChanged, q, &Manager::deviceChanged);
362}
363
364void ManagerPrivate::removeAdapter(const QString &adapterPath)
365{
366 AdapterPtr adapter = m_adapters.value(adapterPath);
367 if (!adapter) {
368 return;
369 }
370
371 // Make sure we always remove all devices before removing the adapter
372 const auto devices = adapter->devices();
373 for (const DevicePtr &device : devices) {
374 removeDevice(device->ubi());
375 }
376
377 m_adapters.remove(adapterPath);
378 Q_EMIT adapter->adapterRemoved(adapter);
379
380 if (m_adapters.isEmpty()) {
381 Q_EMIT q->allAdaptersRemoved();
382 }
383
384 disconnect(adapter.data(), &Adapter::adapterChanged, q, &Manager::adapterChanged);
385 disconnect(adapter.data(), &Adapter::poweredChanged, this, &ManagerPrivate::adapterPoweredChanged);
386}
387
388void ManagerPrivate::removeDevice(const QString &devicePath)
389{
390 DevicePtr device = m_devices.take(devicePath);
391 if (!device) {
392 return;
393 }
394
395 device->adapter()->d->removeDevice(device);
396
397 disconnect(device.data(), &Device::deviceChanged, q, &Manager::deviceChanged);
398}
399
400bool ManagerPrivate::rfkillBlocked() const
401{
402 return m_rfkill->state() == Rfkill::SoftBlocked || m_rfkill->state() == Rfkill::HardBlocked;
403}
404
405void ManagerPrivate::setUsableAdapter(const AdapterPtr &adapter)
406{
407 if (m_usableAdapter == adapter) {
408 return;
409 }
410
411 qCDebug(BLUEZQT) << "Setting usable adapter" << adapter;
412
413 bool wasBtOperational = q->isBluetoothOperational();
414
415 m_usableAdapter = adapter;
416 Q_EMIT q->usableAdapterChanged(m_usableAdapter);
417
418 if (wasBtOperational != q->isBluetoothOperational()) {
419 Q_EMIT q->bluetoothOperationalChanged(q->isBluetoothOperational());
420 }
421}
422
423void ManagerPrivate::propertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalidated)
424{
425 // Cut anything after device path to forward it to Device to handle
426 const QString path_full = message().path();
427 const QString path_device = path_full.section(QLatin1Char('/'), 0, 4);
428
429 QTimer::singleShot(0, this, [=]() {
430 AdapterPtr adapter = m_adapters.value(path_device);
431 if (adapter) {
432 adapter->d->propertiesChanged(interface, changed, invalidated);
433 return;
434 }
435 DevicePtr device = m_devices.value(path_device);
436 if (device) {
437 device->d->propertiesChanged(path_full, interface, changed, invalidated);
438 return;
439 }
440 qCDebug(BLUEZQT) << "Unhandled property change" << interface << changed << invalidated;
441 });
442}
443
444void ManagerPrivate::dummy()
445{
446}
447
448} // namespace BluezQt
449
450#include "moc_manager_p.cpp"
451

source code of bluez-qt/src/manager_p.cpp