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
18namespace BluezQt
19{
20typedef org::freedesktop::DBus::ObjectManager DBusObjectManager;
21
22ObexManagerPrivate::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
39void 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
65void 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
85void 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
104void 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
146void 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
174void 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
185void 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
194void 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
206void 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
217void 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
226void 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
236void ObexManagerPrivate::dummy()
237{
238}
239
240} // namespace BluezQt
241
242#include "moc_obexmanager_p.cpp"
243

source code of bluez-qt/src/obexmanager_p.cpp