1 | // Copyright (C) 2016 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 <QtCore/QLoggingCategory> |
5 | #include <QtCore/QRandomGenerator> |
6 | #include <QtDBus/QDBusContext> |
7 | |
8 | #include "qbluetoothlocaldevice.h" |
9 | #include "qbluetoothaddress.h" |
10 | #include "qbluetoothlocaldevice_p.h" |
11 | |
12 | #include "bluez/bluez5_helper_p.h" |
13 | #include "bluez/objectmanager_p.h" |
14 | #include "bluez/properties_p.h" |
15 | #include "bluez/adapter1_bluez5_p.h" |
16 | #include "bluez/device1_bluez5_p.h" |
17 | |
18 | QT_BEGIN_NAMESPACE |
19 | |
20 | Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) |
21 | |
22 | QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) : |
23 | QObject(parent), |
24 | d_ptr(new QBluetoothLocalDevicePrivate(this)) |
25 | { |
26 | d_ptr->currentMode = hostMode(); |
27 | } |
28 | |
29 | QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent) : |
30 | QObject(parent), |
31 | d_ptr(new QBluetoothLocalDevicePrivate(this, address)) |
32 | { |
33 | d_ptr->currentMode = hostMode(); |
34 | } |
35 | |
36 | QString QBluetoothLocalDevice::name() const |
37 | { |
38 | if (d_ptr->adapter) |
39 | return d_ptr->adapter->alias(); |
40 | |
41 | return QString(); |
42 | } |
43 | |
44 | QBluetoothAddress QBluetoothLocalDevice::address() const |
45 | { |
46 | if (d_ptr->adapter) |
47 | return QBluetoothAddress(d_ptr->adapter->address()); |
48 | |
49 | return QBluetoothAddress(); |
50 | } |
51 | |
52 | void QBluetoothLocalDevice::powerOn() |
53 | { |
54 | if (d_ptr->adapter) |
55 | d_ptr->adapter->setPowered(true); |
56 | } |
57 | |
58 | void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode) |
59 | { |
60 | if (!isValid()) |
61 | return; |
62 | |
63 | Q_D(QBluetoothLocalDevice); |
64 | |
65 | if (d->pendingHostModeChange != -1) { |
66 | qCWarning(QT_BT_BLUEZ) << "setHostMode() ignored due to already pending mode change" ; |
67 | return; |
68 | } |
69 | |
70 | switch (mode) { |
71 | case HostDiscoverableLimitedInquiry: |
72 | case HostDiscoverable: |
73 | if (hostMode() == HostPoweredOff) { |
74 | // We first have to wait for BT to be powered on, |
75 | // then we can set the host mode correctly |
76 | d->pendingHostModeChange = static_cast<int>(HostDiscoverable); |
77 | d->adapter->setPowered(true); |
78 | } else { |
79 | d->adapter->setDiscoverable(true); |
80 | } |
81 | break; |
82 | case HostConnectable: |
83 | if (hostMode() == HostPoweredOff) { |
84 | d->pendingHostModeChange = static_cast<int>(HostConnectable); |
85 | d->adapter->setPowered(true); |
86 | } else { |
87 | d->adapter->setDiscoverable(false); |
88 | } |
89 | break; |
90 | case HostPoweredOff: |
91 | d->adapter->setPowered(false); |
92 | break; |
93 | } |
94 | } |
95 | |
96 | QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const |
97 | { |
98 | if (d_ptr->adapter) { |
99 | if (!d_ptr->adapter->powered()) |
100 | return HostPoweredOff; |
101 | else if (d_ptr->adapter->discoverable()) |
102 | return HostDiscoverable; |
103 | else if (d_ptr->adapter->powered()) |
104 | return HostConnectable; |
105 | } |
106 | |
107 | return HostPoweredOff; |
108 | } |
109 | |
110 | QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const |
111 | { |
112 | return d_ptr->connectedDevices(); |
113 | } |
114 | |
115 | QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices() |
116 | { |
117 | QList<QBluetoothHostInfo> localDevices; |
118 | |
119 | initializeBluez5(); |
120 | OrgFreedesktopDBusObjectManagerInterface manager( |
121 | QStringLiteral("org.bluez" ), QStringLiteral("/" ), QDBusConnection::systemBus()); |
122 | QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects(); |
123 | reply.waitForFinished(); |
124 | if (reply.isError()) |
125 | return localDevices; |
126 | |
127 | ManagedObjectList managedObjectList = reply.value(); |
128 | for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); |
129 | it != managedObjectList.constEnd(); ++it) { |
130 | const InterfaceList &ifaceList = it.value(); |
131 | |
132 | for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); |
133 | ++jt) { |
134 | const QString &iface = jt.key(); |
135 | const QVariantMap &ifaceValues = jt.value(); |
136 | |
137 | if (iface == QStringLiteral("org.bluez.Adapter1" )) { |
138 | QBluetoothHostInfo hostInfo; |
139 | const QString temp = ifaceValues.value(QStringLiteral("Address" )).toString(); |
140 | |
141 | hostInfo.setAddress(QBluetoothAddress(temp)); |
142 | if (hostInfo.address().isNull()) |
143 | continue; |
144 | hostInfo.setName(ifaceValues.value(QStringLiteral("Name" )).toString()); |
145 | localDevices.append(t: hostInfo); |
146 | } |
147 | } |
148 | } |
149 | return localDevices; |
150 | } |
151 | |
152 | void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing) |
153 | { |
154 | if (!isValid() || address.isNull()) { |
155 | QMetaObject::invokeMethod(obj: this, member: "errorOccurred" , c: Qt::QueuedConnection, |
156 | Q_ARG(QBluetoothLocalDevice::Error, |
157 | QBluetoothLocalDevice::PairingError)); |
158 | return; |
159 | } |
160 | |
161 | const Pairing current_pairing = pairingStatus(address); |
162 | if (current_pairing == pairing) { |
163 | if (d_ptr->adapter) { |
164 | // A possibly running discovery or pending pairing request should be canceled |
165 | if (d_ptr->pairingDiscoveryTimer && d_ptr->pairingDiscoveryTimer->isActive()) { |
166 | d_ptr->pairingDiscoveryTimer->stop(); |
167 | } |
168 | |
169 | if (d_ptr->pairingTarget) { |
170 | qCDebug(QT_BT_BLUEZ) << "Cancelling pending pairing request to" << d_ptr->pairingTarget->address(); |
171 | QDBusPendingReply<> cancelReply = d_ptr->pairingTarget->CancelPairing(); |
172 | cancelReply.waitForFinished(); |
173 | delete d_ptr->pairingTarget; |
174 | d_ptr->pairingTarget = nullptr; |
175 | } |
176 | } |
177 | QMetaObject::invokeMethod(obj: this, member: "pairingFinished" , c: Qt::QueuedConnection, |
178 | Q_ARG(QBluetoothAddress, address), |
179 | Q_ARG(QBluetoothLocalDevice::Pairing, pairing)); |
180 | return; |
181 | } |
182 | |
183 | d_ptr->requestPairing(address, targetPairing: pairing); |
184 | } |
185 | |
186 | void QBluetoothLocalDevicePrivate::requestPairing(const QBluetoothAddress &targetAddress, |
187 | QBluetoothLocalDevice::Pairing targetPairing) |
188 | { |
189 | if (!isValid()) |
190 | return; |
191 | |
192 | //are we already discovering something? -> abort those attempts |
193 | if (pairingDiscoveryTimer && pairingDiscoveryTimer->isActive()) { |
194 | pairingDiscoveryTimer->stop(); |
195 | QtBluezDiscoveryManager::instance()->unregisterDiscoveryInterest(adapterPath: adapter->path()); |
196 | } |
197 | |
198 | if (pairingTarget) { |
199 | delete pairingTarget; |
200 | pairingTarget = nullptr; |
201 | } |
202 | |
203 | // pairing implies that the device was found |
204 | // if we cannot find it we may have to turn on Discovery mode for a limited amount of time |
205 | |
206 | // check device doesn't already exist |
207 | QDBusPendingReply<ManagedObjectList> reply = manager->GetManagedObjects(); |
208 | reply.waitForFinished(); |
209 | if (reply.isError()) { |
210 | emit q_ptr->errorOccurred(error: QBluetoothLocalDevice::PairingError); |
211 | return; |
212 | } |
213 | |
214 | ManagedObjectList managedObjectList = reply.value(); |
215 | for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) { |
216 | const QDBusObjectPath &path = it.key(); |
217 | const InterfaceList &ifaceList = it.value(); |
218 | |
219 | for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) { |
220 | const QString &iface = jt.key(); |
221 | |
222 | if (iface == QStringLiteral("org.bluez.Device1" )) { |
223 | |
224 | OrgBluezDevice1Interface device(QStringLiteral("org.bluez" ), |
225 | path.path(), |
226 | QDBusConnection::systemBus()); |
227 | if (targetAddress == QBluetoothAddress(device.address())) { |
228 | qCDebug(QT_BT_BLUEZ) << "Initiating direct pair to" << targetAddress.toString(); |
229 | //device exist -> directly work with it |
230 | processPairing(objectPath: path.path(), target: targetPairing); |
231 | return; |
232 | } |
233 | } |
234 | } |
235 | } |
236 | |
237 | //no device matching -> turn on discovery |
238 | QtBluezDiscoveryManager::instance()->registerDiscoveryInterest(adapterPath: adapter->path()); |
239 | |
240 | address = targetAddress; |
241 | pairing = targetPairing; |
242 | if (!pairingDiscoveryTimer) { |
243 | pairingDiscoveryTimer = new QTimer(this); |
244 | pairingDiscoveryTimer->setSingleShot(true); |
245 | pairingDiscoveryTimer->setInterval(20000); //20s |
246 | connect(sender: pairingDiscoveryTimer, signal: &QTimer::timeout, |
247 | context: this, slot: &QBluetoothLocalDevicePrivate::pairingDiscoveryTimedOut); |
248 | } |
249 | |
250 | qCDebug(QT_BT_BLUEZ) << "Initiating discovery for pairing on" << targetAddress.toString(); |
251 | pairingDiscoveryTimer->start(); |
252 | } |
253 | |
254 | /*! |
255 | * \internal |
256 | * |
257 | * Found a matching device. Now we must ensure its pairing/trusted state is as desired. |
258 | * If it has to be paired then we need another roundtrip through the event loop |
259 | * while we wait for the user to accept the pairing dialogs. |
260 | */ |
261 | void QBluetoothLocalDevicePrivate::processPairing(const QString &objectPath, |
262 | QBluetoothLocalDevice::Pairing target) |
263 | { |
264 | if (pairingTarget) |
265 | delete pairingTarget; |
266 | |
267 | //stop possibly running discovery |
268 | if (pairingDiscoveryTimer && pairingDiscoveryTimer->isActive()) { |
269 | pairingDiscoveryTimer->stop(); |
270 | |
271 | QtBluezDiscoveryManager::instance()->unregisterDiscoveryInterest(adapterPath: adapter->path()); |
272 | } |
273 | |
274 | pairingTarget = new OrgBluezDevice1Interface(QStringLiteral("org.bluez" ), objectPath, |
275 | QDBusConnection::systemBus(), this); |
276 | const QBluetoothAddress targetAddress(pairingTarget->address()); |
277 | |
278 | Q_Q(QBluetoothLocalDevice); |
279 | |
280 | switch (target) { |
281 | case QBluetoothLocalDevice::Unpaired: { |
282 | delete pairingTarget; |
283 | pairingTarget = nullptr; |
284 | |
285 | QDBusPendingReply<> removeReply = adapter->RemoveDevice(device: QDBusObjectPath(objectPath)); |
286 | auto watcher = new QDBusPendingCallWatcher(removeReply, this); |
287 | connect(sender: watcher, signal: &QDBusPendingCallWatcher::finished, |
288 | context: this, slot: [q, targetAddress](QDBusPendingCallWatcher* watcher){ |
289 | QDBusPendingReply<> reply = *watcher; |
290 | if (reply.isError()) |
291 | emit q->errorOccurred(error: QBluetoothLocalDevice::PairingError); |
292 | else |
293 | emit q->pairingFinished(address: targetAddress, pairing: QBluetoothLocalDevice::Unpaired); |
294 | |
295 | watcher->deleteLater(); |
296 | }); |
297 | break; |
298 | } |
299 | case QBluetoothLocalDevice::Paired: |
300 | case QBluetoothLocalDevice::AuthorizedPaired: |
301 | pairing = target; |
302 | |
303 | if (!pairingTarget->paired()) { |
304 | qCDebug(QT_BT_BLUEZ) << "Sending pairing request to" << pairingTarget->address(); |
305 | //initiate the pairing |
306 | QDBusPendingReply<> pairReply = pairingTarget->Pair(); |
307 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pairReply, this); |
308 | connect(sender: watcher, signal: &QDBusPendingCallWatcher::finished, |
309 | context: this, slot: &QBluetoothLocalDevicePrivate::pairingCompleted); |
310 | return; |
311 | } |
312 | |
313 | //already paired but Trust level must be adjusted |
314 | if (target == QBluetoothLocalDevice::AuthorizedPaired && !pairingTarget->trusted()) |
315 | pairingTarget->setTrusted(true); |
316 | else if (target == QBluetoothLocalDevice::Paired && pairingTarget->trusted()) |
317 | pairingTarget->setTrusted(false); |
318 | |
319 | delete pairingTarget; |
320 | pairingTarget = nullptr; |
321 | |
322 | emit q->pairingFinished(address: targetAddress, pairing: target); |
323 | |
324 | break; |
325 | default: |
326 | break; |
327 | } |
328 | } |
329 | |
330 | void QBluetoothLocalDevicePrivate::pairingDiscoveryTimedOut() |
331 | { |
332 | qCWarning(QT_BT_BLUEZ) << "Discovery for pairing purposes failed. Cannot find parable device." ; |
333 | |
334 | QtBluezDiscoveryManager::instance()->unregisterDiscoveryInterest(adapterPath: adapter->path()); |
335 | |
336 | emit q_ptr->errorOccurred(error: QBluetoothLocalDevice::PairingError); |
337 | } |
338 | |
339 | QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus( |
340 | const QBluetoothAddress &address) const |
341 | { |
342 | if (address.isNull()) |
343 | return Unpaired; |
344 | |
345 | if (isValid()) |
346 | { |
347 | QDBusPendingReply<ManagedObjectList> reply = d_ptr->manager->GetManagedObjects(); |
348 | reply.waitForFinished(); |
349 | if (reply.isError()) |
350 | return Unpaired; |
351 | |
352 | ManagedObjectList managedObjectList = reply.value(); |
353 | for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) { |
354 | const QDBusObjectPath &path = it.key(); |
355 | const InterfaceList &ifaceList = it.value(); |
356 | |
357 | for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) { |
358 | const QString &iface = jt.key(); |
359 | |
360 | if (iface == QStringLiteral("org.bluez.Device1" )) { |
361 | |
362 | OrgBluezDevice1Interface device(QStringLiteral("org.bluez" ), |
363 | path.path(), |
364 | QDBusConnection::systemBus()); |
365 | |
366 | if (address == QBluetoothAddress(device.address())) { |
367 | if (device.trusted() && device.paired()) |
368 | return AuthorizedPaired; |
369 | else if (device.paired()) |
370 | return Paired; |
371 | else |
372 | return Unpaired; |
373 | } |
374 | } |
375 | } |
376 | } |
377 | } |
378 | |
379 | return Unpaired; |
380 | } |
381 | |
382 | QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, |
383 | QBluetoothAddress address) : |
384 | localAddress(address), |
385 | pendingHostModeChange(-1), |
386 | q_ptr(q) |
387 | { |
388 | registerQBluetoothLocalDeviceMetaType(); |
389 | initializeBluez5(); |
390 | initializeAdapter(); |
391 | |
392 | connectDeviceChanges(); |
393 | } |
394 | |
395 | bool objectPathIsForThisDevice(const QString &adapterPath, const QString &objectPath) |
396 | { |
397 | return (!adapterPath.isEmpty() && objectPath.startsWith(s: adapterPath)); |
398 | } |
399 | |
400 | void QBluetoothLocalDevicePrivate::connectDeviceChanges() |
401 | { |
402 | if (isValid()) { |
403 | //setup property change notifications for all existing devices |
404 | QDBusPendingReply<ManagedObjectList> reply = manager->GetManagedObjects(); |
405 | reply.waitForFinished(); |
406 | if (reply.isError()) |
407 | return; |
408 | |
409 | OrgFreedesktopDBusPropertiesInterface *monitor = nullptr; |
410 | |
411 | ManagedObjectList managedObjectList = reply.value(); |
412 | for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) { |
413 | const QDBusObjectPath &path = it.key(); |
414 | const InterfaceList &ifaceList = it.value(); |
415 | |
416 | // don't track connected devices from other adapters but the current |
417 | if (!objectPathIsForThisDevice(adapterPath: deviceAdapterPath, objectPath: path.path())) |
418 | continue; |
419 | |
420 | for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) { |
421 | const QString &iface = jt.key(); |
422 | const QVariantMap &ifaceValues = jt.value(); |
423 | |
424 | if (iface == QStringLiteral("org.bluez.Device1" )) { |
425 | monitor = new OrgFreedesktopDBusPropertiesInterface(QStringLiteral("org.bluez" ), |
426 | path.path(), |
427 | QDBusConnection::systemBus(), this); |
428 | connect(sender: monitor, signal: &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, |
429 | context: this, slot: &QBluetoothLocalDevicePrivate::PropertiesChanged); |
430 | deviceChangeMonitors.insert(key: path.path(), value: monitor); |
431 | |
432 | if (ifaceValues.value(QStringLiteral("Connected" ), defaultValue: false).toBool()) { |
433 | QBluetoothAddress address(ifaceValues.value(QStringLiteral("Address" )).toString()); |
434 | connectedDevicesSet.insert(value: address); |
435 | } |
436 | } |
437 | } |
438 | } |
439 | } |
440 | } |
441 | |
442 | QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate() |
443 | { |
444 | delete adapter; |
445 | delete adapterProperties; |
446 | delete manager; |
447 | delete pairingTarget; |
448 | |
449 | qDeleteAll(c: deviceChangeMonitors); |
450 | } |
451 | |
452 | void QBluetoothLocalDevicePrivate::initializeAdapter() |
453 | { |
454 | if (adapter) |
455 | return; |
456 | |
457 | //get all local adapters |
458 | if (!manager) |
459 | manager = new OrgFreedesktopDBusObjectManagerInterface( |
460 | QStringLiteral("org.bluez" ), |
461 | QStringLiteral("/" ), |
462 | QDBusConnection::systemBus(), this); |
463 | |
464 | connect(sender: manager, signal: &OrgFreedesktopDBusObjectManagerInterface::InterfacesAdded, |
465 | context: this, slot: &QBluetoothLocalDevicePrivate::InterfacesAdded); |
466 | connect(sender: manager, signal: &OrgFreedesktopDBusObjectManagerInterface::InterfacesRemoved, |
467 | context: this, slot: &QBluetoothLocalDevicePrivate::InterfacesRemoved); |
468 | |
469 | bool ok = true; |
470 | const QString adapterPath = findAdapterForAddress(wantedAddress: localAddress, ok: &ok); |
471 | if (!ok || adapterPath.isEmpty()) |
472 | return; |
473 | |
474 | deviceAdapterPath = adapterPath; |
475 | adapter = new OrgBluezAdapter1Interface(QStringLiteral("org.bluez" ), adapterPath, |
476 | QDBusConnection::systemBus(), this); |
477 | |
478 | if (adapter) { |
479 | //hook up propertiesChanged for current adapter |
480 | adapterProperties = new OrgFreedesktopDBusPropertiesInterface( |
481 | QStringLiteral("org.bluez" ), adapter->path(), |
482 | QDBusConnection::systemBus(), this); |
483 | connect(sender: adapterProperties, signal: &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, |
484 | context: this, slot: &QBluetoothLocalDevicePrivate::PropertiesChanged); |
485 | } |
486 | } |
487 | |
488 | void QBluetoothLocalDevicePrivate::PropertiesChanged(const QString &interface, |
489 | const QVariantMap &changed_properties, |
490 | const QStringList &/*invalidated_properties*/, |
491 | const QDBusMessage &) |
492 | { |
493 | //qDebug() << "Change" << interface << changed_properties; |
494 | if (interface == QStringLiteral("org.bluez.Adapter1" )) { |
495 | //update host mode |
496 | if (changed_properties.contains(QStringLiteral("Discoverable" )) |
497 | || changed_properties.contains(QStringLiteral("Powered" ))) { |
498 | |
499 | QBluetoothLocalDevice::HostMode mode; |
500 | |
501 | if (!adapter->powered()) { |
502 | mode = QBluetoothLocalDevice::HostPoweredOff; |
503 | } else { |
504 | if (adapter->discoverable()) |
505 | mode = QBluetoothLocalDevice::HostDiscoverable; |
506 | else |
507 | mode = QBluetoothLocalDevice::HostConnectable; |
508 | |
509 | if (pendingHostModeChange != -1) { |
510 | |
511 | if (static_cast<int>(mode) != pendingHostModeChange) { |
512 | adapter->setDiscoverable( |
513 | pendingHostModeChange |
514 | == static_cast<int>(QBluetoothLocalDevice::HostDiscoverable)); |
515 | pendingHostModeChange = -1; |
516 | return; |
517 | } |
518 | pendingHostModeChange = -1; |
519 | } |
520 | } |
521 | |
522 | if (mode != currentMode) |
523 | emit q_ptr->hostModeStateChanged(state: mode); |
524 | |
525 | currentMode = mode; |
526 | } |
527 | } else if (interface == QStringLiteral("org.bluez.Device1" ) |
528 | && changed_properties.contains(QStringLiteral("Connected" ))) { |
529 | // update list of connected devices |
530 | OrgFreedesktopDBusPropertiesInterface *senderIface = |
531 | qobject_cast<OrgFreedesktopDBusPropertiesInterface*>(object: sender()); |
532 | if (!senderIface) |
533 | return; |
534 | |
535 | const QString currentPath = senderIface->path(); |
536 | bool isConnected = changed_properties.value(QStringLiteral("Connected" ), defaultValue: false).toBool(); |
537 | OrgBluezDevice1Interface device(QStringLiteral("org.bluez" ), currentPath, |
538 | QDBusConnection::systemBus()); |
539 | const QBluetoothAddress changedAddress(device.address()); |
540 | bool isInSet = connectedDevicesSet.contains(value: changedAddress); |
541 | if (isConnected && !isInSet) { |
542 | connectedDevicesSet.insert(value: changedAddress); |
543 | emit q_ptr->deviceConnected(address: changedAddress); |
544 | } else if (!isConnected && isInSet) { |
545 | connectedDevicesSet.remove(value: changedAddress); |
546 | emit q_ptr->deviceDisconnected(address: changedAddress); |
547 | } |
548 | } |
549 | } |
550 | |
551 | void QBluetoothLocalDevicePrivate::InterfacesAdded(const QDBusObjectPath &object_path, InterfaceList interfaces_and_properties) |
552 | { |
553 | if (interfaces_and_properties.contains(QStringLiteral("org.bluez.Device1" )) |
554 | && !deviceChangeMonitors.contains(key: object_path.path())) { |
555 | // a new device was added which we need to add to list of known devices |
556 | |
557 | if (objectPathIsForThisDevice(adapterPath: deviceAdapterPath, objectPath: object_path.path())) { |
558 | OrgFreedesktopDBusPropertiesInterface *monitor = new OrgFreedesktopDBusPropertiesInterface( |
559 | QStringLiteral("org.bluez" ), |
560 | object_path.path(), |
561 | QDBusConnection::systemBus()); |
562 | connect(sender: monitor, signal: &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, |
563 | context: this, slot: &QBluetoothLocalDevicePrivate::PropertiesChanged); |
564 | deviceChangeMonitors.insert(key: object_path.path(), value: monitor); |
565 | |
566 | const QVariantMap ifaceValues = interfaces_and_properties.value(QStringLiteral("org.bluez.Device1" )); |
567 | if (ifaceValues.value(QStringLiteral("Connected" ), defaultValue: false).toBool()) { |
568 | QBluetoothAddress address(ifaceValues.value(QStringLiteral("Address" )).toString()); |
569 | connectedDevicesSet.insert(value: address); |
570 | emit q_ptr->deviceConnected(address); |
571 | } |
572 | } |
573 | } |
574 | |
575 | if (pairingDiscoveryTimer && pairingDiscoveryTimer->isActive() |
576 | && interfaces_and_properties.contains(QStringLiteral("org.bluez.Device1" ))) { |
577 | //device discovery for pairing found new remote device |
578 | OrgBluezDevice1Interface device(QStringLiteral("org.bluez" ), |
579 | object_path.path(), QDBusConnection::systemBus()); |
580 | if (!address.isNull() && address == QBluetoothAddress(device.address())) |
581 | processPairing(objectPath: object_path.path(), target: pairing); |
582 | } |
583 | } |
584 | |
585 | void QBluetoothLocalDevicePrivate::InterfacesRemoved(const QDBusObjectPath &object_path, |
586 | const QStringList &interfaces) |
587 | { |
588 | if (deviceChangeMonitors.contains(key: object_path.path()) |
589 | && interfaces.contains(str: QLatin1String("org.bluez.Device1" ))) { |
590 | |
591 | if (objectPathIsForThisDevice(adapterPath: deviceAdapterPath, objectPath: object_path.path())) { |
592 | //a device was removed |
593 | delete deviceChangeMonitors.take(key: object_path.path()); |
594 | |
595 | //the path contains the address (e.g.: /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX) |
596 | //-> use it to update current list of connected devices |
597 | QString addressString = object_path.path().right(n: 17); |
598 | addressString.replace(QStringLiteral("_" ), QStringLiteral(":" )); |
599 | const QBluetoothAddress address(addressString); |
600 | bool found = connectedDevicesSet.remove(value: address); |
601 | if (found) |
602 | emit q_ptr->deviceDisconnected(address); |
603 | } |
604 | } |
605 | |
606 | if (adapter && object_path.path() == adapter->path() |
607 | && interfaces.contains(str: QLatin1String("org.bluez.Adapter1" ))) { |
608 | qCDebug(QT_BT_BLUEZ) << "Adapter" << adapter->path() << "was removed" ; |
609 | // current adapter was removed -> invalidate the instance |
610 | delete adapter; |
611 | adapter = nullptr; |
612 | manager->deleteLater(); |
613 | manager = nullptr; |
614 | delete adapterProperties; |
615 | adapterProperties = nullptr; |
616 | |
617 | delete pairingTarget; |
618 | pairingTarget = nullptr; |
619 | |
620 | // turn off connectivity monitoring |
621 | qDeleteAll(c: deviceChangeMonitors); |
622 | deviceChangeMonitors.clear(); |
623 | connectedDevicesSet.clear(); |
624 | } |
625 | } |
626 | |
627 | bool QBluetoothLocalDevicePrivate::isValid() const |
628 | { |
629 | return adapter && manager; |
630 | } |
631 | |
632 | QList<QBluetoothAddress> QBluetoothLocalDevicePrivate::connectedDevices() const |
633 | { |
634 | return connectedDevicesSet.values(); |
635 | } |
636 | |
637 | void QBluetoothLocalDevicePrivate::pairingCompleted(QDBusPendingCallWatcher *watcher) |
638 | { |
639 | Q_Q(QBluetoothLocalDevice); |
640 | QDBusPendingReply<> reply = *watcher; |
641 | |
642 | if (reply.isError()) { |
643 | qCWarning(QT_BT_BLUEZ) << "Failed to create pairing" << reply.error().name(); |
644 | if (reply.error().name() != QStringLiteral("org.bluez.Error.AuthenticationCanceled" )) |
645 | emit q->errorOccurred(error: QBluetoothLocalDevice::PairingError); |
646 | watcher->deleteLater(); |
647 | return; |
648 | } |
649 | |
650 | if (adapter) { |
651 | if (!pairingTarget) { |
652 | qCWarning(QT_BT_BLUEZ) << "Pairing target expected but found null pointer." ; |
653 | emit q->errorOccurred(error: QBluetoothLocalDevice::PairingError); |
654 | watcher->deleteLater(); |
655 | return; |
656 | } |
657 | |
658 | if (!pairingTarget->paired()) { |
659 | qCWarning(QT_BT_BLUEZ) << "Device was not paired as requested" ; |
660 | emit q->errorOccurred(error: QBluetoothLocalDevice::PairingError); |
661 | watcher->deleteLater(); |
662 | return; |
663 | } |
664 | |
665 | const QBluetoothAddress targetAddress(pairingTarget->address()); |
666 | |
667 | if (pairing == QBluetoothLocalDevice::AuthorizedPaired && !pairingTarget->trusted()) |
668 | pairingTarget->setTrusted(true); |
669 | else if (pairing == QBluetoothLocalDevice::Paired && pairingTarget->trusted()) |
670 | pairingTarget->setTrusted(false); |
671 | |
672 | delete pairingTarget; |
673 | pairingTarget = nullptr; |
674 | |
675 | emit q->pairingFinished(address: targetAddress, pairing); |
676 | } |
677 | |
678 | watcher->deleteLater(); |
679 | } |
680 | |
681 | QT_END_NAMESPACE |
682 | |
683 | #include "moc_qbluetoothlocaldevice_p.cpp" |
684 | |