diff options
Diffstat (limited to 'src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp')
-rw-r--r-- | src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp new file mode 100644 index 00000000..abcaa23d --- /dev/null +++ b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp @@ -0,0 +1,323 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbluetoothservicediscoveryagent.h" +#include "qbluetoothservicediscoveryagent_p.h" + +#include "bluez/manager_p.h" +#include "bluez/adapter_p.h" +#include "bluez/device_p.h" + +#include <QtDBus/QDBusPendingCallWatcher> + +//#define QTM_SERVICEDISCOVERY_DEBUG + +#ifdef QTM_SERVICEDISCOVERY_DEBUG +#include <QtCore/QDebug> +#endif + +QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(const QBluetoothAddress &address) +: error(QBluetoothServiceDiscoveryAgent::NoError), state(Inactive), deviceAddress(address), + deviceDiscoveryAgent(0), mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), + singleDevice(false), manager(0), device(0) +{ + qRegisterMetaType<ServiceMap>("ServiceMap"); + qDBusRegisterMetaType<ServiceMap>(); +} + +QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate() +{ + delete device; + delete manager; +} + +void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address) +{ + Q_Q(QBluetoothServiceDiscoveryAgent); + +#ifdef QTM_SERVICEDISCOVERY_DEBUG + qDebug() << "Full discovery on: " << address.toString(); +#endif + + manager = new OrgBluezManagerInterface(QLatin1String("org.bluez"), QLatin1String("/"), + QDBusConnection::systemBus()); + + QDBusPendingReply<QDBusObjectPath> reply = manager->DefaultAdapter(); + reply.waitForFinished(); + if (reply.isError()) { + if (singleDevice) { + error = QBluetoothServiceDiscoveryAgent::DeviceDiscoveryError; + errorString = QBluetoothServiceDiscoveryAgent::tr("Unable to find default adapter"); + emit q->error(error); + } + _q_serviceDiscoveryFinished(); + return; + } + + adapter = new OrgBluezAdapterInterface(QLatin1String("org.bluez"), reply.value().path(), + QDBusConnection::systemBus()); + + QDBusPendingReply<QDBusObjectPath> deviceObjectPath = adapter->CreateDevice(address.toString()); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(deviceObjectPath, q); + watcher->setProperty("_q_BTaddress", QVariant::fromValue(address)); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + q, SLOT(_q_createdDevice(QDBusPendingCallWatcher*))); + +} + +void QBluetoothServiceDiscoveryAgentPrivate::stop() +{ +#ifdef QTM_SERVICEDISCOVERY_DEBUG + qDebug() << Q_FUNC_INFO << "Stop called"; +#endif + if(device){ + QDBusPendingReply<> reply = device->CancelDiscovery(); + reply.waitForFinished(); + + discoveredDevices.clear(); + setDiscoveryState(Inactive); + Q_Q(QBluetoothServiceDiscoveryAgent); + emit q->canceled(); + +// qDebug() << "Stop done"; + } +} + +void QBluetoothServiceDiscoveryAgentPrivate::_q_createdDevice(QDBusPendingCallWatcher *watcher) +{ + Q_Q(QBluetoothServiceDiscoveryAgent); + + const QBluetoothAddress &address = watcher->property("_q_BTaddress").value<QBluetoothAddress>(); + +#ifdef QTM_SERVICEDISCOVERY_DEBUG + qDebug() << Q_FUNC_INFO << "created" << address.toString(); +#endif + + QDBusPendingReply<QDBusObjectPath> deviceObjectPath = *watcher; + if (deviceObjectPath.isError()) { + if (deviceObjectPath.error().name() != QLatin1String("org.bluez.Error.AlreadyExists")) { + _q_serviceDiscoveryFinished(); +#ifdef QTM_SERVICEDISCOVERY_DEBUG + qDebug() << "Create device failed Error: " << error << deviceObjectPath.error().name(); +#endif + return; + } + + deviceObjectPath = adapter->FindDevice(address.toString()); + deviceObjectPath.waitForFinished(); + if (deviceObjectPath.isError()) { + if (singleDevice) { + error = QBluetoothServiceDiscoveryAgent::DeviceDiscoveryError; + errorString = QBluetoothServiceDiscoveryAgent::tr("Unable to access device"); + emit q->error(error); + } + _q_serviceDiscoveryFinished(); +#ifdef QTM_SERVICEDISCOVERY_DEBUG + qDebug() << "Can't find device after creation Error: " << error << deviceObjectPath.error().name(); +#endif + return; + } + } + + device = new OrgBluezDeviceInterface(QLatin1String("org.bluez"), + deviceObjectPath.value().path(), + QDBusConnection::systemBus()); + + QDBusPendingReply<QVariantMap> deviceReply = device->GetProperties(); + deviceReply.waitForFinished(); + if(deviceReply.isError()) + return; + QVariantMap v = deviceReply.value(); + QStringList device_uuids = v.value(QLatin1String("UUIDs")).toStringList(); + + QString pattern; + foreach (const QBluetoothUuid &uuid, uuidFilter) + pattern += uuid.toString().remove(QLatin1Char('{')).remove(QLatin1Char('}')) + QLatin1Char(' '); + +#ifdef QTM_SERVICEDISCOVERY_DEBUG + qDebug() << Q_FUNC_INFO << "Discover: " << pattern.trimmed(); +#endif + QDBusPendingReply<ServiceMap> discoverReply = device->DiscoverServices(pattern.trimmed()); + watcher = new QDBusPendingCallWatcher(discoverReply, q); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + q, SLOT(_q_discoveredServices(QDBusPendingCallWatcher*))); + +} + +void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingCallWatcher *watcher) +{ +#ifdef QTM_SERVICEDISCOVERY_DEBUG + qDebug() << Q_FUNC_INFO; +#endif + + QDBusPendingReply<ServiceMap> reply = *watcher; + if (reply.isError()) { +#ifdef QTM_SERVICEDISCOVERY_DEBUG + qDebug() << "discoveredServices error: " << error << reply.error().message(); +#endif + watcher->deleteLater(); + if (singleDevice) { + Q_Q(QBluetoothServiceDiscoveryAgent); + error = QBluetoothServiceDiscoveryAgent::UnknownError; + errorString = reply.error().message(); + emit q->error(error); + } + _q_serviceDiscoveryFinished(); + return; + } + + ServiceMap map = reply.value(); + +#ifdef QTM_SERVICEDISCOVERY_DEBUG + qDebug() << "Parsing xml" << discoveredDevices.at(0).address().toString() << discoveredDevices.count() << map.count(); +#endif + + foreach (const QString &record, reply.value()) { + QXmlStreamReader xml(record); + +#ifdef QTM_SERVICEDISCOVERY_DEBUG + // qDebug() << "Service xml" << record; +#endif + + QBluetoothServiceInfo serviceInfo; + serviceInfo.setDevice(discoveredDevices.at(0)); + + while (!xml.atEnd()) { + xml.readNext(); + + if (xml.tokenType() == QXmlStreamReader::StartElement && + xml.name() == QLatin1String("attribute")) { + quint16 attributeId = + xml.attributes().value(QLatin1String("id")).toString().toUShort(0, 0); + + if (xml.readNextStartElement()) { + QVariant value = readAttributeValue(xml); + + serviceInfo.setAttribute(attributeId, value); + } + } + } + + if (!serviceInfo.isValid()) + continue; + + Q_Q(QBluetoothServiceDiscoveryAgent); + + discoveredServices.append(serviceInfo); +#ifdef QTM_SERVICEDISCOVERY_DEBUG + qDebug() << "Discovered services" << discoveredDevices.at(0).address().toString(); +#endif + emit q->serviceDiscovered(serviceInfo); + // could stop discovery, check for state + if(discoveryState() == Inactive){ + qDebug() << "Exit discovery after stop"; + break; + } + } + + watcher->deleteLater(); + + _q_serviceDiscoveryFinished(); +} + +QVariant QBluetoothServiceDiscoveryAgentPrivate::readAttributeValue(QXmlStreamReader &xml) +{ + if (xml.name() == QLatin1String("boolean")) { + const QString value = xml.attributes().value(QLatin1String("value")).toString(); + xml.skipCurrentElement(); + return value == QLatin1String("true"); + } else if (xml.name() == QLatin1String("uint8")) { + quint8 value = xml.attributes().value(QLatin1String("value")).toString().toUShort(0, 0); + xml.skipCurrentElement(); + return value; + } else if (xml.name() == QLatin1String("uint16")) { + quint16 value = xml.attributes().value(QLatin1String("value")).toString().toUShort(0, 0); + xml.skipCurrentElement(); + return value; + } else if (xml.name() == QLatin1String("uint32")) { + quint32 value = xml.attributes().value(QLatin1String("value")).toString().toUInt(0, 0); + xml.skipCurrentElement(); + return value; + } else if (xml.name() == QLatin1String("uint64")) { + quint64 value = xml.attributes().value(QLatin1String("value")).toString().toULongLong(0, 0); + xml.skipCurrentElement(); + return value; + } else if (xml.name() == QLatin1String("uuid")) { + QBluetoothUuid uuid; + const QString value = xml.attributes().value(QLatin1String("value")).toString(); + if (value.startsWith(QLatin1String("0x"))) { + if (value.length() == 6) { + quint16 v = value.toUShort(0, 0); + uuid = QBluetoothUuid(v); + } else if (value.length() == 10) { + quint32 v = value.toUInt(0, 0); + uuid = QBluetoothUuid(v); + } + } else { + uuid = QBluetoothUuid(value); + } + xml.skipCurrentElement(); + return QVariant::fromValue(uuid); + } else if (xml.name() == QLatin1String("text")) { + QString value = xml.attributes().value(QLatin1String("value")).toString(); + if (xml.attributes().value(QLatin1String("encoding")) == QLatin1String("hex")) + value = QString::fromUtf8(QByteArray::fromHex(value.toAscii())); + xml.skipCurrentElement(); + return value; + } else if (xml.name() == QLatin1String("sequence")) { + QBluetoothServiceInfo::Sequence sequence; + + while (xml.readNextStartElement()) { + QVariant value = readAttributeValue(xml); + sequence.append(value); + } + + return QVariant::fromValue<QBluetoothServiceInfo::Sequence>(sequence); + } else { + qWarning("unknown attribute type %s %s", + xml.name().toString().toLocal8Bit().constData(), + xml.attributes().value(QLatin1String("value")).toString().toLocal8Bit().constData()); + Q_ASSERT(false); + xml.skipCurrentElement(); + return QVariant(); + } +} |