From 26e9cf5bc608c7b50fc27a73442966b3880f0825 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Wed, 7 May 2014 16:56:17 +0200 Subject: Service discovery via Bluez5 At the same time we shift the ServiceClassUUID to service name mapping to QBluetoothUuid. It was used by Android and now Bluez 5 uses it too. Task-number: QTBUG-32085 Change-Id: I9f2d4dc4e2997683485f2ba7aaefb646cb72fb75 Reviewed-by: Aaron McCarthy --- src/bluetooth/bluetooth.pro | 2 + src/bluetooth/bluez/bluez5_helper.cpp | 188 +++++++++ src/bluetooth/bluez/bluez5_helper_p.h | 3 + src/bluetooth/qbluetoothservicediscoveryagent.cpp | 9 +- src/bluetooth/qbluetoothservicediscoveryagent.h | 1 + .../qbluetoothservicediscoveryagent_android.cpp | 78 +--- .../qbluetoothservicediscoveryagent_bluez.cpp | 447 +++++++++++++++++++-- src/bluetooth/qbluetoothservicediscoveryagent_p.h | 13 +- src/bluetooth/qbluetoothuuid.cpp | 82 +++- src/bluetooth/qbluetoothuuid.h | 4 +- 10 files changed, 717 insertions(+), 110 deletions(-) diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index 9caaf794..95c7ae19 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -72,6 +72,8 @@ config_bluez:qtHaveModule(dbus) { qbluetoothlocaldevice_bluez.cpp \ qbluetoothtransferreply_bluez.cpp + CONFIG += link_pkgconfig + PKGCONFIG_PRIVATE += bluez } else:CONFIG(blackberry) { DEFINES += QT_QNX_BLUETOOTH diff --git a/src/bluetooth/bluez/bluez5_helper.cpp b/src/bluetooth/bluez/bluez5_helper.cpp index 7a20abac..c995d948 100644 --- a/src/bluetooth/bluez/bluez5_helper.cpp +++ b/src/bluetooth/bluez/bluez5_helper.cpp @@ -46,6 +46,7 @@ #include "objectmanager_p.h" #include "properties_p.h" #include "adapter1_bluez5_p.h" +#include QT_BEGIN_NAMESPACE @@ -269,4 +270,191 @@ void QtBluezDiscoveryManager::removeAdapterFromMonitoring(const QString &dbusPat emit discoveryInterrupted(dbusPath); } +#define BUFFER_SIZE 1024 + +static void parseAttributeValues(sdp_data_t *data, int indentation, QByteArray &xmlOutput) +{ + if (!data) + return; + + const int length = indentation*2 + 1; + QByteArray indentString(length, ' '); + + char snBuffer[BUFFER_SIZE]; + + xmlOutput.append(indentString); + + // deal with every dtd type + switch (data->dtd) { + case SDP_DATA_NIL: + xmlOutput.append("\n"); + break; + case SDP_UINT8: + qsnprintf(snBuffer, BUFFER_SIZE, "\n", data->val.uint8); + xmlOutput.append(snBuffer); + break; + case SDP_UINT16: + qsnprintf(snBuffer, BUFFER_SIZE, "\n", data->val.uint16); + xmlOutput.append(snBuffer); + break; + case SDP_UINT32: + qsnprintf(snBuffer, BUFFER_SIZE, "\n", data->val.uint32); + xmlOutput.append(snBuffer); + break; + case SDP_UINT64: + qsnprintf(snBuffer, BUFFER_SIZE, "\n", data->val.uint64); + xmlOutput.append(snBuffer); + break; + case SDP_UINT128: + xmlOutput.append("val.uint128.data[i]); + xmlOutput.append(snBuffer); + xmlOutput.append("\"/>\n"); + break; + case SDP_INT8: + qsnprintf(snBuffer, BUFFER_SIZE, "/n", data->val.int8); + xmlOutput.append(snBuffer); + break; + case SDP_INT16: + qsnprintf(snBuffer, BUFFER_SIZE, "/n", data->val.int16); + xmlOutput.append(snBuffer); + break; + case SDP_INT32: + qsnprintf(snBuffer, BUFFER_SIZE, "/n", data->val.int32); + xmlOutput.append(snBuffer); + break; + case SDP_INT64: + qsnprintf(snBuffer, BUFFER_SIZE, "/n", data->val.int64); + xmlOutput.append(snBuffer); + break; + case SDP_INT128: + xmlOutput.append("val.int128.data[i]); + xmlOutput.append(snBuffer); + xmlOutput.append("\"/>\n"); + break; + case SDP_UUID_UNSPEC: + break; + case SDP_UUID16: + case SDP_UUID32: + xmlOutput.append("val.uuid), snBuffer, BUFFER_SIZE); + xmlOutput.append(snBuffer); + xmlOutput.append("\"/>\n"); + break; + case SDP_UUID128: + xmlOutput.append("val.uuid), snBuffer, BUFFER_SIZE); + xmlOutput.append(snBuffer); + xmlOutput.append("\"/>\n"); + break; + case SDP_TEXT_STR_UNSPEC: + break; + case SDP_TEXT_STR8: + case SDP_TEXT_STR16: + case SDP_TEXT_STR32: + { + xmlOutput.append("val.str, data->unitSize); + + bool hasNonPrintableChar = false; + for (int i = 0; i < text.count() && !hasNonPrintableChar; i++) { + if (!isprint(text[i])) { + hasNonPrintableChar = true; + break; + } + } + + if (hasNonPrintableChar) { + xmlOutput.append("encoding=\"hex\" value=\""); + xmlOutput.append(text.toHex()); + } else { + text.replace("&", "&"); + text.replace("<", "<"); + text.replace(">", ">"); + text.replace("\"", """); + + xmlOutput.append("value=\""); + xmlOutput.append(text); + } + + xmlOutput.append("\"/>\n"); + break; + } + case SDP_BOOL: + if (data->val.uint8) + xmlOutput.append("\n"); + else + xmlOutput.append("\n"); + break; + case SDP_SEQ_UNSPEC: + break; + case SDP_SEQ8: + case SDP_SEQ16: + case SDP_SEQ32: + xmlOutput.append("\n"); + parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput); + xmlOutput.append(indentString); + xmlOutput.append("\n"); + break; + case SDP_ALT_UNSPEC: + break; + case SDP_ALT8: + case SDP_ALT16: + case SDP_ALT32: + xmlOutput.append("\n"); + parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput); + xmlOutput.append(indentString); + xmlOutput.append("\n"); + break; + case SDP_URL_STR_UNSPEC: + break; + case SDP_URL_STR8: + case SDP_URL_STR16: + case SDP_URL_STR32: + strncpy(snBuffer, data->val.str, data->unitSize - 1); + xmlOutput.append("\n"); + break; + default: + qDebug(QT_BT_BLUEZ) << "Unknown dtd type"; + } + + parseAttributeValues(data->next, indentation, xmlOutput); +} + +static void parseAttribute(void *value, void *extraData) +{ + sdp_data_t *data = (sdp_data_t *) value; + QByteArray *xmlOutput = static_cast(extraData); + + char buffer[BUFFER_SIZE]; + + ::qsnprintf(buffer, BUFFER_SIZE, " \n", data->attrId); + xmlOutput->append(buffer); + + parseAttributeValues(data, 2, *xmlOutput); + + xmlOutput->append(" \n"); +} + +// the resulting xml output is based on the already used xml parser +QByteArray parseSdpRecord(sdp_record_t *record) +{ + if (!record || !record->attrlist) + return QByteArray(); + + QByteArray xmlOutput; + + xmlOutput.append("\n\n"); + + sdp_list_foreach(record->attrlist, parseAttribute, &xmlOutput); + xmlOutput.append(""); + + return xmlOutput; +} + QT_END_NAMESPACE diff --git a/src/bluetooth/bluez/bluez5_helper_p.h b/src/bluetooth/bluez/bluez5_helper_p.h index 49abc4af..2429c09c 100644 --- a/src/bluetooth/bluez/bluez5_helper_p.h +++ b/src/bluetooth/bluez/bluez5_helper_p.h @@ -44,6 +44,7 @@ #include #include +#include typedef QMap InterfaceList; typedef QMap ManagedObjectList; @@ -55,6 +56,8 @@ QT_BEGIN_NAMESPACE bool isBluez5(); +QByteArray parseSdpRecord(sdp_record_t *record); + class QtBluezDiscoveryManagerPrivate; class QtBluezDiscoveryManager : public QObject { diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.cpp b/src/bluetooth/qbluetoothservicediscoveryagent.cpp index 36849eed..fb9fb120 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -178,6 +178,8 @@ QBluetoothServiceDiscoveryAgent::~QBluetoothServiceDiscoveryAgent() that if a remote Bluetooth device moves out of range in between two subsequent calls to \l start() the list may contain stale entries. + \note The list of services should always be cleared before the discovery mode is changed. + \sa clear() */ QList QBluetoothServiceDiscoveryAgent::discoveredServices() const @@ -277,6 +279,11 @@ void QBluetoothServiceDiscoveryAgent::start(DiscoveryMode mode) if (d->discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::Inactive && d->error != InvalidBluetoothAdapterError) { +#ifdef QT_BLUEZ_BLUETOOTH + // done to avoid repeated parsing for adapter address + // on Bluez5 + d->foundHostAdapterPath.clear(); +#endif d->setDiscoveryMode(mode); if (d->deviceAddress.isNull()) { d->startDeviceDiscovery(); diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.h b/src/bluetooth/qbluetoothservicediscoveryagent.h index 59c7b74b..37c26a78 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent.h @@ -114,6 +114,7 @@ private: #ifdef QT_BLUEZ_BLUETOOTH Q_PRIVATE_SLOT(d_func(), void _q_discoveredServices(QDBusPendingCallWatcher*)) Q_PRIVATE_SLOT(d_func(), void _q_createdDevice(QDBusPendingCallWatcher*)) + Q_PRIVATE_SLOT(d_func(), void _q_finishSdpScan(QBluetoothServiceDiscoveryAgent::Error, const QString &, const QStringList &)) #endif #ifdef QT_ANDROID_BLUETOOTH Q_PRIVATE_SLOT(d_func(), void _q_processFetchedUuids(const QBluetoothAddress &address, diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp index 2643d7a9..1b248e68 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp @@ -304,80 +304,6 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_processFetchedUuids( } } - -static QString serviceNameForClassUuid(const uint value) -{ - switch (value & 0xffff) { - case QBluetoothUuid::ServiceDiscoveryServer: return QBluetoothServiceDiscoveryAgent::tr("Service Discovery"); - //case QBluetoothUuid::BrowseGroupDescriptor: return QString(); - //case QBluetoothUuid::PublicBrowseGroup: return QString(); - case QBluetoothUuid::SerialPort: return QBluetoothServiceDiscoveryAgent::tr("Serial Port Profile"); - case QBluetoothUuid::LANAccessUsingPPP: return QBluetoothServiceDiscoveryAgent::tr("LAN Access Profile"); - case QBluetoothUuid::DialupNetworking: return QBluetoothServiceDiscoveryAgent::tr("Dial-up Networking"); - case QBluetoothUuid::IrMCSync: return QBluetoothServiceDiscoveryAgent::tr("Synchronization"); - case QBluetoothUuid::ObexObjectPush: return QBluetoothServiceDiscoveryAgent::tr("Object Push"); - case QBluetoothUuid::OBEXFileTransfer: return QBluetoothServiceDiscoveryAgent::tr("File Transfer"); - case QBluetoothUuid::IrMCSyncCommand: return QBluetoothServiceDiscoveryAgent::tr("Synchronization Command"); - case QBluetoothUuid::Headset: return QBluetoothServiceDiscoveryAgent::tr("Headset"); - case QBluetoothUuid::AudioSource: return QBluetoothServiceDiscoveryAgent::tr("Advanced Audio Distribution Source"); - case QBluetoothUuid::AudioSink: return QBluetoothServiceDiscoveryAgent::tr("Advanced Audio Distribution Sink"); - case QBluetoothUuid::AV_RemoteControlTarget: return QBluetoothServiceDiscoveryAgent::tr("Audio/Video Remote Control Target"); - case QBluetoothUuid::AdvancedAudioDistribution: return QBluetoothServiceDiscoveryAgent::tr("Advanced Audio Distribution"); - case QBluetoothUuid::AV_RemoteControl: return QBluetoothServiceDiscoveryAgent::tr("Audio/Video Remote Control"); - case QBluetoothUuid::AV_RemoteControlController: return QBluetoothServiceDiscoveryAgent::tr("Audio/Video Remote Control Controller"); - case QBluetoothUuid::HeadsetAG: return QBluetoothServiceDiscoveryAgent::tr("Headset AG"); - case QBluetoothUuid::PANU: return QBluetoothServiceDiscoveryAgent::tr("Personal Area Networking (PANU)"); - case QBluetoothUuid::NAP: return QBluetoothServiceDiscoveryAgent::tr("Personal Area Networking (NAP)"); - case QBluetoothUuid::GN: return QBluetoothServiceDiscoveryAgent::tr("Personal Area Networking (GN)"); - case QBluetoothUuid::DirectPrinting: return QBluetoothServiceDiscoveryAgent::tr("Basic Direct Printing (BPP)"); - case QBluetoothUuid::ReferencePrinting: return QBluetoothServiceDiscoveryAgent::tr("Basic Reference Printing (BPP)"); - case QBluetoothUuid::BasicImage: return QBluetoothServiceDiscoveryAgent::tr("Basic Imaging Profile"); - case QBluetoothUuid::ImagingResponder: return QBluetoothServiceDiscoveryAgent::tr("Basic Imaging Responder"); - case QBluetoothUuid::ImagingAutomaticArchive: return QBluetoothServiceDiscoveryAgent::tr("Basic Imaging Archive"); - case QBluetoothUuid::ImagingReferenceObjects: return QBluetoothServiceDiscoveryAgent::tr("Basic Imaging Ref Objects"); - case QBluetoothUuid::Handsfree: return QBluetoothServiceDiscoveryAgent::tr("Hands-Free"); - case QBluetoothUuid::HandsfreeAudioGateway: return QBluetoothServiceDiscoveryAgent::tr("Hands-Free AG"); - case QBluetoothUuid::DirectPrintingReferenceObjectsService: return QBluetoothServiceDiscoveryAgent::tr("Basic Printing RefObject Service"); - case QBluetoothUuid::ReflectedUI: return QBluetoothServiceDiscoveryAgent::tr("Basic Printing Reflected UI"); - case QBluetoothUuid::BasicPrinting: return QBluetoothServiceDiscoveryAgent::tr("Basic Printing"); - case QBluetoothUuid::PrintingStatus: return QBluetoothServiceDiscoveryAgent::tr("Basic Printing Status"); - case QBluetoothUuid::HumanInterfaceDeviceService: return QBluetoothServiceDiscoveryAgent::tr("Human Interface Device"); - case QBluetoothUuid::HardcopyCableReplacement: return QBluetoothServiceDiscoveryAgent::tr("Hardcopy Cable Replacement"); - case QBluetoothUuid::HCRPrint: return QBluetoothServiceDiscoveryAgent::tr("Hardcopy Cable Replacement Print"); - case QBluetoothUuid::HCRScan: return QBluetoothServiceDiscoveryAgent::tr("Hardcopy Cable Replacement Scan"); - case QBluetoothUuid::SIMAccess: return QBluetoothServiceDiscoveryAgent::tr("SIM Access"); - case QBluetoothUuid::PhonebookAccessPCE: return QBluetoothServiceDiscoveryAgent::tr("Phonebook Access PCE"); - case QBluetoothUuid::PhonebookAccessPSE: return QBluetoothServiceDiscoveryAgent::tr("Phonebook Access PSE"); - case QBluetoothUuid::PhonebookAccess: return QBluetoothServiceDiscoveryAgent::tr("Phonebook Access"); - case QBluetoothUuid::HeadsetHS: return QBluetoothServiceDiscoveryAgent::tr("Headset HS"); - case QBluetoothUuid::MessageAccessServer: return QBluetoothServiceDiscoveryAgent::tr("Message Access Server"); - case QBluetoothUuid::MessageNotificationServer: return QBluetoothServiceDiscoveryAgent::tr("Message Notification Server"); - case QBluetoothUuid::MessageAccessProfile: return QBluetoothServiceDiscoveryAgent::tr("Message Access"); - case QBluetoothUuid::GNSS: return QBluetoothServiceDiscoveryAgent::tr("Global Navigation Satellite System"); - case QBluetoothUuid::GNSSServer: return QBluetoothServiceDiscoveryAgent::tr("Global Navigation Satellite System Server"); - case QBluetoothUuid::Display3D: return QBluetoothServiceDiscoveryAgent::tr("3D Synchronization Display"); - case QBluetoothUuid::Glasses3D: return QBluetoothServiceDiscoveryAgent::tr("3D Synchronization Glasses"); - case QBluetoothUuid::Synchronization3D: return QBluetoothServiceDiscoveryAgent::tr("3D Synchronization"); - case QBluetoothUuid::MPSProfile: return QBluetoothServiceDiscoveryAgent::tr("Multi-Profile Specification (Profile)"); - case QBluetoothUuid::MPSService: return QBluetoothServiceDiscoveryAgent::tr("Multi-Profile Specification"); - case QBluetoothUuid::PnPInformation: return QBluetoothServiceDiscoveryAgent::tr("Device Identification"); - //case QBluetoothUuid::GenericNetworking: return QBluetoothServiceDiscoveryAgent::tr(""); - //case QBluetoothUuid::GenericFileTransfer: return QBluetoothServiceDiscoveryAgent::tr(""); - //case QBluetoothUuid::GenericAudio: return QBluetoothServiceDiscoveryAgent::tr(""); - //case QBluetoothUuid::GenericTelephony: return QBluetoothServiceDiscoveryAgent::tr(""); - case QBluetoothUuid::VideoSource: return QBluetoothServiceDiscoveryAgent::tr("Video Source"); - case QBluetoothUuid::VideoSink: return QBluetoothServiceDiscoveryAgent::tr("Video Sink"); - case QBluetoothUuid::VideoDistribution: return QBluetoothServiceDiscoveryAgent::tr("Video Distribution"); - case QBluetoothUuid::HDP: return QBluetoothServiceDiscoveryAgent::tr("Health Device"); - case QBluetoothUuid::HDPSource: return QBluetoothServiceDiscoveryAgent::tr("Health Device Source"); - case QBluetoothUuid::HDPSink: return QBluetoothServiceDiscoveryAgent::tr("Health Device Sink"); - default: - break; - } - - return QString(); -} - void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QBluetoothDeviceInfo &remoteDevice, const QList &uuids) { /* Android doesn't provide decent SDP data. A list of uuids is close to meaning-less @@ -483,7 +409,9 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB QBluetoothServiceInfo::Sequence classId; classId << QVariant::fromValue(uuids.at(i)); serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId); - serviceInfo.setServiceName(serviceNameForClassUuid(uuids.at(i).data1)); + QBluetoothUuid::ServiceClassUuid clsId + = static_cast(uuids.at(i).toUInt16()); + serviceInfo.setServiceName(QBluetoothUuid::serviceClassToString(clsId)); } //don't include the service if we already discovered it before diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp index 0c9addfb..a65b99f6 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp @@ -45,30 +45,58 @@ #include "bluez/manager_p.h" #include "bluez/adapter_p.h" #include "bluez/device_p.h" +#include "bluez/bluez5_helper_p.h" +#include "bluez/objectmanager_p.h" +#include "bluez/adapter1_bluez5_p.h" + +#include "bluetooth/bluetooth.h" +#include "bluetooth/sdp.h" +#include "bluetooth/sdp_lib.h" #include #include +#include QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) +static inline void convertAddress(quint64 from, quint8 (&to)[6]) +{ + to[0] = (from >> 0) & 0xff; + to[1] = (from >> 8) & 0xff; + to[2] = (from >> 16) & 0xff; + to[3] = (from >> 24) & 0xff; + to[4] = (from >> 32) & 0xff; + to[5] = (from >> 40) & 0xff; +} + +Q_GLOBAL_STATIC_WITH_ARGS(QUuid, btBaseUuid, ("{00000000-0000-1000-8000-00805F9B34FB}")); + QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(const QBluetoothAddress &deviceAdapter) : error(QBluetoothServiceDiscoveryAgent::NoError), m_deviceAdapterAddress(deviceAdapter), state(Inactive), deviceDiscoveryAgent(0), mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), singleDevice(false), - manager(0), adapter(0), device(0) + manager(0), managerBluez5(0), adapter(0), device(0) { - qRegisterMetaType("ServiceMap"); - qDBusRegisterMetaType(); + if (isBluez5()) { + managerBluez5 = new OrgFreedesktopDBusObjectManagerInterface( + QStringLiteral("org.bluez"), QStringLiteral("/"), + QDBusConnection::systemBus()); + qRegisterMetaType("QBluetoothServiceDiscoveryAgent::Error"); + } else { + qRegisterMetaType("ServiceMap"); + qDBusRegisterMetaType(); - manager = new OrgBluezManagerInterface(QStringLiteral("org.bluez"), QStringLiteral("/"), - QDBusConnection::systemBus()); + manager = new OrgBluezManagerInterface(QStringLiteral("org.bluez"), QStringLiteral("/"), + QDBusConnection::systemBus()); + } } QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate() { delete device; delete manager; + delete managerBluez5; delete adapter; } @@ -76,7 +104,12 @@ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &addr { Q_Q(QBluetoothServiceDiscoveryAgent); - qCDebug(QT_BT_BLUEZ) << "Full discovery on: " << address.toString(); + qCDebug(QT_BT_BLUEZ) << "Discovery on: " << address.toString() << "Mode:" << DiscoveryMode(); + + if (managerBluez5) { + startBluez5(address); + return; + } QDBusPendingReply reply; if (m_deviceAdapterAddress.isNull()) @@ -105,6 +138,242 @@ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &addr } +void QBluetoothServiceDiscoveryAgentPrivate::startBluez5(const QBluetoothAddress &address) +{ + Q_Q(QBluetoothServiceDiscoveryAgent); + + if (foundHostAdapterPath.isEmpty()) { + // check that we match adapter addresses or use first if it wasn't specified + + QDBusPendingReply reply = managerBluez5->GetManagedObjects(); + reply.waitForFinished(); + if (reply.isError()) { + discoveredDevices.clear(); + error = QBluetoothServiceDiscoveryAgent::InputOutputError; + errorString = reply.error().message(); + emit q->error(error); + _q_serviceDiscoveryFinished(); + return; + } + + const QString desiredAdapter = m_deviceAdapterAddress.toString(); + foreach (const QDBusObjectPath &path, reply.value().keys()) { + const InterfaceList ifaceList = reply.value().value(path); + foreach (const QString &iface, ifaceList.keys()) { + if (iface == QStringLiteral("org.bluez.Adapter1")) { + if (m_deviceAdapterAddress.isNull() + || desiredAdapter == ifaceList.value(iface). + value(QStringLiteral("Address")).toString()) { + // use first adapter or we just matched one + foundHostAdapterPath = path.path(); + } + + if (!foundHostAdapterPath.isEmpty()) + break; + } + } + + if (!foundHostAdapterPath.isEmpty()) + break; + } + + if (foundHostAdapterPath.isEmpty()) { + // Cannot find a local adapter + // Abort any outstanding discoveries + discoveredDevices.clear(); + + error = QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError; + errorString = QBluetoothServiceDiscoveryAgent::tr("Cannot find local Bluetooth adapter"); + emit q->error(error); + _q_serviceDiscoveryFinished(); + + return; + } + } + + // ensure we didn't go offline yet + OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), + foundHostAdapterPath, QDBusConnection::systemBus()); + if (!adapter.powered()) { + discoveredDevices.clear(); + + error = QBluetoothServiceDiscoveryAgent::PoweredOffError; + errorString = QBluetoothServiceDiscoveryAgent::tr("Local device is powered off"); + emit q->error(error); + + _q_serviceDiscoveryFinished(); + return; + } + + if (DiscoveryMode() == QBluetoothServiceDiscoveryAgent::MinimalDiscovery) { + performMinimalServiceDiscovery(address); + } else { + // we need to run the discovery in a different thread + // as it involves blocking calls + QtConcurrent::run(this, &QBluetoothServiceDiscoveryAgentPrivate::runSdpScan, + address, QBluetoothAddress(adapter.address())); + } +} + +/* + * This function runs in a different thread. We need to be very careful what we + * access from here. That's why invokeMethod is used below. + */ +void QBluetoothServiceDiscoveryAgentPrivate::runSdpScan( + const QBluetoothAddress &remoteAddress, const QBluetoothAddress localAddress) +{ + Q_Q(QBluetoothServiceDiscoveryAgent); + + // connect to SDP server + bdaddr_t local, remote; + convertAddress(localAddress.toUInt64(), local.b); + convertAddress(remoteAddress.toUInt64(), remote.b); + + /* We use singleshot timer below because this function runs in a different + * thread than the rest of this class. + */ + + sdp_session_t *session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY); + // try one more time if first attempt fails + if (!session) + session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY); + + qCDebug(QT_BT_BLUEZ) << "SDP for" << remoteAddress.toString() << session << qt_error_string(errno); + if (!session) { + if (singleDevice) { + // was sole device without result -> error + QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection, + Q_ARG(QBluetoothServiceDiscoveryAgent::Error, + QBluetoothServiceDiscoveryAgent::InputOutputError), + Q_ARG(QString, + QBluetoothServiceDiscoveryAgent::tr("Unable to access device")), + Q_ARG(QStringList, QStringList())); + } else { + // go to next device + QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection, + Q_ARG(QBluetoothServiceDiscoveryAgent::Error, + QBluetoothServiceDiscoveryAgent::NoError), + Q_ARG(QString, QString()), + Q_ARG(QStringList, QStringList())); + } + + return; + } + + + // set the filter for service matches + uuid_t publicBrowseGroupUuid; + sdp_uuid16_create(&publicBrowseGroupUuid, QBluetoothUuid::PublicBrowseGroup); + sdp_list_t *serviceFilter; + serviceFilter = sdp_list_append(0, &publicBrowseGroupUuid); + + uint32_t attributeRange = 0x0000ffff; //all attributes + sdp_list_t *attributes; + attributes = sdp_list_append(0, &attributeRange); + + sdp_list_t* sdpResults; + int result = sdp_service_search_attr_req(session, serviceFilter, SDP_ATTR_REQ_RANGE, + attributes, &sdpResults); + sdp_list_free(attributes, 0); + sdp_list_free(serviceFilter, 0); + + if (result != 0) { + qCDebug(QT_BT_BLUEZ) << "SDP search failed" << qt_error_string(errno); + sdp_close(session); + if (singleDevice) { + QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection, + Q_ARG(QBluetoothServiceDiscoveryAgent::Error, + QBluetoothServiceDiscoveryAgent::InputOutputError), + Q_ARG(QString, + QBluetoothServiceDiscoveryAgent::tr("Unable to access device")), + Q_ARG(QStringList, QStringList())); + } else { + QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection, + Q_ARG(QBluetoothServiceDiscoveryAgent::Error, + QBluetoothServiceDiscoveryAgent::NoError), + Q_ARG(QString, QString()), + Q_ARG(QStringList, QStringList())); + } + return; + } + + qCDebug(QT_BT_BLUEZ) << "SDP search a success. Iterating results" << sdpResults; + QStringList xmlRecords; + + // process the results + for ( ; sdpResults; sdpResults = sdpResults->next) { + sdp_record_t *record = (sdp_record_t *) sdpResults->data; + + QByteArray xml = parseSdpRecord(record); + if (xml.isEmpty()) + continue; + + //qDebug() << xml; + xmlRecords.append(QString::fromUtf8(xml)); + } + + sdp_close(session); + + QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection, + Q_ARG(QBluetoothServiceDiscoveryAgent::Error, + QBluetoothServiceDiscoveryAgent::NoError), + Q_ARG(QString, QString()), + Q_ARG(QStringList, xmlRecords)); +} + +void QBluetoothServiceDiscoveryAgentPrivate::_q_finishSdpScan(QBluetoothServiceDiscoveryAgent::Error errorCode, + const QString &errorDescription, + const QStringList &xmlRecords) +{ + Q_Q(QBluetoothServiceDiscoveryAgent); + + if (errorCode != QBluetoothServiceDiscoveryAgent::NoError) { + qCWarning(QT_BT_BLUEZ) << "SDP search failed for" + << discoveredDevices.at(0).address().toString(); + // We have an error which we need to indicate and stop further processing + discoveredDevices.clear(); + error = errorCode; + errorString = errorDescription; + emit q->error(error); + } else if (!xmlRecords.isEmpty()) { + foreach (const QString &record, xmlRecords) { + const QBluetoothServiceInfo serviceInfo = parseServiceXml(record); + + //apply uuidFilter + if (!uuidFilter.isEmpty()) { + bool serviceNameMatched = uuidFilter.contains(serviceInfo.serviceUuid()); + bool serviceClassMatched = false; + foreach (const QBluetoothUuid &id, serviceInfo.serviceClassUuids()) { + if (uuidFilter.contains(id)) { + serviceClassMatched = true; + break; + } + } + + if (!serviceNameMatched && !serviceClassMatched) + continue; + } + + if (!serviceInfo.isValid()) + continue; + + discoveredServices.append(serviceInfo); + qCDebug(QT_BT_BLUEZ) << "Discovered services" << discoveredDevices.at(0).address().toString() + << serviceInfo.serviceName() << serviceInfo.serviceUuid() + << ">>>" << serviceInfo.serviceClassUuids(); + + // TODO check for duplicates (wait until stable has merged due to related change) + emit q->serviceDiscovered(serviceInfo); + + // could stop discovery, check for state + if (discoveryState() == Inactive) + qCDebug(QT_BT_BLUEZ) << "Exit discovery after stop"; + } + } + + _q_serviceDiscoveryFinished(); +} + void QBluetoothServiceDiscoveryAgentPrivate::stop() { qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO << "Stop called"; @@ -186,6 +455,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_createdDevice(QDBusPendingCallWa q, SLOT(_q_discoveredServices(QDBusPendingCallWatcher*))); } +// Bluez 4 void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingCallWatcher *watcher) { if (!device) @@ -214,29 +484,13 @@ 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()) { - QXmlStreamReader xml(record); - - 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(QStringLiteral("id")).toString().toUShort(0, 0); - - if (xml.readNextStartElement()) { - QVariant value = readAttributeValue(xml); - - serviceInfo.setAttribute(attributeId, value); - } - } - } + const QBluetoothServiceInfo serviceInfo = parseServiceXml(record); if (!serviceInfo.isValid()) - continue; + return; + + // Don't need to apply uuidFilter because Bluez 4 applies + // search pattern during DiscoverServices() call Q_Q(QBluetoothServiceDiscoveryAgent); @@ -254,15 +508,14 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingC if (!alreadyDiscovered) { discoveredServices.append(serviceInfo); - qCDebug(QT_BT_BLUEZ) << "Discovered services" << discoveredDevices.at(0).address().toString(); + qCDebug(QT_BT_BLUEZ) << "Discovered services" << discoveredDevices.at(0).address().toString() + << serviceInfo.serviceName(); emit q->serviceDiscovered(serviceInfo); } // could stop discovery, check for state - if(discoveryState() == Inactive){ + if (discoveryState() == Inactive) qCDebug(QT_BT_BLUEZ) << "Exit discovery after stop"; - break; - } } watcher->deleteLater(); @@ -272,6 +525,138 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingC _q_serviceDiscoveryFinished(); } +QBluetoothServiceInfo QBluetoothServiceDiscoveryAgentPrivate::parseServiceXml(const QString& xmlRecord) +{ + QXmlStreamReader xml(xmlRecord); + + 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); + } + } + } + + return serviceInfo; +} + +void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress) +{ + if (foundHostAdapterPath.isEmpty()) { + _q_serviceDiscoveryFinished(); + return; + } + + Q_Q(QBluetoothServiceDiscoveryAgent); + + QDBusPendingReply reply = managerBluez5->GetManagedObjects(); + reply.waitForFinished(); + if (reply.isError()) { + if (singleDevice) { + error = QBluetoothServiceDiscoveryAgent::InputOutputError; + errorString = reply.error().message(); + emit q->error(error); + + } + _q_serviceDiscoveryFinished(); + return; + } + + QStringList uuidStrings; + foreach (const QDBusObjectPath &path, reply.value().keys()) { + const InterfaceList ifaceList = reply.value().value(path); + foreach (const QString &iface, ifaceList.keys()) { + if (iface == QStringLiteral("org.bluez.Device1")) { + const QVariantMap details = ifaceList.value(iface); + if (deviceAddress.toString() + == details.value(QStringLiteral("Address")).toString()) { + uuidStrings = details.value(QStringLiteral("UUIDs")).toStringList(); + break; + + } + } + } + if (!uuidStrings.isEmpty()) + break; + } + + if (uuidStrings.isEmpty() || discoveredDevices.isEmpty()) { + // nothing found -> go to next uuid + _q_serviceDiscoveryFinished(); + return; + } + + qCDebug(QT_BT_BLUEZ) << "Minimal uuid list for" << deviceAddress.toString() << uuidStrings; + + QBluetoothUuid uuid; + for (int i = 0; i < uuidStrings.count(); i++) { + uuid = QBluetoothUuid(uuidStrings.at(i)); + if (uuid.isNull()) + continue; + + //apply uuidFilter + if (!uuidFilter.isEmpty() && !uuidFilter.contains(uuid)) + continue; + + bool isBaseUuid = false; + if (btBaseUuid()->data2 == uuid.data2 && btBaseUuid()->data3 == uuid.data3 + && btBaseUuid()->data4[0] == uuid.data4[0] && btBaseUuid()->data4[1] == uuid.data4[1] + && btBaseUuid()->data4[2] == uuid.data4[2] && btBaseUuid()->data4[3] == uuid.data4[3] + && btBaseUuid()->data4[4] == uuid.data4[4] && btBaseUuid()->data4[5] == uuid.data4[5] + && btBaseUuid()->data4[6] == uuid.data4[6] && btBaseUuid()->data4[7] == uuid.data4[7]) + { + isBaseUuid = true; + } + + QBluetoothServiceInfo serviceInfo; + serviceInfo.setDevice(discoveredDevices.at(0)); + + if (!isBaseUuid) { + serviceInfo.setServiceUuid(uuid); + serviceInfo.setServiceName(QBluetoothServiceDiscoveryAgent::tr("Custom Service")); + } else { + // set uuid as service class id + QBluetoothServiceInfo::Sequence classId; + classId << QVariant::fromValue(uuid); + serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId); + QBluetoothUuid::ServiceClassUuid clsId + = static_cast(uuid.data1 & 0xffff); + serviceInfo.setServiceName(QBluetoothUuid::serviceClassToString(clsId)); + } + + //don't include the service if we already discovered it before + bool alreadyDiscovered = false; + for (int j = 0; j < discoveredServices.count(); j++) { + const QBluetoothServiceInfo &info = discoveredServices.at(j); + if (info.device() == serviceInfo.device() + && info.serviceClassUuids() == serviceInfo.serviceClassUuids() + && info.serviceUuid() == serviceInfo.serviceUuid()) { + alreadyDiscovered = true; + break; + } + } + + if (!alreadyDiscovered) { + discoveredServices << serviceInfo; + //qCDebug(QT_BT_ANDROID) << serviceInfo; + emit q->serviceDiscovered(serviceInfo); + } + } + + _q_serviceDiscoveryFinished(); +} + QVariant QBluetoothServiceDiscoveryAgentPrivate::readAttributeValue(QXmlStreamReader &xml) { if (xml.name() == QLatin1String("boolean")) { diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h index d878869d..b3910a7e 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -53,6 +53,7 @@ class OrgBluezManagerInterface; class OrgBluezAdapterInterface; class OrgBluezDeviceInterface; +class OrgFreedesktopDBusObjectManagerInterface; QT_BEGIN_NAMESPACE class QDBusPendingCallWatcher; class QXmlStreamReader; @@ -116,6 +117,9 @@ public: #ifdef QT_BLUEZ_BLUETOOTH void _q_discoveredServices(QDBusPendingCallWatcher *watcher); void _q_createdDevice(QDBusPendingCallWatcher *watcher); + void _q_finishSdpScan(QBluetoothServiceDiscoveryAgent::Error errorCode, + const QString &errorDescription, + const QStringList &xmlRecords); #endif #ifdef QT_ANDROID_BLUETOOTH void _q_processFetchedUuids(const QBluetoothAddress &address, const QList &uuids); @@ -131,7 +135,12 @@ private: void stop(); #ifdef QT_BLUEZ_BLUETOOTH + void startBluez5(const QBluetoothAddress &address); + void runSdpScan(const QBluetoothAddress &remoteAddress, + const QBluetoothAddress localAddress); QVariant readAttributeValue(QXmlStreamReader &xml); + QBluetoothServiceInfo parseServiceXml(const QString& xml); + void performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress); #endif #ifdef QT_QNX_BLUETOOTH @@ -170,7 +179,9 @@ private: bool singleDevice; #ifdef QT_BLUEZ_BLUETOOTH + QString foundHostAdapterPath; OrgBluezManagerInterface *manager; + OrgFreedesktopDBusObjectManagerInterface *managerBluez5; OrgBluezAdapterInterface *adapter; OrgBluezDeviceInterface *device; #endif diff --git a/src/bluetooth/qbluetoothuuid.cpp b/src/bluetooth/qbluetoothuuid.cpp index 85d48d49..91d0fc8b 100644 --- a/src/bluetooth/qbluetoothuuid.cpp +++ b/src/bluetooth/qbluetoothuuid.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qbluetoothuuid.h" +#include "qbluetoothservicediscoveryagent.h" #include #include @@ -363,6 +364,85 @@ quint128 QBluetoothUuid::toUInt128() const return uuid; } +/*! + Returns a human-readable and translated name for the given service class + represented by \a uuid. + + \sa QBluetoothUuid::ServiceClassUuid + */ +QString QBluetoothUuid::serviceClassToString(QBluetoothUuid::ServiceClassUuid uuid) +{ + switch (uuid) { + case QBluetoothUuid::ServiceDiscoveryServer: return QBluetoothServiceDiscoveryAgent::tr("Service Discovery"); + case QBluetoothUuid::BrowseGroupDescriptor: return QBluetoothServiceDiscoveryAgent::tr("Browse Group Descriptor"); + case QBluetoothUuid::PublicBrowseGroup: return QBluetoothServiceDiscoveryAgent::tr("Public Browse Group"); + case QBluetoothUuid::SerialPort: return QBluetoothServiceDiscoveryAgent::tr("Serial Port Profile"); + case QBluetoothUuid::LANAccessUsingPPP: return QBluetoothServiceDiscoveryAgent::tr("LAN Access Profile"); + case QBluetoothUuid::DialupNetworking: return QBluetoothServiceDiscoveryAgent::tr("Dial-Up Networking"); + case QBluetoothUuid::IrMCSync: return QBluetoothServiceDiscoveryAgent::tr("Synchronization"); + case QBluetoothUuid::ObexObjectPush: return QBluetoothServiceDiscoveryAgent::tr("Object Push"); + case QBluetoothUuid::OBEXFileTransfer: return QBluetoothServiceDiscoveryAgent::tr("File Transfer"); + case QBluetoothUuid::IrMCSyncCommand: return QBluetoothServiceDiscoveryAgent::tr("Synchronization Command"); + case QBluetoothUuid::Headset: return QBluetoothServiceDiscoveryAgent::tr("Headset"); + case QBluetoothUuid::AudioSource: return QBluetoothServiceDiscoveryAgent::tr("Audio Source"); + case QBluetoothUuid::AudioSink: return QBluetoothServiceDiscoveryAgent::tr("Audio Sink"); + case QBluetoothUuid::AV_RemoteControlTarget: return QBluetoothServiceDiscoveryAgent::tr("Audio/Video Remote Control Target"); + case QBluetoothUuid::AdvancedAudioDistribution: return QBluetoothServiceDiscoveryAgent::tr("Advanced Audio Distribution"); + case QBluetoothUuid::AV_RemoteControl: return QBluetoothServiceDiscoveryAgent::tr("Audio/Video Remote Control"); + case QBluetoothUuid::AV_RemoteControlController: return QBluetoothServiceDiscoveryAgent::tr("Audio/Video Remote Control Controller"); + case QBluetoothUuid::HeadsetAG: return QBluetoothServiceDiscoveryAgent::tr("Headset AG"); + case QBluetoothUuid::PANU: return QBluetoothServiceDiscoveryAgent::tr("Personal Area Networking (PANU)"); + case QBluetoothUuid::NAP: return QBluetoothServiceDiscoveryAgent::tr("Personal Area Networking (NAP)"); + case QBluetoothUuid::GN: return QBluetoothServiceDiscoveryAgent::tr("Personal Area Networking (GN)"); + case QBluetoothUuid::DirectPrinting: return QBluetoothServiceDiscoveryAgent::tr("Basic Direct Printing (BPP)"); + case QBluetoothUuid::ReferencePrinting: return QBluetoothServiceDiscoveryAgent::tr("Basic Reference Printing (BPP)"); + case QBluetoothUuid::BasicImage: return QBluetoothServiceDiscoveryAgent::tr("Basic Imaging Profile"); + case QBluetoothUuid::ImagingResponder: return QBluetoothServiceDiscoveryAgent::tr("Basic Imaging Responder"); + case QBluetoothUuid::ImagingAutomaticArchive: return QBluetoothServiceDiscoveryAgent::tr("Basic Imaging Archive"); + case QBluetoothUuid::ImagingReferenceObjects: return QBluetoothServiceDiscoveryAgent::tr("Basic Imaging Ref Objects"); + case QBluetoothUuid::Handsfree: return QBluetoothServiceDiscoveryAgent::tr("Hands-Free"); + case QBluetoothUuid::HandsfreeAudioGateway: return QBluetoothServiceDiscoveryAgent::tr("Hands-Free AG"); + case QBluetoothUuid::DirectPrintingReferenceObjectsService: return QBluetoothServiceDiscoveryAgent::tr("Basic Printing RefObject Service"); + case QBluetoothUuid::ReflectedUI: return QBluetoothServiceDiscoveryAgent::tr("Basic Printing Reflected UI"); + case QBluetoothUuid::BasicPrinting: return QBluetoothServiceDiscoveryAgent::tr("Basic Printing"); + case QBluetoothUuid::PrintingStatus: return QBluetoothServiceDiscoveryAgent::tr("Basic Printing Status"); + case QBluetoothUuid::HumanInterfaceDeviceService: return QBluetoothServiceDiscoveryAgent::tr("Human Interface Device"); + case QBluetoothUuid::HardcopyCableReplacement: return QBluetoothServiceDiscoveryAgent::tr("Hardcopy Cable Replacement"); + case QBluetoothUuid::HCRPrint: return QBluetoothServiceDiscoveryAgent::tr("Hardcopy Cable Replacement Print"); + case QBluetoothUuid::HCRScan: return QBluetoothServiceDiscoveryAgent::tr("Hardcopy Cable Replacement Scan"); + case QBluetoothUuid::SIMAccess: return QBluetoothServiceDiscoveryAgent::tr("SIM Access Server"); + case QBluetoothUuid::PhonebookAccessPCE: return QBluetoothServiceDiscoveryAgent::tr("Phonebook Access PCE"); + case QBluetoothUuid::PhonebookAccessPSE: return QBluetoothServiceDiscoveryAgent::tr("Phonebook Access PSE"); + case QBluetoothUuid::PhonebookAccess: return QBluetoothServiceDiscoveryAgent::tr("Phonebook Access"); + case QBluetoothUuid::HeadsetHS: return QBluetoothServiceDiscoveryAgent::tr("Headset HS"); + case QBluetoothUuid::MessageAccessServer: return QBluetoothServiceDiscoveryAgent::tr("Message Access Server"); + case QBluetoothUuid::MessageNotificationServer: return QBluetoothServiceDiscoveryAgent::tr("Message Notification Server"); + case QBluetoothUuid::MessageAccessProfile: return QBluetoothServiceDiscoveryAgent::tr("Message Access"); + case QBluetoothUuid::GNSS: return QBluetoothServiceDiscoveryAgent::tr("Global Navigation Satellite System"); + case QBluetoothUuid::GNSSServer: return QBluetoothServiceDiscoveryAgent::tr("Global Navigation Satellite System Server"); + case QBluetoothUuid::Display3D: return QBluetoothServiceDiscoveryAgent::tr("3D Synchronization Display"); + case QBluetoothUuid::Glasses3D: return QBluetoothServiceDiscoveryAgent::tr("3D Synchronization Glasses"); + case QBluetoothUuid::Synchronization3D: return QBluetoothServiceDiscoveryAgent::tr("3D Synchronization"); + case QBluetoothUuid::MPSProfile: return QBluetoothServiceDiscoveryAgent::tr("Multi-Profile Specification (Profile)"); + case QBluetoothUuid::MPSService: return QBluetoothServiceDiscoveryAgent::tr("Multi-Profile Specification"); + case QBluetoothUuid::PnPInformation: return QBluetoothServiceDiscoveryAgent::tr("Device Identification"); + case QBluetoothUuid::GenericNetworking: return QBluetoothServiceDiscoveryAgent::tr("Generic Networking"); + case QBluetoothUuid::GenericFileTransfer: return QBluetoothServiceDiscoveryAgent::tr("Generic File Transfer"); + case QBluetoothUuid::GenericAudio: return QBluetoothServiceDiscoveryAgent::tr("Generic Audio"); + case QBluetoothUuid::GenericTelephony: return QBluetoothServiceDiscoveryAgent::tr("Generic Telephony"); + case QBluetoothUuid::VideoSource: return QBluetoothServiceDiscoveryAgent::tr("Video Source"); + case QBluetoothUuid::VideoSink: return QBluetoothServiceDiscoveryAgent::tr("Video Sink"); + case QBluetoothUuid::VideoDistribution: return QBluetoothServiceDiscoveryAgent::tr("Video Distribution"); + case QBluetoothUuid::HDP: return QBluetoothServiceDiscoveryAgent::tr("Health Device"); + case QBluetoothUuid::HDPSource: return QBluetoothServiceDiscoveryAgent::tr("Health Device Source"); + case QBluetoothUuid::HDPSink: return QBluetoothServiceDiscoveryAgent::tr("Health Device Sink"); + default: + break; + } + + return QString(); +} + /*! Returns true if \a other is equal to this Bluetooth UUID, otherwise false. */ diff --git a/src/bluetooth/qbluetoothuuid.h b/src/bluetooth/qbluetoothuuid.h index f33d8c13..46cff826 100644 --- a/src/bluetooth/qbluetoothuuid.h +++ b/src/bluetooth/qbluetoothuuid.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -172,6 +172,8 @@ public: quint16 toUInt16(bool *ok = 0) const; quint32 toUInt32(bool *ok = 0) const; quint128 toUInt128() const; + + static QString serviceClassToString(ServiceClassUuid uuid); }; #ifndef QT_NO_DEBUG_STREAM -- cgit v1.2.3