diff options
author | Alex Blasche <alexander.blasche@theqtcompany.com> | 2016-08-03 13:35:38 +0200 |
---|---|---|
committer | Alex Blasche <alexander.blasche@theqtcompany.com> | 2016-08-03 13:35:38 +0200 |
commit | bb3f0064e60107c17ba5a90dd35d64116a4a8a89 (patch) | |
tree | e1d068fdfd205f9051691542aba04882fd80e110 | |
parent | 907c43f83768bf1b7fea89960ff5b60a714007d6 (diff) | |
parent | 4976498621fb05804201dad52eab2c59d94f0da3 (diff) |
Merge remote-tracking branch 'gerrit/btle' into dev
Merges Qt Bluetooth Low Energy port on WinRT into mainline.
Change-Id: Ic519955dfee4a57635c525a79b900340fbc5562f
-rw-r--r-- | src/bluetooth/bluetooth.pro | 15 | ||||
-rw-r--r-- | src/bluetooth/osx/osxbtdeviceinquiry.mm | 2 | ||||
-rw-r--r-- | src/bluetooth/qbluetooth.cpp | 1 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 15 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp | 360 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller.cpp | 3 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_p.h | 28 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_winrt.cpp | 1012 | ||||
-rw-r--r-- | tests/auto/qbluetoothdevicediscoveryagent/tst_qbluetoothdevicediscoveryagent.cpp | 6 | ||||
-rw-r--r-- | tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp | 18 |
10 files changed, 1447 insertions, 13 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index 2d29eb40..b2b7f54c 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -193,6 +193,21 @@ config_bluez:qtHaveModule(dbus) { SOURCES -= qlowenergycontroller_p.cpp SOURCES -= qlowenergyservice.cpp SOURCES -= qlowenergycontroller.cpp +} else:winphone { + DEFINES += QT_WINRT_BLUETOOTH + QT += core-private + + # remove dummy warning once platform port is complete + include(dummy/dummy.pri) + + SOURCES += \ + qbluetoothdevicediscoveryagent_winrt.cpp \ + qbluetoothlocaldevice_p.cpp \ + qbluetoothserver_p.cpp \ + qbluetoothservicediscoveryagent_p.cpp \ + qbluetoothserviceinfo_p.cpp \ + qbluetoothsocket_p.cpp \ + qlowenergycontroller_winrt.cpp } else { message("Unsupported Bluetooth platform, will not build a working QtBluetooth library.") message("Either no Qt D-Bus found or no BlueZ headers available.") diff --git a/src/bluetooth/osx/osxbtdeviceinquiry.mm b/src/bluetooth/osx/osxbtdeviceinquiry.mm index a47ed5a1..870a9c88 100644 --- a/src/bluetooth/osx/osxbtdeviceinquiry.mm +++ b/src/bluetooth/osx/osxbtdeviceinquiry.mm @@ -133,6 +133,8 @@ using namespace QT_NAMESPACE; const IOReturn res = [m_inquiry stop]; if (res != kIOReturnSuccess) m_active = true; + else + qCDebug(QT_BT_OSX) << "-stop, success (waiting for 'inquiryComplete')"; return res; } diff --git a/src/bluetooth/qbluetooth.cpp b/src/bluetooth/qbluetooth.cpp index 483fe65a..37a4774d 100644 --- a/src/bluetooth/qbluetooth.cpp +++ b/src/bluetooth/qbluetooth.cpp @@ -101,5 +101,6 @@ namespace QBluetooth { Q_LOGGING_CATEGORY(QT_BT, "qt.bluetooth") Q_LOGGING_CATEGORY(QT_BT_ANDROID, "qt.bluetooth.android") Q_LOGGING_CATEGORY(QT_BT_BLUEZ, "qt.bluetooth.bluez") +Q_LOGGING_CATEGORY(QT_BT_WINRT, "qt.bluetooth.winphone") QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index 51764613..06cf29c2 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -78,10 +78,14 @@ class QDBusVariant; QT_END_NAMESPACE #endif +#ifdef QT_WINRT_BLUETOOTH +class QWinRTBluetoothDeviceDiscoveryWorker; +#endif + QT_BEGIN_NAMESPACE class QBluetoothDeviceDiscoveryAgentPrivate -#if defined(QT_ANDROID_BLUETOOTH) +#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) : public QObject { Q_OBJECT @@ -155,6 +159,15 @@ private: QTimer extendedDiscoveryTimer; #endif +#ifdef QT_WINRT_BLUETOOTH +private slots: + void onListInitializationCompleted(); + +private: + void disconnectAndClearWorker(); + QPointer<QWinRTBluetoothDeviceDiscoveryWorker> worker; +#endif + int lowEnergySearchTimeout; QBluetoothDeviceDiscoveryAgent::DiscoveryMethods requestedMethods; QBluetoothDeviceDiscoveryAgent *q_ptr; diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp new file mode 100644 index 00000000..bfa39fc1 --- /dev/null +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp @@ -0,0 +1,360 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbluetoothdevicediscoveryagent.h" +#include "qbluetoothdevicediscoveryagent_p.h" +#include "qbluetoothaddress.h" +#include "qbluetoothuuid.h" +#include "qfunctions_winrt.h" + +#include <QtCore/QLoggingCategory> +#include <QtCore/private/qeventdispatcher_winrt_p.h> + +#include <wrl.h> +#include <windows.devices.enumeration.h> +#include <windows.devices.bluetooth.h> +#include <windows.foundation.collections.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Devices; +using namespace ABI::Windows::Devices::Bluetooth; +using namespace ABI::Windows::Devices::Enumeration; + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) + +#define WARN_AND_RETURN_IF_FAILED(msg, ret) \ + if (FAILED(hr)) { \ + qCWarning(QT_BT_WINRT) << msg; \ + ret; \ + } + +QBluetoothDeviceInfo bluetoothInfoFromDeviceId(HSTRING deviceId) +{ + ComPtr<IBluetoothDeviceStatics> deviceStatics; + ComPtr<IBluetoothDevice> device; + HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothDevice).Get(), &deviceStatics); + WARN_AND_RETURN_IF_FAILED("Could not obtain bluetooth device statics", return QBluetoothDeviceInfo()); + ComPtr<IAsyncOperation<BluetoothDevice *>> deviceFromIdOperation; + hr = deviceStatics->FromIdAsync(deviceId, &deviceFromIdOperation); + WARN_AND_RETURN_IF_FAILED("Could not obtain bluetooth device from id", return QBluetoothDeviceInfo()); + QWinRTFunctions::await(deviceFromIdOperation, device.GetAddressOf()); + WARN_AND_RETURN_IF_FAILED("Could not wait for bluetooth device operation to finish", return QBluetoothDeviceInfo()); + if (!device) + return QBluetoothDeviceInfo(); + UINT64 address; + HString name; + ComPtr<IBluetoothClassOfDevice> classOfDevice; + UINT32 classOfDeviceInt; + device->get_BluetoothAddress(&address); + device->get_Name(name.GetAddressOf()); + const QString btName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr)); + device->get_ClassOfDevice(&classOfDevice); + classOfDevice->get_RawValue(&classOfDeviceInt); + IVectorView <Rfcomm::RfcommDeviceService *> *deviceServices; + hr = device->get_RfcommServices(&deviceServices); + WARN_AND_RETURN_IF_FAILED("Could not obtain bluetooth device services", return QBluetoothDeviceInfo()); + uint serviceCount; + deviceServices->get_Size(&serviceCount); + QList<QBluetoothUuid> uuids; + for (uint i = 0; i < serviceCount; ++i) { + ComPtr<Rfcomm::IRfcommDeviceService> service; + deviceServices->GetAt(i, &service); + ComPtr<Rfcomm::IRfcommServiceId> id; + service->get_ServiceId(&id); + GUID uuid; + id->get_Uuid(&uuid); + uuids.append(QBluetoothUuid(uuid)); + } + + qCDebug(QT_BT_WINRT) << "Discovered BT device: " << QString::number(address) << btName + << "Num UUIDs" << uuids.count(); + + QBluetoothDeviceInfo info(QBluetoothAddress(address), btName, classOfDeviceInt); + info.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration); + info.setServiceUuids(uuids, QBluetoothDeviceInfo::DataIncomplete); + info.setCached(true); + + return info; +} + +QBluetoothDeviceInfo bluetoothInfoFromLeDeviceId(HSTRING deviceId) +{ + ComPtr<IBluetoothLEDeviceStatics> deviceStatics; + ComPtr<IBluetoothLEDevice> device; + HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(), &deviceStatics); + WARN_AND_RETURN_IF_FAILED("Could not obtain bluetooth LE device statics", return QBluetoothDeviceInfo()); + ComPtr<IAsyncOperation<BluetoothLEDevice *>> deviceFromIdOperation; + hr = deviceStatics->FromIdAsync(deviceId, &deviceFromIdOperation); + WARN_AND_RETURN_IF_FAILED("Could not obtain bluetooth LE device from id", return QBluetoothDeviceInfo()); + QWinRTFunctions::await(deviceFromIdOperation, device.GetAddressOf()); + WARN_AND_RETURN_IF_FAILED("Could not wait for bluetooth LE device operation to finish", return QBluetoothDeviceInfo()); + if (!device) + return QBluetoothDeviceInfo(); + UINT64 address; + HString name; + device->get_BluetoothAddress(&address); + device->get_Name(name.GetAddressOf()); + const QString btName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr)); + IVectorView <GenericAttributeProfile::GattDeviceService *> *deviceServices; + hr = device->get_GattServices(&deviceServices); + WARN_AND_RETURN_IF_FAILED("Could not obtain bluetooth LE device services", return QBluetoothDeviceInfo()); + uint serviceCount; + deviceServices->get_Size(&serviceCount); + QList<QBluetoothUuid> uuids; + for (uint i = 0; i < serviceCount; ++i) { + ComPtr<GenericAttributeProfile::IGattDeviceService> service; + deviceServices->GetAt(i, &service); + ComPtr<Rfcomm::IRfcommServiceId> id; + GUID uuid; + service->get_Uuid(&uuid); + uuids.append(QBluetoothUuid(uuid)); + } + + qCDebug(QT_BT_WINRT) << "Discovered BTLE device: " << QString::number(address) << btName + << "Num UUIDs" << uuids.count(); + + QBluetoothDeviceInfo info(QBluetoothAddress(address), btName, 0); + info.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration); + info.setServiceUuids(uuids, QBluetoothDeviceInfo::DataIncomplete); + info.setCached(true); + + return info; +} + +class QWinRTBluetoothDeviceDiscoveryWorker : public QObject +{ + Q_OBJECT +public: + QWinRTBluetoothDeviceDiscoveryWorker() : initializedModes(0) + { + } + + void start() + { + QEventDispatcherWinRT::runOnXamlThread([this]() { + startDeviceDiscovery(BT); + + startDeviceDiscovery(BTLE); + return S_OK; + }); + + qCDebug(QT_BT_WINRT) << "Worker started"; + } + + ~QWinRTBluetoothDeviceDiscoveryWorker() + { + } + +private: + enum BTMode { + BT = 0x1, + BTLE = 0x2, + BTAll = BT|BTLE + }; + + void startDeviceDiscovery(BTMode mode) + { + HString deviceSelector; + ComPtr<IDeviceInformationStatics> deviceInformationStatics; + HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(), &deviceInformationStatics); + WARN_AND_RETURN_IF_FAILED("Could not obtain device information statics", return); + if (mode == BTLE) { + ComPtr<IBluetoothLEDeviceStatics> bluetoothLeDeviceStatics; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(), &bluetoothLeDeviceStatics); + WARN_AND_RETURN_IF_FAILED("Could not obtain bluetooth LE device statics", return); + bluetoothLeDeviceStatics->GetDeviceSelector(deviceSelector.GetAddressOf()); + } else { + ComPtr<IBluetoothDeviceStatics> bluetoothDeviceStatics; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothDevice).Get(), &bluetoothDeviceStatics); + WARN_AND_RETURN_IF_FAILED("Could not obtain bluetooth device statics", return); + bluetoothDeviceStatics->GetDeviceSelector(deviceSelector.GetAddressOf()); + } + ComPtr<IAsyncOperation<DeviceInformationCollection *>> op; + hr = deviceInformationStatics->FindAllAsyncAqsFilter(deviceSelector.Get(), &op); + WARN_AND_RETURN_IF_FAILED("Could not start bluetooth device discovery operation", return); + op->put_Completed( + Callback<IAsyncOperationCompletedHandler<DeviceInformationCollection *>>([this, mode](IAsyncOperation<DeviceInformationCollection *> *op, AsyncStatus) { + onDeviceDiscoveryFinished(op, mode); + return S_OK; + }).Get()); + WARN_AND_RETURN_IF_FAILED("Could not add callback to bluetooth device discovery operation", return); + } + + void onDeviceDiscoveryFinished(IAsyncOperation<DeviceInformationCollection *> *op, BTMode mode) + { + qCDebug(QT_BT_WINRT) << (mode == BT ? "BT" : "BTLE") << "scan completed"; + ComPtr<IVectorView<DeviceInformation *>> devices; + op->GetResults(&devices); + onDevicesFound(devices.Get(), mode); + initializedModes |= mode; + if (initializedModes == BTAll) { + qCDebug(QT_BT_WINRT) << "All scans completed"; + emit initializationCompleted(); + deleteLater(); + } + } + + void onDeviceAdded(IDeviceInformation *deviceInfo, BTMode mode) + { + HString deviceId; + deviceInfo->get_Id(deviceId.GetAddressOf()); + const QBluetoothDeviceInfo info = mode == BTLE + ? bluetoothInfoFromLeDeviceId(deviceId.Get()) + : bluetoothInfoFromDeviceId(deviceId.Get()); + if (!info.isValid()) + return; + + for (QVector<QBluetoothDeviceInfo>::iterator iter = deviceList.begin(); + iter != deviceList.end(); ++iter) { + // one of them has to be the traditional device, the other one the BTLE version (found in both scans) + if (iter->address() == info.address()) { + qCDebug(QT_BT_WINRT) << "Updating device" << iter->name() << iter->address(); + // merge service uuids + QList<QBluetoothUuid> uuids = iter->serviceUuids(); + uuids.append(info.serviceUuids()); + const QSet<QBluetoothUuid> uuidSet = uuids.toSet(); + if (iter->serviceUuids().count() != uuidSet.count()) + iter->setServiceUuids(uuidSet.toList(), QBluetoothDeviceInfo::DataIncomplete); + + iter->setCoreConfigurations(QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration); + return; + } + } + qCDebug(QT_BT_WINRT) << "Adding device" << info.name() << info.address(); + deviceList.append(info); + } + + void onDevicesFound(IVectorView<DeviceInformation *> *devices, BTMode mode) + { + quint32 deviceCount; + HRESULT hr = devices->get_Size(&deviceCount); + deviceList.reserve(deviceList.length() + deviceCount); + for (quint32 i = 0; i < deviceCount; ++i) { + ComPtr<IDeviceInformation> device; + hr = devices->GetAt(i, &device); + onDeviceAdded(device.Get(), mode); + } + } + +Q_SIGNALS: + void initializationCompleted(); + +public: + QVector<QBluetoothDeviceInfo> deviceList; + +private: + quint8 initializedModes; +}; + +QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( + const QBluetoothAddress &deviceAdapter, + QBluetoothDeviceDiscoveryAgent *parent) + + : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), + lastError(QBluetoothDeviceDiscoveryAgent::NoError), + lowEnergySearchTimeout(-1), // TODO + q_ptr(parent) +{ + Q_UNUSED(deviceAdapter); +} + +QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() +{ + disconnectAndClearWorker(); +} + +bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const +{ + return worker; +} + +QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() +{ + return (ClassicMethod | LowEnergyMethod); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods) +{ + if (worker) + return; + + // The worker handles its lifetime on its own (basically deletes itself as soon + // as it's done with its work) to prevent windows callbacks that access objects + // that have already been destroyed. Thus we create a new worker for every start + // and just forget about it as soon as the operation is canceled or finished. + worker = new QWinRTBluetoothDeviceDiscoveryWorker(); + discoveredDevices.clear(); + connect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::initializationCompleted, + this, &QBluetoothDeviceDiscoveryAgentPrivate::onListInitializationCompleted); + worker->start(); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::stop() +{ + Q_Q(QBluetoothDeviceDiscoveryAgent); + if (worker) { + disconnectAndClearWorker(); + emit q->canceled(); + } +} + +void QBluetoothDeviceDiscoveryAgentPrivate::onListInitializationCompleted() +{ + Q_Q(QBluetoothDeviceDiscoveryAgent); + discoveredDevices = worker->deviceList.toList(); + foreach (const QBluetoothDeviceInfo &info, worker->deviceList) + emit q->deviceDiscovered(info); + + disconnectAndClearWorker(); + emit q->finished(); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::disconnectAndClearWorker() +{ + if (!worker) + return; + + disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::initializationCompleted, + this, &QBluetoothDeviceDiscoveryAgentPrivate::onListInitializationCompleted); + // worker deletion is done by the worker itself (see comment in start()) + worker.clear(); +} + +QT_END_NAMESPACE + +#include <qbluetoothdevicediscoveryagent_winrt.moc> diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp index c6cac44b..0095b90a 100644 --- a/src/bluetooth/qlowenergycontroller.cpp +++ b/src/bluetooth/qlowenergycontroller.cpp @@ -312,6 +312,9 @@ void QLowEnergyControllerPrivate::setError( bool QLowEnergyControllerPrivate::isValidLocalAdapter() { +#ifdef QT_WINRT_BLUETOOTH + return true; +#endif if (localAdapter.isNull()) return false; diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h index 2256025c..38e38752 100644 --- a/src/bluetooth/qlowenergycontroller_p.h +++ b/src/bluetooth/qlowenergycontroller_p.h @@ -82,6 +82,24 @@ QT_END_NAMESPACE #elif defined(QT_ANDROID_BLUETOOTH) #include <QtAndroidExtras/QAndroidJniObject> #include "android/lowenergynotificationhub_p.h" +#elif defined(QT_WINRT_BLUETOOTH) +#include <wrl.h> + +namespace ABI { + namespace Windows { + namespace Devices { + namespace Bluetooth { + struct IBluetoothLEDevice; + namespace GenericAttributeProfile { + struct IGattDeviceService; + struct IGattCharacteristic; + } + } + } + } +} + +class QWinRTLowEnergyServiceHandler; #endif #include <functional> @@ -414,6 +432,16 @@ private slots: QLowEnergyService::ServiceError errorCode); void characteristicChanged(int charHandle, const QByteArray &data); void serviceError(int attributeHandle, QLowEnergyService::ServiceError errorCode); +#elif defined(QT_WINRT_BLUETOOTH) +private: + Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice> mDevice; + EventRegistrationToken mStatusChangedToken; + + Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattDeviceService> getNativeService(const QBluetoothUuid &serviceUuid); + Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattCharacteristic> getNativeCharacteristic(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid); + + void obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer, + Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattDeviceService> nativeService); #endif private: QLowEnergyController *q_ptr; diff --git a/src/bluetooth/qlowenergycontroller_winrt.cpp b/src/bluetooth/qlowenergycontroller_winrt.cpp new file mode 100644 index 00000000..7de12d2c --- /dev/null +++ b/src/bluetooth/qlowenergycontroller_winrt.cpp @@ -0,0 +1,1012 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlowenergycontroller_p.h" + +#include <QtCore/qfunctions_winrt.h> +#include <QtCore/QLoggingCategory> +#include <private/qeventdispatcher_winrt_p.h> + +#include <functional> +#include <robuffer.h> +#include <windows.devices.enumeration.h> +#include <windows.devices.bluetooth.h> +#include <windows.foundation.collections.h> +#include <windows.storage.streams.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Devices; +using namespace ABI::Windows::Devices::Bluetooth; +using namespace ABI::Windows::Devices::Bluetooth::GenericAttributeProfile; +using namespace ABI::Windows::Devices::Enumeration; +using namespace ABI::Windows::Storage::Streams; + +QT_BEGIN_NAMESPACE + +typedef ITypedEventHandler<BluetoothLEDevice *, IInspectable *> StatusHandler; +typedef GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConfigDescriptorResult; +typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharConfigDescriptorResult; + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) + +static QVector<QBluetoothUuid> getIncludedServiceIds(const ComPtr<IGattDeviceService> &service) +{ + QVector<QBluetoothUuid> result; + ComPtr<IGattDeviceService2> service2; + HRESULT hr = service.As(&service2); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<GattDeviceService *>> includedServices; + hr = service2->GetAllIncludedServices(&includedServices); + Q_ASSERT_SUCCEEDED(hr); + + uint count; + includedServices->get_Size(&count); + for (uint i = 0; i < count; ++i) { + ComPtr<IGattDeviceService> includedService; + hr = includedServices->GetAt(i, &includedService); + Q_ASSERT_SUCCEEDED(hr); + GUID guuid; + hr = includedService->get_Uuid(&guuid); + Q_ASSERT_SUCCEEDED(hr); + const QBluetoothUuid service(guuid); + result << service; + + result << getIncludedServiceIds(includedService); + } + return result; +} + +static QByteArray byteArrayFromBuffer(const ComPtr<IBuffer> &buffer, bool isWCharString = false) +{ + ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess; + HRESULT hr = buffer.As(&byteAccess); + Q_ASSERT_SUCCEEDED(hr); + char *data; + hr = byteAccess->Buffer(reinterpret_cast<byte **>(&data)); + Q_ASSERT_SUCCEEDED(hr); + UINT32 size; + hr = buffer->get_Length(&size); + Q_ASSERT_SUCCEEDED(hr); + if (isWCharString) { + QString valueString = QString::fromUtf16(reinterpret_cast<ushort *>(data)).left(size / 2); + return valueString.toUtf8(); + } + return QByteArray(data, size); +} + +static QByteArray byteArrayFromGattResult(const ComPtr<IGattReadResult> &gattResult, bool isWCharString = false) +{ + ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; + HRESULT hr; + hr = gattResult->get_Value(&buffer); + Q_ASSERT_SUCCEEDED(hr); + return byteArrayFromBuffer(buffer, isWCharString); +} + +class QWinRTLowEnergyServiceHandler : public QObject +{ + Q_OBJECT +public: + QWinRTLowEnergyServiceHandler(const QBluetoothUuid &service, const ComPtr<IGattDeviceService2> &deviceService) + : mService(service) + , mDeviceService(deviceService) + { + qCDebug(QT_BT_WINRT) << __FUNCTION__; + } + + ~QWinRTLowEnergyServiceHandler() + { + } + +public slots: + void obtainCharList() + { + quint16 startHandle = 0; + quint16 endHandle = 0; + qCDebug(QT_BT_WINRT) << __FUNCTION__; + ComPtr<IVectorView<GattCharacteristic *>> characteristics; + HRESULT hr = mDeviceService->GetAllCharacteristics(&characteristics); + Q_ASSERT_SUCCEEDED(hr); + if (!characteristics) { + emit charListObtained(mService, mCharacteristicList, startHandle, endHandle); + QThread::currentThread()->quit(); + return; + } + + uint characteristicsCount; + hr = characteristics->get_Size(&characteristicsCount); + for (uint i = 0; i < characteristicsCount; ++i) { + ComPtr<IGattCharacteristic> characteristic; + hr = characteristics->GetAt(i, &characteristic); + Q_ASSERT_SUCCEEDED(hr); + quint16 handle; + hr = characteristic->get_AttributeHandle(&handle); + Q_ASSERT_SUCCEEDED(hr); + QLowEnergyServicePrivate::CharData charData; + charData.valueHandle = handle + 1; + if (startHandle == 0 || startHandle > handle) + startHandle = handle; + if (endHandle == 0 || endHandle < handle) + endHandle = handle; + GUID guuid; + hr = characteristic->get_Uuid(&guuid); + Q_ASSERT_SUCCEEDED(hr); + charData.uuid = QBluetoothUuid(guuid); + GattCharacteristicProperties properties; + hr = characteristic->get_CharacteristicProperties(&properties); + Q_ASSERT_SUCCEEDED(hr); + charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties); + if (charData.properties & QLowEnergyCharacteristic::Read) { + ComPtr<IAsyncOperation<GattReadResult *>> readOp; + hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); + ComPtr<IGattReadResult> readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + if (readResult) + charData.value = byteArrayFromGattResult(readResult); + } + ComPtr<IGattCharacteristic2> characteristic2; + hr = characteristic.As(&characteristic2); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<GattDescriptor *>> descriptors; + hr = characteristic2->GetAllDescriptors(&descriptors); + Q_ASSERT_SUCCEEDED(hr); + uint descriptorCount; + hr = descriptors->get_Size(&descriptorCount); + Q_ASSERT_SUCCEEDED(hr); + for (uint j = 0; j < descriptorCount; ++j) { + QLowEnergyServicePrivate::DescData descData; + ComPtr<IGattDescriptor> descriptor; + hr = descriptors->GetAt(j, &descriptor); + Q_ASSERT_SUCCEEDED(hr); + quint16 descHandle; + hr = descriptor->get_AttributeHandle(&descHandle); + Q_ASSERT_SUCCEEDED(hr); + GUID descriptorUuid; + hr = descriptor->get_Uuid(&descriptorUuid); + Q_ASSERT_SUCCEEDED(hr); + descData.uuid = QBluetoothUuid(descriptorUuid); + if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { + ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp; + hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IClientCharConfigDescriptorResult> readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + GattClientCharacteristicConfigurationDescriptorValue value; + hr = readResult->get_ClientCharacteristicConfigurationDescriptor(&value); + Q_ASSERT_SUCCEEDED(hr); + quint16 result = 0; + bool correct = false; + if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) { + result |= GattClientCharacteristicConfigurationDescriptorValue_Indicate; + correct = true; + } + if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) { + result |= GattClientCharacteristicConfigurationDescriptorValue_Notify; + correct = true; + } + if (value == GattClientCharacteristicConfigurationDescriptorValue_None) { + correct = true; + } + if (!correct) + continue; + + descData.value = QByteArray(2, Qt::Uninitialized); + qToLittleEndian(result, descData.value.data()); + } else { + ComPtr<IAsyncOperation<GattReadResult *>> readOp; + hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IGattReadResult> readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription) + descData.value = byteArrayFromGattResult(readResult, true); + else + descData.value = byteArrayFromGattResult(readResult); + } + charData.descriptorList.insert(descHandle, descData); + } + mCharacteristicList.insert(handle, charData); + } + emit charListObtained(mService, mCharacteristicList, startHandle, endHandle); + QThread::currentThread()->quit(); + } + +public: + QBluetoothUuid mService; + ComPtr<IGattDeviceService2> mDeviceService; + QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> mCharacteristicList; + +signals: + void charListObtained(const QBluetoothUuid &service, QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> charList, + QLowEnergyHandle startHandle, QLowEnergyHandle endHandle); +}; + +QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() + : QObject(), + state(QLowEnergyController::UnconnectedState), + error(QLowEnergyController::NoError) +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__; +} + +QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() +{ + if (mDevice && mStatusChangedToken.value) + mDevice->remove_ConnectionStatusChanged(mStatusChangedToken); +} + +void QLowEnergyControllerPrivate::init() +{ +} + +void QLowEnergyControllerPrivate::connectToDevice() +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__; + Q_Q(QLowEnergyController); + if (remoteDevice.isNull()) { + qWarning() << "Invalid/null remote device address"; + setError(QLowEnergyController::UnknownRemoteDeviceError); + return; + } + + setState(QLowEnergyController::ConnectingState); + + ComPtr<IBluetoothLEDeviceStatics> deviceStatics; + HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(), &deviceStatics); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IAsyncOperation<BluetoothLEDevice *>> deviceFromIdOperation; + hr = deviceStatics->FromBluetoothAddressAsync(remoteDevice.toUInt64(), &deviceFromIdOperation); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(deviceFromIdOperation, mDevice.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + if (!mDevice) { + qCDebug(QT_BT_WINRT) << "Could not find LE device"; + setError(QLowEnergyController::InvalidBluetoothAdapterError); + setState(QLowEnergyController::UnconnectedState); + } + BluetoothConnectionStatus status; + hr = mDevice->get_ConnectionStatus(&status); + Q_ASSERT_SUCCEEDED(hr); + hr = QEventDispatcherWinRT::runOnXamlThread([this, q]() { + HRESULT hr; + hr = mDevice->add_ConnectionStatusChanged(Callback<StatusHandler>([this, q](IBluetoothLEDevice *dev, IInspectable *) { + BluetoothConnectionStatus status; + dev->get_ConnectionStatus(&status); + if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { + setState(QLowEnergyController::ConnectedState); + emit q->connected(); + } else if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) { + setState(QLowEnergyController::UnconnectedState); + emit q->disconnected(); + } + return S_OK; + }).Get(), &mStatusChangedToken); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + }); + + if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { + setState(QLowEnergyController::ConnectedState); + emit q->connected(); + return; + } + + ComPtr<IVectorView <GattDeviceService *>> deviceServices; + hr = mDevice->get_GattServices(&deviceServices); + Q_ASSERT_SUCCEEDED(hr); + uint serviceCount; + hr = deviceServices->get_Size(&serviceCount); + Q_ASSERT_SUCCEEDED(hr); + // Windows Phone automatically connects to the device as soon as a service value is read/written. + // Thus we read one value in order to establish the connection. + for (uint i = 0; i < serviceCount; ++i) { + ComPtr<IGattDeviceService> service; + hr = deviceServices->GetAt(i, &service); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IGattDeviceService2> service2; + hr = service.As(&service2); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<GattCharacteristic *>> characteristics; + hr = service2->GetAllCharacteristics(&characteristics); + if (hr == E_ACCESSDENIED) { + // Everything will work as expected up until this point if the manifest capabilties + // for bluetooth LE are not set. + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic list. Please check your " + "manifest capabilities"; + setState(QLowEnergyController::UnconnectedState); + setError(QLowEnergyController::ConnectionError); + return; + } else { + Q_ASSERT_SUCCEEDED(hr); + } + uint characteristicsCount; + hr = characteristics->get_Size(&characteristicsCount); + Q_ASSERT_SUCCEEDED(hr); + for (uint j = 0; j < characteristicsCount; ++j) { + ComPtr<IGattCharacteristic> characteristic; + hr = characteristics->GetAt(j, &characteristic); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IAsyncOperation<GattReadResult *>> op; + GattCharacteristicProperties props; + hr = characteristic->get_CharacteristicProperties(&props); + Q_ASSERT_SUCCEEDED(hr); + if (!(props & GattCharacteristicProperties_Read)) + continue; + hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IGattReadResult> result; + hr = QWinRTFunctions::await(op, result.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; + hr = result->get_Value(&buffer); + Q_ASSERT_SUCCEEDED(hr); + if (!buffer) { + qCDebug(QT_BT_WINRT) << "Problem reading value"; + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + } + return; + } + } +} + +void QLowEnergyControllerPrivate::disconnectFromDevice() +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__; + Q_Q(QLowEnergyController); + setState(QLowEnergyController::UnconnectedState); + emit q->disconnected(); +} + +ComPtr<IGattDeviceService> QLowEnergyControllerPrivate::getNativeService(const QBluetoothUuid &serviceUuid) +{ + ComPtr<IGattDeviceService> deviceService; + HRESULT hr; + hr = mDevice->GetGattService(serviceUuid, &deviceService); + if (FAILED(hr)) + qCDebug(QT_BT_WINRT) << "Could not obtain native service for Uuid" << serviceUuid; + return deviceService; +} + +ComPtr<IGattCharacteristic> QLowEnergyControllerPrivate::getNativeCharacteristic(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid) +{ + ComPtr<IGattDeviceService> service = getNativeService(serviceUuid); + if (!service) + return nullptr; + + ComPtr<IVectorView<GattCharacteristic *>> characteristics; + HRESULT hr = service->GetCharacteristics(charUuid, &characteristics); + RETURN_IF_FAILED("Could not obtain native characteristics for service", return nullptr); + ComPtr<IGattCharacteristic> characteristic; + hr = characteristics->GetAt(0, &characteristic); + RETURN_IF_FAILED("Could not obtain first characteristic for service", return nullptr); + return characteristic; +} + +void QLowEnergyControllerPrivate::obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer, + ComPtr<IGattDeviceService> service) +{ + Q_Q(QLowEnergyController); + ComPtr<IGattDeviceService2> service2; + HRESULT hr = service.As(&service2); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<GattDeviceService *>> includedServices; + hr = service2->GetAllIncludedServices(&includedServices); + Q_ASSERT_SUCCEEDED(hr); + + uint count; + includedServices->get_Size(&count); + for (uint i = 0; i < count; ++i) { + ComPtr<IGattDeviceService> includedService; + hr = includedServices->GetAt(i, &includedService); + Q_ASSERT_SUCCEEDED(hr); + GUID guuid; + hr = includedService->get_Uuid(&guuid); + Q_ASSERT_SUCCEEDED(hr); + const QBluetoothUuid includedUuid(guuid); + QSharedPointer<QLowEnergyServicePrivate> includedPointer; + if (serviceList.contains(includedUuid)) { + includedPointer = serviceList.value(includedUuid); + } else { + QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate(); + priv->uuid = includedUuid; + priv->setController(this); + + includedPointer = QSharedPointer<QLowEnergyServicePrivate>(priv); + serviceList.insert(includedUuid, includedPointer); + } + includedPointer->type |= QLowEnergyService::IncludedService; + servicePointer->includedServices.append(includedUuid); + + obtainIncludedServices(includedPointer, includedService); + + emit q->serviceDiscovered(includedUuid); + } +} + +void QLowEnergyControllerPrivate::discoverServices() +{ + Q_Q(QLowEnergyController); + + qCDebug(QT_BT_WINRT) << "Service discovery initiated"; + ComPtr<IVectorView<GattDeviceService *>> deviceServices; + HRESULT hr = mDevice->get_GattServices(&deviceServices); + Q_ASSERT_SUCCEEDED(hr); + uint serviceCount; + hr = deviceServices->get_Size(&serviceCount); + Q_ASSERT_SUCCEEDED(hr); + for (uint i = 0; i < serviceCount; ++i) { + ComPtr<IGattDeviceService> deviceService; + hr = deviceServices->GetAt(i, &deviceService); + Q_ASSERT_SUCCEEDED(hr); + GUID guuid; + hr = deviceService->get_Uuid(&guuid); + Q_ASSERT_SUCCEEDED(hr); + const QBluetoothUuid service(guuid); + + QSharedPointer<QLowEnergyServicePrivate> pointer; + if (serviceList.contains(service)) { + pointer = serviceList.value(service); + } else { + QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate(); + priv->uuid = service; + priv->setController(this); + + pointer = QSharedPointer<QLowEnergyServicePrivate>(priv); + serviceList.insert(service, pointer); + } + pointer->type |= QLowEnergyService::PrimaryService; + + obtainIncludedServices(pointer, deviceService); + + emit q->serviceDiscovered(service); + } + + setState(QLowEnergyController::DiscoveredState); + emit q->discoveryFinished(); +} + +void QLowEnergyControllerPrivate::discoverServiceDetails(const QBluetoothUuid &service) +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__ << service; + if (!serviceList.contains(service)) { + qCWarning(QT_BT_WINRT) << "Discovery done of unknown service:" + << service.toString(); + return; + } + + ComPtr<IGattDeviceService> deviceService = getNativeService(service); + if (!deviceService) { + qCDebug(QT_BT_WINRT) << "Could not obtain native service for uuid " << service; + return; + } + + //update service data + QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service); + + pointer->setState(QLowEnergyService::DiscoveringServices); + ComPtr<IGattDeviceService2> deviceService2; + HRESULT hr = deviceService.As(&deviceService2); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<GattDeviceService *>> deviceServices; + hr = deviceService2->GetAllIncludedServices(&deviceServices); + Q_ASSERT_SUCCEEDED(hr); + uint serviceCount; + hr = deviceServices->get_Size(&serviceCount); + Q_ASSERT_SUCCEEDED(hr); + for (uint i = 0; i < serviceCount; ++i) { + ComPtr<IGattDeviceService> includedService; + hr = deviceServices->GetAt(i, &includedService); + Q_ASSERT_SUCCEEDED(hr); + GUID guuid; + hr = includedService->get_Uuid(&guuid); + Q_ASSERT_SUCCEEDED(hr); + + const QBluetoothUuid service(guuid); + if (service.isNull()) { + qCDebug(QT_BT_WINRT) << "Could not find service"; + return; + } + + pointer->includedServices.append(service); + + // update the type of the included service + QSharedPointer<QLowEnergyServicePrivate> otherService = serviceList.value(service); + if (!otherService.isNull()) + otherService->type |= QLowEnergyService::IncludedService; + } + + QWinRTLowEnergyServiceHandler *worker = new QWinRTLowEnergyServiceHandler(service, deviceService2); + QThread *thread = new QThread; + worker->moveToThread(thread); + connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandler::obtainCharList); + connect(thread, &QThread::finished, thread, &QObject::deleteLater); + connect(thread, &QThread::finished, worker, &QObject::deleteLater); + connect(worker, &QWinRTLowEnergyServiceHandler::charListObtained, + [this, thread](const QBluetoothUuid &service, QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> charList + , QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) { + if (!serviceList.contains(service)) { + qCWarning(QT_BT_WINRT) << "Discovery done of unknown service:" + << service.toString(); + return; + } + + QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service); + pointer->startHandle = startHandle; + pointer->endHandle = endHandle; + pointer->characteristicList = charList; + pointer->setState(QLowEnergyService::ServiceDiscovered); + thread->exit(0); + }); + thread->start(); +} + +void QLowEnergyControllerPrivate::startAdvertising(const QLowEnergyAdvertisingParameters &, const QLowEnergyAdvertisingData &, const QLowEnergyAdvertisingData &) +{ + Q_UNIMPLEMENTED(); +} + +void QLowEnergyControllerPrivate::stopAdvertising() +{ + Q_UNIMPLEMENTED(); +} + +void QLowEnergyControllerPrivate::requestConnectionUpdate(const QLowEnergyConnectionParameters &) +{ + Q_UNIMPLEMENTED(); +} + +void QLowEnergyControllerPrivate::readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle) +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle; + Q_ASSERT(!service.isNull()); + if (!service->characteristicList.contains(charHandle)) { + qCDebug(QT_BT_WINRT) << charHandle << "could not be found in service" << service->uuid; + service->setError(QLowEnergyService::CharacteristicReadError); + return; + } + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, service, this]() { + const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); + if (!(charData.properties & QLowEnergyCharacteristic::Read)) + qCDebug(QT_BT_WINRT) << "Read flag is not set for characteristic" << charData.uuid; + + ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid); + if (!characteristic) { + qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid + << "from service" << service->uuid; + service->setError(QLowEnergyService::CharacteristicReadError); + return S_OK; + } + ComPtr<IAsyncOperation<GattReadResult*>> readOp; + HRESULT hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); + Q_ASSERT_SUCCEEDED(hr); + auto readCompletedLambda = [charData, charHandle, service] + (IAsyncOperation<GattReadResult*> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "read operation failed."; + service->setError(QLowEnergyService::CharacteristicReadError); + return S_OK; + } + ComPtr<IGattReadResult> characteristicValue; + HRESULT hr; + hr = op->GetResults(&characteristicValue); + if (FAILED(hr)) { + qCDebug(QT_BT_WINRT) << "Could not obtain result for characteristic" << charHandle; + service->setError(QLowEnergyService::CharacteristicReadError); + return S_OK; + } + + const QByteArray value = byteArrayFromGattResult(characteristicValue); + QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); + charData.value = value; + service->characteristicList.insert(charHandle, charData); + emit service->characteristicRead(QLowEnergyCharacteristic(service, charHandle), value); + return S_OK; + }; + hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>(readCompletedLambda).Get()); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + }); + Q_ASSERT_SUCCEEDED(hr); +} + +void QLowEnergyControllerPrivate::readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QLowEnergyHandle descHandle) +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle; + Q_ASSERT(!service.isNull()); + if (!service->characteristicList.contains(charHandle)) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "in characteristic" << charHandle + << "cannot be found in service" << service->uuid; + service->setError(QLowEnergyService::DescriptorReadError); + return; + } + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, descHandle, service, this]() { + QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); + ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid); + if (!characteristic) { + qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid + << "from service" << service->uuid; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + + // Get native descriptor + if (!charData.descriptorList.contains(descHandle)) + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "cannot be found in characteristic" << charHandle; + QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle); + if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { + ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp; + HRESULT hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp); + Q_ASSERT_SUCCEEDED(hr); + auto readCompletedLambda = [&charData, charHandle, &descData, descHandle, service] + (IAsyncOperation<ClientCharConfigDescriptorResult *> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "read operation failed"; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + ComPtr<IClientCharConfigDescriptorResult> iValue; + HRESULT hr; + hr = op->GetResults(&iValue); + if (FAILED(hr)) { + qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + GattClientCharacteristicConfigurationDescriptorValue value; + hr = iValue->get_ClientCharacteristicConfigurationDescriptor(&value); + if (FAILED(hr)) { + qCDebug(QT_BT_WINRT) << "Could not obtain value for descriptor" << descHandle; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + quint16 result = 0; + bool correct = false; + if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) { + result |= QLowEnergyCharacteristic::Indicate; + correct = true; + } + if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) { + result |= QLowEnergyCharacteristic::Notify; + correct = true; + } + if (value == GattClientCharacteristicConfigurationDescriptorValue_None) + correct = true; + if (!correct) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle + << "read operation failed. Obtained unexpected value."; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + descData.value = QByteArray(2, Qt::Uninitialized); + qToLittleEndian(result, descData.value.data()); + charData.descriptorList.insert(descHandle, descData); + emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle), + descData.value); + return S_OK; + }; + hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<ClientCharConfigDescriptorResult *>>(readCompletedLambda).Get()); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + } else { + ComPtr<IVectorView<GattDescriptor *>> descriptors; + HRESULT hr = characteristic->GetDescriptors(descData.uuid, &descriptors); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IGattDescriptor> descriptor; + hr = descriptors->GetAt(0, &descriptor); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IAsyncOperation<GattReadResult*>> readOp; + hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); + Q_ASSERT_SUCCEEDED(hr); + auto readCompletedLambda = [&charData, charHandle, &descData, descHandle, service] + (IAsyncOperation<GattReadResult*> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "read operation failed"; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + ComPtr<IGattReadResult> descriptorValue; + HRESULT hr; + hr = op->GetResults(&descriptorValue); + if (FAILED(hr)) { + qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription) + descData.value = byteArrayFromGattResult(descriptorValue, true); + else + descData.value = byteArrayFromGattResult(descriptorValue); + charData.descriptorList.insert(descHandle, descData); + emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle), + descData.value); + return S_OK; + }; + hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>(readCompletedLambda).Get()); + return S_OK; + } + }); + Q_ASSERT_SUCCEEDED(hr); +} + +void QLowEnergyControllerPrivate::writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QByteArray &newValue, + QLowEnergyService::WriteMode mode) +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << newValue << mode; + Q_ASSERT(!service.isNull()); + if (!service->characteristicList.contains(charHandle)) { + qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "cannot be found in service" << service->uuid; + service->setError(QLowEnergyService::CharacteristicWriteError); + return; + } + + QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); + const bool writeWithResponse = mode == QLowEnergyService::WriteWithResponse; + if (!(charData.properties & (writeWithResponse ? QLowEnergyCharacteristic::Write : QLowEnergyCharacteristic::WriteNoResponse))) + qCDebug(QT_BT_WINRT) << "Write flag is not set for characteristic" << charHandle; + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([charData, charHandle, this, service, newValue, writeWithResponse]() { + ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid); + if (!characteristic) { + qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid + << "from service" << service->uuid; + service->setError(QLowEnergyService::CharacteristicWriteError); + return S_OK; + } + ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory; + HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; + const int length = newValue.length(); + hr = bufferFactory->Create(length, &buffer); + Q_ASSERT_SUCCEEDED(hr); + hr = buffer->put_Length(length); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess; + hr = buffer.As(&byteAccess); + Q_ASSERT_SUCCEEDED(hr); + byte *bytes; + hr = byteAccess->Buffer(&bytes); + Q_ASSERT_SUCCEEDED(hr); + memcpy(bytes, newValue, length); + ComPtr<IAsyncOperation<GattCommunicationStatus>> writeOp; + GattWriteOption option = writeWithResponse ? GattWriteOption_WriteWithResponse : GattWriteOption_WriteWithoutResponse; + hr = characteristic->WriteValueWithOptionAsync(buffer.Get(), option, &writeOp); + Q_ASSERT_SUCCEEDED(hr); + auto writeCompletedLambda =[charData, charHandle, newValue, service, this] + (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation failed"; + service->setError(QLowEnergyService::CharacteristicWriteError); + return S_OK; + } + GattCommunicationStatus result; + HRESULT hr; + hr = op->GetResults(&result); + if (hr == E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH) { + qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation was tried with invalid value length"; + service->setError(QLowEnergyService::CharacteristicWriteError); + return S_OK; + } + Q_ASSERT_SUCCEEDED(hr); + if (result != GattCommunicationStatus_Success) { + qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation failed"; + service->setError(QLowEnergyService::CharacteristicWriteError); + return S_OK; + } + // only update cache when property is readable. Otherwise it remains + // empty. + if (charData.properties & QLowEnergyCharacteristic::Read) + updateValueOfCharacteristic(charHandle, newValue, false); + emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle), newValue); + return S_OK; + }; + hr = writeOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>(writeCompletedLambda).Get()); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + }); + Q_ASSERT_SUCCEEDED(hr); +} + +void QLowEnergyControllerPrivate::writeDescriptor( + const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QLowEnergyHandle descHandle, + const QByteArray &newValue) +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle << newValue; + Q_ASSERT(!service.isNull()); + if (!service->characteristicList.contains(charHandle)) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "in characteristic" << charHandle + << "could not be found in service" << service->uuid; + service->setError(QLowEnergyService::DescriptorWriteError); + return; + } + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, descHandle, this, service, newValue]() { + const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); + ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid); + if (!characteristic) { + qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid + << "from service" << service->uuid; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + + // Get native descriptor + if (!charData.descriptorList.contains(descHandle)) + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "could not be found in Characteristic" << charHandle; + + QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle); + if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { + GattClientCharacteristicConfigurationDescriptorValue value; + quint16 intValue = qFromLittleEndian<quint16>(newValue); + if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Indicate && intValue & GattClientCharacteristicConfigurationDescriptorValue_Notify) { + qCWarning(QT_BT_WINRT) << "Setting both Indicate and Notify is not supported on WinRT"; + value = (GattClientCharacteristicConfigurationDescriptorValue)(GattClientCharacteristicConfigurationDescriptorValue_Indicate | GattClientCharacteristicConfigurationDescriptorValue_Notify); + } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Indicate) { + value = GattClientCharacteristicConfigurationDescriptorValue_Indicate; + } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Notify) { + value = GattClientCharacteristicConfigurationDescriptorValue_Notify; + } else if (intValue == 0) { + value = GattClientCharacteristicConfigurationDescriptorValue_None; + } else { + Q_ASSERT(false); + } + ComPtr<IAsyncOperation<enum GattCommunicationStatus>> writeOp; + HRESULT hr = characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(value, &writeOp); + Q_ASSERT_SUCCEEDED(hr); + auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this] + (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed"; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + GattCommunicationStatus result; + HRESULT hr; + hr = op->GetResults(&result); + if (FAILED(hr)) { + qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + if (result != GattCommunicationStatus_Success) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed"; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + updateValueOfDescriptor(charHandle, descHandle, newValue, false); + emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle), newValue); + return S_OK; + }; + hr = writeOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus >>(writeCompletedLambda).Get()); + Q_ASSERT_SUCCEEDED(hr); + } else { + ComPtr<IVectorView<GattDescriptor *>> descriptors; + HRESULT hr = characteristic->GetDescriptors(descData.uuid, &descriptors); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IGattDescriptor> descriptor; + hr = descriptors->GetAt(0, &descriptor); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; + const int length = newValue.length(); + hr = bufferFactory->Create(length, &buffer); + Q_ASSERT_SUCCEEDED(hr); + hr = buffer->put_Length(length); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess; + hr = buffer.As(&byteAccess); + Q_ASSERT_SUCCEEDED(hr); + byte *bytes; + hr = byteAccess->Buffer(&bytes); + Q_ASSERT_SUCCEEDED(hr); + memcpy(bytes, newValue, length); + ComPtr<IAsyncOperation<GattCommunicationStatus>> writeOp; + hr = descriptor->WriteValueAsync(buffer.Get(), &writeOp); + Q_ASSERT_SUCCEEDED(hr); + auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this] + (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed"; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + GattCommunicationStatus result; + HRESULT hr; + hr = op->GetResults(&result); + if (FAILED(hr)) { + qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + if (result != GattCommunicationStatus_Success) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed"; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + updateValueOfDescriptor(charHandle, descHandle, newValue, false); + emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle), newValue); + return S_OK; + }; + hr = writeOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>(writeCompletedLambda).Get()); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + } + return S_OK; + }); + Q_ASSERT_SUCCEEDED(hr); +} + + +void QLowEnergyControllerPrivate::addToGenericAttributeList(const QLowEnergyServiceData &, QLowEnergyHandle) +{ + Q_UNIMPLEMENTED(); +} + +QT_END_NAMESPACE + +#include "qlowenergycontroller_winrt.moc" diff --git a/tests/auto/qbluetoothdevicediscoveryagent/tst_qbluetoothdevicediscoveryagent.cpp b/tests/auto/qbluetoothdevicediscoveryagent/tst_qbluetoothdevicediscoveryagent.cpp index d37a67de..f8ab72f7 100644 --- a/tests/auto/qbluetoothdevicediscoveryagent/tst_qbluetoothdevicediscoveryagent.cpp +++ b/tests/auto/qbluetoothdevicediscoveryagent/tst_qbluetoothdevicediscoveryagent.cpp @@ -471,10 +471,10 @@ void tst_QBluetoothDeviceDiscoveryAgent::tst_deviceDiscovery() } } } -#ifdef Q_OS_IOS - //On iOS, we do not have access to the local device/adapter, numberOfAdapters is 0, +#if defined(Q_OS_IOS) || defined(Q_OS_WINRT) + //On iOS/WinRT, we do not have access to the local device/adapter, numberOfAdapters is 0, //so we skip this test at all. - QSKIP("iOS: no local Bluetooth device available. Skipping remaining part of test."); + QSKIP("iOS/WinRT: no local Bluetooth device available. Skipping remaining part of test."); #endif //For multiple Bluetooth adapter do the check only for GeneralUnlimitedInquiry. diff --git a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp index 773c673b..4acfe4d1 100644 --- a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp +++ b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp @@ -120,7 +120,7 @@ tst_QLowEnergyController::~tst_QLowEnergyController() void tst_QLowEnergyController::initTestCase() { -#ifndef Q_OS_MAC +#if !defined(Q_OS_MAC) && !defined(Q_OS_WINRT) if (remoteDevice.isNull() || QBluetoothLocalDevice::allDevices().isEmpty()) { qWarning("No remote device or local adapter found."); @@ -244,7 +244,7 @@ void tst_QLowEnergyController::tst_connect() { QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) +#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) || defined(Q_OS_WINRT) if (!remoteDeviceInfo.isValid()) #else if (localAdapters.isEmpty() || !remoteDeviceInfo.isValid()) @@ -260,7 +260,7 @@ void tst_QLowEnergyController::tst_connect() else QCOMPARE(control.remoteName(), remoteDeviceInfo.name()); -#if !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) +#if !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(Q_OS_WINRT) const QBluetoothAddress localAdapter = localAdapters.at(0).address(); QCOMPARE(control.localAddress(), localAdapter); QVERIFY(!control.localAddress().isNull()); @@ -403,7 +403,7 @@ void tst_QLowEnergyController::tst_connect() void tst_QLowEnergyController::tst_concurrentDiscovery() { -#ifndef Q_OS_MAC +#if !defined(Q_OS_MAC) && !defined(Q_OS_WINRT) QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); if (localAdapters.isEmpty()) QSKIP("No local Bluetooth device found. Skipping test."); @@ -440,7 +440,7 @@ void tst_QLowEnergyController::tst_concurrentDiscovery() 30000); } -#ifdef Q_OS_ANDROID +#if defined(Q_OS_ANDROID) || defined(Q_OS_WINRT) QCOMPARE(control.state(), QLowEnergyController::ConnectedState); QCOMPARE(control2.state(), QLowEnergyController::ConnectedState); control2.disconnectFromDevice(); @@ -1642,7 +1642,7 @@ void tst_QLowEnergyController::tst_defaultBehavior() void tst_QLowEnergyController::tst_writeCharacteristic() { -#ifndef Q_OS_MAC +#if !defined(Q_OS_MAC) && !defined(Q_OS_WINRT) QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); if (localAdapters.isEmpty()) QSKIP("No local Bluetooth device found. Skipping test."); @@ -1816,7 +1816,7 @@ void tst_QLowEnergyController::tst_writeCharacteristic() void tst_QLowEnergyController::tst_readWriteDescriptor() { -#ifndef Q_OS_MAC +#if !defined(Q_OS_MAC) && !defined(Q_OS_WINRT) QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); if (localAdapters.isEmpty()) QSKIP("No local Bluetooth device found. Skipping test."); @@ -2239,7 +2239,7 @@ void tst_QLowEnergyController::tst_customProgrammableDevice() */ void tst_QLowEnergyController::tst_errorCases() { -#ifndef Q_OS_MAC +#if !defined(Q_OS_MAC) && !defined(Q_OS_WINRT) QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); if (localAdapters.isEmpty()) QSKIP("No local Bluetooth device found. Skipping test."); @@ -2461,7 +2461,7 @@ void tst_QLowEnergyController::tst_errorCases() */ void tst_QLowEnergyController::tst_writeCharacteristicNoResponse() { -#ifndef Q_OS_MAC +#if !defined(Q_OS_MAC) && !defined(Q_OS_WINRT) QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); if (localAdapters.isEmpty()) QSKIP("No local Bluetooth device found. Skipping test."); |