1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtBluetooth module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include <QtCore/QLoggingCategory> |
41 | #include <QtCore/QRandomGenerator> |
42 | #include <QtDBus/QDBusContext> |
43 | |
44 | #include "qbluetoothlocaldevice.h" |
45 | #include "qbluetoothaddress.h" |
46 | #include "qbluetoothlocaldevice_p.h" |
47 | |
48 | #include "bluez/manager_p.h" |
49 | #include "bluez/adapter_p.h" |
50 | #include "bluez/agent_p.h" |
51 | #include "bluez/device_p.h" |
52 | #include "bluez/bluez5_helper_p.h" |
53 | #include "bluez/objectmanager_p.h" |
54 | #include "bluez/properties_p.h" |
55 | #include "bluez/adapter1_bluez5_p.h" |
56 | #include "bluez/device1_bluez5_p.h" |
57 | |
58 | QT_BEGIN_NAMESPACE |
59 | |
60 | Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) |
61 | |
62 | static const QLatin1String agentPath("/qt/agent" ); |
63 | |
64 | inline uint qHash(const QBluetoothAddress &address) |
65 | { |
66 | return ::qHash(address.toUInt64()); |
67 | } |
68 | |
69 | QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) : |
70 | QObject(parent), |
71 | d_ptr(new QBluetoothLocalDevicePrivate(this)) |
72 | { |
73 | } |
74 | |
75 | QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent) : |
76 | QObject(parent), |
77 | d_ptr(new QBluetoothLocalDevicePrivate(this, address)) |
78 | { |
79 | } |
80 | |
81 | QString QBluetoothLocalDevice::name() const |
82 | { |
83 | if (d_ptr->adapter) { |
84 | QDBusPendingReply<QVariantMap> reply = d_ptr->adapter->GetProperties(); |
85 | reply.waitForFinished(); |
86 | if (reply.isError()) |
87 | return QString(); |
88 | |
89 | return reply.value().value(QStringLiteral("Name" )).toString(); |
90 | } else if (d_ptr->adapterBluez5) { |
91 | return d_ptr->adapterBluez5->alias(); |
92 | } |
93 | |
94 | return QString(); |
95 | } |
96 | |
97 | QBluetoothAddress QBluetoothLocalDevice::address() const |
98 | { |
99 | if (d_ptr->adapter) { |
100 | QDBusPendingReply<QVariantMap> reply = d_ptr->adapter->GetProperties(); |
101 | reply.waitForFinished(); |
102 | if (reply.isError()) |
103 | return QBluetoothAddress(); |
104 | |
105 | return QBluetoothAddress(reply.value().value(QStringLiteral("Address" )).toString()); |
106 | } else if (d_ptr->adapterBluez5) { |
107 | return QBluetoothAddress(d_ptr->adapterBluez5->address()); |
108 | } |
109 | |
110 | return QBluetoothAddress(); |
111 | } |
112 | |
113 | void QBluetoothLocalDevice::powerOn() |
114 | { |
115 | if (d_ptr->adapter) |
116 | d_ptr->adapter->SetProperty(QStringLiteral("Powered" ), QDBusVariant(QVariant::fromValue(true))); |
117 | else if (d_ptr->adapterBluez5) |
118 | d_ptr->adapterBluez5->setPowered(true); |
119 | } |
120 | |
121 | void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode) |
122 | { |
123 | if (!isValid()) |
124 | return; |
125 | |
126 | Q_D(QBluetoothLocalDevice); |
127 | |
128 | switch (mode) { |
129 | case HostDiscoverableLimitedInquiry: |
130 | case HostDiscoverable: |
131 | if (hostMode() == HostPoweredOff) { |
132 | // We first have to wait for BT to be powered on, |
133 | // then we can set the host mode correctly |
134 | d->pendingHostModeChange = static_cast<int>(HostDiscoverable); |
135 | if (d->adapter) { |
136 | d->adapter->SetProperty(QStringLiteral("Powered" ), |
137 | QDBusVariant(QVariant::fromValue(true))); |
138 | } else { |
139 | d->adapterBluez5->setPowered(true); |
140 | } |
141 | } else { |
142 | if (d->adapter) { |
143 | d->adapter->SetProperty(QStringLiteral("Discoverable" ), |
144 | QDBusVariant(QVariant::fromValue(true))); |
145 | } else { |
146 | d->adapterBluez5->setDiscoverable(true); |
147 | } |
148 | } |
149 | break; |
150 | case HostConnectable: |
151 | if (hostMode() == HostPoweredOff) { |
152 | d->pendingHostModeChange = static_cast<int>(HostConnectable); |
153 | if (d->adapter) { |
154 | d->adapter->SetProperty(QStringLiteral("Powered" ), |
155 | QDBusVariant(QVariant::fromValue(true))); |
156 | } else { |
157 | d->adapterBluez5->setPowered(true); |
158 | } |
159 | } else { |
160 | if (d->adapter) { |
161 | d->adapter->SetProperty(QStringLiteral("Discoverable" ), |
162 | QDBusVariant(QVariant::fromValue(false))); |
163 | } else { |
164 | d->adapterBluez5->setDiscoverable(false); |
165 | } |
166 | } |
167 | break; |
168 | case HostPoweredOff: |
169 | if (d->adapter) { |
170 | d->adapter->SetProperty(QStringLiteral("Powered" ), |
171 | QDBusVariant(QVariant::fromValue(false))); |
172 | } else { |
173 | d->adapterBluez5->setPowered(false); |
174 | } |
175 | break; |
176 | } |
177 | } |
178 | |
179 | QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const |
180 | { |
181 | if (d_ptr->adapter) { |
182 | QDBusPendingReply<QVariantMap> reply = d_ptr->adapter->GetProperties(); |
183 | reply.waitForFinished(); |
184 | if (reply.isError()) |
185 | return HostPoweredOff; |
186 | |
187 | if (!reply.value().value(QStringLiteral("Powered" )).toBool()) |
188 | return HostPoweredOff; |
189 | else if (reply.value().value(QStringLiteral("Discoverable" )).toBool()) |
190 | return HostDiscoverable; |
191 | else if (reply.value().value(QStringLiteral("Powered" )).toBool()) |
192 | return HostConnectable; |
193 | } else if (d_ptr->adapterBluez5) { |
194 | if (!d_ptr->adapterBluez5->powered()) |
195 | return HostPoweredOff; |
196 | else if (d_ptr->adapterBluez5->discoverable()) |
197 | return HostDiscoverable; |
198 | else if (d_ptr->adapterBluez5->powered()) |
199 | return HostConnectable; |
200 | } |
201 | |
202 | return HostPoweredOff; |
203 | } |
204 | |
205 | QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const |
206 | { |
207 | return d_ptr->connectedDevices(); |
208 | } |
209 | |
210 | QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices() |
211 | { |
212 | QList<QBluetoothHostInfo> localDevices; |
213 | |
214 | if (isBluez5()) { |
215 | OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral("org.bluez" ), |
216 | QStringLiteral("/" ), |
217 | QDBusConnection::systemBus()); |
218 | QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects(); |
219 | reply.waitForFinished(); |
220 | if (reply.isError()) |
221 | return localDevices; |
222 | |
223 | ManagedObjectList managedObjectList = reply.value(); |
224 | for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) { |
225 | const InterfaceList &ifaceList = it.value(); |
226 | |
227 | for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) { |
228 | const QString &iface = jt.key(); |
229 | const QVariantMap &ifaceValues = jt.value(); |
230 | |
231 | if (iface == QStringLiteral("org.bluez.Adapter1" )) { |
232 | QBluetoothHostInfo hostInfo; |
233 | const QString temp = ifaceValues.value(QStringLiteral("Address" )).toString(); |
234 | |
235 | hostInfo.setAddress(QBluetoothAddress(temp)); |
236 | if (hostInfo.address().isNull()) |
237 | continue; |
238 | hostInfo.setName(ifaceValues.value(QStringLiteral("Name" )).toString()); |
239 | localDevices.append(hostInfo); |
240 | } |
241 | } |
242 | } |
243 | } else { |
244 | OrgBluezManagerInterface manager(QStringLiteral("org.bluez" ), QStringLiteral("/" ), |
245 | QDBusConnection::systemBus()); |
246 | |
247 | QDBusPendingReply<QList<QDBusObjectPath> > reply = manager.ListAdapters(); |
248 | reply.waitForFinished(); |
249 | if (reply.isError()) |
250 | return localDevices; |
251 | |
252 | const QList<QDBusObjectPath> paths = reply.value(); |
253 | for (const QDBusObjectPath &path : paths) { |
254 | QBluetoothHostInfo hostinfo; |
255 | OrgBluezAdapterInterface adapter(QStringLiteral("org.bluez" ), path.path(), |
256 | QDBusConnection::systemBus()); |
257 | |
258 | QDBusPendingReply<QVariantMap> reply = adapter.GetProperties(); |
259 | reply.waitForFinished(); |
260 | if (reply.isError()) |
261 | continue; |
262 | |
263 | hostinfo.setAddress(QBluetoothAddress( |
264 | reply.value().value(QStringLiteral("Address" )).toString())); |
265 | hostinfo.setName(reply.value().value(QStringLiteral("Name" )).toString()); |
266 | |
267 | localDevices.append(hostinfo); |
268 | } |
269 | } |
270 | |
271 | return localDevices; |
272 | } |
273 | |
274 | static inline OrgBluezDeviceInterface *getDevice(const QBluetoothAddress &address, |
275 | QBluetoothLocalDevicePrivate *d_ptr) |
276 | { |
277 | if (!d_ptr || !d_ptr->adapter) |
278 | return nullptr; |
279 | QDBusPendingReply<QDBusObjectPath> reply = d_ptr->adapter->FindDevice(address.toString()); |
280 | reply.waitForFinished(); |
281 | if (reply.isError()) { |
282 | qCWarning(QT_BT_BLUEZ) << Q_FUNC_INFO << "reply failed" << reply.error(); |
283 | return nullptr; |
284 | } |
285 | |
286 | QDBusObjectPath path = reply.value(); |
287 | |
288 | return new OrgBluezDeviceInterface(QStringLiteral("org.bluez" ), path.path(), |
289 | QDBusConnection::systemBus()); |
290 | } |
291 | |
292 | void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing) |
293 | { |
294 | if (!isValid() || address.isNull()) { |
295 | QMetaObject::invokeMethod(this, "error" , Qt::QueuedConnection, |
296 | Q_ARG(QBluetoothLocalDevice::Error, |
297 | QBluetoothLocalDevice::PairingError)); |
298 | return; |
299 | } |
300 | |
301 | const Pairing current_pairing = pairingStatus(address); |
302 | if (current_pairing == pairing) { |
303 | if (d_ptr->adapterBluez5) { |
304 | // A possibly running discovery or pending pairing request should be canceled |
305 | if (d_ptr->pairingDiscoveryTimer && d_ptr->pairingDiscoveryTimer->isActive()) { |
306 | d_ptr->pairingDiscoveryTimer->stop(); |
307 | } |
308 | |
309 | if (d_ptr->pairingTarget) { |
310 | qCDebug(QT_BT_BLUEZ) << "Cancelling pending pairing request to" << d_ptr->pairingTarget->address(); |
311 | QDBusPendingReply<> cancelReply = d_ptr->pairingTarget->CancelPairing(); |
312 | cancelReply.waitForFinished(); |
313 | delete d_ptr->pairingTarget; |
314 | d_ptr->pairingTarget = nullptr; |
315 | } |
316 | |
317 | } |
318 | QMetaObject::invokeMethod(this, "pairingFinished" , Qt::QueuedConnection, |
319 | Q_ARG(QBluetoothAddress, address), |
320 | Q_ARG(QBluetoothLocalDevice::Pairing, pairing)); |
321 | return; |
322 | } |
323 | |
324 | if (d_ptr->adapterBluez5) { |
325 | d_ptr->requestPairingBluez5(address, pairing); |
326 | return; |
327 | } |
328 | |
329 | if (pairing == Paired || pairing == AuthorizedPaired) { |
330 | d_ptr->address = address; |
331 | d_ptr->pairing = pairing; |
332 | |
333 | if (!d_ptr->agent) { |
334 | d_ptr->agent = new OrgBluezAgentAdaptor(d_ptr); |
335 | bool res = QDBusConnection::systemBus().registerObject(d_ptr->agent_path, d_ptr); |
336 | if (!res) { |
337 | QMetaObject::invokeMethod(this, "error" , Qt::QueuedConnection, |
338 | Q_ARG(QBluetoothLocalDevice::Error, |
339 | QBluetoothLocalDevice::PairingError)); |
340 | qCWarning(QT_BT_BLUEZ) << "Failed to register agent" ; |
341 | return; |
342 | } |
343 | } |
344 | |
345 | if (current_pairing == Paired && pairing == AuthorizedPaired) { |
346 | OrgBluezDeviceInterface *device = getDevice(address, d_ptr); |
347 | if (!device) { |
348 | QMetaObject::invokeMethod(this, "error" , Qt::QueuedConnection, |
349 | Q_ARG(QBluetoothLocalDevice::Error, |
350 | QBluetoothLocalDevice::PairingError)); |
351 | return; |
352 | } |
353 | QDBusPendingReply<> deviceReply |
354 | = device->SetProperty(QStringLiteral("Trusted" ), QDBusVariant(true)); |
355 | deviceReply.waitForFinished(); |
356 | if (deviceReply.isError()) { |
357 | qCWarning(QT_BT_BLUEZ) << Q_FUNC_INFO << "reply failed" << deviceReply.error(); |
358 | QMetaObject::invokeMethod(this, "error" , Qt::QueuedConnection, |
359 | Q_ARG(QBluetoothLocalDevice::Error, |
360 | QBluetoothLocalDevice::PairingError)); |
361 | delete device; |
362 | return; |
363 | } |
364 | delete device; |
365 | QMetaObject::invokeMethod(this, "pairingFinished" , Qt::QueuedConnection, |
366 | Q_ARG(QBluetoothAddress, address), |
367 | Q_ARG(QBluetoothLocalDevice::Pairing, |
368 | QBluetoothLocalDevice::AuthorizedPaired)); |
369 | } else if (current_pairing == AuthorizedPaired && pairing == Paired) { |
370 | OrgBluezDeviceInterface *device = getDevice(address, d_ptr); |
371 | if (!device) { |
372 | QMetaObject::invokeMethod(this, "error" , Qt::QueuedConnection, |
373 | Q_ARG(QBluetoothLocalDevice::Error, |
374 | QBluetoothLocalDevice::PairingError)); |
375 | return; |
376 | } |
377 | QDBusPendingReply<> deviceReply |
378 | = device->SetProperty(QStringLiteral("Trusted" ), QDBusVariant(false)); |
379 | deviceReply.waitForFinished(); |
380 | if (deviceReply.isError()) { |
381 | qCWarning(QT_BT_BLUEZ) << Q_FUNC_INFO << "reply failed" << deviceReply.error(); |
382 | QMetaObject::invokeMethod(this, "error" , Qt::QueuedConnection, |
383 | Q_ARG(QBluetoothLocalDevice::Error, |
384 | QBluetoothLocalDevice::PairingError)); |
385 | delete device; |
386 | return; |
387 | } |
388 | delete device; |
389 | QMetaObject::invokeMethod(this, "pairingFinished" , Qt::QueuedConnection, |
390 | Q_ARG(QBluetoothAddress, address), |
391 | Q_ARG(QBluetoothLocalDevice::Pairing, |
392 | QBluetoothLocalDevice::Paired)); |
393 | } else { |
394 | QDBusPendingReply<QDBusObjectPath> reply |
395 | = d_ptr->adapter->CreatePairedDevice(address.toString(), |
396 | QDBusObjectPath(d_ptr->agent_path), |
397 | QStringLiteral("NoInputNoOutput" )); |
398 | |
399 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); |
400 | connect(watcher, &QDBusPendingCallWatcher::finished, |
401 | d_ptr, &QBluetoothLocalDevicePrivate::pairingCompleted); |
402 | |
403 | if (reply.isError()) |
404 | qCWarning(QT_BT_BLUEZ) << Q_FUNC_INFO << reply.error() << d_ptr->agent_path; |
405 | } |
406 | } else if (pairing == Unpaired) { |
407 | QDBusPendingReply<QDBusObjectPath> reply = this->d_ptr->adapter->FindDevice( |
408 | address.toString()); |
409 | reply.waitForFinished(); |
410 | if (reply.isError()) { |
411 | qCWarning(QT_BT_BLUEZ) << Q_FUNC_INFO << "failed to find device" << reply.error(); |
412 | QMetaObject::invokeMethod(this, "error" , Qt::QueuedConnection, |
413 | Q_ARG(QBluetoothLocalDevice::Error, |
414 | QBluetoothLocalDevice::PairingError)); |
415 | return; |
416 | } |
417 | QDBusPendingReply<> removeReply = this->d_ptr->adapter->RemoveDevice(reply.value()); |
418 | removeReply.waitForFinished(); |
419 | if (removeReply.isError()) { |
420 | qCWarning(QT_BT_BLUEZ) << Q_FUNC_INFO << "failed to remove device" |
421 | << removeReply.error(); |
422 | QMetaObject::invokeMethod(this, "error" , Qt::QueuedConnection, |
423 | Q_ARG(QBluetoothLocalDevice::Error, |
424 | QBluetoothLocalDevice::PairingError)); |
425 | } else { |
426 | QMetaObject::invokeMethod(this, "pairingFinished" , Qt::QueuedConnection, |
427 | Q_ARG(QBluetoothAddress, address), |
428 | Q_ARG(QBluetoothLocalDevice::Pairing, |
429 | QBluetoothLocalDevice::Unpaired)); |
430 | } |
431 | } |
432 | } |
433 | |
434 | void QBluetoothLocalDevicePrivate::requestPairingBluez5(const QBluetoothAddress &targetAddress, |
435 | QBluetoothLocalDevice::Pairing targetPairing) |
436 | { |
437 | if (!managerBluez5) |
438 | return; |
439 | |
440 | //are we already discovering something? -> abort those attempts |
441 | if (pairingDiscoveryTimer && pairingDiscoveryTimer->isActive()) { |
442 | pairingDiscoveryTimer->stop(); |
443 | QtBluezDiscoveryManager::instance()->unregisterDiscoveryInterest( |
444 | adapterBluez5->path()); |
445 | } |
446 | |
447 | if (pairingTarget) { |
448 | delete pairingTarget; |
449 | pairingTarget = nullptr; |
450 | } |
451 | |
452 | // pairing implies that the device was found |
453 | // if we cannot find it we may have to turn on Discovery mode for a limited amount of time |
454 | |
455 | // check device doesn't already exist |
456 | QDBusPendingReply<ManagedObjectList> reply = managerBluez5->GetManagedObjects(); |
457 | reply.waitForFinished(); |
458 | if (reply.isError()) { |
459 | emit q_ptr->error(QBluetoothLocalDevice::PairingError); |
460 | return; |
461 | } |
462 | |
463 | ManagedObjectList managedObjectList = reply.value(); |
464 | for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) { |
465 | const QDBusObjectPath &path = it.key(); |
466 | const InterfaceList &ifaceList = it.value(); |
467 | |
468 | for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) { |
469 | const QString &iface = jt.key(); |
470 | |
471 | if (iface == QStringLiteral("org.bluez.Device1" )) { |
472 | |
473 | OrgBluezDevice1Interface device(QStringLiteral("org.bluez" ), |
474 | path.path(), |
475 | QDBusConnection::systemBus()); |
476 | if (targetAddress == QBluetoothAddress(device.address())) { |
477 | qCDebug(QT_BT_BLUEZ) << "Initiating direct pair to" << targetAddress.toString(); |
478 | //device exist -> directly work with it |
479 | processPairingBluez5(path.path(), targetPairing); |
480 | return; |
481 | } |
482 | } |
483 | } |
484 | } |
485 | |
486 | //no device matching -> turn on discovery |
487 | QtBluezDiscoveryManager::instance()->registerDiscoveryInterest(adapterBluez5->path()); |
488 | |
489 | address = targetAddress; |
490 | pairing = targetPairing; |
491 | if (!pairingDiscoveryTimer) { |
492 | pairingDiscoveryTimer = new QTimer(this); |
493 | pairingDiscoveryTimer->setSingleShot(true); |
494 | pairingDiscoveryTimer->setInterval(20000); //20s |
495 | connect(pairingDiscoveryTimer, &QTimer::timeout, |
496 | this, &QBluetoothLocalDevicePrivate::pairingDiscoveryTimedOut); |
497 | } |
498 | |
499 | qCDebug(QT_BT_BLUEZ) << "Initiating discovery for pairing on" << targetAddress.toString(); |
500 | pairingDiscoveryTimer->start(); |
501 | } |
502 | |
503 | /*! |
504 | * \internal |
505 | * |
506 | * Found a matching device. Now we must ensure its pairing/trusted state is as desired. |
507 | * If it has to be paired then we need another roundtrip through the event loop |
508 | * while we wait for the user to accept the pairing dialogs. |
509 | */ |
510 | void QBluetoothLocalDevicePrivate::processPairingBluez5(const QString &objectPath, |
511 | QBluetoothLocalDevice::Pairing target) |
512 | { |
513 | if (pairingTarget) |
514 | delete pairingTarget; |
515 | |
516 | //stop possibly running discovery |
517 | if (pairingDiscoveryTimer && pairingDiscoveryTimer->isActive()) { |
518 | pairingDiscoveryTimer->stop(); |
519 | |
520 | QtBluezDiscoveryManager::instance()->unregisterDiscoveryInterest( |
521 | adapterBluez5->path()); |
522 | } |
523 | |
524 | pairingTarget = new OrgBluezDevice1Interface(QStringLiteral("org.bluez" ), objectPath, |
525 | QDBusConnection::systemBus(), this); |
526 | const QBluetoothAddress targetAddress(pairingTarget->address()); |
527 | |
528 | Q_Q(QBluetoothLocalDevice); |
529 | |
530 | switch (target) { |
531 | case QBluetoothLocalDevice::Unpaired: { |
532 | delete pairingTarget; |
533 | pairingTarget = nullptr; |
534 | |
535 | QDBusPendingReply<> removeReply = adapterBluez5->RemoveDevice(QDBusObjectPath(objectPath)); |
536 | auto watcher = new QDBusPendingCallWatcher(removeReply, this); |
537 | connect(watcher, &QDBusPendingCallWatcher::finished, |
538 | this, [q, targetAddress](QDBusPendingCallWatcher* watcher){ |
539 | QDBusPendingReply<> reply = *watcher; |
540 | if (reply.isError()) |
541 | emit q->error(QBluetoothLocalDevice::PairingError); |
542 | else |
543 | emit q->pairingFinished(targetAddress, QBluetoothLocalDevice::Unpaired); |
544 | |
545 | watcher->deleteLater(); |
546 | }); |
547 | break; |
548 | } |
549 | case QBluetoothLocalDevice::Paired: |
550 | case QBluetoothLocalDevice::AuthorizedPaired: |
551 | pairing = target; |
552 | |
553 | if (!pairingTarget->paired()) { |
554 | qCDebug(QT_BT_BLUEZ) << "Sending pairing request to" << pairingTarget->address(); |
555 | //initiate the pairing |
556 | QDBusPendingReply<> pairReply = pairingTarget->Pair(); |
557 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pairReply, this); |
558 | connect(watcher, &QDBusPendingCallWatcher::finished, |
559 | this, &QBluetoothLocalDevicePrivate::pairingCompleted); |
560 | return; |
561 | } |
562 | |
563 | //already paired but Trust level must be adjusted |
564 | if (target == QBluetoothLocalDevice::AuthorizedPaired && !pairingTarget->trusted()) |
565 | pairingTarget->setTrusted(true); |
566 | else if (target == QBluetoothLocalDevice::Paired && pairingTarget->trusted()) |
567 | pairingTarget->setTrusted(false); |
568 | |
569 | delete pairingTarget; |
570 | pairingTarget = nullptr; |
571 | |
572 | emit q->pairingFinished(targetAddress, target); |
573 | |
574 | break; |
575 | default: |
576 | break; |
577 | } |
578 | } |
579 | |
580 | void QBluetoothLocalDevicePrivate::pairingDiscoveryTimedOut() |
581 | { |
582 | qCWarning(QT_BT_BLUEZ) << "Discovery for pairing purposes failed. Cannot find parable device." ; |
583 | |
584 | QtBluezDiscoveryManager::instance()->unregisterDiscoveryInterest( |
585 | adapterBluez5->path()); |
586 | |
587 | emit q_ptr->error(QBluetoothLocalDevice::PairingError); |
588 | } |
589 | |
590 | QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus( |
591 | const QBluetoothAddress &address) const |
592 | { |
593 | if (address.isNull()) |
594 | return Unpaired; |
595 | |
596 | if (d_ptr->adapter) { |
597 | OrgBluezDeviceInterface *device = getDevice(address, d_ptr); |
598 | |
599 | if (!device) |
600 | return Unpaired; |
601 | |
602 | QDBusPendingReply<QVariantMap> deviceReply = device->GetProperties(); |
603 | deviceReply.waitForFinished(); |
604 | if (deviceReply.isError()) { |
605 | delete device; |
606 | return Unpaired; |
607 | } |
608 | |
609 | QVariantMap map = deviceReply.value(); |
610 | |
611 | if (map.value(QStringLiteral("Trusted" )).toBool() && map.value(QStringLiteral("Paired" )).toBool()) { |
612 | delete device; |
613 | return AuthorizedPaired; |
614 | } else if (map.value(QStringLiteral("Paired" )).toBool()) { |
615 | delete device; |
616 | return Paired; |
617 | } |
618 | delete device; |
619 | } else if (d_ptr->adapterBluez5) { |
620 | |
621 | QDBusPendingReply<ManagedObjectList> reply = d_ptr->managerBluez5->GetManagedObjects(); |
622 | reply.waitForFinished(); |
623 | if (reply.isError()) |
624 | return Unpaired; |
625 | |
626 | ManagedObjectList managedObjectList = reply.value(); |
627 | for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) { |
628 | const QDBusObjectPath &path = it.key(); |
629 | const InterfaceList &ifaceList = it.value(); |
630 | |
631 | for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) { |
632 | const QString &iface = jt.key(); |
633 | |
634 | if (iface == QStringLiteral("org.bluez.Device1" )) { |
635 | |
636 | OrgBluezDevice1Interface device(QStringLiteral("org.bluez" ), |
637 | path.path(), |
638 | QDBusConnection::systemBus()); |
639 | |
640 | if (address == QBluetoothAddress(device.address())) { |
641 | if (device.trusted() && device.paired()) |
642 | return AuthorizedPaired; |
643 | else if (device.paired()) |
644 | return Paired; |
645 | else |
646 | return Unpaired; |
647 | } |
648 | } |
649 | } |
650 | } |
651 | } |
652 | |
653 | return Unpaired; |
654 | } |
655 | |
656 | QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, |
657 | QBluetoothAddress address) : |
658 | localAddress(address), |
659 | pendingHostModeChange(-1), |
660 | q_ptr(q) |
661 | { |
662 | registerQBluetoothLocalDeviceMetaType(); |
663 | |
664 | if (isBluez5()) |
665 | initializeAdapterBluez5(); |
666 | else |
667 | initializeAdapter(); |
668 | |
669 | connectDeviceChanges(); |
670 | } |
671 | |
672 | bool objectPathIsForThisDevice(const QString &adapterPath, const QString &objectPath) |
673 | { |
674 | return (!adapterPath.isEmpty() && objectPath.startsWith(adapterPath)); |
675 | } |
676 | |
677 | void QBluetoothLocalDevicePrivate::connectDeviceChanges() |
678 | { |
679 | if (adapter) { // invalid QBluetoothLocalDevice due to wrong local adapter address |
680 | createCache(); |
681 | connect(adapter, &OrgBluezAdapterInterface::PropertyChanged, |
682 | this, &QBluetoothLocalDevicePrivate::PropertyChanged); |
683 | connect(adapter, &OrgBluezAdapterInterface::DeviceCreated, |
684 | this, &QBluetoothLocalDevicePrivate::_q_deviceCreated); |
685 | connect(adapter, &OrgBluezAdapterInterface::DeviceRemoved, |
686 | this, &QBluetoothLocalDevicePrivate::_q_deviceRemoved); |
687 | } else if (adapterBluez5 && managerBluez5) { |
688 | //setup property change notifications for all existing devices |
689 | QDBusPendingReply<ManagedObjectList> reply = managerBluez5->GetManagedObjects(); |
690 | reply.waitForFinished(); |
691 | if (reply.isError()) |
692 | return; |
693 | |
694 | OrgFreedesktopDBusPropertiesInterface *monitor = nullptr; |
695 | |
696 | ManagedObjectList managedObjectList = reply.value(); |
697 | for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) { |
698 | const QDBusObjectPath &path = it.key(); |
699 | const InterfaceList &ifaceList = it.value(); |
700 | |
701 | // don't track connected devices from other adapters but the current |
702 | if (!objectPathIsForThisDevice(deviceAdapterPath, path.path())) |
703 | continue; |
704 | |
705 | for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) { |
706 | const QString &iface = jt.key(); |
707 | const QVariantMap &ifaceValues = jt.value(); |
708 | |
709 | if (iface == QStringLiteral("org.bluez.Device1" )) { |
710 | monitor = new OrgFreedesktopDBusPropertiesInterface(QStringLiteral("org.bluez" ), |
711 | path.path(), |
712 | QDBusConnection::systemBus(), this); |
713 | connect(monitor, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, |
714 | this, &QBluetoothLocalDevicePrivate::PropertiesChanged); |
715 | deviceChangeMonitors.insert(path.path(), monitor); |
716 | |
717 | if (ifaceValues.value(QStringLiteral("Connected" ), false).toBool()) { |
718 | QBluetoothAddress address(ifaceValues.value(QStringLiteral("Address" )).toString()); |
719 | connectedDevicesSet.insert(address); |
720 | } |
721 | } |
722 | } |
723 | } |
724 | } |
725 | } |
726 | |
727 | QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate() |
728 | { |
729 | delete msgConnection; |
730 | delete adapter; |
731 | delete adapterBluez5; |
732 | delete adapterProperties; |
733 | delete managerBluez5; |
734 | delete agent; |
735 | delete pairingTarget; |
736 | delete manager; |
737 | |
738 | qDeleteAll(devices); |
739 | qDeleteAll(deviceChangeMonitors); |
740 | } |
741 | |
742 | void QBluetoothLocalDevicePrivate::initializeAdapter() |
743 | { |
744 | if (adapter) |
745 | return; |
746 | |
747 | QScopedPointer<OrgBluezManagerInterface> man(new OrgBluezManagerInterface( |
748 | QStringLiteral("org.bluez" ), QStringLiteral("/" ), |
749 | QDBusConnection::systemBus())); |
750 | |
751 | if (localAddress == QBluetoothAddress()) { |
752 | QDBusPendingReply<QDBusObjectPath> reply = man->DefaultAdapter(); |
753 | reply.waitForFinished(); |
754 | if (reply.isError()) |
755 | return; |
756 | |
757 | adapter = new OrgBluezAdapterInterface(QStringLiteral("org.bluez" ), |
758 | reply.value().path(), QDBusConnection::systemBus()); |
759 | } else { |
760 | QDBusPendingReply<QList<QDBusObjectPath> > reply = man->ListAdapters(); |
761 | reply.waitForFinished(); |
762 | if (reply.isError()) |
763 | return; |
764 | |
765 | const QList<QDBusObjectPath> paths = reply.value(); |
766 | for (const QDBusObjectPath &path : paths) { |
767 | OrgBluezAdapterInterface *tmpAdapter |
768 | = new OrgBluezAdapterInterface(QStringLiteral("org.bluez" ), |
769 | path.path(), QDBusConnection::systemBus()); |
770 | |
771 | QDBusPendingReply<QVariantMap> reply = tmpAdapter->GetProperties(); |
772 | reply.waitForFinished(); |
773 | if (reply.isError()) { |
774 | delete tmpAdapter; |
775 | continue; |
776 | } |
777 | |
778 | QBluetoothAddress path_address(reply.value().value(QStringLiteral("Address" )).toString()); |
779 | |
780 | if (path_address == localAddress) { |
781 | adapter = tmpAdapter; |
782 | break; |
783 | } else { |
784 | delete tmpAdapter; |
785 | } |
786 | } |
787 | } |
788 | |
789 | // monitor case when local adapter is removed |
790 | manager = man.take(); |
791 | connect(manager, &OrgBluezManagerInterface::AdapterRemoved, |
792 | this, &QBluetoothLocalDevicePrivate::adapterRemoved); |
793 | |
794 | currentMode = static_cast<QBluetoothLocalDevice::HostMode>(-1); |
795 | if (adapter) { |
796 | connect(adapter, &OrgBluezAdapterInterface::PropertyChanged, |
797 | this, &QBluetoothLocalDevicePrivate::PropertyChanged); |
798 | |
799 | agent_path = agentPath; |
800 | agent_path.append(QString::fromLatin1("/%1" ).arg(QRandomGenerator::global()->generate())); |
801 | } |
802 | } |
803 | |
804 | void QBluetoothLocalDevicePrivate::initializeAdapterBluez5() |
805 | { |
806 | if (adapterBluez5) |
807 | return; |
808 | |
809 | //get all local adapters |
810 | if (!managerBluez5) |
811 | managerBluez5 = new OrgFreedesktopDBusObjectManagerInterface( |
812 | QStringLiteral("org.bluez" ), |
813 | QStringLiteral("/" ), |
814 | QDBusConnection::systemBus(), this); |
815 | |
816 | connect(managerBluez5, &OrgFreedesktopDBusObjectManagerInterface::InterfacesAdded, |
817 | this, &QBluetoothLocalDevicePrivate::InterfacesAdded); |
818 | connect(managerBluez5, &OrgFreedesktopDBusObjectManagerInterface::InterfacesRemoved, |
819 | this, &QBluetoothLocalDevicePrivate::InterfacesRemoved); |
820 | |
821 | bool ok = true; |
822 | const QString adapterPath = findAdapterForAddress(localAddress, &ok); |
823 | if (!ok || adapterPath.isEmpty()) |
824 | return; |
825 | |
826 | deviceAdapterPath = adapterPath; |
827 | adapterBluez5 = new OrgBluezAdapter1Interface(QStringLiteral("org.bluez" ), |
828 | adapterPath, |
829 | QDBusConnection::systemBus(), this); |
830 | |
831 | if (adapterBluez5) { |
832 | //hook up propertiesChanged for current adapter |
833 | adapterProperties = new OrgFreedesktopDBusPropertiesInterface( |
834 | QStringLiteral("org.bluez" ), adapterBluez5->path(), |
835 | QDBusConnection::systemBus(), this); |
836 | connect(adapterProperties, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, |
837 | this, &QBluetoothLocalDevicePrivate::PropertiesChanged); |
838 | } |
839 | |
840 | currentMode = static_cast<QBluetoothLocalDevice::HostMode>(-1); |
841 | } |
842 | |
843 | // Bluez 5 |
844 | void QBluetoothLocalDevicePrivate::PropertiesChanged(const QString &interface, |
845 | const QVariantMap &changed_properties, |
846 | const QStringList &/*invalidated_properties*/) |
847 | { |
848 | //qDebug() << "Change" << interface << changed_properties; |
849 | if (interface == QStringLiteral("org.bluez.Adapter1" )) { |
850 | //update host mode |
851 | if (changed_properties.contains(QStringLiteral("Discoverable" )) |
852 | || changed_properties.contains(QStringLiteral("Powered" ))) { |
853 | |
854 | QBluetoothLocalDevice::HostMode mode; |
855 | |
856 | if (!adapterBluez5->powered()) { |
857 | mode = QBluetoothLocalDevice::HostPoweredOff; |
858 | } else { |
859 | if (adapterBluez5->discoverable()) |
860 | mode = QBluetoothLocalDevice::HostDiscoverable; |
861 | else |
862 | mode = QBluetoothLocalDevice::HostConnectable; |
863 | |
864 | if (pendingHostModeChange != -1) { |
865 | |
866 | if (static_cast<int>(mode) != pendingHostModeChange) { |
867 | adapterBluez5->setDiscoverable( |
868 | pendingHostModeChange |
869 | == static_cast<int>(QBluetoothLocalDevice::HostDiscoverable)); |
870 | pendingHostModeChange = -1; |
871 | return; |
872 | } |
873 | pendingHostModeChange = -1; |
874 | } |
875 | } |
876 | |
877 | if (mode != currentMode) |
878 | emit q_ptr->hostModeStateChanged(mode); |
879 | |
880 | currentMode = mode; |
881 | } |
882 | } else if (interface == QStringLiteral("org.bluez.Device1" ) |
883 | && changed_properties.contains(QStringLiteral("Connected" ))) { |
884 | // update list of connected devices |
885 | OrgFreedesktopDBusPropertiesInterface *senderIface = |
886 | qobject_cast<OrgFreedesktopDBusPropertiesInterface*>(sender()); |
887 | if (!senderIface) |
888 | return; |
889 | |
890 | const QString currentPath = senderIface->path(); |
891 | bool isConnected = changed_properties.value(QStringLiteral("Connected" ), false).toBool(); |
892 | OrgBluezDevice1Interface device(QStringLiteral("org.bluez" ), currentPath, |
893 | QDBusConnection::systemBus()); |
894 | const QBluetoothAddress changedAddress(device.address()); |
895 | bool isInSet = connectedDevicesSet.contains(changedAddress); |
896 | if (isConnected && !isInSet) { |
897 | connectedDevicesSet.insert(changedAddress); |
898 | emit q_ptr->deviceConnected(changedAddress); |
899 | } else if (!isConnected && isInSet) { |
900 | connectedDevicesSet.remove(changedAddress); |
901 | emit q_ptr->deviceDisconnected(changedAddress); |
902 | } |
903 | } |
904 | } |
905 | |
906 | void QBluetoothLocalDevicePrivate::InterfacesAdded(const QDBusObjectPath &object_path, InterfaceList interfaces_and_properties) |
907 | { |
908 | if (interfaces_and_properties.contains(QStringLiteral("org.bluez.Device1" )) |
909 | && !deviceChangeMonitors.contains(object_path.path())) { |
910 | // a new device was added which we need to add to list of known devices |
911 | |
912 | if (objectPathIsForThisDevice(deviceAdapterPath, object_path.path())) { |
913 | OrgFreedesktopDBusPropertiesInterface *monitor = new OrgFreedesktopDBusPropertiesInterface( |
914 | QStringLiteral("org.bluez" ), |
915 | object_path.path(), |
916 | QDBusConnection::systemBus()); |
917 | connect(monitor, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, |
918 | this, &QBluetoothLocalDevicePrivate::PropertiesChanged); |
919 | deviceChangeMonitors.insert(object_path.path(), monitor); |
920 | |
921 | const QVariantMap ifaceValues = interfaces_and_properties.value(QStringLiteral("org.bluez.Device1" )); |
922 | if (ifaceValues.value(QStringLiteral("Connected" ), false).toBool()) { |
923 | QBluetoothAddress address(ifaceValues.value(QStringLiteral("Address" )).toString()); |
924 | connectedDevicesSet.insert(address); |
925 | emit q_ptr->deviceConnected(address); |
926 | } |
927 | } |
928 | } |
929 | |
930 | if (pairingDiscoveryTimer && pairingDiscoveryTimer->isActive() |
931 | && interfaces_and_properties.contains(QStringLiteral("org.bluez.Device1" ))) { |
932 | //device discovery for pairing found new remote device |
933 | OrgBluezDevice1Interface device(QStringLiteral("org.bluez" ), |
934 | object_path.path(), QDBusConnection::systemBus()); |
935 | if (!address.isNull() && address == QBluetoothAddress(device.address())) |
936 | processPairingBluez5(object_path.path(), pairing); |
937 | } |
938 | } |
939 | |
940 | void QBluetoothLocalDevicePrivate::InterfacesRemoved(const QDBusObjectPath &object_path, |
941 | const QStringList &interfaces) |
942 | { |
943 | if (deviceChangeMonitors.contains(object_path.path()) |
944 | && interfaces.contains(QLatin1String("org.bluez.Device1" ))) { |
945 | |
946 | if (objectPathIsForThisDevice(deviceAdapterPath, object_path.path())) { |
947 | //a device was removed |
948 | delete deviceChangeMonitors.take(object_path.path()); |
949 | |
950 | //the path contains the address (e.g.: /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX) |
951 | //-> use it to update current list of connected devices |
952 | QString addressString = object_path.path().right(17); |
953 | addressString.replace(QStringLiteral("_" ), QStringLiteral(":" )); |
954 | const QBluetoothAddress address(addressString); |
955 | bool found = connectedDevicesSet.remove(address); |
956 | if (found) |
957 | emit q_ptr->deviceDisconnected(address); |
958 | } |
959 | } |
960 | |
961 | if (adapterBluez5 |
962 | && object_path.path() == adapterBluez5->path() |
963 | && interfaces.contains(QLatin1String("org.bluez.Adapter1" ))) { |
964 | qCDebug(QT_BT_BLUEZ) << "Adapter" << adapterBluez5->path() << "was removed" ; |
965 | // current adapter was removed -> invalidate the instance |
966 | delete adapterBluez5; |
967 | adapterBluez5 = nullptr; |
968 | managerBluez5->deleteLater(); |
969 | managerBluez5 = nullptr; |
970 | delete adapterProperties; |
971 | adapterProperties = nullptr; |
972 | |
973 | delete pairingTarget; |
974 | pairingTarget = nullptr; |
975 | |
976 | // turn off connectivity monitoring |
977 | qDeleteAll(deviceChangeMonitors); |
978 | deviceChangeMonitors.clear(); |
979 | connectedDevicesSet.clear(); |
980 | } |
981 | } |
982 | |
983 | bool QBluetoothLocalDevicePrivate::isValid() const |
984 | { |
985 | return adapter || adapterBluez5; |
986 | } |
987 | |
988 | // Bluez 4 |
989 | void QBluetoothLocalDevicePrivate::adapterRemoved(const QDBusObjectPath &devicePath) |
990 | { |
991 | if (!adapter ) |
992 | return; |
993 | |
994 | if (adapter->path() != devicePath.path()) |
995 | return; |
996 | |
997 | qCDebug(QT_BT_BLUEZ) << "Adapter" << devicePath.path() |
998 | << "was removed. Invalidating object." ; |
999 | // the current adapter was removed |
1000 | delete adapter; |
1001 | adapter = nullptr; |
1002 | manager->deleteLater(); |
1003 | manager = nullptr; |
1004 | |
1005 | // stop all pairing related activities |
1006 | if (agent) { |
1007 | QDBusConnection::systemBus().unregisterObject(agent_path); |
1008 | delete agent; |
1009 | agent = nullptr; |
1010 | } |
1011 | |
1012 | delete msgConnection; |
1013 | msgConnection = nullptr; |
1014 | |
1015 | // stop all connectivity monitoring |
1016 | qDeleteAll(devices); |
1017 | devices.clear(); |
1018 | connectedDevicesSet.clear(); |
1019 | } |
1020 | |
1021 | void QBluetoothLocalDevicePrivate::RequestConfirmation(const QDBusObjectPath &in0, uint in1) |
1022 | { |
1023 | Q_UNUSED(in0); |
1024 | Q_Q(QBluetoothLocalDevice); |
1025 | setDelayedReply(true); |
1026 | msgConfirmation = message(); |
1027 | msgConnection = new QDBusConnection(connection()); |
1028 | emit q->pairingDisplayConfirmation(address, QString::fromLatin1("%1" ).arg(in1)); |
1029 | } |
1030 | |
1031 | void QBluetoothLocalDevicePrivate::_q_deviceCreated(const QDBusObjectPath &device) |
1032 | { |
1033 | OrgBluezDeviceInterface *deviceInterface |
1034 | = new OrgBluezDeviceInterface(QStringLiteral("org.bluez" ), |
1035 | device.path(), |
1036 | QDBusConnection::systemBus(), this); |
1037 | connect(deviceInterface, &OrgBluezDeviceInterface::PropertyChanged, |
1038 | this, &QBluetoothLocalDevicePrivate::_q_devicePropertyChanged); |
1039 | devices << deviceInterface; |
1040 | QDBusPendingReply<QVariantMap> properties |
1041 | = deviceInterface->asyncCall(QStringLiteral("GetProperties" )); |
1042 | |
1043 | properties.waitForFinished(); |
1044 | if (!properties.isValid()) { |
1045 | qCritical() << "Unable to get device properties from: " << device.path(); |
1046 | return; |
1047 | } |
1048 | const QBluetoothAddress address |
1049 | = QBluetoothAddress(properties.value().value(QStringLiteral("Address" )).toString()); |
1050 | const bool connected = properties.value().value(QStringLiteral("Connected" )).toBool(); |
1051 | |
1052 | if (connected) { |
1053 | connectedDevicesSet.insert(address); |
1054 | emit q_ptr->deviceConnected(address); |
1055 | } else { |
1056 | connectedDevicesSet.remove(address); |
1057 | emit q_ptr->deviceDisconnected(address); |
1058 | } |
1059 | } |
1060 | |
1061 | void QBluetoothLocalDevicePrivate::_q_deviceRemoved(const QDBusObjectPath &device) |
1062 | { |
1063 | for (OrgBluezDeviceInterface *deviceInterface : qAsConst(devices)) { |
1064 | if (deviceInterface->path() == device.path()) { |
1065 | devices.remove(deviceInterface); |
1066 | delete deviceInterface; // deviceDisconnected is already emitted by _q_devicePropertyChanged |
1067 | break; |
1068 | } |
1069 | } |
1070 | } |
1071 | |
1072 | void QBluetoothLocalDevicePrivate::_q_devicePropertyChanged(const QString &property, |
1073 | const QDBusVariant &value) |
1074 | { |
1075 | OrgBluezDeviceInterface *deviceInterface = qobject_cast<OrgBluezDeviceInterface *>(sender()); |
1076 | if (deviceInterface && property == QLatin1String("Connected" )) { |
1077 | QDBusPendingReply<QVariantMap> propertiesReply = deviceInterface->GetProperties(); |
1078 | propertiesReply.waitForFinished(); |
1079 | if (propertiesReply.isError()) { |
1080 | qCWarning(QT_BT_BLUEZ) << propertiesReply.error().message(); |
1081 | return; |
1082 | } |
1083 | const QVariantMap properties = propertiesReply.value(); |
1084 | const QBluetoothAddress address |
1085 | = QBluetoothAddress(properties.value(QStringLiteral("Address" )).toString()); |
1086 | const bool connected = value.variant().toBool(); |
1087 | |
1088 | if (connected) { |
1089 | connectedDevicesSet.insert(address); |
1090 | emit q_ptr->deviceConnected(address); |
1091 | } else { |
1092 | connectedDevicesSet.remove(address); |
1093 | emit q_ptr->deviceDisconnected(address); |
1094 | } |
1095 | } |
1096 | } |
1097 | |
1098 | void QBluetoothLocalDevicePrivate::createCache() |
1099 | { |
1100 | if (!adapter) |
1101 | return; |
1102 | |
1103 | QDBusPendingReply<QList<QDBusObjectPath> > reply = adapter->ListDevices(); |
1104 | reply.waitForFinished(); |
1105 | if (reply.isError()) { |
1106 | qCWarning(QT_BT_BLUEZ) << reply.error().message(); |
1107 | return; |
1108 | } |
1109 | const QList<QDBusObjectPath> knownDevices = reply.value(); |
1110 | for (const QDBusObjectPath &device : knownDevices) { |
1111 | OrgBluezDeviceInterface *deviceInterface = |
1112 | new OrgBluezDeviceInterface(QStringLiteral("org.bluez" ), |
1113 | device.path(), |
1114 | QDBusConnection::systemBus(), this); |
1115 | connect(deviceInterface, &OrgBluezDeviceInterface::PropertyChanged, |
1116 | this, &QBluetoothLocalDevicePrivate::_q_devicePropertyChanged); |
1117 | devices << deviceInterface; |
1118 | |
1119 | QDBusPendingReply<QVariantMap> properties |
1120 | = deviceInterface->asyncCall(QStringLiteral("GetProperties" )); |
1121 | properties.waitForFinished(); |
1122 | if (!properties.isValid()) { |
1123 | qCWarning(QT_BT_BLUEZ) << "Unable to get properties for device " << device.path(); |
1124 | return; |
1125 | } |
1126 | |
1127 | if (properties.value().value(QStringLiteral("Connected" )).toBool()) { |
1128 | connectedDevicesSet.insert( |
1129 | QBluetoothAddress(properties.value().value(QStringLiteral("Address" )).toString())); |
1130 | } |
1131 | } |
1132 | } |
1133 | |
1134 | QList<QBluetoothAddress> QBluetoothLocalDevicePrivate::connectedDevices() const |
1135 | { |
1136 | return connectedDevicesSet.toList(); |
1137 | } |
1138 | |
1139 | void QBluetoothLocalDevice::pairingConfirmation(bool confirmation) |
1140 | { |
1141 | if (!d_ptr |
1142 | || !d_ptr->msgConfirmation.isReplyRequired() |
1143 | || !d_ptr->msgConnection) |
1144 | return; |
1145 | |
1146 | if (confirmation) { |
1147 | QDBusMessage msg = d_ptr->msgConfirmation.createReply(QVariant(true)); |
1148 | d_ptr->msgConnection->send(msg); |
1149 | } else { |
1150 | QDBusMessage error |
1151 | = d_ptr->msgConfirmation.createErrorReply(QDBusError::AccessDenied, |
1152 | QStringLiteral("Pairing rejected" )); |
1153 | d_ptr->msgConnection->send(error); |
1154 | } |
1155 | delete d_ptr->msgConnection; |
1156 | d_ptr->msgConnection = nullptr; |
1157 | } |
1158 | |
1159 | QString QBluetoothLocalDevicePrivate::RequestPinCode(const QDBusObjectPath &in0) |
1160 | { |
1161 | Q_UNUSED(in0) |
1162 | Q_Q(QBluetoothLocalDevice); |
1163 | qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO << in0.path(); |
1164 | // seeded in constructor, 6 digit pin |
1165 | QString pin = QString::fromLatin1("%1" ).arg(QRandomGenerator::global()->bounded(1000000)); |
1166 | pin = QString::fromLatin1("%1" ).arg(pin, 6, QLatin1Char('0')); |
1167 | |
1168 | emit q->pairingDisplayPinCode(address, pin); |
1169 | return pin; |
1170 | } |
1171 | |
1172 | void QBluetoothLocalDevicePrivate::pairingCompleted(QDBusPendingCallWatcher *watcher) |
1173 | { |
1174 | Q_Q(QBluetoothLocalDevice); |
1175 | QDBusPendingReply<> reply = *watcher; |
1176 | |
1177 | if (reply.isError()) { |
1178 | qCWarning(QT_BT_BLUEZ) << "Failed to create pairing" << reply.error().name(); |
1179 | if (reply.error().name() != QStringLiteral("org.bluez.Error.AuthenticationCanceled" )) |
1180 | emit q->error(QBluetoothLocalDevice::PairingError); |
1181 | watcher->deleteLater(); |
1182 | return; |
1183 | } |
1184 | |
1185 | if (adapter) { |
1186 | QDBusPendingReply<QDBusObjectPath> findReply = adapter->FindDevice(address.toString()); |
1187 | findReply.waitForFinished(); |
1188 | if (findReply.isError()) { |
1189 | qCWarning(QT_BT_BLUEZ) << Q_FUNC_INFO << "failed to find device" << findReply.error(); |
1190 | emit q->error(QBluetoothLocalDevice::PairingError); |
1191 | watcher->deleteLater(); |
1192 | return; |
1193 | } |
1194 | |
1195 | OrgBluezDeviceInterface device(QStringLiteral("org.bluez" ), findReply.value().path(), |
1196 | QDBusConnection::systemBus()); |
1197 | |
1198 | if (pairing == QBluetoothLocalDevice::AuthorizedPaired) { |
1199 | device.SetProperty(QStringLiteral("Trusted" ), QDBusVariant(QVariant(true))); |
1200 | emit q->pairingFinished(address, QBluetoothLocalDevice::AuthorizedPaired); |
1201 | } |
1202 | else { |
1203 | device.SetProperty(QStringLiteral("Trusted" ), QDBusVariant(QVariant(false))); |
1204 | emit q->pairingFinished(address, QBluetoothLocalDevice::Paired); |
1205 | } |
1206 | } else if (adapterBluez5) { |
1207 | if (!pairingTarget) { |
1208 | qCWarning(QT_BT_BLUEZ) << "Pairing target expected but found null pointer." ; |
1209 | emit q->error(QBluetoothLocalDevice::PairingError); |
1210 | watcher->deleteLater(); |
1211 | return; |
1212 | } |
1213 | |
1214 | if (!pairingTarget->paired()) { |
1215 | qCWarning(QT_BT_BLUEZ) << "Device was not paired as requested" ; |
1216 | emit q->error(QBluetoothLocalDevice::PairingError); |
1217 | watcher->deleteLater(); |
1218 | return; |
1219 | } |
1220 | |
1221 | const QBluetoothAddress targetAddress(pairingTarget->address()); |
1222 | |
1223 | if (pairing == QBluetoothLocalDevice::AuthorizedPaired && !pairingTarget->trusted()) |
1224 | pairingTarget->setTrusted(true); |
1225 | else if (pairing == QBluetoothLocalDevice::Paired && pairingTarget->trusted()) |
1226 | pairingTarget->setTrusted(false); |
1227 | |
1228 | delete pairingTarget; |
1229 | pairingTarget = nullptr; |
1230 | |
1231 | emit q->pairingFinished(targetAddress, pairing); |
1232 | } |
1233 | |
1234 | watcher->deleteLater(); |
1235 | } |
1236 | |
1237 | void QBluetoothLocalDevicePrivate::Authorize(const QDBusObjectPath &in0, const QString &in1) |
1238 | { |
1239 | Q_UNUSED(in0) |
1240 | Q_UNUSED(in1) |
1241 | // TODO implement this |
1242 | qCDebug(QT_BT_BLUEZ) << "Got authorize for" << in0.path() << in1; |
1243 | } |
1244 | |
1245 | void QBluetoothLocalDevicePrivate::Cancel() |
1246 | { |
1247 | // TODO implement this |
1248 | qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO; |
1249 | } |
1250 | |
1251 | void QBluetoothLocalDevicePrivate::Release() |
1252 | { |
1253 | // TODO implement this |
1254 | qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO; |
1255 | } |
1256 | |
1257 | void QBluetoothLocalDevicePrivate::ConfirmModeChange(const QString &in0) |
1258 | { |
1259 | Q_UNUSED(in0) |
1260 | // TODO implement this |
1261 | qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO << in0; |
1262 | } |
1263 | |
1264 | void QBluetoothLocalDevicePrivate::DisplayPasskey(const QDBusObjectPath &in0, uint in1, uchar in2) |
1265 | { |
1266 | Q_UNUSED(in0) |
1267 | Q_UNUSED(in1) |
1268 | Q_UNUSED(in2) |
1269 | // TODO implement this |
1270 | qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO << in0.path() << in1 << in2; |
1271 | } |
1272 | |
1273 | uint QBluetoothLocalDevicePrivate::RequestPasskey(const QDBusObjectPath &in0) |
1274 | { |
1275 | Q_UNUSED(in0); |
1276 | qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO; |
1277 | return (QRandomGenerator::global()->bounded(1000000)); |
1278 | } |
1279 | |
1280 | // Bluez 4 |
1281 | void QBluetoothLocalDevicePrivate::PropertyChanged(QString property, QDBusVariant value) |
1282 | { |
1283 | Q_UNUSED(value); |
1284 | |
1285 | if (property != QLatin1String("Powered" ) |
1286 | && property != QLatin1String("Discoverable" )) |
1287 | return; |
1288 | |
1289 | Q_Q(QBluetoothLocalDevice); |
1290 | QBluetoothLocalDevice::HostMode mode; |
1291 | |
1292 | QDBusPendingReply<QVariantMap> reply = adapter->GetProperties(); |
1293 | reply.waitForFinished(); |
1294 | if (reply.isError()) { |
1295 | qCWarning(QT_BT_BLUEZ) << "Failed to get bluetooth properties for mode change" ; |
1296 | return; |
1297 | } |
1298 | |
1299 | QVariantMap map = reply.value(); |
1300 | |
1301 | if (!map.value(QStringLiteral("Powered" )).toBool()) { |
1302 | mode = QBluetoothLocalDevice::HostPoweredOff; |
1303 | } else { |
1304 | if (map.value(QStringLiteral("Discoverable" )).toBool()) |
1305 | mode = QBluetoothLocalDevice::HostDiscoverable; |
1306 | else |
1307 | mode = QBluetoothLocalDevice::HostConnectable; |
1308 | |
1309 | if (pendingHostModeChange != -1) { |
1310 | if ((int)mode != pendingHostModeChange) { |
1311 | if (property == QStringLiteral("Powered" )) |
1312 | return; |
1313 | if (pendingHostModeChange == (int)QBluetoothLocalDevice::HostDiscoverable) { |
1314 | adapter->SetProperty(QStringLiteral("Discoverable" ), |
1315 | QDBusVariant(QVariant::fromValue(true))); |
1316 | } else { |
1317 | adapter->SetProperty(QStringLiteral("Discoverable" ), |
1318 | QDBusVariant(QVariant::fromValue(false))); |
1319 | } |
1320 | pendingHostModeChange = -1; |
1321 | return; |
1322 | } |
1323 | } |
1324 | } |
1325 | |
1326 | if (mode != currentMode) |
1327 | emit q->hostModeStateChanged(mode); |
1328 | |
1329 | currentMode = mode; |
1330 | } |
1331 | |
1332 | #include "moc_qbluetoothlocaldevice_p.cpp" |
1333 | |
1334 | QT_END_NAMESPACE |
1335 | |