File indexing completed on 2024-12-08 12:15:36

0001 /*
0002  * BluezQt - Asynchronous Bluez wrapper library
0003  *
0004  * SPDX-FileCopyrightText: 2014-2015 David Rosca <nowrep@gmail.com>
0005  *
0006  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007  */
0008 
0009 #include "obexmanager_p.h"
0010 #include "debug.h"
0011 #include "obexmanager.h"
0012 #include "obexsession.h"
0013 #include "obexsession_p.h"
0014 #include "utils.h"
0015 
0016 #include <QDBusServiceWatcher>
0017 
0018 namespace BluezQt
0019 {
0020 typedef org::freedesktop::DBus::ObjectManager DBusObjectManager;
0021 
0022 ObexManagerPrivate::ObexManagerPrivate(ObexManager *qq)
0023     : QObject(qq)
0024     , q(qq)
0025     , m_obexClient(nullptr)
0026     , m_obexAgentManager(nullptr)
0027     , m_dbusObjectManager(nullptr)
0028     , m_initialized(false)
0029     , m_obexRunning(false)
0030     , m_loaded(false)
0031 {
0032     qDBusRegisterMetaType<DBusManagerStruct>();
0033     qDBusRegisterMetaType<QVariantMapMap>();
0034 
0035     m_timer.setSingleShot(true);
0036     connect(&m_timer, &QTimer::timeout, this, &ObexManagerPrivate::load);
0037 }
0038 
0039 void ObexManagerPrivate::init()
0040 {
0041     // Keep an eye on org.bluez.obex service
0042     QDBusServiceWatcher *serviceWatcher = new QDBusServiceWatcher(Strings::orgBluezObex(),
0043                                                                   DBusConnection::orgBluezObex(),
0044                                                                   QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration,
0045                                                                   this);
0046 
0047     connect(serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &ObexManagerPrivate::serviceRegistered);
0048     connect(serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &ObexManagerPrivate::serviceUnregistered);
0049 
0050     // Update the current state of org.bluez.obex service
0051     if (!DBusConnection::orgBluezObex().isConnected()) {
0052         Q_EMIT initError(QStringLiteral("DBus session bus is not connected!"));
0053         return;
0054     }
0055 
0056     QDBusMessage call =
0057         QDBusMessage::createMethodCall(Strings::orgFreedesktopDBus(), QStringLiteral("/"), Strings::orgFreedesktopDBus(), QStringLiteral("NameHasOwner"));
0058 
0059     call << Strings::orgBluezObex();
0060 
0061     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(DBusConnection::orgBluezObex().asyncCall(call));
0062     connect(watcher, &QDBusPendingCallWatcher::finished, this, &ObexManagerPrivate::nameHasOwnerFinished);
0063 }
0064 
0065 void ObexManagerPrivate::nameHasOwnerFinished(QDBusPendingCallWatcher *watcher)
0066 {
0067     const QDBusPendingReply<bool> &reply = *watcher;
0068     watcher->deleteLater();
0069 
0070     if (reply.isError()) {
0071         Q_EMIT initError(reply.error().message());
0072         return;
0073     }
0074 
0075     m_obexRunning = reply.value();
0076 
0077     if (m_obexRunning) {
0078         load();
0079     } else {
0080         m_initialized = true;
0081         Q_EMIT initFinished();
0082     }
0083 }
0084 
0085 void ObexManagerPrivate::load()
0086 {
0087     if (!m_obexRunning || m_loaded) {
0088         return;
0089     }
0090 
0091     // Force QDBus to cache owner of org.bluez.obex - this will be the only blocking call on session connection
0092     DBusConnection::orgBluezObex()
0093         .connect(Strings::orgBluezObex(), QStringLiteral("/"), Strings::orgFreedesktopDBus(), QStringLiteral("Dummy"), this, SLOT(dummy()));
0094 
0095     m_dbusObjectManager = new DBusObjectManager(Strings::orgBluezObex(), QStringLiteral("/"), DBusConnection::orgBluezObex(), this);
0096 
0097     connect(m_dbusObjectManager, &DBusObjectManager::InterfacesAdded, this, &ObexManagerPrivate::interfacesAdded);
0098     connect(m_dbusObjectManager, &DBusObjectManager::InterfacesRemoved, this, &ObexManagerPrivate::interfacesRemoved);
0099 
0100     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(m_dbusObjectManager->GetManagedObjects(), this);
0101     connect(watcher, &QDBusPendingCallWatcher::finished, this, &ObexManagerPrivate::getManagedObjectsFinished);
0102 }
0103 
0104 void ObexManagerPrivate::getManagedObjectsFinished(QDBusPendingCallWatcher *watcher)
0105 {
0106     const QDBusPendingReply<DBusManagerStruct> &reply = *watcher;
0107     watcher->deleteLater();
0108 
0109     if (reply.isError()) {
0110         Q_EMIT initError(reply.error().message());
0111         return;
0112     }
0113 
0114     DBusManagerStruct::const_iterator it;
0115     const DBusManagerStruct &managedObjects = reply.value();
0116 
0117     for (it = managedObjects.constBegin(); it != managedObjects.constEnd(); ++it) {
0118         const QString &path = it.key().path();
0119         const QVariantMapMap &interfaces = it.value();
0120 
0121         if (interfaces.contains(Strings::orgBluezObexSession1())) {
0122             addSession(path, interfaces.value(Strings::orgBluezObexSession1()));
0123         } else if (interfaces.contains(Strings::orgBluezObexClient1()) && interfaces.contains(Strings::orgBluezObexAgentManager1())) {
0124             m_obexClient = new ObexClient(Strings::orgBluezObex(), path, DBusConnection::orgBluezObex(), this);
0125             m_obexAgentManager = new ObexAgentManager(Strings::orgBluezObex(), path, DBusConnection::orgBluezObex(), this);
0126         }
0127     }
0128 
0129     if (!m_obexClient) {
0130         Q_EMIT initError(QStringLiteral("Cannot find org.bluez.obex.Client1 object!"));
0131         return;
0132     }
0133 
0134     if (!m_obexAgentManager) {
0135         Q_EMIT initError(QStringLiteral("Cannot find org.bluez.obex.AgentManager1 object!"));
0136         return;
0137     }
0138 
0139     m_loaded = true;
0140     m_initialized = true;
0141 
0142     Q_EMIT q->operationalChanged(true);
0143     Q_EMIT initFinished();
0144 }
0145 
0146 void ObexManagerPrivate::clear()
0147 {
0148     m_loaded = false;
0149 
0150     // Delete all sessions
0151     while (!m_sessions.isEmpty()) {
0152         ObexSessionPtr session = m_sessions.begin().value();
0153         m_sessions.remove(m_sessions.begin().key());
0154         Q_EMIT q->sessionRemoved(session);
0155     }
0156 
0157     // Delete all other objects
0158     if (m_obexClient) {
0159         m_obexClient->deleteLater();
0160         m_obexClient = nullptr;
0161     }
0162 
0163     if (m_obexAgentManager) {
0164         m_obexAgentManager->deleteLater();
0165         m_obexAgentManager = nullptr;
0166     }
0167 
0168     if (m_dbusObjectManager) {
0169         m_dbusObjectManager->deleteLater();
0170         m_dbusObjectManager = nullptr;
0171     }
0172 }
0173 
0174 void ObexManagerPrivate::serviceRegistered()
0175 {
0176     qCDebug(BLUEZQT) << "Obex service registered";
0177     m_obexRunning = true;
0178 
0179     // Client1 and AgentManager1 objects are not ready by the time org.bluez.obex is registered
0180     // nor will the ObjectManager emits interfacesAdded for adding them...
0181     // So we delay the call to load() by 0.5s
0182     m_timer.start(500);
0183 }
0184 
0185 void ObexManagerPrivate::serviceUnregistered()
0186 {
0187     qCDebug(BLUEZQT) << "Obex service unregistered";
0188     m_obexRunning = false;
0189 
0190     clear();
0191     Q_EMIT q->operationalChanged(false);
0192 }
0193 
0194 void ObexManagerPrivate::interfacesAdded(const QDBusObjectPath &objectPath, const QVariantMapMap &interfaces)
0195 {
0196     const QString &path = objectPath.path();
0197     QVariantMapMap::const_iterator it;
0198 
0199     for (it = interfaces.constBegin(); it != interfaces.constEnd(); ++it) {
0200         if (it.key() == Strings::orgBluezObexSession1()) {
0201             addSession(path, it.value());
0202         }
0203     }
0204 }
0205 
0206 void ObexManagerPrivate::interfacesRemoved(const QDBusObjectPath &objectPath, const QStringList &interfaces)
0207 {
0208     const QString &path = objectPath.path();
0209 
0210     for (const QString &interface : interfaces) {
0211         if (interface == Strings::orgBluezObexSession1()) {
0212             removeSession(path);
0213         }
0214     }
0215 }
0216 
0217 void ObexManagerPrivate::addSession(const QString &sessionPath, const QVariantMap &properties)
0218 {
0219     ObexSessionPtr session = ObexSessionPtr(new ObexSession(sessionPath, properties));
0220     session->d->q = session.toWeakRef();
0221     m_sessions.insert(sessionPath, session);
0222 
0223     Q_EMIT q->sessionAdded(session);
0224 }
0225 
0226 void ObexManagerPrivate::removeSession(const QString &sessionPath)
0227 {
0228     ObexSessionPtr session = m_sessions.take(sessionPath);
0229     if (!session) {
0230         return;
0231     }
0232 
0233     Q_EMIT q->sessionRemoved(session);
0234 }
0235 
0236 void ObexManagerPrivate::dummy()
0237 {
0238 }
0239 
0240 } // namespace BluezQt
0241 
0242 #include "moc_obexmanager_p.cpp"