summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bluetooth/bluez/battery1.cpp26
-rw-r--r--src/bluetooth/bluez/battery1_p.h51
-rw-r--r--src/bluetooth/bluez/bluez.pri2
-rwxr-xr-xsrc/bluetooth/bluez/generate1
-rw-r--r--src/bluetooth/bluez/org.bluez.Battery1.xml7
-rw-r--r--src/bluetooth/qlowenergycontroller_bluezdbus.cpp222
-rw-r--r--src/bluetooth/qlowenergycontroller_bluezdbus_p.h6
7 files changed, 299 insertions, 16 deletions
diff --git a/src/bluetooth/bluez/battery1.cpp b/src/bluetooth/bluez/battery1.cpp
new file mode 100644
index 00000000..f23cc205
--- /dev/null
+++ b/src/bluetooth/bluez/battery1.cpp
@@ -0,0 +1,26 @@
+/*
+ * This file was generated by qdbusxml2cpp version 0.8
+ * Command line was: qdbusxml2cpp -p battery1_p.h:battery1.cpp org.bluez.Battery1.xml
+ *
+ * qdbusxml2cpp is Copyright (C) 2018 The Qt Company Ltd.
+ *
+ * This is an auto-generated file.
+ * This file may have been hand-edited. Look for HAND-EDIT comments
+ * before re-generating it.
+ */
+
+#include "battery1_p.h"
+
+/*
+ * Implementation of interface class OrgBluezBattery1Interface
+ */
+
+OrgBluezBattery1Interface::OrgBluezBattery1Interface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
+ : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
+{
+}
+
+OrgBluezBattery1Interface::~OrgBluezBattery1Interface()
+{
+}
+
diff --git a/src/bluetooth/bluez/battery1_p.h b/src/bluetooth/bluez/battery1_p.h
new file mode 100644
index 00000000..035382df
--- /dev/null
+++ b/src/bluetooth/bluez/battery1_p.h
@@ -0,0 +1,51 @@
+/*
+ * This file was generated by qdbusxml2cpp version 0.8
+ * Command line was: qdbusxml2cpp -p battery1_p.h:battery1.cpp org.bluez.Battery1.xml
+ *
+ * qdbusxml2cpp is Copyright (C) 2018 The Qt Company Ltd.
+ *
+ * This is an auto-generated file.
+ * Do not edit! All changes made to it will be lost.
+ */
+
+#ifndef BATTERY1_P_H
+#define BATTERY1_P_H
+
+#include <QtCore/QObject>
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+#include <QtCore/QMap>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QVariant>
+#include <QtDBus/QtDBus>
+
+/*
+ * Proxy class for interface org.bluez.Battery1
+ */
+class OrgBluezBattery1Interface: public QDBusAbstractInterface
+{
+ Q_OBJECT
+public:
+ static inline const char *staticInterfaceName()
+ { return "org.bluez.Battery1"; }
+
+public:
+ OrgBluezBattery1Interface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr);
+
+ ~OrgBluezBattery1Interface();
+
+ Q_PROPERTY(uchar Percentage READ percentage)
+ inline uchar percentage() const
+ { return qvariant_cast< uchar >(property("Percentage")); }
+
+public Q_SLOTS: // METHODS
+Q_SIGNALS: // SIGNALS
+};
+
+namespace org {
+ namespace bluez {
+ typedef ::OrgBluezBattery1Interface Battery1;
+ }
+}
+#endif
diff --git a/src/bluetooth/bluez/bluez.pri b/src/bluetooth/bluez/bluez.pri
index 4201f104..af7f0e0c 100644
--- a/src/bluetooth/bluez/bluez.pri
+++ b/src/bluetooth/bluez/bluez.pri
@@ -22,6 +22,7 @@ HEADERS += bluez/manager_p.h \
bluez/gattchar1_p.h \
bluez/gattdesc1_p.h \
bluez/gattservice1_p.h \
+ bluez/battery1_p.h \
bluez/bluez_data_p.h \
bluez/hcimanager_p.h \
bluez/remotedevicemanager_p.h \
@@ -51,6 +52,7 @@ SOURCES += bluez/manager.cpp \
bluez/gattchar1.cpp \
bluez/gattdesc1.cpp \
bluez/gattservice1.cpp \
+ bluez/battery1.cpp \
bluez/hcimanager.cpp \
bluez/remotedevicemanager.cpp \
bluez/bluetoothmanagement.cpp
diff --git a/src/bluetooth/bluez/generate b/src/bluetooth/bluez/generate
index 73d3b3f5..b3db63ff 100755
--- a/src/bluetooth/bluez/generate
+++ b/src/bluetooth/bluez/generate
@@ -24,4 +24,5 @@ qdbusxml2cpp -p obex_transfer1_bluez5_p.h:obex_transfer1_bluez5_p.h org.bluez.ob
qdbusxml2cpp -p gattchar1_p.h:gattchar1.cpp org.bluez.GattCharacteristic1.xml
qdbusxml2cpp -p gattdesc1_p.h:gattdesc1.cpp org.bluez.GattDescriptor1.xml
qdbusxml2cpp -p gattservice1_p.h:gattservice1.cpp org.bluez.GattService1.xml
+qdbusxml2cpp -p battery1_p.h:battery1.cpp org.bluez.Battery1.xml
diff --git a/src/bluetooth/bluez/org.bluez.Battery1.xml b/src/bluetooth/bluez/org.bluez.Battery1.xml
new file mode 100644
index 00000000..efa0309d
--- /dev/null
+++ b/src/bluetooth/bluez/org.bluez.Battery1.xml
@@ -0,0 +1,7 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.bluez.Battery1">
+ <property name="Percentage" type="y" access="read"></property>
+ </interface>
+</node>
diff --git a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp
index 73fd94ae..e881a1b9 100644
--- a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp
+++ b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp
@@ -42,9 +42,9 @@
#include "bluez/bluez5_helper_p.h"
#include "bluez/device1_bluez5_p.h"
#include "bluez/gattservice1_p.h"
-
#include "bluez/gattchar1_p.h"
#include "bluez/gattdesc1_p.h"
+#include "bluez/battery1_p.h"
#include "bluez/objectmanager_p.h"
#include "bluez/properties_p.h"
@@ -120,6 +120,53 @@ void QLowEnergyControllerPrivateBluezDBus::devicePropertiesChanged(
}
}
}
+ } else if (interface == QStringLiteral("org.bluez.Battery1")) {
+ qCDebug(QT_BT_BLUEZ) << "######" << interface << changedProperties;
+ if (changedProperties.contains(QStringLiteral("Percentage"))) {
+ // if battery service is discovered and ClientCharConfig is enabled
+ // emit characteristicChanged() signal
+ const QBluetoothUuid uuid(QBluetoothUuid::BatteryService);
+ if (!serviceList.contains(uuid) || !dbusServices.contains(uuid)
+ || !dbusServices[uuid].hasBatteryService
+ || dbusServices[uuid].batteryInterface.isNull())
+ return;
+
+ QSharedPointer<QLowEnergyServicePrivate> serviceData = serviceList.value(uuid);
+ if (serviceData->state != QLowEnergyService::ServiceDiscovered)
+ return;
+
+ QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData>::iterator iter;
+ iter = serviceData->characteristicList.begin();
+ while (iter != serviceData->characteristicList.end()) {
+ auto &charData = iter.value();
+ if (charData.uuid != QBluetoothUuid::BatteryLevel)
+ continue;
+
+ // Client Characteristic Notification enabled?
+ bool cccActive = false;
+ for (const QLowEnergyServicePrivate::DescData &descData : qAsConst(charData.descriptorList)) {
+ if (descData.uuid != QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration))
+ continue;
+ if (descData.value == QByteArray::fromHex("0100")
+ || descData.value == QByteArray::fromHex("0200")) {
+ cccActive = true;
+ break;
+ }
+ }
+
+ const QByteArray newValue(1, char(dbusServices[uuid].batteryInterface->percentage()));
+ qCDebug(QT_BT_BLUEZ) << "Battery1 char update" << cccActive
+ << charData.value.toHex() << "->" << newValue.toHex();
+ if (cccActive && newValue != charData.value) {
+ qCDebug(QT_BT_BLUEZ) << "Property update for Battery1";
+ charData.value = newValue;
+ QLowEnergyCharacteristic ch(serviceData, iter.key());
+ emit serviceData->characteristicChanged(ch, newValue);
+ }
+
+ break;
+ }
+ }
}
}
@@ -358,10 +405,50 @@ void QLowEnergyControllerPrivateBluezDBus::discoverServices()
Q_Q(QLowEnergyController);
+ auto setupServicePrivate = [&, q](
+ QLowEnergyService::ServiceType type, const QBluetoothUuid &uuid, const QString &path){
+ QSharedPointer<QLowEnergyServicePrivate> priv = QSharedPointer<QLowEnergyServicePrivate>::create();
+ priv->uuid = uuid;
+ priv->type = type; // we make a guess we cannot validate
+ priv->setController(this);
+
+ GattService serviceContainer;
+ serviceContainer.servicePath = path;
+ if (uuid == QBluetoothUuid::BatteryService)
+ serviceContainer.hasBatteryService = true;
+
+ serviceList.insert(priv->uuid, priv);
+ dbusServices.insert(priv->uuid, serviceContainer);
+
+ emit q->serviceDiscovered(priv->uuid);
+ };
+
const ManagedObjectList managedObjectList = reply.value();
const QString servicePathPrefix = device->path().append(QStringLiteral("/service"));
for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
const InterfaceList &ifaceList = it.value();
+
+ if (!it.key().path().startsWith(device->path()))
+ continue;
+
+ // Since Bluez 5.48 battery services (0x180f) are no longer exposed
+ // as generic services under servicePathPrefix.
+ // A dedicated org.bluez.Battery1 interface is exposed. Here we are going to revert
+ // Bettery1 to the generic pattern.
+ if (it.key().path() == device->path()) {
+ // find Battery1 service
+ for (InterfaceList::const_iterator battIter = ifaceList.constBegin(); battIter != ifaceList.constEnd(); ++battIter) {
+ const QString &iface = battIter.key();
+ if (iface == QStringLiteral("org.bluez.Battery1")) {
+ qCDebug(QT_BT_BLUEZ) << "Found dedicated Battery service -> emulating generic btle access";
+ setupServicePrivate(QLowEnergyService::PrimaryService,
+ QBluetoothUuid::BatteryService,
+ it.key().path());
+ }
+ }
+ continue;
+ }
+
if (!it.key().path().startsWith(servicePathPrefix))
continue;
@@ -372,21 +459,10 @@ void QLowEnergyControllerPrivateBluezDBus::discoverServices()
QScopedPointer<OrgBluezGattService1Interface> service(new OrgBluezGattService1Interface(
QStringLiteral("org.bluez"),it.key().path(),
QDBusConnection::systemBus(), this));
-
- QSharedPointer<QLowEnergyServicePrivate> priv = QSharedPointer<QLowEnergyServicePrivate>::create();
- priv->uuid = QBluetoothUuid(service->uUID());
- service->primary()
- ? priv->type = QLowEnergyService::PrimaryService
- : priv->type = QLowEnergyService::IncludedService;
- priv->setController(this);
-
- GattService serviceContainer;
- serviceContainer.servicePath = it.key().path();
-
- serviceList.insert(priv->uuid, priv);
- dbusServices.insert(priv->uuid, serviceContainer);
-
- emit q->serviceDiscovered(priv->uuid);
+ setupServicePrivate(service->primary()
+ ? QLowEnergyService::PrimaryService
+ : QLowEnergyService::IncludedService,
+ QBluetoothUuid(service->uUID()), it.key().path());
}
}
}
@@ -395,6 +471,57 @@ void QLowEnergyControllerPrivateBluezDBus::discoverServices()
emit q->discoveryFinished();
}
+void QLowEnergyControllerPrivateBluezDBus::discoverBatteryServiceDetails(
+ GattService &dbusData, QSharedPointer<QLowEnergyServicePrivate> serviceData)
+{
+ // This process exists to work around the fact that Battery services (0x180f)
+ // are not mapped as generic services but use the Battery1 interface.
+ // Artificial chararacteristics and descriptors are created to emulate the generic behavior.
+
+ auto batteryService = QSharedPointer<OrgBluezBattery1Interface>::create(
+ QStringLiteral("org.bluez"), dbusData.servicePath,
+ QDBusConnection::systemBus());
+ dbusData.batteryInterface = batteryService;
+
+ serviceData->startHandle = runningHandle++; //service start handle
+
+ // Create BatteryLevel char
+ QLowEnergyHandle indexHandle = runningHandle++; // char handle index
+ QLowEnergyServicePrivate::CharData charData;
+
+ charData.valueHandle = runningHandle++;
+ charData.properties.setFlag(QLowEnergyCharacteristic::Read);
+ charData.properties.setFlag(QLowEnergyCharacteristic::Notify);
+ charData.uuid = QBluetoothUuid::BatteryLevel;
+ charData.value = QByteArray(1, char(batteryService->percentage()));
+
+ // Create the descriptors for the BatteryLevel
+ // They are hardcoded although CCC may change
+ QLowEnergyServicePrivate::DescData descData;
+ QLowEnergyHandle descriptorHandle = runningHandle++;
+ descData.uuid = QBluetoothUuid::ClientCharacteristicConfiguration;
+ descData.value = QByteArray::fromHex("0000"); // all configs off
+ charData.descriptorList.insert(descriptorHandle, descData);
+
+ descriptorHandle = runningHandle++;
+ descData.uuid = QBluetoothUuid::CharacteristicPresentationFormat;
+ //for details see Characteristic Presentation Format Vol3, Part G 3.3.3.5
+ // unsigend 8 bit, exp=1, org.bluetooth.unit.percentage, namespace & description
+ // bit order: little endian
+ descData.value = QByteArray::fromHex("0400ad27011131");
+ charData.descriptorList.insert(descriptorHandle, descData);
+
+ descriptorHandle = runningHandle++;
+ descData.uuid = QBluetoothUuid::ReportReference;
+ descData.value = QByteArray::fromHex("0401");
+ charData.descriptorList.insert(descriptorHandle, descData);
+
+ serviceData->characteristicList[indexHandle] = charData;
+ serviceData->endHandle = runningHandle++;
+
+ serviceData->setState(QLowEnergyService::ServiceDiscovered);
+}
+
void QLowEnergyControllerPrivateBluezDBus::discoverServiceDetails(const QBluetoothUuid &service)
{
if (!serviceList.contains(service) || !dbusServices.contains(service)) {
@@ -410,6 +537,12 @@ void QLowEnergyControllerPrivateBluezDBus::discoverServiceDetails(const QBluetoo
GattService &dbusData = dbusServices[service];
dbusData.characteristics.clear();
+ if (dbusData.hasBatteryService) {
+ qCDebug(QT_BT_BLUEZ) << "Triggering Battery1 service discovery on " << dbusData.servicePath;
+ discoverBatteryServiceDetails(dbusData, serviceData);
+ return;
+ }
+
QDBusPendingReply<ManagedObjectList> reply = managerBluez->GetManagedObjects();
reply.waitForFinished();
if (reply.isError()) {
@@ -971,6 +1104,20 @@ void QLowEnergyControllerPrivateBluezDBus::readCharacteristic(
qCWarning(QT_BT_BLUEZ) << "Reading non-readable char" << charHandle;
}
+ const GattService &gattService = dbusServices[service->uuid];
+ if (gattService.hasBatteryService && !gattService.batteryInterface.isNull()) {
+ // Reread from dbus interface and write to local cache
+ const QByteArray newValue(1, char(gattService.batteryInterface->percentage()));
+ quint16 result = updateValueOfCharacteristic(charHandle, newValue, false);
+ if (result > 0) {
+ QLowEnergyCharacteristic ch(service, charHandle);
+ emit service->characteristicRead(ch, newValue);
+ } else {
+ service->setError(QLowEnergyService::CharacteristicReadError);
+ }
+ return;
+ }
+
GattJob job;
job.flags = GattJob::JobFlags({GattJob::CharRead});
job.service = service;
@@ -994,6 +1141,17 @@ void QLowEnergyControllerPrivateBluezDBus::readDescriptor(
if (!charDetails.descriptorList.contains(descriptorHandle))
return;
+ const GattService &gattService = dbusServices[service->uuid];
+ if (gattService.hasBatteryService && !gattService.batteryInterface.isNull()) {
+ auto descriptor = descriptorForHandle(descriptorHandle);
+ if (descriptor.isValid())
+ emit service->descriptorRead(descriptor, descriptor.value());
+ else
+ service->setError(QLowEnergyService::DescriptorReadError);
+
+ return;
+ }
+
GattJob job;
job.flags = GattJob::JobFlags({GattJob::DescRead});
job.service = service;
@@ -1017,6 +1175,14 @@ void QLowEnergyControllerPrivateBluezDBus::writeCharacteristic(
}
if (role == QLowEnergyController::CentralRole) {
+ const GattService &gattService = dbusServices[service->uuid];
+ if (gattService.hasBatteryService && !gattService.batteryInterface.isNull()) {
+ //Battery1 interface is readonly
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+
GattJob job;
job.flags = GattJob::JobFlags({GattJob::CharWrite});
job.service = service;
@@ -1043,6 +1209,30 @@ void QLowEnergyControllerPrivateBluezDBus::writeDescriptor(
return;
if (role == QLowEnergyController::CentralRole) {
+ const GattService &gattService = dbusServices[service->uuid];
+ if (gattService.hasBatteryService && !gattService.batteryInterface.isNull()) {
+ auto descriptor = descriptorForHandle(descriptorHandle);
+ if (!descriptor.isValid())
+ return;
+
+ if (descriptor.uuid() == QBluetoothUuid::ClientCharacteristicConfiguration) {
+ if (newValue == QByteArray::fromHex("0000")
+ || newValue == QByteArray::fromHex("0100")
+ || newValue == QByteArray::fromHex("0200")) {
+ quint16 result = updateValueOfDescriptor(charHandle, descriptorHandle, newValue, false);
+ if (result > 0)
+ emit service->descriptorWritten(descriptor, newValue);
+ else
+ emit service->setError(QLowEnergyService::DescriptorWriteError);
+
+ }
+ } else {
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ }
+
+ return;
+ }
+
GattJob job;
job.flags = GattJob::JobFlags({GattJob::DescWrite});
job.service = service;
diff --git a/src/bluetooth/qlowenergycontroller_bluezdbus_p.h b/src/bluetooth/qlowenergycontroller_bluezdbus_p.h
index 4c0f5b74..4c169d0b 100644
--- a/src/bluetooth/qlowenergycontroller_bluezdbus_p.h
+++ b/src/bluetooth/qlowenergycontroller_bluezdbus_p.h
@@ -58,6 +58,7 @@
#include <QtDBus/QDBusObjectPath>
class OrgBluezAdapter1Interface;
+class OrgBluezBattery1Interface;
class OrgBluezDevice1Interface;
class OrgBluezGattCharacteristic1Interface;
class OrgBluezGattDescriptor1Interface;
@@ -155,6 +156,9 @@ private:
{
QString servicePath;
QVector<GattCharacteristic> characteristics;
+
+ bool hasBatteryService = false;
+ QSharedPointer<OrgBluezBattery1Interface> batteryInterface;
};
QHash<QBluetoothUuid, GattService> dbusServices;
@@ -183,6 +187,8 @@ private:
bool jobPending = false;
void prepareNextJob();
+ void discoverBatteryServiceDetails(GattService &dbusData,
+ QSharedPointer<QLowEnergyServicePrivate> serviceData);
};
QT_END_NAMESPACE