summaryrefslogtreecommitdiffstats
path: root/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp')
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp250
1 files changed, 235 insertions, 15 deletions
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp
index 4752a0ab..8e5c3aaa 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp
@@ -41,10 +41,13 @@
#include "qbluetoothservicediscoveryagent.h"
#include "qbluetoothservicediscoveryagent_p.h"
+#include "qlowenergycharacteristicinfo_p.h"
+#include "qlowenergyserviceinfo_p.h"
#include "bluez/manager_p.h"
#include "bluez/adapter_p.h"
#include "bluez/device_p.h"
+#include "bluez/characteristic_p.h"
#include "bluez/bluez5_helper_p.h"
#include "bluez/objectmanager_p.h"
#include "bluez/adapter1_bluez5_p.h"
@@ -127,6 +130,15 @@ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &addr
adapter = new OrgBluezAdapterInterface(QStringLiteral("org.bluez"), reply.value().path(),
QDBusConnection::systemBus());
+ if (m_deviceAdapterAddress.isNull()) {
+ QDBusPendingReply<QVariantMap> reply = adapter->GetProperties();
+ reply.waitForFinished();
+ if (!reply.isError()) {
+ const QBluetoothAddress path_address(reply.value().value(QStringLiteral("Address")).toString());
+ m_deviceAdapterAddress = path_address;
+ }
+ }
+
QDBusPendingReply<QDBusObjectPath> deviceObjectPath = adapter->CreateDevice(address.toString());
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(deviceObjectPath, q);
@@ -314,7 +326,8 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_finishSdpScan(QBluetoothServiceD
emit q->error(error);
} else if (!xmlRecords.isEmpty() && discoveryState() != Inactive) {
foreach (const QString &record, xmlRecords) {
- const QBluetoothServiceInfo serviceInfo = parseServiceXml(record);
+ bool isBtleService = false;
+ const QBluetoothServiceInfo serviceInfo = parseServiceXml(record, &isBtleService);
//apply uuidFilter
if (!uuidFilter.isEmpty()) {
@@ -416,17 +429,73 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_createdDevice(QDBusPendingCallWa
delete adapter;
adapter = 0;
- QString pattern;
- foreach (const QBluetoothUuid &uuid, uuidFilter)
- pattern += uuid.toString().remove(QLatin1Char('{')).remove(QLatin1Char('}')) + QLatin1Char(' ');
+ QVariantMap deviceProperties;
+ QString classType;
+ QDBusPendingReply<QVariantMap> deviceReply = device->GetProperties();
+ deviceReply.waitForFinished();
+ if (!deviceReply.isError()) {
+ deviceProperties = deviceReply.value();
+ classType = deviceProperties.value(QStringLiteral("Class")).toString();
+ }
+
+ /*
+ * Low Energy services in bluez are represented as the list of the paths that can be
+ * accessed with org.bluez.Characteristic
+ */
+ //QDBusArgument services = v.value(QLatin1String("Services")).value<QDBusArgument>();
- pattern = pattern.trimmed();
- qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO << "Discover restrictions:" << pattern;
- QDBusPendingReply<ServiceMap> discoverReply = device->DiscoverServices(pattern);
- watcher = new QDBusPendingCallWatcher(discoverReply, q);
- QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
- q, SLOT(_q_discoveredServices(QDBusPendingCallWatcher*)));
+ /*
+ * Bluez v4.1 does not have extra bit which gives information if device is Bluetooth
+ * Low Energy device and the way to discover it is with Class property of the Bluetooth device.
+ * Low Energy devices do not have property Class.
+ * In case we have LE device finish service discovery; otherwise search for regular services.
+ */
+ if (classType.isEmpty()) { //is BLE device or device properties above not retrievable
+ qCDebug(QT_BT_BLUEZ) << "Discovered BLE-only device. Normal service discovery skipped.";
+ delete device;
+ device = 0;
+
+ const QStringList deviceUuids = deviceProperties.value(QStringLiteral("UUIDs")).toStringList();
+ for (int i = 0; i < deviceUuids.size(); i++) {
+ QString b = deviceUuids.at(i);
+ b = b.remove(QLatin1Char('{')).remove(QLatin1Char('}'));
+ const QBluetoothUuid uuid(b);
+
+ qCDebug(QT_BT_BLUEZ) << "Discovered BLE service" << uuid << uuidFilter.size();
+ QLowEnergyServiceInfo lowEnergyService(uuid);
+ lowEnergyService.setDevice(discoveredDevices.at(0));
+ if (uuidFilter.isEmpty())
+ emit q->serviceDiscovered(lowEnergyService);
+ else {
+ for (int j = 0; j < uuidFilter.size(); j++) {
+ if (uuidFilter.at(j) == uuid)
+ emit q->serviceDiscovered(lowEnergyService);
+
+ }
+ }
+
+ }
+
+ if (singleDevice && deviceReply.isError()) {
+ error = QBluetoothServiceDiscoveryAgent::InputOutputError;
+ errorString = QBluetoothServiceDiscoveryAgent::tr("Unable to access device");
+ emit q->error(error);
+ }
+ _q_serviceDiscoveryFinished();
+ } else {
+ QString pattern;
+ foreach (const QBluetoothUuid &uuid, uuidFilter)
+ pattern += uuid.toString().remove(QLatin1Char('{')).remove(QLatin1Char('}')) + QLatin1Char(' ');
+
+ pattern = pattern.trimmed();
+ qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO << "Discover restrictions:" << pattern;
+
+ QDBusPendingReply<ServiceMap> discoverReply = device->DiscoverServices(pattern);
+ watcher = new QDBusPendingCallWatcher(discoverReply, q);
+ QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ q, SLOT(_q_discoveredServices(QDBusPendingCallWatcher*)));
+ }
}
// Bluez 4
@@ -436,13 +505,13 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingC
return;
qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO;
+ Q_Q(QBluetoothServiceDiscoveryAgent);
QDBusPendingReply<ServiceMap> reply = *watcher;
if (reply.isError()) {
qCDebug(QT_BT_BLUEZ) << "discoveredServices error: " << error << reply.error().message();
watcher->deleteLater();
if (singleDevice) {
- Q_Q(QBluetoothServiceDiscoveryAgent);
error = QBluetoothServiceDiscoveryAgent::UnknownError;
errorString = reply.error().message();
emit q->error(error);
@@ -457,8 +526,17 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingC
qCDebug(QT_BT_BLUEZ) << "Parsing xml" << discoveredDevices.at(0).address().toString() << discoveredDevices.count() << map.count();
+
+
foreach (const QString &record, reply.value()) {
- QBluetoothServiceInfo serviceInfo = parseServiceXml(record);
+ bool isBtleService = false;
+ QBluetoothServiceInfo serviceInfo = parseServiceXml(record, &isBtleService);
+
+ if (isBtleService) {
+ qCDebug(QT_BT_BLUEZ) << "Discovered BLE services" << discoveredDevices.at(0).address().toString()
+ << serviceInfo.serviceName() << serviceInfo.serviceUuid() << serviceInfo.serviceClassUuids();
+ continue;
+ }
if (!serviceInfo.isValid())
continue;
@@ -501,7 +579,8 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingC
_q_serviceDiscoveryFinished();
}
-QBluetoothServiceInfo QBluetoothServiceDiscoveryAgentPrivate::parseServiceXml(const QString& xmlRecord)
+QBluetoothServiceInfo QBluetoothServiceDiscoveryAgentPrivate::parseServiceXml(
+ const QString& xmlRecord, bool *isBtleService)
{
QXmlStreamReader xml(xmlRecord);
@@ -518,7 +597,24 @@ QBluetoothServiceInfo QBluetoothServiceDiscoveryAgentPrivate::parseServiceXml(co
if (xml.readNextStartElement()) {
QVariant value = readAttributeValue(xml);
-
+ if (isBtleService) {
+ if (attributeId == 1) {// Attribute with id 1 contains UUID of the service
+ const QBluetoothServiceInfo::Sequence seq =
+ value.value<QBluetoothServiceInfo::Sequence>();
+ for (int i = 0; i < seq.count(); i++) {
+ const QBluetoothUuid uuid = seq.at(i).value<QBluetoothUuid>();
+ if ((uuid.data1 & 0x1800) == 0x1800) {// We are taking into consideration that LE services starts at 0x1800
+ //TODO don't emit in the middle of nowhere
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ QLowEnergyServiceInfo leService(uuid);
+ leService.setDevice(discoveredDevices.at(0));
+ *isBtleService = true;
+ emit q->serviceDiscovered(leService);
+ break;
+ }
+ }
+ }
+ }
serviceInfo.setAttribute(attributeId, value);
}
}
@@ -559,7 +655,6 @@ void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(cons
== details.value(QStringLiteral("Address")).toString()) {
uuidStrings = details.value(QStringLiteral("UUIDs")).toStringList();
break;
-
}
}
}
@@ -585,6 +680,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(cons
if (!uuidFilter.isEmpty() && !uuidFilter.contains(uuid))
continue;
+ // TODO deal with BTLE services under Bluez 5 -> right now they are normal services
QBluetoothServiceInfo serviceInfo;
serviceInfo.setDevice(discoveredDevices.at(0));
@@ -613,6 +709,130 @@ void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(cons
_q_serviceDiscoveryFinished();
}
+/*
+ * Following three methods are implemented in this way to avoid blocking the main thread.
+ * We first go through list of services path and then through services characteristics paths.
+ *
+ * Bluez v4.x does not have support for LE devices through org.bluez interfaces. Because of that
+ * these functions will not be used now. I propose to leave them commented because in Bluez v5.x
+ * we have support for LE devices and we can use these functions.
+ */
+/*
+void QBluetoothServiceDiscoveryAgentPrivate::_g_discoveredGattService()
+{
+
+ if (!gattServices.empty()) {
+ qDebug() << gattServices.at(0) << gattServices.size();
+ gattService = QLowEnergyServiceInfo(gattServices.at(0));
+ gattService.getProperties();
+ QObject::connect(gattService.d_ptr.data(), SIGNAL(finished()), this, SLOT(_g_discoveredGattService()));
+ characteristic = new OrgBluezCharacteristicInterface(QLatin1String("org.bluez"), gattServices.at(0), QDBusConnection::systemBus());
+ QDBusPendingReply<QList<QDBusObjectPath> > characterictisReply = characteristic->DiscoverCharacteristics();
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(characterictisReply, this);
+ QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(_q_discoverGattCharacteristics(QDBusPendingCallWatcher*)));
+
+ gattServices.removeFirst();
+ q_ptr->lowEnergyServiceDiscovered(gattService);
+ emit gattService.d_ptr->finished();
+ }
+ else
+ q_ptr->finished();
+
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::_q_discoverGattCharacteristics(QDBusPendingCallWatcher *watcher)
+{
+
+ QDBusPendingReply<QList<QDBusObjectPath> > characterictisReply = *watcher;
+ if (characterictisReply.isError()){
+ qDebug()<< "Discovering service characteristic error" << characterictisReply.error().message();
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ error = QBluetoothServiceDiscoveryAgent::UnknownError;
+ errorString = characterictisReply.error().message();
+ emit q->error(error);
+ _q_serviceDiscoveryFinished();
+ return;
+ }
+
+ foreach (const QDBusObjectPath &characteristicPath, characterictisReply.value())
+ gattCharacteristics.append(characteristicPath.path());
+ characteristic = new OrgBluezCharacteristicInterface(QLatin1String("org.bluez"), gattCharacteristics.at(0), QDBusConnection::systemBus());
+ QDBusPendingReply<QVariantMap> characteristicProperty = characteristic->GetProperties();
+ watcher = new QDBusPendingCallWatcher(characteristicProperty, this);
+ _q_discoveredGattCharacteristic(watcher);
+
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredGattCharacteristic(QDBusPendingCallWatcher *watcher)
+{
+
+ QDBusPendingReply<QVariantMap> characteristicProperty = *watcher;
+ //qDebug()<<characteristicProperty.value();
+ if (characteristicProperty.isError()){
+ qDebug() << "Characteristic properties error" << characteristicProperty.error().message();
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ error = QBluetoothServiceDiscoveryAgent::UnknownError;
+ errorString = characteristicProperty.error().message();
+ emit q->error(error);
+ _q_serviceDiscoveryFinished();
+ return;
+ }
+ QStringList serviceName;
+
+ if (characteristicProperty.isError())
+ qDebug()<<characteristicProperty.error().message();
+
+ QVariantMap properties = characteristicProperty.value();
+ QString name = properties.value(QLatin1String("Name")).toString();
+ QString description = properties.value(QLatin1String("Description")).toString();
+ serviceName = description.split(QStringLiteral(" "));
+ QString charUuid = properties.value(QLatin1String("UUID")).toString();
+ QBluetoothUuid characteristicUuid(charUuid);
+
+ QVariant value = properties.value(QLatin1String("Value"));
+ QByteArray byteValue = QByteArray();
+ if (value.type() == QVariant::ByteArray)
+ byteValue = value.toByteArray();
+
+ //qDebug() << name << description << characteristicUuid.toString()<< byteValue.size();
+ gattCharacteristic = QLowEnergyCharacteristicInfo(name, description, characteristicUuid, byteValue);
+ gattCharacteristic.setPath(gattCharacteristics.at(0));
+ qDebug() << gattCharacteristics.at(0);
+ gattService.addCharacteristic(gattCharacteristic);
+
+
+ //Testing part for setting the property
+ QString b = "f000aa02-0451-4000-b000-000000000000";
+ QBluetoothUuid u(b);
+ if (gattCharacteristic.uuid() == u){
+ for (int j = 0; j< byteValue.size(); j++){
+ qDebug() << (int) byteValue.at(j);
+ byteValue[j]=1;
+ qDebug() << (int) byteValue.at(j);
+ }
+ bool s = gattCharacteristic.setPropertyValue(QStringLiteral("Value"), byteValue);
+ qDebug() <<s;
+ }
+
+ QString serName = serviceName.at(0) + " Service";
+
+ gattCharacteristics.removeFirst();
+ if (gattCharacteristics.isEmpty()){
+ q_ptr->lowEnergyServiceDiscovered(gattService);
+ emit gattService.d_ptr->finished();
+ }
+ else{
+ OrgBluezCharacteristicInterface *characteristicProperties = new OrgBluezCharacteristicInterface(QLatin1String("org.bluez"), gattCharacteristics.at(0), QDBusConnection::systemBus());
+ QDBusPendingReply<QVariantMap> characteristicProperty = characteristicProperties->GetProperties();
+ watcher = new QDBusPendingCallWatcher(characteristicProperty, this);
+ QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(_q_discoveredGattCharacteristic(QDBusPendingCallWatcher*)));
+ }
+
+}
+*/
+
QVariant QBluetoothServiceDiscoveryAgentPrivate::readAttributeValue(QXmlStreamReader &xml)
{
if (xml.name() == QLatin1String("boolean")) {