File indexing completed on 2024-12-01 12:29:49

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 "manager_p.h"
0010 #include "adapter.h"
0011 #include "adapter_p.h"
0012 #include "debug.h"
0013 #include "device.h"
0014 #include "device_p.h"
0015 #include "manager.h"
0016 #include "utils.h"
0017 
0018 #include <QDBusServiceWatcher>
0019 
0020 namespace BluezQt
0021 {
0022 ManagerPrivate::ManagerPrivate(Manager *parent)
0023     : QObject(parent)
0024     , q(parent)
0025     , m_dbusObjectManager(nullptr)
0026     , m_bluezAgentManager(nullptr)
0027     , m_bluezProfileManager(nullptr)
0028     , m_initialized(false)
0029     , m_bluezRunning(false)
0030     , m_loaded(false)
0031     , m_adaptersLoaded(false)
0032 {
0033     qDBusRegisterMetaType<DBusManagerStruct>();
0034     qDBusRegisterMetaType<QVariantMapMap>();
0035 
0036     m_rfkill = new Rfkill(this);
0037     m_bluetoothBlocked = rfkillBlocked();
0038     connect(m_rfkill, &Rfkill::stateChanged, this, &ManagerPrivate::rfkillStateChanged);
0039 
0040     connect(q, &Manager::adapterRemoved, this, &ManagerPrivate::adapterRemoved);
0041 }
0042 
0043 void ManagerPrivate::init()
0044 {
0045     // Keep an eye on org.bluez service
0046     QDBusServiceWatcher *serviceWatcher = new QDBusServiceWatcher(Strings::orgBluez(),
0047                                                                   DBusConnection::orgBluez(),
0048                                                                   QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration,
0049                                                                   this);
0050 
0051     connect(serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &ManagerPrivate::serviceRegistered);
0052     connect(serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &ManagerPrivate::serviceUnregistered);
0053 
0054     // Update the current state of org.bluez service
0055     if (!DBusConnection::orgBluez().isConnected()) {
0056         Q_EMIT initError(QStringLiteral("DBus system bus is not connected!"));
0057         return;
0058     }
0059 
0060     QDBusMessage call =
0061         QDBusMessage::createMethodCall(Strings::orgFreedesktopDBus(), QStringLiteral("/"), Strings::orgFreedesktopDBus(), QStringLiteral("NameHasOwner"));
0062 
0063     call << Strings::orgBluez();
0064 
0065     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(DBusConnection::orgBluez().asyncCall(call));
0066     connect(watcher, &QDBusPendingCallWatcher::finished, this, &ManagerPrivate::nameHasOwnerFinished);
0067 
0068     DBusConnection::orgBluez().connect(Strings::orgBluez(),
0069                                        QString(),
0070                                        Strings::orgFreedesktopDBusProperties(),
0071                                        QStringLiteral("PropertiesChanged"),
0072                                        this,
0073                                        SLOT(propertiesChanged(QString, QVariantMap, QStringList)));
0074 }
0075 
0076 void ManagerPrivate::nameHasOwnerFinished(QDBusPendingCallWatcher *watcher)
0077 {
0078     const QDBusPendingReply<bool> &reply = *watcher;
0079     watcher->deleteLater();
0080 
0081     if (reply.isError()) {
0082         Q_EMIT initError(reply.error().message());
0083         return;
0084     }
0085 
0086     m_bluezRunning = reply.value();
0087 
0088     if (m_bluezRunning) {
0089         load();
0090     } else {
0091         m_initialized = true;
0092         Q_EMIT initFinished();
0093     }
0094 }
0095 
0096 void ManagerPrivate::load()
0097 {
0098     if (!m_bluezRunning || m_loaded) {
0099         return;
0100     }
0101 
0102     // Force QDBus to cache owner of org.bluez - this will be the only blocking call on system connection
0103     DBusConnection::orgBluez().connect(Strings::orgBluez(), QStringLiteral("/"), Strings::orgFreedesktopDBus(), QStringLiteral("Dummy"), this, SLOT(dummy()));
0104 
0105     m_dbusObjectManager = new DBusObjectManager(Strings::orgBluez(), QStringLiteral("/"), DBusConnection::orgBluez(), this);
0106 
0107     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(m_dbusObjectManager->GetManagedObjects(), this);
0108     connect(watcher, &QDBusPendingCallWatcher::finished, this, &ManagerPrivate::getManagedObjectsFinished);
0109 }
0110 
0111 void ManagerPrivate::getManagedObjectsFinished(QDBusPendingCallWatcher *watcher)
0112 {
0113     const QDBusPendingReply<DBusManagerStruct> &reply = *watcher;
0114     watcher->deleteLater();
0115 
0116     if (reply.isError()) {
0117         Q_EMIT initError(reply.error().message());
0118         return;
0119     }
0120 
0121     DBusManagerStruct::const_iterator it;
0122     const DBusManagerStruct &managedObjects = reply.value();
0123 
0124     for (it = managedObjects.constBegin(); it != managedObjects.constEnd(); ++it) {
0125         const QString &path = it.key().path();
0126         const QVariantMapMap &interfaces = it.value();
0127 
0128         interfacesAdded(it.key(), interfaces);
0129 
0130         if (interfaces.contains(Strings::orgBluezAgentManager1())) {
0131             m_bluezAgentManager = new BluezAgentManager(Strings::orgBluez(), path, DBusConnection::orgBluez(), this);
0132         }
0133         if (interfaces.contains(Strings::orgBluezProfileManager1())) {
0134             m_bluezProfileManager = new BluezProfileManager(Strings::orgBluez(), path, DBusConnection::orgBluez(), this);
0135         }
0136     }
0137 
0138     if (!m_bluezAgentManager) {
0139         Q_EMIT initError(QStringLiteral("Cannot find org.bluez.AgentManager1 object!"));
0140         return;
0141     }
0142 
0143     if (!m_bluezProfileManager) {
0144         Q_EMIT initError(QStringLiteral("Cannot find org.bluez.ProfileManager1 object!"));
0145         return;
0146     }
0147 
0148     connect(m_dbusObjectManager, &DBusObjectManager::InterfacesAdded, this, &ManagerPrivate::interfacesAdded);
0149     connect(m_dbusObjectManager, &DBusObjectManager::InterfacesRemoved, this, &ManagerPrivate::interfacesRemoved);
0150 
0151     m_loaded = true;
0152     m_initialized = true;
0153 
0154     Q_EMIT q->operationalChanged(true);
0155 
0156     if (q->isBluetoothOperational()) {
0157         Q_EMIT q->bluetoothOperationalChanged(true);
0158     }
0159 
0160     Q_EMIT initFinished();
0161 }
0162 
0163 void ManagerPrivate::clear()
0164 {
0165     m_loaded = false;
0166 
0167     // Delete all devices first
0168     while (!m_devices.isEmpty()) {
0169         DevicePtr device = m_devices.begin().value();
0170         m_devices.remove(m_devices.begin().key());
0171         device->adapter()->d->removeDevice(device);
0172     }
0173 
0174     // Delete all adapters
0175     while (!m_adapters.isEmpty()) {
0176         AdapterPtr adapter = m_adapters.begin().value();
0177         m_adapters.remove(m_adapters.begin().key());
0178         Q_EMIT adapter->adapterRemoved(adapter);
0179 
0180         if (m_adapters.isEmpty()) {
0181             Q_EMIT q->allAdaptersRemoved();
0182         }
0183     }
0184 
0185     // Delete all other objects
0186     m_usableAdapter.clear();
0187 
0188     if (m_dbusObjectManager) {
0189         m_dbusObjectManager->deleteLater();
0190         m_dbusObjectManager = nullptr;
0191     }
0192 
0193     if (m_bluezAgentManager) {
0194         m_bluezAgentManager->deleteLater();
0195         m_bluezAgentManager = nullptr;
0196     }
0197 }
0198 
0199 AdapterPtr ManagerPrivate::findUsableAdapter() const
0200 {
0201     for (AdapterPtr adapter : std::as_const(m_adapters)) {
0202         if (adapter->isPowered()) {
0203             return adapter;
0204         }
0205     }
0206     return AdapterPtr();
0207 }
0208 
0209 void ManagerPrivate::serviceRegistered()
0210 {
0211     qCDebug(BLUEZQT) << "BlueZ service registered";
0212     m_bluezRunning = true;
0213 
0214     load();
0215 }
0216 
0217 void ManagerPrivate::serviceUnregistered()
0218 {
0219     qCDebug(BLUEZQT) << "BlueZ service unregistered";
0220 
0221     bool wasBtOperational = q->isBluetoothOperational();
0222     m_bluezRunning = false;
0223 
0224     if (wasBtOperational) {
0225         Q_EMIT q->bluetoothOperationalChanged(false);
0226     }
0227 
0228     clear();
0229     Q_EMIT q->operationalChanged(false);
0230 }
0231 
0232 void ManagerPrivate::interfacesAdded(const QDBusObjectPath &objectPath, const QVariantMapMap &interfaces)
0233 {
0234     const QString &path = objectPath.path();
0235     QVariantMapMap::const_iterator it;
0236 
0237     for (it = interfaces.constBegin(); it != interfaces.constEnd(); ++it) {
0238         if (it.key() == Strings::orgBluezAdapter1()) {
0239             addAdapter(path, it.value());
0240         } else if (it.key() == Strings::orgBluezDevice1()) {
0241             addDevice(path, it.value());
0242         }
0243     }
0244 
0245     for (auto it = m_adapters.cbegin(); it != m_adapters.cend(); ++it) {
0246         if (path.startsWith(it.value()->ubi())) {
0247             it.value()->d->interfacesAdded(path, interfaces);
0248             break;
0249         }
0250     }
0251 
0252     for (auto it = m_devices.cbegin(); it != m_devices.cend(); ++it) {
0253         if (path.startsWith(it.value()->ubi())) {
0254             it.value()->d->interfacesAdded(path, interfaces);
0255             break;
0256         }
0257     }
0258 }
0259 
0260 void ManagerPrivate::interfacesRemoved(const QDBusObjectPath &objectPath, const QStringList &interfaces)
0261 {
0262     const QString &path = objectPath.path();
0263 
0264     for (const QString &interface : interfaces) {
0265         if (interface == Strings::orgBluezAdapter1()) {
0266             removeAdapter(path);
0267         } else if (interface == Strings::orgBluezDevice1()) {
0268             removeDevice(path);
0269         }
0270     }
0271 
0272     for (auto it = m_adapters.cbegin(); it != m_adapters.cend(); ++it) {
0273         if (path.startsWith(it.value()->ubi())) {
0274             it.value()->d->interfacesRemoved(path, interfaces);
0275             break;
0276         }
0277     }
0278 
0279     for (auto it = m_devices.cbegin(); it != m_devices.cend(); ++it) {
0280         if (path.startsWith(it.value()->ubi())) {
0281             it.value()->d->interfacesRemoved(path, interfaces);
0282             break;
0283         }
0284     }
0285 }
0286 
0287 void ManagerPrivate::adapterRemoved(const AdapterPtr &adapter)
0288 {
0289     disconnect(adapter.data(), &Adapter::poweredChanged, this, &ManagerPrivate::adapterPoweredChanged);
0290 
0291     // Current usable adapter was removed
0292     if (adapter == m_usableAdapter) {
0293         setUsableAdapter(findUsableAdapter());
0294     }
0295 }
0296 
0297 void ManagerPrivate::adapterPoweredChanged(bool powered)
0298 {
0299     Q_ASSERT(qobject_cast<Adapter *>(sender()));
0300     AdapterPtr adapter = static_cast<Adapter *>(sender())->toSharedPtr();
0301 
0302     // Current usable adapter was powered off
0303     if (m_usableAdapter == adapter && !powered) {
0304         setUsableAdapter(findUsableAdapter());
0305     }
0306 
0307     // Adapter was powered on, set it as usable
0308     if (!m_usableAdapter && powered) {
0309         setUsableAdapter(adapter);
0310     }
0311 }
0312 
0313 void ManagerPrivate::rfkillStateChanged(Rfkill::State state)
0314 {
0315     Q_UNUSED(state)
0316 
0317     bool blocked = rfkillBlocked();
0318     bool wasBtOperational = q->isBluetoothOperational();
0319 
0320     if (m_bluetoothBlocked != blocked) {
0321         m_bluetoothBlocked = blocked;
0322         Q_EMIT q->bluetoothBlockedChanged(m_bluetoothBlocked);
0323         if (wasBtOperational != q->isBluetoothOperational()) {
0324             Q_EMIT q->bluetoothOperationalChanged(q->isBluetoothOperational());
0325         }
0326     }
0327 }
0328 
0329 void ManagerPrivate::addAdapter(const QString &adapterPath, const QVariantMap &properties)
0330 {
0331     AdapterPtr adapter = AdapterPtr(new Adapter(adapterPath, properties));
0332     adapter->d->q = adapter.toWeakRef();
0333     m_adapters.insert(adapterPath, adapter);
0334 
0335     Q_EMIT q->adapterAdded(adapter);
0336 
0337     // Powered adapter was added, set it as usable
0338     if (!m_usableAdapter && adapter->isPowered()) {
0339         setUsableAdapter(adapter);
0340     }
0341 
0342     connect(adapter.data(), &Adapter::deviceAdded, q, &Manager::deviceAdded);
0343     connect(adapter.data(), &Adapter::adapterRemoved, q, &Manager::adapterRemoved);
0344     connect(adapter.data(), &Adapter::adapterChanged, q, &Manager::adapterChanged);
0345     connect(adapter.data(), &Adapter::poweredChanged, this, &ManagerPrivate::adapterPoweredChanged);
0346 }
0347 
0348 void ManagerPrivate::addDevice(const QString &devicePath, const QVariantMap &properties)
0349 {
0350     AdapterPtr adapter = m_adapters.value(properties.value(QStringLiteral("Adapter")).value<QDBusObjectPath>().path());
0351     if (!adapter) {
0352         return;
0353     }
0354 
0355     DevicePtr device = DevicePtr(new Device(devicePath, properties, adapter));
0356     device->d->q = device.toWeakRef();
0357     m_devices.insert(devicePath, device);
0358     adapter->d->addDevice(device);
0359 
0360     connect(device.data(), &Device::deviceRemoved, q, &Manager::deviceRemoved);
0361     connect(device.data(), &Device::deviceChanged, q, &Manager::deviceChanged);
0362 }
0363 
0364 void ManagerPrivate::removeAdapter(const QString &adapterPath)
0365 {
0366     AdapterPtr adapter = m_adapters.value(adapterPath);
0367     if (!adapter) {
0368         return;
0369     }
0370 
0371     // Make sure we always remove all devices before removing the adapter
0372     const auto devices = adapter->devices();
0373     for (const DevicePtr &device : devices) {
0374         removeDevice(device->ubi());
0375     }
0376 
0377     m_adapters.remove(adapterPath);
0378     Q_EMIT adapter->adapterRemoved(adapter);
0379 
0380     if (m_adapters.isEmpty()) {
0381         Q_EMIT q->allAdaptersRemoved();
0382     }
0383 
0384     disconnect(adapter.data(), &Adapter::adapterChanged, q, &Manager::adapterChanged);
0385     disconnect(adapter.data(), &Adapter::poweredChanged, this, &ManagerPrivate::adapterPoweredChanged);
0386 }
0387 
0388 void ManagerPrivate::removeDevice(const QString &devicePath)
0389 {
0390     DevicePtr device = m_devices.take(devicePath);
0391     if (!device) {
0392         return;
0393     }
0394 
0395     device->adapter()->d->removeDevice(device);
0396 
0397     disconnect(device.data(), &Device::deviceChanged, q, &Manager::deviceChanged);
0398 }
0399 
0400 bool ManagerPrivate::rfkillBlocked() const
0401 {
0402     return m_rfkill->state() == Rfkill::SoftBlocked || m_rfkill->state() == Rfkill::HardBlocked;
0403 }
0404 
0405 void ManagerPrivate::setUsableAdapter(const AdapterPtr &adapter)
0406 {
0407     if (m_usableAdapter == adapter) {
0408         return;
0409     }
0410 
0411     qCDebug(BLUEZQT) << "Setting usable adapter" << adapter;
0412 
0413     bool wasBtOperational = q->isBluetoothOperational();
0414 
0415     m_usableAdapter = adapter;
0416     Q_EMIT q->usableAdapterChanged(m_usableAdapter);
0417 
0418     if (wasBtOperational != q->isBluetoothOperational()) {
0419         Q_EMIT q->bluetoothOperationalChanged(q->isBluetoothOperational());
0420     }
0421 }
0422 
0423 void ManagerPrivate::propertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalidated)
0424 {
0425     // Cut anything after device path to forward it to Device to handle
0426     const QString path_full = message().path();
0427     const QString path_device = path_full.section(QLatin1Char('/'), 0, 4);
0428 
0429     QTimer::singleShot(0, this, [=]() {
0430         AdapterPtr adapter = m_adapters.value(path_device);
0431         if (adapter) {
0432             adapter->d->propertiesChanged(interface, changed, invalidated);
0433             return;
0434         }
0435         DevicePtr device = m_devices.value(path_device);
0436         if (device) {
0437             device->d->propertiesChanged(path_full, interface, changed, invalidated);
0438             return;
0439         }
0440         qCDebug(BLUEZQT) << "Unhandled property change" << interface << changed << invalidated;
0441     });
0442 }
0443 
0444 void ManagerPrivate::dummy()
0445 {
0446 }
0447 
0448 } // namespace BluezQt
0449 
0450 #include "moc_manager_p.cpp"