1 | /* |
2 | * BluezQt - Asynchronous Bluez wrapper library |
3 | * |
4 | * SPDX-FileCopyrightText: 2014-2015 David Rosca <nowrep@gmail.com> |
5 | * |
6 | * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
7 | */ |
8 | |
9 | #include "obexmanager_p.h" |
10 | #include "debug.h" |
11 | #include "obexmanager.h" |
12 | #include "obexsession.h" |
13 | #include "obexsession_p.h" |
14 | #include "utils.h" |
15 | |
16 | #include <QDBusServiceWatcher> |
17 | |
18 | namespace BluezQt |
19 | { |
20 | typedef org::freedesktop::DBus::ObjectManager DBusObjectManager; |
21 | |
22 | ObexManagerPrivate::ObexManagerPrivate(ObexManager *qq) |
23 | : QObject(qq) |
24 | , q(qq) |
25 | , m_obexClient(nullptr) |
26 | , m_obexAgentManager(nullptr) |
27 | , m_dbusObjectManager(nullptr) |
28 | , m_initialized(false) |
29 | , m_obexRunning(false) |
30 | , m_loaded(false) |
31 | { |
32 | qDBusRegisterMetaType<DBusManagerStruct>(); |
33 | qDBusRegisterMetaType<QVariantMapMap>(); |
34 | |
35 | m_timer.setSingleShot(true); |
36 | connect(sender: &m_timer, signal: &QTimer::timeout, context: this, slot: &ObexManagerPrivate::load); |
37 | } |
38 | |
39 | void ObexManagerPrivate::init() |
40 | { |
41 | // Keep an eye on org.bluez.obex service |
42 | QDBusServiceWatcher *serviceWatcher = new QDBusServiceWatcher(Strings::orgBluezObex(), |
43 | DBusConnection::orgBluezObex(), |
44 | QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration, |
45 | this); |
46 | |
47 | connect(sender: serviceWatcher, signal: &QDBusServiceWatcher::serviceRegistered, context: this, slot: &ObexManagerPrivate::serviceRegistered); |
48 | connect(sender: serviceWatcher, signal: &QDBusServiceWatcher::serviceUnregistered, context: this, slot: &ObexManagerPrivate::serviceUnregistered); |
49 | |
50 | // Update the current state of org.bluez.obex service |
51 | if (!DBusConnection::orgBluezObex().isConnected()) { |
52 | Q_EMIT initError(QStringLiteral("DBus session bus is not connected!" )); |
53 | return; |
54 | } |
55 | |
56 | QDBusMessage call = |
57 | QDBusMessage::createMethodCall(destination: Strings::orgFreedesktopDBus(), QStringLiteral("/" ), interface: Strings::orgFreedesktopDBus(), QStringLiteral("NameHasOwner" )); |
58 | |
59 | call << Strings::orgBluezObex(); |
60 | |
61 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(DBusConnection::orgBluezObex().asyncCall(message: call)); |
62 | connect(sender: watcher, signal: &QDBusPendingCallWatcher::finished, context: this, slot: &ObexManagerPrivate::nameHasOwnerFinished); |
63 | } |
64 | |
65 | void ObexManagerPrivate::nameHasOwnerFinished(QDBusPendingCallWatcher *watcher) |
66 | { |
67 | const QDBusPendingReply<bool> &reply = *watcher; |
68 | watcher->deleteLater(); |
69 | |
70 | if (reply.isError()) { |
71 | Q_EMIT initError(errorText: reply.error().message()); |
72 | return; |
73 | } |
74 | |
75 | m_obexRunning = reply.value(); |
76 | |
77 | if (m_obexRunning) { |
78 | load(); |
79 | } else { |
80 | m_initialized = true; |
81 | Q_EMIT initFinished(); |
82 | } |
83 | } |
84 | |
85 | void ObexManagerPrivate::load() |
86 | { |
87 | if (!m_obexRunning || m_loaded) { |
88 | return; |
89 | } |
90 | |
91 | // Force QDBus to cache owner of org.bluez.obex - this will be the only blocking call on session connection |
92 | DBusConnection::orgBluezObex() |
93 | .connect(service: Strings::orgBluezObex(), QStringLiteral("/" ), interface: Strings::orgFreedesktopDBus(), QStringLiteral("Dummy" ), receiver: this, SLOT(dummy())); |
94 | |
95 | m_dbusObjectManager = new DBusObjectManager(Strings::orgBluezObex(), QStringLiteral("/" ), DBusConnection::orgBluezObex(), this); |
96 | |
97 | connect(sender: m_dbusObjectManager, signal: &DBusObjectManager::InterfacesAdded, context: this, slot: &ObexManagerPrivate::interfacesAdded); |
98 | connect(sender: m_dbusObjectManager, signal: &DBusObjectManager::InterfacesRemoved, context: this, slot: &ObexManagerPrivate::interfacesRemoved); |
99 | |
100 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(m_dbusObjectManager->GetManagedObjects(), this); |
101 | connect(sender: watcher, signal: &QDBusPendingCallWatcher::finished, context: this, slot: &ObexManagerPrivate::getManagedObjectsFinished); |
102 | } |
103 | |
104 | void ObexManagerPrivate::getManagedObjectsFinished(QDBusPendingCallWatcher *watcher) |
105 | { |
106 | const QDBusPendingReply<DBusManagerStruct> &reply = *watcher; |
107 | watcher->deleteLater(); |
108 | |
109 | if (reply.isError()) { |
110 | Q_EMIT initError(errorText: reply.error().message()); |
111 | return; |
112 | } |
113 | |
114 | DBusManagerStruct::const_iterator it; |
115 | const DBusManagerStruct &managedObjects = reply.value(); |
116 | |
117 | for (it = managedObjects.constBegin(); it != managedObjects.constEnd(); ++it) { |
118 | const QString &path = it.key().path(); |
119 | const QVariantMapMap &interfaces = it.value(); |
120 | |
121 | if (interfaces.contains(key: Strings::orgBluezObexSession1())) { |
122 | addSession(sessionPath: path, properties: interfaces.value(key: Strings::orgBluezObexSession1())); |
123 | } else if (interfaces.contains(key: Strings::orgBluezObexClient1()) && interfaces.contains(key: Strings::orgBluezObexAgentManager1())) { |
124 | m_obexClient = new ObexClient(Strings::orgBluezObex(), path, DBusConnection::orgBluezObex(), this); |
125 | m_obexAgentManager = new ObexAgentManager(Strings::orgBluezObex(), path, DBusConnection::orgBluezObex(), this); |
126 | } |
127 | } |
128 | |
129 | if (!m_obexClient) { |
130 | Q_EMIT initError(QStringLiteral("Cannot find org.bluez.obex.Client1 object!" )); |
131 | return; |
132 | } |
133 | |
134 | if (!m_obexAgentManager) { |
135 | Q_EMIT initError(QStringLiteral("Cannot find org.bluez.obex.AgentManager1 object!" )); |
136 | return; |
137 | } |
138 | |
139 | m_loaded = true; |
140 | m_initialized = true; |
141 | |
142 | Q_EMIT q->operationalChanged(operational: true); |
143 | Q_EMIT initFinished(); |
144 | } |
145 | |
146 | void ObexManagerPrivate::clear() |
147 | { |
148 | m_loaded = false; |
149 | |
150 | // Delete all sessions |
151 | while (!m_sessions.isEmpty()) { |
152 | ObexSessionPtr session = m_sessions.begin().value(); |
153 | m_sessions.remove(key: m_sessions.begin().key()); |
154 | Q_EMIT q->sessionRemoved(session); |
155 | } |
156 | |
157 | // Delete all other objects |
158 | if (m_obexClient) { |
159 | m_obexClient->deleteLater(); |
160 | m_obexClient = nullptr; |
161 | } |
162 | |
163 | if (m_obexAgentManager) { |
164 | m_obexAgentManager->deleteLater(); |
165 | m_obexAgentManager = nullptr; |
166 | } |
167 | |
168 | if (m_dbusObjectManager) { |
169 | m_dbusObjectManager->deleteLater(); |
170 | m_dbusObjectManager = nullptr; |
171 | } |
172 | } |
173 | |
174 | void ObexManagerPrivate::serviceRegistered() |
175 | { |
176 | qCDebug(BLUEZQT) << "Obex service registered" ; |
177 | m_obexRunning = true; |
178 | |
179 | // Client1 and AgentManager1 objects are not ready by the time org.bluez.obex is registered |
180 | // nor will the ObjectManager emits interfacesAdded for adding them... |
181 | // So we delay the call to load() by 0.5s |
182 | m_timer.start(msec: 500); |
183 | } |
184 | |
185 | void ObexManagerPrivate::serviceUnregistered() |
186 | { |
187 | qCDebug(BLUEZQT) << "Obex service unregistered" ; |
188 | m_obexRunning = false; |
189 | |
190 | clear(); |
191 | Q_EMIT q->operationalChanged(operational: false); |
192 | } |
193 | |
194 | void ObexManagerPrivate::interfacesAdded(const QDBusObjectPath &objectPath, const QVariantMapMap &interfaces) |
195 | { |
196 | const QString &path = objectPath.path(); |
197 | QVariantMapMap::const_iterator it; |
198 | |
199 | for (it = interfaces.constBegin(); it != interfaces.constEnd(); ++it) { |
200 | if (it.key() == Strings::orgBluezObexSession1()) { |
201 | addSession(sessionPath: path, properties: it.value()); |
202 | } |
203 | } |
204 | } |
205 | |
206 | void ObexManagerPrivate::interfacesRemoved(const QDBusObjectPath &objectPath, const QStringList &interfaces) |
207 | { |
208 | const QString &path = objectPath.path(); |
209 | |
210 | for (const QString &interface : interfaces) { |
211 | if (interface == Strings::orgBluezObexSession1()) { |
212 | removeSession(sessionPath: path); |
213 | } |
214 | } |
215 | } |
216 | |
217 | void ObexManagerPrivate::addSession(const QString &sessionPath, const QVariantMap &properties) |
218 | { |
219 | ObexSessionPtr session = ObexSessionPtr(new ObexSession(sessionPath, properties)); |
220 | session->d->q = session.toWeakRef(); |
221 | m_sessions.insert(key: sessionPath, value: session); |
222 | |
223 | Q_EMIT q->sessionAdded(session); |
224 | } |
225 | |
226 | void ObexManagerPrivate::removeSession(const QString &sessionPath) |
227 | { |
228 | ObexSessionPtr session = m_sessions.take(key: sessionPath); |
229 | if (!session) { |
230 | return; |
231 | } |
232 | |
233 | Q_EMIT q->sessionRemoved(session); |
234 | } |
235 | |
236 | void ObexManagerPrivate::dummy() |
237 | { |
238 | } |
239 | |
240 | } // namespace BluezQt |
241 | |
242 | #include "moc_obexmanager_p.cpp" |
243 | |