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
58QT_BEGIN_NAMESPACE
59
60Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
61
62static const QLatin1String agentPath("/qt/agent");
63
64inline uint qHash(const QBluetoothAddress &address)
65{
66 return ::qHash(address.toUInt64());
67}
68
69QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) :
70 QObject(parent),
71 d_ptr(new QBluetoothLocalDevicePrivate(this))
72{
73}
74
75QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent) :
76 QObject(parent),
77 d_ptr(new QBluetoothLocalDevicePrivate(this, address))
78{
79}
80
81QString 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
97QBluetoothAddress 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
113void 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
121void 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
179QBluetoothLocalDevice::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
205QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
206{
207 return d_ptr->connectedDevices();
208}
209
210QList<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
274static 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
292void 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
434void 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 */
510void 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
580void 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
590QBluetoothLocalDevice::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
656QBluetoothLocalDevicePrivate::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
672bool objectPathIsForThisDevice(const QString &adapterPath, const QString &objectPath)
673{
674 return (!adapterPath.isEmpty() && objectPath.startsWith(adapterPath));
675}
676
677void 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
727QBluetoothLocalDevicePrivate::~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
742void 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
804void 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
844void 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
906void 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
940void 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
983bool QBluetoothLocalDevicePrivate::isValid() const
984{
985 return adapter || adapterBluez5;
986}
987
988// Bluez 4
989void 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
1021void 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
1031void 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
1061void 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
1072void 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
1098void 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
1134QList<QBluetoothAddress> QBluetoothLocalDevicePrivate::connectedDevices() const
1135{
1136 return connectedDevicesSet.toList();
1137}
1138
1139void 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
1159QString 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
1172void 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
1237void 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
1245void QBluetoothLocalDevicePrivate::Cancel()
1246{
1247 // TODO implement this
1248 qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO;
1249}
1250
1251void QBluetoothLocalDevicePrivate::Release()
1252{
1253 // TODO implement this
1254 qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO;
1255}
1256
1257void 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
1264void 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
1273uint 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
1281void 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
1334QT_END_NAMESPACE
1335