| 1 | // Copyright (C) 2017 The Qt Company Ltd. | 
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only | 
| 3 |  | 
| 4 | #include <QtCore/QLoggingCategory> | 
| 5 |  | 
| 6 | #include "remotedevicemanager_p.h" | 
| 7 | #include "bluez5_helper_p.h" | 
| 8 | #include "device1_bluez5_p.h" | 
| 9 | #include "objectmanager_p.h" | 
| 10 |  | 
| 11 | QT_BEGIN_NAMESPACE | 
| 12 |  | 
| 13 | Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) | 
| 14 |  | 
| 15 | /*! | 
| 16 |  * Convenience wrapper around org.bluez.Device1 management | 
| 17 |  * | 
| 18 |  * Very simple and not thread safe. | 
| 19 |  */ | 
| 20 |  | 
| 21 | RemoteDeviceManager::RemoteDeviceManager( | 
| 22 |         const QBluetoothAddress &address, QObject *parent) | 
| 23 |     : QObject(parent), localAddress(address) | 
| 24 | { | 
| 25 |     initializeBluez5(); | 
| 26 |  | 
| 27 |     bool ok = false; | 
| 28 |     adapterPath = findAdapterForAddress(wantedAddress: address, ok: &ok); | 
| 29 |     if (!ok || adapterPath.isEmpty()) { | 
| 30 |         qCWarning(QT_BT_BLUEZ) << "Cannot initialize RemoteDeviceManager" ; | 
| 31 |     } | 
| 32 | } | 
| 33 |  | 
| 34 | bool RemoteDeviceManager::scheduleJob(JobType job, const QList<QBluetoothAddress> &remoteDevices) | 
| 35 | { | 
| 36 |     if (adapterPath.isEmpty()) | 
| 37 |         return false; | 
| 38 |  | 
| 39 |     for (const auto& remote : remoteDevices) | 
| 40 |         jobQueue.push_back(x: std::make_pair(x&: job, y: remote)); | 
| 41 |  | 
| 42 |     QTimer::singleShot(interval: 0, receiver: this, slot: [this](){ runQueue(); }); | 
| 43 |     return true; | 
| 44 | } | 
| 45 |  | 
| 46 | void RemoteDeviceManager::runQueue() | 
| 47 | { | 
| 48 |     if (jobInProgress || adapterPath.isEmpty()) | 
| 49 |         return; | 
| 50 |  | 
| 51 |     if (jobQueue.empty()) | 
| 52 |         return; | 
| 53 |  | 
| 54 |     jobInProgress = true; | 
| 55 |     switch (jobQueue.front().first) { | 
| 56 |     case JobType::JobDisconnectDevice: | 
| 57 |         disconnectDevice(remote: jobQueue.front().second); | 
| 58 |         break; | 
| 59 |     default: | 
| 60 |         break; | 
| 61 |     } | 
| 62 | } | 
| 63 |  | 
| 64 | void RemoteDeviceManager::prepareNextJob() | 
| 65 | { | 
| 66 |     Q_ASSERT(!jobQueue.empty()); | 
| 67 |  | 
| 68 |     jobQueue.pop_front(); | 
| 69 |     jobInProgress = false; | 
| 70 |  | 
| 71 |     qCDebug(QT_BT_BLUEZ) << "RemoteDeviceManager job queue status:"  << jobQueue.empty(); | 
| 72 |     if (jobQueue.empty()) | 
| 73 |         emit finished(); | 
| 74 |     else | 
| 75 |         runQueue(); | 
| 76 | } | 
| 77 |  | 
| 78 | void RemoteDeviceManager::disconnectDevice(const QBluetoothAddress &remote) | 
| 79 | { | 
| 80 |     // collect initial set of information | 
| 81 |     OrgFreedesktopDBusObjectManagerInterface managerBluez5( | 
| 82 |                                                QStringLiteral("org.bluez" ), | 
| 83 |                                                QStringLiteral("/" ), | 
| 84 |                                                QDBusConnection::systemBus(), this); | 
| 85 |     QDBusPendingReply<ManagedObjectList> reply = managerBluez5.GetManagedObjects(); | 
| 86 |     reply.waitForFinished(); | 
| 87 |     if (reply.isError()) { | 
| 88 |         QTimer::singleShot(interval: 0, receiver: this, slot: [this](){ prepareNextJob(); }); | 
| 89 |         return; | 
| 90 |     } | 
| 91 |  | 
| 92 |     bool jobStarted = false; | 
| 93 |     ManagedObjectList managedObjectList = reply.value(); | 
| 94 |     for (auto it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) { | 
| 95 |         const QDBusObjectPath &path = it.key(); | 
| 96 |         const InterfaceList &ifaceList = it.value(); | 
| 97 |  | 
| 98 |         for (auto jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) { | 
| 99 |             const QString &iface = jt.key(); | 
| 100 |  | 
| 101 |             if (path.path().indexOf(s: adapterPath) != 0) | 
| 102 |                 continue; //devices whose path doesn't start with same path we skip | 
| 103 |  | 
| 104 |             if (iface != QStringLiteral("org.bluez.Device1" )) | 
| 105 |                 continue; | 
| 106 |  | 
| 107 |             const QBluetoothAddress foundAddress(ifaceList.value(key: iface).value(QStringLiteral("Address" )).toString()); | 
| 108 |             if (foundAddress != remote) | 
| 109 |                 continue; | 
| 110 |  | 
| 111 |             // found the correct Device1 path | 
| 112 |             OrgBluezDevice1Interface* device1 = new OrgBluezDevice1Interface(QStringLiteral("org.bluez" ), | 
| 113 |                                                                              path.path(), | 
| 114 |                                                                              QDBusConnection::systemBus(), | 
| 115 |                                                                              this); | 
| 116 |             QDBusPendingReply<> asyncReply = device1->Disconnect(); | 
| 117 |             QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(asyncReply, this); | 
| 118 |             const auto watcherFinished = [this, device1](QDBusPendingCallWatcher* call) { | 
| 119 |                 call->deleteLater(); | 
| 120 |                 device1->deleteLater(); | 
| 121 |                 prepareNextJob(); | 
| 122 |             }; | 
| 123 |             connect(sender: watcher, signal: &QDBusPendingCallWatcher::finished, context: this, slot: watcherFinished); | 
| 124 |             jobStarted = true; | 
| 125 |             break; | 
| 126 |         } | 
| 127 |     } | 
| 128 |  | 
| 129 |     if (!jobStarted) { | 
| 130 |         qCDebug(QT_BT_BLUEZ) << "RemoteDeviceManager JobDisconnectDevice failed" ; | 
| 131 |         QTimer::singleShot(interval: 0, receiver: this, slot: [this](){ prepareNextJob(); }); | 
| 132 |     } | 
| 133 | } | 
| 134 |  | 
| 135 | QT_END_NAMESPACE | 
| 136 |  | 
| 137 | #include "moc_remotedevicemanager_p.cpp" | 
| 138 |  |