diff options
Diffstat (limited to 'src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp')
-rw-r--r-- | src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp | 250 |
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")) { |