diff options
author | Alex Blasche <alexander.blasche@digia.com> | 2014-02-10 15:37:17 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-02-13 09:47:27 +0100 |
commit | 173d16efb54ccc152f19afb9b1c2a87915fb414b (patch) | |
tree | f07ce85ba2cb973e3c08f3ed84252d92ee1c16de /src/bluetooth/qbluetoothservicediscoveryagent_android.cpp | |
parent | dd75b1f776695006ca96fd43f995c3ba0549b968 (diff) |
Port QtBluetooth to Android
This is a feature merge to dev targeting Qt 5.3.
Known issues:
-QTBUG-36754: QBluetoothServer::close() crashes
-QTBUG-36763: QBluetothTransferManager port to Android not possible
-QTBUG-36764: Improve QBluetoothLocalDevice::connectedDevices()
-QTBUG-36810: Remove direct use of Android action strings
The above issues and some other minor TODO's will be addressed
until final release time.
Task-number: QTBUG-33792
[ChangeLog][QtBluetooth][Android] QtBluetooth has been ported to
Android.
Change-Id: I31ba83e3b7d6aa68e7258b7e43235de7d1a6e68a
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>
Reviewed-by: Alex Blasche <alexander.blasche@digia.com>
Diffstat (limited to 'src/bluetooth/qbluetoothservicediscoveryagent_android.cpp')
-rw-r--r-- | src/bluetooth/qbluetoothservicediscoveryagent_android.cpp | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp new file mode 100644 index 00000000..72a4d03b --- /dev/null +++ b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp @@ -0,0 +1,513 @@ +/**************************************************************************** +** +** 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. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QLoggingCategory> +#include <QtCore/QTimer> +#include <QtCore/private/qjnihelpers_p.h> +#include <QtAndroidExtras/QAndroidJniEnvironment> +#include <QtBluetooth/QBluetoothHostInfo> +#include <QtBluetooth/QBluetoothLocalDevice> +#include <QtBluetooth/QBluetoothServiceDiscoveryAgent> + +#include "qbluetoothservicediscoveryagent_p.h" +#include "android/servicediscoverybroadcastreceiver_p.h" +#include "android/localdevicebroadcastreceiver_p.h" + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) + +Q_GLOBAL_STATIC_WITH_ARGS(QUuid, btBaseUuid, ("{00000000-0000-1000-8000-00805F9B34FB}")) + +QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( + const QBluetoothAddress &deviceAdapter) + : error(QBluetoothServiceDiscoveryAgent::NoError), + state(Inactive), deviceDiscoveryAgent(0), + mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), + singleDevice(false), receiver(0), localDeviceReceiver(0) +{ + QList<QBluetoothHostInfo> devices = QBluetoothLocalDevice::allDevices(); + Q_ASSERT(devices.count() == 1); //Android only supports one device at the moment + + if (deviceAdapter.isNull() && devices.count() > 0 ) + m_deviceAdapterAddress = devices.at(0).address(); + else + m_deviceAdapterAddress = deviceAdapter; + + if (QtAndroidPrivate::androidSdkVersion() < 15) + qCWarning(QT_BT_ANDROID) + << "SDP not supported by Android API below version 15. Detected version: " + << QtAndroidPrivate::androidSdkVersion() + << "Service discovery will return empty list."; + + + /* We assume that the current local adapter has been passed. + Android only supports one adapter at the moment. If m_deviceAdapterAddress + doesn't match the local adapter then we won't get to this point since + we have an InvalidBluetoothAdapter error. + + The logic below must change once there is more than one adapter. + */ + + btAdapter = QAndroidJniObject::callStaticObjectMethod("android/bluetooth/BluetoothAdapter", + "getDefaultAdapter", + "()Landroid/bluetooth/BluetoothAdapter;"); + if (!btAdapter.isValid()) + qCWarning(QT_BT_ANDROID) << "Platform does not support Bluetooth"; + + qRegisterMetaType<QList<QBluetoothUuid> >("QList<QBluetoothUuid>"); +} + +QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate() +{ + delete receiver; + delete localDeviceReceiver; +} + +void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address) +{ + Q_Q(QBluetoothServiceDiscoveryAgent); + + if (!btAdapter.isValid()) { + error = QBluetoothServiceDiscoveryAgent::UnknownError; + errorString = QBluetoothServiceDiscoveryAgent::tr("Platform does not support Bluetooth."); + + //abort any outstanding discoveries + discoveredDevices.clear(); + emit q->error(error); + _q_serviceDiscoveryFinished(); + + return; + } + + /* SDP discovery was officially added by Android API v15 + * BluetoothDevice.getUuids() existed in earlier APIs already and in the future we may use + * reflection to support earlier Android versions than 15. Unfortunately + * BluetoothDevice.fetchUuidsWithSdp() and related APIs had some structure changes + * over time. Therefore we won't attempt this with reflection. + * + * TODO: Use reflection to support getUuuids() where possible. + * */ + if (QtAndroidPrivate::androidSdkVersion() < 15) { + qCWarning(QT_BT_ANDROID) << "Aborting SDP enquiry due to too low Android API version (requires v15+)"; + + error = QBluetoothServiceDiscoveryAgent::UnknownError; + errorString = QBluetoothServiceDiscoveryAgent::tr("Android API below v15 does not support SDP discovery."); + + //abort any outstanding discoveries + sdpCache.clear(); + discoveredDevices.clear(); + emit q->error(error); + _q_serviceDiscoveryFinished(); + + return; + } + + QAndroidJniObject inputString = QAndroidJniObject::fromString(address.toString()); + QAndroidJniObject remoteDevice = + btAdapter.callObjectMethod("getRemoteDevice", + "(Ljava/lang/String;)Landroid/bluetooth/BluetoothDevice;", + inputString.object<jstring>()); + QAndroidJniEnvironment env; + if (env->ExceptionCheck()) { + env->ExceptionClear(); + env->ExceptionDescribe(); + + //if it was only device then its error -> otherwise go to next device + if (singleDevice) { + error = QBluetoothServiceDiscoveryAgent::InputOutputError; + errorString = QBluetoothServiceDiscoveryAgent::tr("Cannot create Android BluetoothDevice."); + + qCWarning(QT_BT_ANDROID) << "Cannot start SDP for" << discoveredDevices.at(0).name() + << "(" << address.toString() << ")"; + emit q->error(error); + } + _q_serviceDiscoveryFinished(); + return; + } + + + if (mode == QBluetoothServiceDiscoveryAgent::MinimalDiscovery) { + qCDebug(QT_BT_ANDROID) << "Minimal discovery on (" << discoveredDevices.at(0).name() + << ")" << address.toString() ; + + //Minimal discovery uses BluetoothDevice.getUuids() + QAndroidJniObject parcelUuidArray = remoteDevice.callObjectMethod( + "getUuids", "()[Landroid/os/ParcelUuid;"); + + if (!parcelUuidArray.isValid()) { + if (singleDevice) { + error = QBluetoothServiceDiscoveryAgent::InputOutputError; + errorString = QBluetoothServiceDiscoveryAgent::tr("Cannot obtain service uuids."); + emit q->error(error); + } + qCWarning(QT_BT_ANDROID) << "Cannot retrieve SDP UUIDs for" << discoveredDevices.at(0).name() + << "(" << address.toString() << ")"; + _q_serviceDiscoveryFinished(); + return; + } + + const QList<QBluetoothUuid> results = ServiceDiscoveryBroadcastReceiver::convertParcelableArray(parcelUuidArray); + populateDiscoveredServices(discoveredDevices.at(0), results); + + _q_serviceDiscoveryFinished(); + } else { + qCDebug(QT_BT_ANDROID) << "Full discovery on (" << discoveredDevices.at(0).name() + << ")" << address.toString(); + + //Full discovery uses BluetoothDevice.fetchUuidsWithSdp() + if (!receiver) { + receiver = new ServiceDiscoveryBroadcastReceiver(); + QObject::connect(receiver, SIGNAL(uuidFetchFinished(QBluetoothAddress,QList<QBluetoothUuid>)), + q, SLOT(_q_processFetchedUuids(const QBluetoothAddress&,const QList<QBluetoothUuid>&))); + } + + if (!localDeviceReceiver) { + localDeviceReceiver = new LocalDeviceBroadcastReceiver(); + QObject::connect(localDeviceReceiver, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode)), + q, SLOT(_q_hostModeStateChanged(QBluetoothLocalDevice::HostMode))); + } + + jboolean result = remoteDevice.callMethod<jboolean>("fetchUuidsWithSdp"); + if (!result) { + //kill receiver to limit load of signals + receiver->deleteLater(); + receiver = 0; + qCWarning(QT_BT_ANDROID) << "Cannot start dynamic fetch."; + _q_serviceDiscoveryFinished(); + } + } +} + +void QBluetoothServiceDiscoveryAgentPrivate::stop() +{ + sdpCache.clear(); + discoveredDevices.clear(); + + //kill receiver to limit load of signals + receiver->deleteLater(); + receiver = 0; + + Q_Q(QBluetoothServiceDiscoveryAgent); + emit q->canceled(); + +} + +void QBluetoothServiceDiscoveryAgentPrivate::_q_processFetchedUuids( + const QBluetoothAddress &address, const QList<QBluetoothUuid> &uuids) +{ + //don't leave more data through if we are not interested anymore + if (discoveredDevices.count() == 0) + return; + + if (QT_BT_ANDROID().isDebugEnabled()) { + qCDebug(QT_BT_ANDROID) << "Found UUID for" << address.toString() + << "\ncount: " << uuids.count(); + + QString result; + for (int i = 0; i<uuids.count(); i++) + result += uuids.at(i).toString() + QStringLiteral("**"); + qCDebug(QT_BT_ANDROID) << result; + } + + /* In general there are two uuid events per device. + * We'll wait for the second event to arrive before we process the UUIDs. + * We utilize a timeout to catch cases when the second + * event doesn't arrive at all. + * Generally we assume that the second uuid event carries the most up-to-date + * set of uuids and discard the first events results. + */ + + if (sdpCache.contains(address)) { + //second event + QPair<QBluetoothDeviceInfo,QList<QBluetoothUuid> > pair = sdpCache.take(address); + + //prefer second uuid set over first + populateDiscoveredServices(pair.first, uuids); + + if (discoveredDevices.count() == 1 && sdpCache.isEmpty()) { + //last regular uuid data set from OS -> we finish here + _q_serviceDiscoveryFinished(); + } + } else { + //first event + QPair<QBluetoothDeviceInfo,QList<QBluetoothUuid> > pair; + pair.first = discoveredDevices.at(0); + pair.second = uuids; + + if (pair.first.address() != address) + return; + + sdpCache.insert(address, pair); + + //the discovery on the last device cannot immediately finish + //we have to grant the 2 seconds timeout delay + if (discoveredDevices.count() == 1) { + Q_Q(QBluetoothServiceDiscoveryAgent); + QTimer::singleShot(4000, q, SLOT(_q_fetchUuidsTimeout())); + return; + } + + _q_serviceDiscoveryFinished(); + } +} + + +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("Personak Area Networking (GN)"); + case QBluetoothUuid::DirectPrinting: return QBluetoothServiceDiscoveryAgent::tr("Basic Printing (DP)"); + //case QBluetoothUuid::ReferencePrinting: return QBluetoothServiceDiscoveryAgent::tr(""); + 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 Accress"); + case QBluetoothUuid::PnPInformation: return QBluetoothServiceDiscoveryAgent::tr("Navigation Satellite System"); + //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<QBluetoothUuid> &uuids) +{ + /* Android doesn't provide decent SDP data. A list of uuids is close to meaning-less + * + * The following approach is chosen: + * - If we see an SPP service class and we see + * one or more custom uuids we match them up. Such services will always be SPP services. + * - If we see a custom uuid but no SPP uuid then we return + * BluetoothServiceInfo instance with just a servuceUuid (no service class set) + * - Any other service uuid will stand on its own. + * */ + + Q_Q(QBluetoothServiceDiscoveryAgent); + + //find SPP and custom uuid + QBluetoothUuid uuid; + int sppIndex = -1; + QVector<int> customUuids; + + for (int i = 0; i < uuids.count(); i++) { + uuid = uuids.at(i); + + if (uuid.isNull()) + continue; + + bool isBaseUuuidSuffix = 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]) + { + isBaseUuuidSuffix = true; + } + + //check for SPP protocol + if (isBaseUuuidSuffix && ((uuid.data1 & 0xffff) == QBluetoothUuid::SerialPort)) + sppIndex = i; + + if (!isBaseUuuidSuffix) + customUuids.append(i); + } + + for (int i = 0; i < uuids.count(); i++) { + if (i == sppIndex) //skip SPP service class id + continue; + + QBluetoothServiceInfo serviceInfo; + serviceInfo.setDevice(remoteDevice); + + QBluetoothServiceInfo::Sequence protocolDescriptorList; + protocolDescriptorList << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap)); + + if (customUuids.contains(i) && sppIndex > -1) { + //we have a custom uuid of service class type SPP + + //set rfcomm protocol + QBluetoothServiceInfo::Sequence protocol; + protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm)) + << QVariant::fromValue(0); + protocolDescriptorList.append(QVariant::fromValue(protocol)); + + //set SPP service class uuid + QBluetoothServiceInfo::Sequence classId; + classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); + serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList, + classId); + classId.prepend(QVariant::fromValue(uuids.at(i))); + serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId); + + serviceInfo.setServiceName(QBluetoothServiceDiscoveryAgent::tr("Serial Port Profile")); + //TODO Remove line below - work around + serviceInfo.setServiceUuid(uuids.at(i)); + } else if (customUuids.contains(i)) { + //custom uuid but no serial port + serviceInfo.setServiceUuid(uuids.at(i)); + } + + //Check if the UUID is in the uuidFilter + if (!uuidFilter.isEmpty() && !uuidFilter.contains(serviceInfo.serviceUuid())) + continue; + + serviceInfo.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList); + serviceInfo.setAttribute(QBluetoothServiceInfo::BrowseGroupList, + QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup)); + + if (!customUuids.contains(i)) { + //if we don't have custom uuid use it as class id as well + QList<QBluetoothUuid> serviceClassId; + serviceClassId << uuids.at(i); + serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, QVariant::fromValue(serviceClassId)); + serviceInfo.setServiceName(serviceNameForClassUuid(uuids.at(i).data1)); + } + + //don't include the service if we already discovered it before + bool alreadyDiscovered = false; + for (int i = 0; i < discoveredServices.count(); i++) { + const QBluetoothServiceInfo &info = discoveredServices.at(i); + 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); + } + } +} + +void QBluetoothServiceDiscoveryAgentPrivate::_q_fetchUuidsTimeout() +{ + if (sdpCache.isEmpty()) + return; + + QPair<QBluetoothDeviceInfo,QList<QBluetoothUuid> > pair; + const QList<QBluetoothAddress> keys = sdpCache.keys(); + foreach (const QBluetoothAddress &key, keys) { + pair = sdpCache.take(key); + populateDiscoveredServices(pair.first, pair.second); + } + + Q_ASSERT(sdpCache.isEmpty()); + + //kill receiver to limit load of signals + receiver->deleteLater(); + receiver = 0; + _q_serviceDiscoveryFinished(); +} + +void QBluetoothServiceDiscoveryAgentPrivate::_q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state) +{ + if (discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::ServiceDiscovery && + state == QBluetoothLocalDevice::HostPoweredOff ) { + + discoveredDevices.clear(); + sdpCache.clear(); + error = QBluetoothServiceDiscoveryAgent::PoweredOffError; + errorString = QBluetoothServiceDiscoveryAgent::tr("Device is powered off."); + + //kill receiver to limit load of signals + receiver->deleteLater(); + receiver = 0; + + Q_Q(QBluetoothServiceDiscoveryAgent); + emit q->error(error); + _q_serviceDiscoveryFinished(); + } +} + +QT_END_NAMESPACE |