summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.tests/winrt_btle_no_pairing/main.cpp40
-rw-r--r--config.tests/winrt_btle_no_pairing/winrt.pro3
-rw-r--r--src/bluetooth/bluetooth.pro9
-rw-r--r--src/bluetooth/configure.json13
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp152
-rw-r--r--src/bluetooth/qbluetoothutils_winrt.cpp79
-rw-r--r--src/bluetooth/qbluetoothutils_winrt_p.h62
-rw-r--r--src/bluetooth/qlowenergycharacteristic.h1
-rw-r--r--src/bluetooth/qlowenergycontroller.cpp10
-rw-r--r--src/bluetooth/qlowenergycontroller_winrt_new.cpp1307
-rw-r--r--src/bluetooth/qlowenergycontroller_winrt_new_p.h151
-rw-r--r--src/bluetooth/qlowenergydescriptor.h1
12 files changed, 1820 insertions, 8 deletions
diff --git a/config.tests/winrt_btle_no_pairing/main.cpp b/config.tests/winrt_btle_no_pairing/main.cpp
new file mode 100644
index 00000000..665e357a
--- /dev/null
+++ b/config.tests/winrt_btle_no_pairing/main.cpp
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtConnectivity module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <wrl.h>
+#include <windows.devices.bluetooth.h>
+
+#if defined(_WIN32) && defined(__INTEL_COMPILER)
+#error "Windows ICC fails to build the WinRT backend (QTBUG-68026)."
+#endif
+
+int main()
+{
+ (void)Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattDeviceService3>().Get();
+ return 0;
+}
diff --git a/config.tests/winrt_btle_no_pairing/winrt.pro b/config.tests/winrt_btle_no_pairing/winrt.pro
new file mode 100644
index 00000000..d60fd242
--- /dev/null
+++ b/config.tests/winrt_btle_no_pairing/winrt.pro
@@ -0,0 +1,3 @@
+SOURCES += main.cpp
+
+!winrt: LIBS += runtimeobject.lib
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index 193ed217..2bcdee12 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -229,10 +229,17 @@ qtConfig(bluez) {
qbluetoothservicediscoveryagent_winrt.cpp \
qbluetoothserviceinfo_winrt.cpp \
qbluetoothsocket_winrt.cpp \
+ qbluetoothutils_winrt.cpp \
qlowenergycontroller_winrt.cpp
PRIVATE_HEADERS += qlowenergycontroller_winrt_p.h \
- qbluetoothsocket_winrt_p.h
+ qbluetoothsocket_winrt_p.h \
+ qbluetoothutils_winrt_p.h
+
+ qtConfig(winrt_btle_no_pairing) {
+ SOURCES += qlowenergycontroller_winrt_new.cpp
+ PRIVATE_HEADERS += qlowenergycontroller_winrt_new_p.h
+ }
lessThan(WINDOWS_SDK_VERSION, 14393) {
DEFINES += QT_WINRT_LIMITED_SERVICEDISCOVERY
diff --git a/src/bluetooth/configure.json b/src/bluetooth/configure.json
index 3153aca6..53923e12 100644
--- a/src/bluetooth/configure.json
+++ b/src/bluetooth/configure.json
@@ -28,6 +28,11 @@
"label": "WinRT Bluetooth API",
"type": "compile",
"test": "winrt_bt"
+ },
+ "winrt_btle_no_pairing": {
+ "label": "WinRT extended bluetooth low energy API",
+ "type": "compile",
+ "test": "winrt_btle_no_pairing"
}
},
@@ -51,6 +56,11 @@
"label": "WinRT Bluetooth API (desktop & UWP)",
"condition": "config.win32 && tests.winrt_bt",
"output": [ "privateFeature" ]
+ },
+ "winrt_btle_no_pairing": {
+ "label": "WinRT advanced bluetooth low energy API (desktop & UWP)",
+ "condition": "config.win32 && features.winrt_bt && tests.winrt_btle_no_pairing",
+ "output": [ "privateFeature" ]
}
},
@@ -75,7 +85,8 @@ Only classic Bluetooth will be available."
"bluez",
"bluez_le",
"linux_crypto_api",
- "winrt_bt"
+ "winrt_bt",
+ "winrt_btle_no_pairing"
]
}
]
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
index ef2a69b1..13c550cc 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
@@ -47,6 +47,8 @@
#endif
#include "qfunctions_winrt.h"
+#include <QtBluetooth/private/qtbluetoothglobal_p.h>
+#include <QtBluetooth/private/qbluetoothutils_winrt_p.h>
#include <QtCore/QLoggingCategory>
#include <QtCore/private/qeventdispatcher_winrt_p.h>
@@ -105,7 +107,10 @@ private:
CheckForPairing,
OmitPairingCheck
};
- HRESULT onBluetoothLEDeviceFound(ComPtr<IBluetoothLEDevice> device, PairingCheck pairingCheck = CheckForPairing);
+ HRESULT onBluetoothLEDeviceFound(ComPtr<IBluetoothLEDevice> device, PairingCheck pairingCheck);
+#if QT_CONFIG(winrt_btle_no_pairing)
+ HRESULT onBluetoothLEDeviceFound(ComPtr<IBluetoothLEDevice> device);
+#endif
public slots:
void finishDiscovery();
@@ -120,6 +125,10 @@ public:
private:
ComPtr<IBluetoothLEAdvertisementWatcher> m_leWatcher;
EventRegistrationToken m_leDeviceAddedToken;
+#if QT_CONFIG(winrt_btle_no_pairing)
+ QMutex m_foundDevicesMutex;
+ QMap<quint64, QVector<QBluetoothUuid>> m_foundLEDevicesMap;
+#endif
QVector<quint64> m_foundLEDevices;
int m_pendingPairedDevices;
@@ -249,15 +258,64 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher()
{
HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementWatcher).Get(), &m_leWatcher);
Q_ASSERT_SUCCEEDED(hr);
+#if QT_CONFIG(winrt_btle_no_pairing)
+ if (supportsNewLEApi()) {
+ hr = m_leWatcher->put_ScanningMode(BluetoothLEScanningMode_Active);
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+#endif // winrt_btle_no_pairing
hr = m_leWatcher->add_Received(Callback<ITypedEventHandler<BluetoothLEAdvertisementWatcher *, BluetoothLEAdvertisementReceivedEventArgs *>>([this](IBluetoothLEAdvertisementWatcher *, IBluetoothLEAdvertisementReceivedEventArgs *args) {
quint64 address;
HRESULT hr;
hr = args->get_BluetoothAddress(&address);
Q_ASSERT_SUCCEEDED(hr);
- if (m_foundLEDevices.contains(address))
- return S_OK;
+#if QT_CONFIG(winrt_btle_no_pairing)
+ if (supportsNewLEApi()) {
+ ComPtr<IBluetoothLEAdvertisement> ad;
+ hr = args->get_Advertisement(&ad);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IVector<GUID>> guids;
+ hr = ad->get_ServiceUuids(&guids);
+ Q_ASSERT_SUCCEEDED(hr);
+ quint32 size;
+ hr = guids->get_Size(&size);
+ Q_ASSERT_SUCCEEDED(hr);
+ QVector<QBluetoothUuid> serviceUuids;
+ for (quint32 i = 0; i < size; ++i) {
+ GUID guid;
+ hr = guids->GetAt(i, &guid);
+ Q_ASSERT_SUCCEEDED(hr);
+ QBluetoothUuid uuid(guid);
+ serviceUuids.append(uuid);
+ }
+ QMutexLocker locker(&m_foundDevicesMutex);
+ // Merge newly found services with list of currently found ones
+ if (m_foundLEDevicesMap.contains(address)) {
+ if (size == 0)
+ return S_OK;
+ QVector<QBluetoothUuid> foundServices = m_foundLEDevicesMap.value(address);
+ bool newServiceAdded = false;
+ for (const QBluetoothUuid &uuid : qAsConst(serviceUuids)) {
+ if (!foundServices.contains(uuid)) {
+ foundServices.append(uuid);
+ newServiceAdded = true;
+ }
+ }
+ if (!newServiceAdded)
+ return S_OK;
+ m_foundLEDevicesMap[address] = foundServices;
+ } else {
+ m_foundLEDevicesMap.insert(address, serviceUuids);
+ }
- m_foundLEDevices.append(address);
+ locker.unlock();
+ } else
+#endif
+ {
+ if (m_foundLEDevices.contains(address))
+ return S_OK;
+ m_foundLEDevices.append(address);
+ }
leBluetoothInfoFromAddressAsync(address);
return S_OK;
}).Get(), &m_leDeviceAddedToken);
@@ -431,7 +489,12 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onPairedBluetoothLEDeviceFoundAsyn
HRESULT hr;
hr = op->GetResults(&device);
Q_ASSERT_SUCCEEDED(hr);
- return onBluetoothLEDeviceFound(device, OmitPairingCheck);
+#if QT_CONFIG(winrt_btle_no_pairing)
+ if (supportsNewLEApi())
+ return onBluetoothLEDeviceFound(device);
+ else
+#endif
+ return onBluetoothLEDeviceFound(device, OmitPairingCheck);
}
HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFoundAsync(IAsyncOperation<BluetoothLEDevice *> *op, AsyncStatus status)
@@ -443,7 +506,12 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFoundAsync(IAsy
HRESULT hr;
hr = op->GetResults(&device);
Q_ASSERT_SUCCEEDED(hr);
- return onBluetoothLEDeviceFound(device, PairingCheck::CheckForPairing);
+#if QT_CONFIG(winrt_btle_no_pairing)
+ if (supportsNewLEApi())
+ return onBluetoothLEDeviceFound(device);
+ else
+#endif
+ return onBluetoothLEDeviceFound(device, PairingCheck::CheckForPairing);
}
HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IBluetoothLEDevice> device, PairingCheck pairingCheck)
@@ -545,6 +613,78 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IB
return S_OK;
}
+#if QT_CONFIG(winrt_btle_no_pairing)
+HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IBluetoothLEDevice> device)
+{
+ if (!device) {
+ qCDebug(QT_BT_WINRT) << "onBluetoothLEDeviceFound: No device given";
+ return S_OK;
+ }
+
+ UINT64 address;
+ HString name;
+ HRESULT hr = device->get_BluetoothAddress(&address);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = device->get_Name(name.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ const QString btName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr));
+
+ ComPtr<IBluetoothLEDevice2> device2;
+ hr = device.As(&device2);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IDeviceInformation> deviceInfo;
+ hr = device2->get_DeviceInformation(&deviceInfo);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (!deviceInfo) {
+ qCDebug(QT_BT_WINRT) << "onBluetoothLEDeviceFound: Could not obtain device information";
+ return S_OK;
+ }
+ ComPtr<IDeviceInformation2> deviceInfo2;
+ hr = deviceInfo.As(&deviceInfo2);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IDeviceInformationPairing> pairing;
+ hr = deviceInfo2->get_Pairing(&pairing);
+ Q_ASSERT_SUCCEEDED(hr);
+ boolean isPaired;
+ hr = pairing->get_IsPaired(&isPaired);
+ Q_ASSERT_SUCCEEDED(hr);
+ QList<QBluetoothUuid> uuids;
+
+ // Use the services obtained from the advertisement data if the device is not paired
+ if (!isPaired) {
+ uuids = m_foundLEDevicesMap.value(address).toList();
+ } else {
+ IVectorView <GenericAttributeProfile::GattDeviceService *> *deviceServices;
+ hr = device->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<GenericAttributeProfile::IGattDeviceService> service;
+ hr = deviceServices->GetAt(i, &service);
+ Q_ASSERT_SUCCEEDED(hr);
+ GUID uuid;
+ hr = service->get_Uuid(&uuid);
+ Q_ASSERT_SUCCEEDED(hr);
+ 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);
+
+ QMetaObject::invokeMethod(this, "deviceFound", Qt::AutoConnection,
+ Q_ARG(QBluetoothDeviceInfo, info));
+ return S_OK;
+}
+#endif // QT_CONFIG(winrt_btle_no_pairing)
+
QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(
const QBluetoothAddress &deviceAdapter,
QBluetoothDeviceDiscoveryAgent *parent)
diff --git a/src/bluetooth/qbluetoothutils_winrt.cpp b/src/bluetooth/qbluetoothutils_winrt.cpp
new file mode 100644
index 00000000..1d44221b
--- /dev/null
+++ b/src/bluetooth/qbluetoothutils_winrt.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "qbluetoothutils_winrt_p.h"
+#include <QtBluetooth/private/qtbluetoothglobal_p.h>
+
+#include <QtCore/qfunctions_winrt.h>
+
+#include <wrl.h>
+#include <windows.foundation.metadata.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation::Metadata;
+
+QT_BEGIN_NAMESPACE
+
+bool supportsNewLEApi()
+{
+ static bool initialized = false;
+ static boolean apiPresent = false;
+ if (initialized)
+ return apiPresent;
+
+ initialized = true;
+#if !QT_CONFIG(winrt_btle_no_pairing)
+ return apiPresent;
+#endif
+
+ ComPtr<IApiInformationStatics> apiInformationStatics;
+ HRESULT hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Foundation_Metadata_ApiInformation).Get(),
+ IID_PPV_ARGS(&apiInformationStatics));
+ if (FAILED(hr))
+ return apiPresent;
+
+ const HStringReference valueRef(L"Windows.Foundation.UniversalApiContract");
+ hr = apiInformationStatics->IsApiContractPresentByMajor(
+ valueRef.Get(), 4, &apiPresent);
+ apiPresent = SUCCEEDED(hr) && apiPresent;
+ return apiPresent;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothutils_winrt_p.h b/src/bluetooth/qbluetoothutils_winrt_p.h
new file mode 100644
index 00000000..c272bae1
--- /dev/null
+++ b/src/bluetooth/qbluetoothutils_winrt_p.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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$
+**
+****************************************************************************/
+
+#ifndef QBLUETOOTHUTILS_WINRT_P_H
+#define QBLUETOOTHUTILS_WINRT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QtGlobal>
+
+QT_BEGIN_NAMESPACE
+
+bool supportsNewLEApi();
+
+QT_END_NAMESPACE
+
+#endif // QBLUETOOTHSOCKET_WINRT_P_H
diff --git a/src/bluetooth/qlowenergycharacteristic.h b/src/bluetooth/qlowenergycharacteristic.h
index cb747e32..9b27d621 100644
--- a/src/bluetooth/qlowenergycharacteristic.h
+++ b/src/bluetooth/qlowenergycharacteristic.h
@@ -103,6 +103,7 @@ protected:
friend class QLowEnergyControllerPrivateCommon;
friend class QLowEnergyControllerPrivateOSX;
friend class QLowEnergyControllerPrivateWinRT;
+ friend class QLowEnergyControllerPrivateWinRTNew;
QLowEnergyCharacteristicPrivate *data = nullptr;
QLowEnergyCharacteristic(QSharedPointer<QLowEnergyServicePrivate> p,
QLowEnergyHandle handle);
diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp
index 6b4c17d6..8fc044fb 100644
--- a/src/bluetooth/qlowenergycontroller.cpp
+++ b/src/bluetooth/qlowenergycontroller.cpp
@@ -55,7 +55,11 @@
#elif defined(QT_ANDROID_BLUETOOTH)
#include "qlowenergycontroller_android_p.h"
#elif defined(QT_WINRT_BLUETOOTH)
+#include "qtbluetoothglobal_p.h"
#include "qlowenergycontroller_winrt_p.h"
+#if QT_CONFIG(winrt_btle_no_pairing)
+#include "qlowenergycontroller_winrt_new_p.h"
+#endif
#else
#include "qlowenergycontroller_p.h"
#endif
@@ -65,6 +69,7 @@
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_BT)
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
/*!
\class QLowEnergyController
@@ -310,7 +315,12 @@ static QLowEnergyControllerPrivate *privateController(QLowEnergyController::Role
return new QLowEnergyControllerPrivateAndroid();
#elif defined(QT_WINRT_BLUETOOTH)
Q_UNUSED(role);
+#if QT_CONFIG(winrt_btle_no_pairing)
+ return createWinRTLowEnergyController();
+#else
+ qCDebug(QT_BT_WINRT) << "Using pre 15063 low energy controller";
return new QLowEnergyControllerPrivateWinRT();
+#endif
#else
Q_UNUSED(role);
return new QLowEnergyControllerPrivateCommon();
diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp
new file mode 100644
index 00000000..7d7d1145
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp
@@ -0,0 +1,1307 @@
+/****************************************************************************
+**
+** 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_winrt_new_p.h"
+#include "qlowenergycontroller_winrt_p.h"
+
+#include <QtBluetooth/QLowEnergyCharacteristicData>
+#include <QtBluetooth/QLowEnergyDescriptorData>
+#include <QtBluetooth/private/qbluetoothutils_winrt_p.h>
+
+#ifdef CLASSIC_APP_BUILD
+#define Q_OS_WINRT
+#endif
+#include <QtCore/qfunctions_winrt.h>
+#include <QtCore/QtEndian>
+#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.foundation.metadata.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::Foundation::Metadata;
+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 ITypedEventHandler<GattCharacteristic *, GattValueChangedEventArgs *> ValueChangedHandler;
+typedef GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConfigDescriptorResult;
+typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharConfigDescriptorResult;
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
+
+QLowEnergyControllerPrivate *createWinRTLowEnergyController()
+{
+ if (supportsNewLEApi()) {
+ qCDebug(QT_BT_WINRT) << "Using new low energy controller";
+ return new QLowEnergyControllerPrivateWinRTNew();
+ }
+
+ qCDebug(QT_BT_WINRT) << "Using pre 15063 low energy controller";
+ return new QLowEnergyControllerPrivateWinRT();
+}
+
+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, int(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 QWinRTLowEnergyServiceHandlerNew : public QObject
+{
+ Q_OBJECT
+public:
+ QWinRTLowEnergyServiceHandlerNew(const QBluetoothUuid &service,
+ const ComPtr<IGattDeviceService2> &deviceService)
+ : mService(service)
+ , mDeviceService(deviceService)
+ {
+ qCDebug(QT_BT_WINRT) << __FUNCTION__;
+ }
+
+ ~QWinRTLowEnergyServiceHandlerNew()
+ {
+ }
+
+public slots:
+ void obtainCharList()
+ {
+ QVector<QBluetoothUuid> indicateChars;
+ 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, indicateChars, startHandle, endHandle);
+ QThread::currentThread()->quit();
+ return;
+ }
+
+ uint characteristicsCount;
+ hr = characteristics->get_Size(&characteristicsCount);
+
+ // If there are no characteristics, we assume that the device is not paired (and not
+ // discovered by Windows) and we use new API (GetCharacteristicsAsync) to discover them
+ // without pairing.
+ if (characteristicsCount == 0) {
+ ComPtr<IGattDeviceService3> deviceService3;
+ hr = mDeviceService.As(&deviceService3);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IAsyncOperation<GattCharacteristicsResult*>> asyncResult;
+ deviceService3->GetCharacteristicsAsync(&asyncResult);
+ hr = asyncResult->put_Completed(
+ Callback<IAsyncOperationCompletedHandler<GattCharacteristicsResult*>>(
+ [this](IAsyncOperation<GattCharacteristicsResult*> *, AsyncStatus status) {
+ if (status != AsyncStatus::Completed) {
+ qCDebug(QT_BT_WINRT) << "Could not obtain characteristics";
+ return S_OK;
+ }
+ // TODO We should check if we found any characteristics. It makes no sense but
+ // there is a possibility that device doesn't state any characteristics under a service.
+ // So, for sanity, we should not continue endless loop here.
+ obtainCharList();
+ return S_OK;
+ }).Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ return;
+ }
+
+ Q_ASSERT_SUCCEEDED(hr);
+ mCharacteristicsCountToBeDiscovered = characteristicsCount;
+ for (uint i = 0; i < characteristicsCount; ++i) {
+ ComPtr<IGattCharacteristic> characteristic;
+ hr = characteristics->GetAt(i, &characteristic);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IGattCharacteristic3> characteristic3;
+ hr = characteristic.As(&characteristic3);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ // For some strange reason, Windows doesn't discover descriptors of characteristics (if not paired).
+ // Qt API assumes that all characteristics and their descriptors are discovered in one go.
+ // So we start 'GetDescriptorsAsync' for each discovered characteristic and finish only
+ // when GetDescriptorsAsync for all characteristics return.
+ ComPtr<IAsyncOperation<GattDescriptorsResult*>> descAsyncResult;
+ hr = characteristic3->GetDescriptorsAsync(&descAsyncResult);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = descAsyncResult->put_Completed(
+ Callback<IAsyncOperationCompletedHandler<GattDescriptorsResult*>>(
+ [this, characteristic](IAsyncOperation<GattDescriptorsResult*> *, AsyncStatus status) {
+ if (status != AsyncStatus::Completed) {
+ qCDebug(QT_BT_WINRT) << "Could not obtain descriptors";
+ return S_OK;
+ }
+ quint16 handle;
+
+ HRESULT hr = characteristic->get_AttributeHandle(&handle);
+ Q_ASSERT_SUCCEEDED(hr);
+ QLowEnergyServicePrivate::CharData charData;
+ charData.valueHandle = handle + 1;
+ if (mStartHandle == 0 || mStartHandle > handle)
+ mStartHandle = handle;
+ if (mEndHandle == 0 || mEndHandle < handle)
+ mEndHandle = 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);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IGattReadResult> readResult;
+ hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ if (readResult)
+ charData.value = byteArrayFromGattResult(readResult);
+ }
+
+ QVector<QBluetoothUuid> indicateChars;
+ ComPtr<IVectorView<GattDescriptor *>> descriptors;
+
+ ComPtr<IGattCharacteristic2> characteristic2;
+ hr = characteristic.As(&characteristic2);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ 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());
+ indicateChars << charData.uuid;
+ } 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);
+ mCharacteristicsCountToBeDiscovered--;
+ if (mCharacteristicsCountToBeDiscovered == 0) {
+ emit charListObtained(mService, mCharacteristicList, indicateChars,
+ mStartHandle, mEndHandle);
+ QThread::currentThread()->quit();
+ }
+ return S_OK;
+ }).Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+ }
+
+public:
+ QBluetoothUuid mService;
+ ComPtr<IGattDeviceService2> mDeviceService;
+ QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> mCharacteristicList;
+ uint mCharacteristicsCountToBeDiscovered;
+ quint16 mStartHandle = 0;
+ quint16 mEndHandle = 0;
+
+signals:
+ void charListObtained(const QBluetoothUuid &service, QHash<QLowEnergyHandle,
+ QLowEnergyServicePrivate::CharData> charList,
+ QVector<QBluetoothUuid> indicateChars,
+ QLowEnergyHandle startHandle, QLowEnergyHandle endHandle);
+};
+
+QLowEnergyControllerPrivateWinRTNew::QLowEnergyControllerPrivateWinRTNew()
+ : QLowEnergyControllerPrivate()
+{
+ registerQLowEnergyControllerMetaType();
+}
+
+QLowEnergyControllerPrivateWinRTNew::~QLowEnergyControllerPrivateWinRTNew()
+{
+ if (mDevice && mStatusChangedToken.value)
+ mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
+
+ qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens";
+ for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens))
+ entry.characteristic->remove_ValueChanged(entry.token);
+}
+
+void QLowEnergyControllerPrivateWinRTNew::init()
+{
+}
+
+void QLowEnergyControllerPrivateWinRTNew::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;
+ HRESULT hr;
+ hr = dev->get_ConnectionStatus(&status);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (state == QLowEnergyController::ConnectingState
+ && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
+ setState(QLowEnergyController::ConnectedState);
+ emit q->connected();
+ } else if (state == QLowEnergyController::ConnectedState
+ && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) {
+ setError(QLowEnergyController::RemoteHostClosedError);
+ setState(QLowEnergyController::UnconnectedState);
+ emit q->disconnected();
+ }
+ return S_OK;
+ }).Get(), &mStatusChangedToken);
+ Q_ASSERT_SUCCEEDED(hr);
+ return S_OK;
+ });
+ Q_ASSERT_SUCCEEDED(hr);
+
+ 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 doesn't provide any explicit connect/reconnect. We need to 'start using' the device
+ // and windows will initiate connection as a cause of that.
+ if (serviceCount == 0) {
+ // If we don't have any services discovered yet (for devices not paired), the simplest
+ // way to initiate connect is to start discovering services. It's not exactly how Qt API
+ // expects it to be but IMHO doesn't do any harm either. Services will already be discovered
+ // when coonnection state changes to 'connected'.
+ ComPtr<IBluetoothLEDevice3> device3;
+ hr = mDevice.As(&device3);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *>> asyncResult;
+ hr = device3->GetGattServicesAsync(&asyncResult);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = asyncResult->put_Completed(
+ Callback<IAsyncOperationCompletedHandler<GenericAttributeProfile::GattDeviceServicesResult *>>(
+ [this, q](IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *> *, AsyncStatus status) {
+ if (status != AsyncStatus::Completed) {
+ qCDebug(QT_BT_WINRT) << "Could not obtain services";
+ return S_OK;
+ }
+ setState(QLowEnergyController::ConnectedState);
+ emit q->connected();
+ return S_OK;
+ }).Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ } else {
+ // 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 if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Connecting to device failed: "
+ << qt_error_string(hr);
+ setError(QLowEnergyController::ConnectionError);
+ setState(QLowEnergyController::UnconnectedState);
+ return;
+ }
+ 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());
+ if (hr == E_INVALIDARG) {
+ // E_INVALIDARG happens when user tries to connect to a device that was paired
+ // before but is not available.
+ qCDebug(QT_BT_WINRT) << "Could not obtain characteristic read result that triggers"
+ "device connection. Is the device reachable?";
+ setError(QLowEnergyController::ConnectionError);
+ setState(QLowEnergyController::UnconnectedState);
+ return;
+ }
+ 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 QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice()
+{
+ qCDebug(QT_BT_WINRT) << __FUNCTION__;
+ Q_Q(QLowEnergyController);
+ setState(QLowEnergyController::UnconnectedState);
+ emit q->disconnected();
+ if (mDevice && mStatusChangedToken.value) {
+ mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
+ mStatusChangedToken.value = 0;
+ }
+}
+
+ComPtr<IGattDeviceService> QLowEnergyControllerPrivateWinRTNew::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> QLowEnergyControllerPrivateWinRTNew::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 QLowEnergyControllerPrivateWinRTNew::registerForValueChanges(const QBluetoothUuid &serviceUuid,
+ const QBluetoothUuid &charUuid)
+{
+ qCDebug(QT_BT_WINRT) << "Registering characteristic" << charUuid << "in service"
+ << serviceUuid << "for value changes";
+ for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) {
+ GUID guuid;
+ HRESULT hr;
+ hr = entry.characteristic->get_Uuid(&guuid);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (QBluetoothUuid(guuid) == charUuid)
+ return;
+ }
+ ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(serviceUuid, charUuid);
+
+ EventRegistrationToken token;
+ HRESULT hr;
+ hr = characteristic->add_ValueChanged(
+ Callback<ValueChangedHandler>(
+ [this](IGattCharacteristic *characteristic, IGattValueChangedEventArgs *args) {
+ HRESULT hr;
+ quint16 handle;
+ hr = characteristic->get_AttributeHandle(&handle);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IBuffer> buffer;
+ hr = args->get_CharacteristicValue(&buffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ characteristicChanged(handle, byteArrayFromBuffer(buffer));
+ return S_OK;
+ }).Get(), &token);
+ Q_ASSERT_SUCCEEDED(hr);
+ mValueChangedTokens.append(ValueChangedEntry(characteristic, token));
+ qCDebug(QT_BT_WINRT) << "Characteristic" << charUuid << "in service"
+ << serviceUuid << "registered for value changes";
+}
+
+void QLowEnergyControllerPrivateWinRTNew::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);
+ // Some devices return ERROR_ACCESS_DISABLED_BY_POLICY
+ if (FAILED(hr))
+ return;
+
+ uint count;
+ hr = includedServices->get_Size(&count);
+ Q_ASSERT_SUCCEEDED(hr);
+ 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 QLowEnergyControllerPrivateWinRTNew::discoverServices()
+{
+ Q_Q(QLowEnergyController);
+
+ qCDebug(QT_BT_WINRT) << "Service discovery initiated";
+
+ ComPtr<IBluetoothLEDevice3> device3;
+ HRESULT hr = mDevice.As(&device3);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *>> asyncResult;
+ hr = device3->GetGattServicesAsync(&asyncResult);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = QEventDispatcherWinRT::runOnXamlThread( [asyncResult, q, this] () {
+ HRESULT hr = asyncResult->put_Completed(
+ Callback<IAsyncOperationCompletedHandler<GenericAttributeProfile::GattDeviceServicesResult *>>(
+ [this, q](IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *> *, AsyncStatus status) {
+ if (status != AsyncStatus::Completed) {
+ qCDebug(QT_BT_WINRT) << "Could not obtain services";
+ return S_OK;
+ }
+ 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();
+
+ return S_OK;
+ }).Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ return hr;
+ });
+ Q_ASSERT_SUCCEEDED(hr);
+}
+
+void QLowEnergyControllerPrivateWinRTNew::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);
+ if (FAILED(hr)) { // ERROR_ACCESS_DISABLED_BY_POLICY
+ qCDebug(QT_BT_WINRT) << "Could not obtain included services list for" << service;
+ pointer->setError(QLowEnergyService::UnknownError);
+ pointer->setState(QLowEnergyService::InvalidService);
+ return;
+ }
+ 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;
+ }
+
+ QWinRTLowEnergyServiceHandlerNew *worker
+ = new QWinRTLowEnergyServiceHandlerNew(service, deviceService2);
+ QThread *thread = new QThread;
+ worker->moveToThread(thread);
+ connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandlerNew::obtainCharList);
+ connect(thread, &QThread::finished, thread, &QObject::deleteLater);
+ connect(thread, &QThread::finished, worker, &QObject::deleteLater);
+ connect(worker, &QWinRTLowEnergyServiceHandlerNew::charListObtained,
+ [this, thread](const QBluetoothUuid &service, QHash<QLowEnergyHandle,
+ QLowEnergyServicePrivate::CharData> charList, QVector<QBluetoothUuid> indicateChars,
+ 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;
+
+ HRESULT hr;
+ hr = QEventDispatcherWinRT::runOnXamlThread([indicateChars, service, this]() {
+ for (const QBluetoothUuid &indicateChar : qAsConst(indicateChars))
+ registerForValueChanges(service, indicateChar);
+ return S_OK;
+ });
+ Q_ASSERT_SUCCEEDED(hr);
+
+ pointer->setState(QLowEnergyService::ServiceDiscovered);
+ thread->exit(0);
+ });
+ thread->start();
+}
+
+void QLowEnergyControllerPrivateWinRTNew::startAdvertising(
+ const QLowEnergyAdvertisingParameters &,
+ const QLowEnergyAdvertisingData &,
+ const QLowEnergyAdvertisingData &)
+{
+ setError(QLowEnergyController::AdvertisingError);
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWinRTNew::stopAdvertising()
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWinRTNew::requestConnectionUpdate(const QLowEnergyConnectionParameters &)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWinRTNew::readCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle)
+{
+ qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle;
+ Q_ASSERT(!service.isNull());
+ if (role == QLowEnergyController::PeripheralRole) {
+ service->setError(QLowEnergyService::CharacteristicReadError);
+ Q_UNIMPLEMENTED();
+ return;
+ }
+
+ 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 QLowEnergyControllerPrivateWinRTNew::readDescriptor(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descHandle)
+{
+ qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle;
+ Q_ASSERT(!service.isNull());
+ if (role == QLowEnergyController::PeripheralRole) {
+ service->setError(QLowEnergyService::DescriptorReadError);
+ Q_UNIMPLEMENTED();
+ return;
+ }
+
+ 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());
+ Q_ASSERT_SUCCEEDED(hr);
+ return S_OK;
+ }
+ });
+ Q_ASSERT_SUCCEEDED(hr);
+}
+
+void QLowEnergyControllerPrivateWinRTNew::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 (role == QLowEnergyController::PeripheralRole) {
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ Q_UNIMPLEMENTED();
+ return;
+ }
+ 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 quint32 length = quint32(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, writeWithResponse, 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);
+ if (writeWithResponse)
+ 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 QLowEnergyControllerPrivateWinRTNew::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 (role == QLowEnergyController::PeripheralRole) {
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ Q_UNIMPLEMENTED();
+ return;
+ }
+
+ 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 {
+ qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle
+ << "write operation failed: Invalid value";
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return S_OK;
+ }
+ 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 quint32 length = quint32(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 QLowEnergyControllerPrivateWinRTNew::addToGenericAttributeList(const QLowEnergyServiceData &,
+ QLowEnergyHandle)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWinRTNew::characteristicChanged(
+ quint16 charHandle, const QByteArray &data)
+{
+ QSharedPointer<QLowEnergyServicePrivate> service =
+ serviceForHandle(charHandle);
+ if (service.isNull())
+ return;
+
+ qCDebug(QT_BT_WINRT) << "Characteristic change notification" << service->uuid
+ << charHandle << data.toHex();
+
+ QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
+ if (!characteristic.isValid()) {
+ qCWarning(QT_BT_WINRT) << "characteristicChanged: Cannot find characteristic";
+ return;
+ }
+
+ // only update cache when property is readable. Otherwise it remains
+ // empty.
+ if (characteristic.properties() & QLowEnergyCharacteristic::Read)
+ updateValueOfCharacteristic(characteristic.attributeHandle(),
+ data, false);
+ emit service->characteristicChanged(characteristic, data);
+}
+
+QT_END_NAMESPACE
+
+#include "qlowenergycontroller_winrt_new.moc"
diff --git a/src/bluetooth/qlowenergycontroller_winrt_new_p.h b/src/bluetooth/qlowenergycontroller_winrt_new_p.h
new file mode 100644
index 00000000..716d2d07
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_winrt_new_p.h
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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$
+**
+****************************************************************************/
+
+#ifndef QLOWENERGYCONTROLLERPRIVATEWINRT_NEW_P_H
+#define QLOWENERGYCONTROLLERPRIVATEWINRT_NEW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include <QtCore/QQueue>
+#include <QtCore/QVector>
+#include <QtBluetooth/qbluetooth.h>
+#include <QtBluetooth/qlowenergycharacteristic.h>
+#include <QtBluetooth/qlowenergyservicedata.h>
+#include "qlowenergycontroller.h"
+#include "qlowenergycontrollerbase_p.h"
+
+#include <wrl.h>
+#include <windows.devices.bluetooth.h>
+
+#include <functional>
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyServiceData;
+class QTimer;
+class QWinRTLowEnergyServiceHandler;
+
+extern void registerQLowEnergyControllerMetaType();
+
+QLowEnergyControllerPrivate *createWinRTLowEnergyController();
+
+class QLowEnergyControllerPrivateWinRTNew final : public QLowEnergyControllerPrivate
+{
+ Q_OBJECT
+public:
+ QLowEnergyControllerPrivateWinRTNew();
+ ~QLowEnergyControllerPrivateWinRTNew() override;
+
+ void init() override;
+
+ void connectToDevice() override;
+ void disconnectFromDevice() override;
+
+ void discoverServices() override;
+ void discoverServiceDetails(const QBluetoothUuid &service) override;
+
+ void startAdvertising(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData) override;
+ void stopAdvertising() override;
+
+ void requestConnectionUpdate(const QLowEnergyConnectionParameters &params) override;
+
+ // read data
+ void readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle) override;
+ void readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle) override;
+
+ // write data
+ void writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QByteArray &newValue, QLowEnergyService::WriteMode mode) override;
+ void writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue) override;
+
+ void addToGenericAttributeList(const QLowEnergyServiceData &service,
+ QLowEnergyHandle startHandle) override;
+
+private slots:
+ void characteristicChanged(quint16 charHandle, const QByteArray &data);
+
+private:
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice> mDevice;
+ EventRegistrationToken mStatusChangedToken;
+ struct ValueChangedEntry {
+ ValueChangedEntry() {}
+ ValueChangedEntry(Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattCharacteristic> c,
+ EventRegistrationToken t)
+ : characteristic(c)
+ , token(t)
+ {
+ }
+
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattCharacteristic> characteristic;
+ EventRegistrationToken token;
+ };
+ QVector<ValueChangedEntry> mValueChangedTokens;
+
+ 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 registerForValueChanges(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid);
+
+ void obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer,
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattDeviceService> nativeService);
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QLOWENERGYCONTROLLERPRIVATEWINRT_NEW_P_H
diff --git a/src/bluetooth/qlowenergydescriptor.h b/src/bluetooth/qlowenergydescriptor.h
index 9dd0cc20..62ca5fd3 100644
--- a/src/bluetooth/qlowenergydescriptor.h
+++ b/src/bluetooth/qlowenergydescriptor.h
@@ -85,6 +85,7 @@ protected:
friend class QLowEnergyControllerPrivateCommon;
friend class QLowEnergyControllerPrivateOSX;
friend class QLowEnergyControllerPrivateWinRT;
+ friend class QLowEnergyControllerPrivateWinRTNew;
QLowEnergyDescriptorPrivate *data = nullptr;
QLowEnergyDescriptor(QSharedPointer<QLowEnergyServicePrivate> p,