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

0001 /*
0002  * BluezQt - Asynchronous Bluez wrapper library
0003  *
0004  * SPDX-FileCopyrightText: 2014 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 "device_p.h"
0010 #include "device.h"
0011 #include "gattserviceremote_p.h"
0012 #include "gattserviceremote.h"
0013 #include "adapter.h"
0014 #include "battery.h"
0015 #include "battery_p.h"
0016 #include "device.h"
0017 #include "input.h"
0018 #include "input_p.h"
0019 #include "macros.h"
0020 #include "mediaplayer.h"
0021 #include "mediaplayer_p.h"
0022 #include "mediatransport.h"
0023 #include "mediatransport_p.h"
0024 #include "utils.h"
0025 
0026 namespace BluezQt
0027 {
0028 static const qint16 INVALID_RSSI = -32768; // qint16 minimum
0029 
0030 DevicePrivate::DevicePrivate(const QString &path, const QVariantMap &properties, const AdapterPtr &adapter)
0031     : QObject()
0032     , m_dbusProperties(nullptr)
0033     , m_deviceClass(0)
0034     , m_appearance(0)
0035     , m_paired(false)
0036     , m_trusted(false)
0037     , m_blocked(false)
0038     , m_legacyPairing(false)
0039     , m_rssi(INVALID_RSSI)
0040     , m_manufacturerData(ManData())
0041     , m_servicesResolved(false)
0042     , m_connected(false)
0043     , m_adapter(adapter)
0044 {
0045     m_bluezDevice = new BluezDevice(Strings::orgBluez(), path, DBusConnection::orgBluez(), this);
0046 
0047     init(properties);
0048 }
0049 
0050 static QHash<QString, QByteArray> toByteArrayHash(const QDBusArgument &arg)
0051 {
0052     if (arg.currentType() != QDBusArgument::MapType) {
0053         return {};
0054     }
0055 
0056     QHash<QString, QByteArray> result;
0057     arg.beginMap();
0058     while (!arg.atEnd()) {
0059         arg.beginMapEntry();
0060         QString key;
0061         QDBusVariant value;
0062         arg >> key >> value;
0063         result.insert(key.toUpper(), value.variant().toByteArray());
0064         arg.endMapEntry();
0065     }
0066     arg.endMap();
0067     return result;
0068 }
0069 
0070 void DevicePrivate::init(const QVariantMap &properties)
0071 {
0072     m_dbusProperties = new DBusProperties(Strings::orgBluez(), m_bluezDevice->path(), DBusConnection::orgBluez(), this);
0073 
0074     // Init properties
0075     m_address = properties.value(QStringLiteral("Address")).toString();
0076     m_name = properties.value(QStringLiteral("Name")).toString();
0077     m_alias = properties.value(QStringLiteral("Alias")).toString();
0078     m_deviceClass = properties.value(QStringLiteral("Class")).toUInt();
0079     m_appearance = properties.value(QStringLiteral("Appearance")).toUInt();
0080     m_icon = properties.value(QStringLiteral("Icon")).toString();
0081     m_paired = properties.value(QStringLiteral("Paired")).toBool();
0082     m_trusted = properties.value(QStringLiteral("Trusted")).toBool();
0083     m_blocked = properties.value(QStringLiteral("Blocked")).toBool();
0084     m_legacyPairing = properties.value(QStringLiteral("LegacyPairing")).toBool();
0085     m_rssi = properties.value(QStringLiteral("RSSI")).toInt();
0086     m_manufacturerData = variantToManData(properties.value(QStringLiteral("ManufacturerData")));
0087     m_servicesResolved = properties.value(QStringLiteral("ServicesResolved")).toBool();
0088     m_connected = properties.value(QStringLiteral("Connected")).toBool();
0089     m_uuids = stringListToUpper(properties.value(QStringLiteral("UUIDs")).toStringList());
0090     m_modalias = properties.value(QStringLiteral("Modalias")).toString();
0091     m_serviceData = toByteArrayHash(properties.value(QStringLiteral("ServiceData")).value<QDBusArgument>());
0092 
0093     if (!m_rssi) {
0094         m_rssi = INVALID_RSSI;
0095     }
0096 }
0097 
0098 void DevicePrivate::interfacesAdded(const QString &path, const QVariantMapMap &interfaces)
0099 {
0100     bool changed = false;
0101     QVariantMapMap::const_iterator it;
0102 
0103     for (it = interfaces.constBegin(); it != interfaces.constEnd(); ++it) {
0104         if (it.key() == Strings::orgBluezBattery1()) {
0105             m_battery = BatteryPtr(new Battery(path, it.value()));
0106             m_battery->d->q = m_battery.toWeakRef();
0107             Q_EMIT q.lock()->batteryChanged(m_battery);
0108             changed = true;
0109         } else if (it.key() == Strings::orgBluezInput1()) {
0110             m_input = InputPtr(new Input(path, it.value()));
0111             m_input->d->q = m_input.toWeakRef();
0112             Q_EMIT q.lock()->inputChanged(m_input);
0113             changed = true;
0114         } else if (it.key() == Strings::orgBluezMediaPlayer1()) {
0115             m_mediaPlayer = MediaPlayerPtr(new MediaPlayer(path, it.value()));
0116             m_mediaPlayer->d->q = m_mediaPlayer.toWeakRef();
0117             Q_EMIT q.lock()->mediaPlayerChanged(m_mediaPlayer);
0118             changed = true;
0119         } else if (it.key() == Strings::orgBluezMediaTransport1()) {
0120             m_mediaTransport = MediaTransportPtr(new MediaTransport(path, it.value()));
0121             m_mediaTransport->d->q = m_mediaTransport.toWeakRef();
0122             Q_EMIT q.lock()->mediaTransportChanged(m_mediaTransport);
0123             changed = true;
0124         } else if (it.key() == Strings::orgBluezGattService1()) {
0125             addGattService(path,it.value());
0126             changed = true;
0127         }
0128     }
0129 
0130     for (auto& service : m_services) {
0131         if (path.startsWith(service->ubi())) {
0132             service->d->interfacesAdded(path, interfaces);
0133             changed = true;
0134         }
0135     }
0136 
0137     if (changed) {
0138         Q_EMIT q.lock()->deviceChanged(q.toStrongRef());
0139     }
0140 }
0141 
0142 void DevicePrivate::interfacesRemoved(const QString &path, const QStringList &interfaces)
0143 {
0144     bool changed = false;
0145 
0146     for (const QString &interface : interfaces) {
0147         if (interface == Strings::orgBluezBattery1() && m_battery && m_battery->d->m_path == path) {
0148             m_battery.clear();
0149             Q_EMIT q.lock()->batteryChanged(m_battery);
0150             changed = true;
0151         } else if (interface == Strings::orgBluezInput1() && m_input && m_input->d->m_path == path) {
0152             m_input.clear();
0153             Q_EMIT q.lock()->inputChanged(m_input);
0154             changed = true;
0155         } else if (interface == Strings::orgBluezMediaPlayer1() && m_mediaPlayer && m_mediaPlayer->d->m_path == path) {
0156             m_mediaPlayer.clear();
0157             Q_EMIT q.lock()->mediaPlayerChanged(m_mediaPlayer);
0158             changed = true;
0159         } else if (interface == Strings::orgBluezMediaTransport1() && m_mediaTransport && m_mediaTransport->d->m_path == path) {
0160             m_mediaTransport.clear();
0161             Q_EMIT q.lock()->mediaTransportChanged(m_mediaTransport);
0162             changed = true;
0163         } else if (interface == Strings::orgBluezGattService1()) {
0164             removeGattService(path);
0165             changed = true;
0166         }
0167     }
0168 
0169     for (auto& service : m_services) {
0170         if (path.startsWith(service->ubi())) {
0171             service->d->interfacesRemoved(path,interfaces);
0172             changed = true;
0173         }
0174     }
0175 
0176     if (changed) {
0177         Q_EMIT q.lock()->deviceChanged(q.toStrongRef());
0178     }
0179 }
0180 
0181 void DevicePrivate::addGattService(const QString &gattServicePath, const QVariantMap &properties)
0182 {
0183     // Check if we have the right path
0184     if (m_bluezDevice->path() != properties.value(QStringLiteral("Device")).value<QDBusObjectPath>().path()) {
0185         return;
0186     }
0187 
0188     DevicePtr device = DevicePtr(this->q);
0189 
0190     if (!device) {
0191         return;
0192     }
0193 
0194     GattServiceRemotePtr gattService = GattServiceRemotePtr(new GattServiceRemote(gattServicePath, properties, device));
0195     gattService->d->q = gattService.toWeakRef();
0196     m_services.append(gattService);
0197 
0198     Q_EMIT device->gattServiceAdded(gattService);
0199     Q_EMIT device->gattServicesChanged(m_services);
0200 
0201     // Connections
0202     connect(gattService.data(),&GattServiceRemote::serviceChanged,q.lock().data(),&Device::gattServiceChanged);
0203 }
0204 
0205 void DevicePrivate::removeGattService(const QString &gattServicePath)
0206 {
0207     DevicePtr device = DevicePtr(this->q);
0208 
0209     if (!device) {
0210         return;
0211     }
0212 
0213     GattServiceRemotePtr gattService = nullptr;
0214     for (int i=0; i < device->gattServices().size(); ++i) {
0215         if (device->gattServices().at(i)->ubi() == gattServicePath) {
0216             gattService = device->gattServices().at(i);
0217         }
0218     }
0219 
0220     if (gattService == nullptr) {
0221         return;
0222     }
0223 
0224     m_services.removeOne(gattService);
0225 
0226     Q_EMIT device->gattServiceRemoved(gattService);
0227     Q_EMIT device->gattServicesChanged(m_services);
0228 
0229     // Connections
0230     disconnect(gattService.data(),&GattServiceRemote::serviceChanged,q.lock().data(),&Device::gattServiceChanged);
0231 }
0232 
0233 QDBusPendingReply<> DevicePrivate::setDBusProperty(const QString &name, const QVariant &value)
0234 {
0235     return m_dbusProperties->Set(Strings::orgBluezDevice1(), name, QDBusVariant(value));
0236 }
0237 
0238 void DevicePrivate::propertiesChanged(const QString &path, const QString &interface, const QVariantMap &changed, const QStringList &invalidated)
0239 {
0240     if (interface == Strings::orgBluezBattery1() && m_battery) {
0241         m_battery->d->propertiesChanged(interface, changed, invalidated);
0242     } else if (interface == Strings::orgBluezInput1() && m_input) {
0243         m_input->d->propertiesChanged(interface, changed, invalidated);
0244     } else if (interface == Strings::orgBluezMediaPlayer1() && m_mediaPlayer) {
0245         m_mediaPlayer->d->propertiesChanged(interface, changed, invalidated);
0246     } else if ((interface == Strings::orgBluezGattService1()) || (interface == Strings::orgBluezGattCharacteristic1()) || (interface == Strings::orgBluezGattDescriptor1())) {
0247         for (GattServiceRemotePtr service : m_services) {
0248             if (path.startsWith(service->ubi())) {
0249                 service->d->propertiesChanged(path, interface, changed, invalidated);
0250                 return;
0251             }
0252         }
0253     } else if (interface != Strings::orgBluezDevice1()) {
0254         return;
0255     }
0256 
0257     QVariantMap::const_iterator i;
0258     for (i = changed.constBegin(); i != changed.constEnd(); ++i) {
0259         const QVariant &value = i.value();
0260         const QString &property = i.key();
0261 
0262         if (property == QLatin1String("Name")) {
0263             namePropertyChanged(value.toString());
0264         } else if (property == QLatin1String("Address")) {
0265             addressPropertyChanged(value.toString());
0266         } else if (property == QLatin1String("Alias")) {
0267             aliasPropertyChanged(value.toString());
0268         } else if (property == QLatin1String("Class")) {
0269             classPropertyChanged(value.toUInt());
0270         } else if (property == QLatin1String("Appearance")) {
0271             PROPERTY_CHANGED(m_appearance, toUInt, appearanceChanged);
0272         } else if (property == QLatin1String("Icon")) {
0273             PROPERTY_CHANGED(m_icon, toString, iconChanged);
0274         } else if (property == QLatin1String("Paired")) {
0275             PROPERTY_CHANGED(m_paired, toBool, pairedChanged);
0276         } else if (property == QLatin1String("Trusted")) {
0277             PROPERTY_CHANGED(m_trusted, toBool, trustedChanged);
0278         } else if (property == QLatin1String("Blocked")) {
0279             PROPERTY_CHANGED(m_blocked, toBool, blockedChanged);
0280         } else if (property == QLatin1String("LegacyPairing")) {
0281             PROPERTY_CHANGED(m_legacyPairing, toBool, legacyPairingChanged);
0282         } else if (property == QLatin1String("RSSI")) {
0283             PROPERTY_CHANGED(m_rssi, toInt, rssiChanged);
0284         } else if (property == QLatin1String("ManufacturerData")) {
0285             PROPERTY_CHANGED2(m_manufacturerData, variantToManData(value), manufacturerDataChanged);
0286         } else if (property == QLatin1String("ServicesResolved")) {
0287             PROPERTY_CHANGED(m_servicesResolved, toBool, servicesResolvedChanged);
0288         } else if (property == QLatin1String("Connected")) {
0289             PROPERTY_CHANGED(m_connected, toBool, connectedChanged);
0290         } else if (property == QLatin1String("Modalias")) {
0291             PROPERTY_CHANGED(m_modalias, toString, modaliasChanged);
0292         } else if (property == QLatin1String("UUIDs")) {
0293             PROPERTY_CHANGED2(m_uuids, stringListToUpper(value.toStringList()), uuidsChanged);
0294         } else if (property == QLatin1String("ServiceData")) {
0295             PROPERTY_CHANGED2(m_serviceData, toByteArrayHash(value.value<QDBusArgument>()), serviceDataChanged);
0296         }
0297     }
0298 
0299     for (const QString &property : invalidated) {
0300         if (property == QLatin1String("Name")) {
0301             namePropertyChanged(QString());
0302         } else if (property == QLatin1String("Class")) {
0303             classPropertyChanged(0);
0304         } else if (property == QLatin1String("Appearance")) {
0305             PROPERTY_INVALIDATED(m_appearance, 0, appearanceChanged);
0306         } else if (property == QLatin1String("Icon")) {
0307             PROPERTY_INVALIDATED(m_icon, QString(), iconChanged);
0308         } else if (property == QLatin1String("RSSI")) {
0309             PROPERTY_INVALIDATED(m_rssi, INVALID_RSSI, rssiChanged);
0310         } else if (property == QLatin1String("ManufacturerData")) {
0311             QMap<uint16_t,QByteArray> map;
0312             PROPERTY_INVALIDATED(m_manufacturerData, map, manufacturerDataChanged);
0313         } else if (property == QLatin1String("ServicesResolved")) {
0314             PROPERTY_INVALIDATED(m_servicesResolved, false, servicesResolvedChanged);
0315         } else if (property == QLatin1String("Modalias")) {
0316             PROPERTY_INVALIDATED(m_modalias, QString(), modaliasChanged);
0317         } else if (property == QLatin1String("UUIDs")) {
0318             PROPERTY_INVALIDATED(m_uuids, QStringList(), uuidsChanged);
0319         } else if (property == QLatin1String("ServiceData")) {
0320             PROPERTY_INVALIDATED(m_serviceData, (QHash<QString, QByteArray>()), serviceDataChanged);
0321         }
0322     }
0323 
0324     Q_EMIT q.lock()->deviceChanged(q.toStrongRef());
0325 }
0326 
0327 void DevicePrivate::namePropertyChanged(const QString &value)
0328 {
0329     if (m_name != value) {
0330         m_name = value;
0331         Q_EMIT q.lock()->remoteNameChanged(m_name);
0332         Q_EMIT q.lock()->friendlyNameChanged(q.lock()->friendlyName());
0333     }
0334 }
0335 
0336 void DevicePrivate::addressPropertyChanged(const QString &value)
0337 {
0338     if (m_address != value) {
0339         m_address = value;
0340         Q_EMIT q.lock()->addressChanged(m_address);
0341     }
0342 }
0343 
0344 void DevicePrivate::aliasPropertyChanged(const QString &value)
0345 {
0346     if (m_alias != value) {
0347         m_alias = value;
0348         Q_EMIT q.lock()->nameChanged(m_alias);
0349         Q_EMIT q.lock()->friendlyNameChanged(q.lock()->friendlyName());
0350     }
0351 }
0352 
0353 void DevicePrivate::classPropertyChanged(quint32 value)
0354 {
0355     if (m_deviceClass != value) {
0356         m_deviceClass = value;
0357         Q_EMIT q.lock()->deviceClassChanged(m_deviceClass);
0358         Q_EMIT q.lock()->typeChanged(q.lock()->type());
0359     }
0360 }
0361 
0362 void DevicePrivate::serviceDataChanged(const QHash<QString, QByteArray> &value)
0363 {
0364     if (m_serviceData != value) {
0365         m_serviceData = value;
0366         Q_EMIT q.lock()->serviceDataChanged(m_serviceData);
0367     }
0368 }
0369 
0370 } // namespace BluezQt
0371 
0372 #include "moc_device_p.cpp"