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 | |