summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Wolff <oliver.wolff@qt.io>2019-08-20 11:17:20 +0200
committerOliver Wolff <oliver.wolff@qt.io>2019-08-20 17:36:40 +0200
commita309d04fa1edb3fa320d420758b3967616ad792a (patch)
tree3199ada0ea620ce5ef283ee68ea4643b904af649
parentccc1544f5ff929f466df8c790a4e7595e177a720 (diff)
parent603074d4895a9802478a7ec1e9173b70e0723bc5 (diff)
Merge "Merge branch 'wip/win' into dev"
-rw-r--r--src/bluetooth/bluetooth.pro18
-rw-r--r--src/bluetooth/configure.json16
-rw-r--r--src/bluetooth/doc/src/bluetooth-index.qdoc19
-rw-r--r--src/bluetooth/doc/src/bluetooth-overview.qdoc5
-rw-r--r--src/bluetooth/qbluetooth.cpp2
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent.cpp4
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent.h1
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_p.h46
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp557
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_p.h24
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_win.cpp260
-rw-r--r--src/bluetooth/qbluetoothserver.cpp3
-rw-r--r--src/bluetooth/qbluetoothserver_android.cpp5
-rw-r--r--src/bluetooth/qbluetoothserver_bluez.cpp5
-rw-r--r--src/bluetooth/qbluetoothserver_osx.mm5
-rw-r--r--src/bluetooth/qbluetoothserver_p.cpp6
-rw-r--r--src/bluetooth/qbluetoothserver_p.h10
-rw-r--r--src/bluetooth/qbluetoothserver_win.cpp239
-rw-r--r--src/bluetooth/qbluetoothserver_winrt.cpp6
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent.h1
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_p.h31
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_win.cpp437
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_p.h13
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_win.cpp120
-rw-r--r--src/bluetooth/qbluetoothsocket.cpp4
-rw-r--r--src/bluetooth/qbluetoothsocket.h1
-rw-r--r--src/bluetooth/qbluetoothsocket_win.cpp597
-rw-r--r--src/bluetooth/qbluetoothsocket_win_p.h118
-rw-r--r--src/bluetooth/qbluetoothtransfermanager.cpp4
-rw-r--r--src/bluetooth/qbluetoothutils_win.cpp2
-rw-r--r--src/bluetooth/qlowenergycharacteristic.h1
-rw-r--r--src/bluetooth/qlowenergycontroller.cpp5
-rw-r--r--src/bluetooth/qlowenergycontroller_win.cpp1356
-rw-r--r--src/bluetooth/qlowenergycontroller_win_p.h185
-rw-r--r--src/bluetooth/qlowenergydescriptor.h1
-rw-r--r--src/bluetooth/qlowenergyserviceprivate_p.h9
-rw-r--r--src/bluetooth/windows/qwinlowenergybluetooth_p.h231
-rw-r--r--src/bluetooth/windows/windows.pri2
-rw-r--r--tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp13
-rw-r--r--tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp2
-rw-r--r--tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp79
41 files changed, 4379 insertions, 64 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index 4e7088aa..e1e4d7a2 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -225,6 +225,24 @@ qtConfig(bluez) {
DEFINES += QT_WINRT_LIMITED_SERVICEDISCOVERY
DEFINES += QT_UCRTVERSION=$$WINDOWS_SDK_VERSION
}
+} else:win32 {
+ QT_PRIVATE = concurrent
+ DEFINES += QT_WIN_BLUETOOTH
+ LIBS += -lbthprops -lws2_32 -lsetupapi
+
+ include(windows/windows.pri)
+
+ SOURCES += \
+ qbluetoothdevicediscoveryagent_win.cpp \
+ qbluetoothlocaldevice_win.cpp \
+ qbluetoothserviceinfo_win.cpp \
+ qbluetoothservicediscoveryagent_win.cpp \
+ qbluetoothsocket_win.cpp \
+ qbluetoothserver_win.cpp \
+ qlowenergycontroller_win.cpp
+
+ PRIVATE_HEADERS += qlowenergycontroller_win_p.h \
+ qbluetoothsocket_win_p.h
} 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/configure.json b/src/bluetooth/configure.json
index 53923e12..3bd95903 100644
--- a/src/bluetooth/configure.json
+++ b/src/bluetooth/configure.json
@@ -2,6 +2,12 @@
"module": "bluetooth",
"testDir": "../../config.tests",
+ "commandline": {
+ "options": {
+ "native-win32-bluetooth": "boolean"
+ }
+ },
+
"libraries": {
"bluez": {
"label": "BlueZ",
@@ -52,9 +58,16 @@
"condition": "features.bluez_le && tests.linux_crypto_api",
"output": [ "privateFeature" ]
},
+ "native-win32-bluetooth": {
+ "label": "Native Win32 Bluetooth",
+ "purpose": "Uses the native Win32 Bluetooth backend.",
+ "section": "Qt Bluetooth",
+ "autoDetect": false,
+ "output": [ "publicFeature", "feature" ]
+ },
"winrt_bt": {
"label": "WinRT Bluetooth API (desktop & UWP)",
- "condition": "config.win32 && tests.winrt_bt",
+ "condition": "config.win32 && !features.native-win32-bluetooth && tests.winrt_bt",
"output": [ "privateFeature" ]
},
"winrt_btle_no_pairing": {
@@ -85,6 +98,7 @@ Only classic Bluetooth will be available."
"bluez",
"bluez_le",
"linux_crypto_api",
+ "native-win32-bluetooth",
"winrt_bt",
"winrt_btle_no_pairing"
]
diff --git a/src/bluetooth/doc/src/bluetooth-index.qdoc b/src/bluetooth/doc/src/bluetooth-index.qdoc
index a0e2a048..7b54df85 100644
--- a/src/bluetooth/doc/src/bluetooth-index.qdoc
+++ b/src/bluetooth/doc/src/bluetooth-index.qdoc
@@ -51,7 +51,7 @@ Currently, the API is supported on the following platforms:
\li x
\li x
\li x
- \li
+ \li x
\row
\li Bluetooth LE Central
\li x
@@ -59,7 +59,7 @@ Currently, the API is supported on the following platforms:
\li x
\li x
\li x
- \li
+ \li x
\row
\li Bluetooth LE Peripheral
\li x
@@ -78,10 +78,12 @@ Currently, the API is supported on the following platforms:
\li
\endtable
-Despite there not being a Win32 port yet, the WinRT backend is automatically used
-if the win32 target platform supports the required WinRT APIs. Minimal requirement is Windows 10 version 1507
-with slightly improved service discovery since Windows 10 version 1607. Therefore Windows 7 and 8.x
-targets are excluded.
+Qt 5.14 adds a native Win32 port supporting Classic Bluetooth on Windows 7 or newer,
+and Bluetooth LE on Windows 8 or newer. It must be enabled at build time by configuration
+option -native-win32-bluetooth. The WinRT backend is used by default if this option is not
+set and the Win32 target platform supports the required WinRT APIs (minimal requirement is
+Windows 10 version 1507, with slightly improved service discovery since Windows 10 version
+1607).
\section1 Overview
@@ -115,7 +117,7 @@ import statement in your \c .qml file:
\section2 Building Qt Bluetooth
Despite the fact that the module can be built for all Qt platforms,
-the module is not ported to all of them. Not supported platforms such as Windows desktop
+the module is not ported to all of them. Not supported platforms
employ a fake or dummy backend which is automatically selected when the
platform is not supported. The dummy backend reports appropriate error messages
and values which allow the Qt Bluetooth developer to detect at runtime that the
@@ -167,6 +169,9 @@ The \l QtBluetooth module exports the following
\row
\li qt.bluetooth.winrt
\li Enables logging of the \l {Qt for WinRT} {WinRT} implementation
+\row
+ \li qt.bluetooth.windows
+ \li Enables logging of the \l {Qt for Windows} {Win32} implementation
\endtable
Logging categories can be used to enable additional warning and debug output
diff --git a/src/bluetooth/doc/src/bluetooth-overview.qdoc b/src/bluetooth/doc/src/bluetooth-overview.qdoc
index 8138e937..902a13b7 100644
--- a/src/bluetooth/doc/src/bluetooth-overview.qdoc
+++ b/src/bluetooth/doc/src/bluetooth-overview.qdoc
@@ -49,6 +49,11 @@
Note that the Object Push Profile is not supported on Android.
+ Note that the Received Signal Strength Indicator (RSSI), as well as
+ the Manufacturer Specific Data advertised by Bluetooth LE devices are
+ not supported by the Win32 backend. Also, it is only possible to find
+ devices that have been previously paired through Windows Settings.
+
The following sections describe how to use the Qt Bluetooth C++ API classes
for the above use cases.
diff --git a/src/bluetooth/qbluetooth.cpp b/src/bluetooth/qbluetooth.cpp
index b14c79cd..1e8ce0b8 100644
--- a/src/bluetooth/qbluetooth.cpp
+++ b/src/bluetooth/qbluetooth.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -101,6 +102,7 @@ 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_WINDOWS, "qt.bluetooth.windows")
Q_LOGGING_CATEGORY(QT_BT_WINRT, "qt.bluetooth.winrt")
Q_LOGGING_CATEGORY(QT_BT_WINRT_SERVICE_THREAD, "qt.bluetooth.winrt.service.thread")
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp
index 20b992ef..f38f8994 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp
@@ -78,6 +78,10 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT)
\note Due to API limitations it is only possible to find devices that have been paired using
Windows' settings on Windows.
+
+ \note The Win32 backend currently does not support the Received Signal Strength
+ Indicator (RSSI), as well as the Manufacturer Specific Data, or other data
+ updates advertised by Bluetooth LE devices after discovery.
*/
/*!
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.h b/src/bluetooth/qbluetoothdevicediscoveryagent.h
index 59a8b456..e566d895 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent.h
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
index a59c6a94..be3a8863 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -83,7 +84,20 @@ class QDBusVariant;
QT_END_NAMESPACE
#endif
-#ifdef QT_WINRT_BLUETOOTH
+#ifdef QT_WIN_BLUETOOTH
+QT_BEGIN_NAMESPACE
+class QThread;
+
+class ThreadWorkerDeviceDiscovery : public QObject
+{
+ Q_OBJECT
+signals:
+ void discoveryCompleted(const QVariant res);
+};
+
+QT_END_NAMESPACE
+
+#elif defined(QT_WINRT_BLUETOOTH)
#include <QtCore/QPointer>
#include <QtCore/QTimer>
@@ -98,7 +112,8 @@ class QWinRTBluetoothDeviceDiscoveryWorker;
#endif
class QBluetoothDeviceDiscoveryAgentPrivate
-#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(Q_OS_DARWIN)
+#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(QT_WIN_BLUETOOTH) \
+ || defined(Q_OS_DARWIN)
: public QObject
#if defined(Q_OS_MACOS)
, public DarwinBluetooth::DeviceInquiryDelegate
@@ -175,6 +190,33 @@ private:
QTimer extendedDiscoveryTimer;
#endif
+#ifdef QT_WIN_BLUETOOTH
+public:
+ static QString discoveredLeDeviceSystemPath(const QBluetoothAddress &deviceAddress);
+
+private:
+ void cancelDiscovery();
+ void restartDiscovery();
+ void finishDiscovery(QBluetoothDeviceDiscoveryAgent::Error errorCode, const QString &errorText);
+
+ void startLeDevicesDiscovery();
+ void completeLeDevicesDiscovery(const QVariant &res);
+ void startClassicDevicesDiscovery(Qt::HANDLE hSearch = nullptr);
+ void completeClassicDevicesDiscovery(const QVariant &res);
+
+ void processDiscoveredDevice(const QBluetoothDeviceInfo &foundDevice);
+
+ QBluetoothAddress adapterAddress;
+ bool pendingCancel;
+ bool pendingStart;
+ bool active;
+
+ QThread *threadLE = nullptr;
+ QThread *threadClassic = nullptr;
+ ThreadWorkerDeviceDiscovery *threadWorkerLE = nullptr;
+ ThreadWorkerDeviceDiscovery *threadWorkerClassic = nullptr;
+#endif
+
#ifdef QT_WINRT_BLUETOOTH
private slots:
void registerDevice(const QBluetoothDeviceInfo &info);
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp
new file mode 100644
index 00000000..159428d4
--- /dev/null
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp
@@ -0,0 +1,557 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 "qbluetoothdevicediscoveryagent.h"
+#include "qbluetoothuuid.h"
+#include "qbluetoothdevicediscoveryagent_p.h"
+#include "qbluetoothlocaldevice_p.h"
+
+#include <QtCore/qmutex.h>
+#include <QtCore/QThread>
+#include <QtCore/QLoggingCategory>
+
+#include <qt_windows.h>
+#include <setupapi.h>
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+struct LeDeviceEntry {
+ QString devicePath;
+ QBluetoothAddress deviceAddress;
+};
+
+Q_GLOBAL_STATIC(QVector<LeDeviceEntry>, cachedLeDeviceEntries)
+Q_GLOBAL_STATIC(QMutex, cachedLeDeviceEntriesGuard)
+
+static QString devicePropertyString(
+ HDEVINFO hDeviceInfo,
+ const PSP_DEVINFO_DATA deviceInfoData,
+ DWORD registryProperty)
+{
+ DWORD propertyRegDataType = 0;
+ DWORD propertyBufferSize = 0;
+ QByteArray propertyBuffer;
+
+ while (!::SetupDiGetDeviceRegistryProperty(
+ hDeviceInfo,
+ deviceInfoData,
+ registryProperty,
+ &propertyRegDataType,
+ propertyBuffer.isEmpty() ? nullptr : reinterpret_cast<PBYTE>(propertyBuffer.data()),
+ propertyBuffer.size(),
+ &propertyBufferSize)) {
+
+ const DWORD error = ::GetLastError();
+ if (error != ERROR_INSUFFICIENT_BUFFER
+ || (propertyRegDataType != REG_SZ
+ && propertyRegDataType != REG_EXPAND_SZ)) {
+ return QString();
+ }
+
+ // add +2 byte to allow to successfully convert to qstring
+ propertyBuffer.fill(0, propertyBufferSize + sizeof(wchar_t));
+ }
+
+ return QString::fromWCharArray(reinterpret_cast<const wchar_t *>(
+ propertyBuffer.constData()));
+}
+
+static QString deviceName(HDEVINFO hDeviceInfo, PSP_DEVINFO_DATA deviceInfoData)
+{
+ return devicePropertyString(hDeviceInfo, deviceInfoData, SPDRP_FRIENDLYNAME);
+}
+
+static QString deviceSystemPath(const PSP_INTERFACE_DEVICE_DETAIL_DATA detailData)
+{
+ return QString::fromWCharArray(detailData->DevicePath);
+}
+
+static QBluetoothAddress deviceAddress(const QString &devicePath)
+{
+ const int firstbound = devicePath.indexOf(QStringLiteral("dev_"));
+ const int lastbound = devicePath.indexOf(QLatin1Char('#'), firstbound);
+ const QString hex = devicePath.mid(firstbound + 4, lastbound - firstbound - 4);
+ bool ok = false;
+ return QBluetoothAddress(hex.toULongLong(&ok, 16));
+}
+
+static QBluetoothDeviceInfo createClassicDeviceInfo(const BLUETOOTH_DEVICE_INFO &foundDevice)
+{
+ QBluetoothDeviceInfo deviceInfo(
+ QBluetoothAddress(foundDevice.Address.ullLong),
+ QString::fromWCharArray(foundDevice.szName),
+ foundDevice.ulClassofDevice);
+
+ deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration);
+
+ if (foundDevice.fRemembered)
+ deviceInfo.setCached(true);
+ return deviceInfo;
+}
+
+static QBluetoothDeviceInfo findFirstClassicDevice(
+ DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND *hSearch)
+{
+ BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = {};
+ searchParams.dwSize = sizeof(searchParams);
+ searchParams.cTimeoutMultiplier = 10; // 12.8 sec
+ searchParams.fIssueInquiry = TRUE;
+ searchParams.fReturnAuthenticated = TRUE;
+ searchParams.fReturnConnected = TRUE;
+ searchParams.fReturnRemembered = TRUE;
+ searchParams.fReturnUnknown = TRUE;
+ searchParams.hRadio = nullptr;
+
+ BLUETOOTH_DEVICE_INFO deviceInfo = {};
+ deviceInfo.dwSize = sizeof(deviceInfo);
+
+ const HBLUETOOTH_DEVICE_FIND hFind = ::BluetoothFindFirstDevice(
+ &searchParams, &deviceInfo);
+
+ QBluetoothDeviceInfo foundDevice;
+ if (hFind) {
+ *hSearch = hFind;
+ *systemErrorCode = NO_ERROR;
+ foundDevice = createClassicDeviceInfo(deviceInfo);
+ } else {
+ *systemErrorCode = int(::GetLastError());
+ }
+
+ return foundDevice;
+}
+
+static QBluetoothDeviceInfo findNextClassicDevice(
+ DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND hSearch)
+{
+ BLUETOOTH_DEVICE_INFO deviceInfo = {};
+ deviceInfo.dwSize = sizeof(deviceInfo);
+
+ QBluetoothDeviceInfo foundDevice;
+ if (!::BluetoothFindNextDevice(hSearch, &deviceInfo)) {
+ *systemErrorCode = int(::GetLastError());
+ } else {
+ *systemErrorCode = NO_ERROR;
+ foundDevice = createClassicDeviceInfo(deviceInfo);
+ }
+
+ return foundDevice;
+}
+
+static void closeClassicSearch(HBLUETOOTH_DEVICE_FIND hSearch)
+{
+ if (hSearch)
+ ::BluetoothFindDeviceClose(hSearch);
+}
+
+static QVector<QBluetoothDeviceInfo> enumerateLeDevices(
+ DWORD *systemErrorCode)
+{
+ // GUID_BLUETOOTHLE_DEVICE_INTERFACE
+ const QUuid deviceInterfaceGuid("781aee18-7733-4ce4-add0-91f41c67b592");
+ const HDEVINFO hDeviceInfo = ::SetupDiGetClassDevs(
+ reinterpret_cast<const GUID *>(&deviceInterfaceGuid),
+ nullptr,
+ nullptr,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+
+ if (hDeviceInfo == INVALID_HANDLE_VALUE) {
+ *systemErrorCode = int(::GetLastError());
+ return QVector<QBluetoothDeviceInfo>();
+ }
+
+ QVector<QBluetoothDeviceInfo> foundDevices;
+ DWORD index = 0;
+
+ QVector<LeDeviceEntry> cachedEntries;
+
+ for (;;) {
+ SP_DEVICE_INTERFACE_DATA deviceInterfaceData = {};
+ deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
+
+ if (!::SetupDiEnumDeviceInterfaces(
+ hDeviceInfo,
+ nullptr,
+ reinterpret_cast<const GUID *>(&deviceInterfaceGuid),
+ index++,
+ &deviceInterfaceData)) {
+ *systemErrorCode = int(::GetLastError());
+ break;
+ }
+
+ DWORD deviceInterfaceDetailDataSize = 0;
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ hDeviceInfo,
+ &deviceInterfaceData,
+ nullptr,
+ deviceInterfaceDetailDataSize,
+ &deviceInterfaceDetailDataSize,
+ nullptr)) {
+ if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ *systemErrorCode = int(::GetLastError());
+ break;
+ }
+ }
+
+ SP_DEVINFO_DATA deviceInfoData = {};
+ deviceInfoData.cbSize = sizeof(deviceInfoData);
+
+ QByteArray deviceInterfaceDetailDataBuffer(
+ deviceInterfaceDetailDataSize, 0);
+
+ PSP_INTERFACE_DEVICE_DETAIL_DATA deviceInterfaceDetailData =
+ reinterpret_cast<PSP_INTERFACE_DEVICE_DETAIL_DATA>
+ (deviceInterfaceDetailDataBuffer.data());
+
+ deviceInterfaceDetailData->cbSize =
+ sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
+
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ hDeviceInfo,
+ &deviceInterfaceData,
+ deviceInterfaceDetailData,
+ deviceInterfaceDetailDataBuffer.size(),
+ &deviceInterfaceDetailDataSize,
+ &deviceInfoData)) {
+ *systemErrorCode = int(::GetLastError());
+ break;
+ }
+
+ const QString systemPath = deviceSystemPath(deviceInterfaceDetailData);
+ const QBluetoothAddress address = deviceAddress(systemPath);
+ if (address.isNull())
+ continue;
+ const QString name = deviceName(hDeviceInfo, &deviceInfoData);
+
+ QBluetoothDeviceInfo deviceInfo(address, name, QBluetoothDeviceInfo::MiscellaneousDevice);
+ deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
+ deviceInfo.setCached(true);
+
+ foundDevices << deviceInfo;
+ cachedEntries << LeDeviceEntry{systemPath, address};
+ }
+
+ QMutexLocker locker(cachedLeDeviceEntriesGuard());
+ cachedLeDeviceEntries()->swap(cachedEntries);
+
+ ::SetupDiDestroyDeviceInfoList(hDeviceInfo);
+ return foundDevices;
+}
+
+struct DiscoveryResult {
+ QVector<QBluetoothDeviceInfo> devices;
+ DWORD systemErrorCode;
+ HBLUETOOTH_DEVICE_FIND hSearch; // Used only for classic devices
+};
+
+QString QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath(
+ const QBluetoothAddress &deviceAddress)
+{
+ // update LE devices cache
+ DWORD dummyErrorCode;
+ enumerateLeDevices(&dummyErrorCode);
+
+ QMutexLocker locker(cachedLeDeviceEntriesGuard());
+ for (const LeDeviceEntry &e: *cachedLeDeviceEntries) {
+ if (e.deviceAddress == deviceAddress)
+ return e.devicePath;
+ }
+ return QString();
+}
+
+QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(
+ const QBluetoothAddress &deviceAdapter,
+ QBluetoothDeviceDiscoveryAgent *parent)
+ : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry)
+ , lastError(QBluetoothDeviceDiscoveryAgent::NoError)
+ , adapterAddress(deviceAdapter)
+ , pendingCancel(false)
+ , pendingStart(false)
+ , active(false)
+ , lowEnergySearchTimeout(-1) // remains -1 -> timeout not supported
+ , q_ptr(parent)
+{
+ threadLE = new QThread;
+ threadWorkerLE = new ThreadWorkerDeviceDiscovery;
+ threadWorkerLE->moveToThread(threadLE);
+ connect(threadWorkerLE, &ThreadWorkerDeviceDiscovery::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery);
+ connect(threadLE, &QThread::finished, threadWorkerLE, &ThreadWorkerDeviceDiscovery::deleteLater);
+ connect(threadLE, &QThread::finished, threadLE, &QThread::deleteLater);
+ threadLE->start();
+
+ threadClassic = new QThread;
+ threadWorkerClassic = new ThreadWorkerDeviceDiscovery;
+ threadWorkerClassic->moveToThread(threadClassic);
+ connect(threadWorkerClassic, &ThreadWorkerDeviceDiscovery::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery);
+ connect(threadClassic, &QThread::finished, threadWorkerClassic, &ThreadWorkerDeviceDiscovery::deleteLater);
+ connect(threadClassic, &QThread::finished, threadClassic, &QThread::deleteLater);
+ threadClassic->start();
+}
+
+QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
+{
+ if (active)
+ stop();
+ if (threadLE)
+ threadLE->quit();
+ if (threadClassic)
+ threadClassic->quit();
+}
+
+bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const
+{
+ if (pendingStart)
+ return true;
+ if (pendingCancel)
+ return false;
+ return active;
+}
+
+QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods()
+{
+ return (LowEnergyMethod | ClassicMethod);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods)
+{
+ requestedMethods = methods;
+
+ if (pendingCancel == true) {
+ pendingStart = true;
+ return;
+ }
+
+ const QList<QBluetoothHostInfo> foundLocalAdapters =
+ QBluetoothLocalDevicePrivate::localAdapters();
+
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+
+ if (foundLocalAdapters.isEmpty()) {
+ qCWarning(QT_BT_WINDOWS) << "Device does not support Bluetooth";
+ lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError;
+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Device does not support Bluetooth");
+ emit q->error(lastError);
+ return;
+ }
+
+ // Check for the local adapter address.
+ auto equals = [this](const QBluetoothHostInfo &adapterInfo) {
+ return adapterAddress == QBluetoothAddress()
+ || adapterAddress == adapterInfo.address();
+ };
+ const auto end = foundLocalAdapters.cend();
+ const auto it = std::find_if(foundLocalAdapters.cbegin(), end, equals);
+ if (it == end) {
+ qCWarning(QT_BT_WINDOWS) << "Incorrect local adapter passed.";
+ lastError = QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError;
+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Passed address is not a local device.");
+ emit q->error(lastError);
+ return;
+ }
+
+ discoveredDevices.clear();
+ active = true;
+
+ // We run LE search first, as it is fast on windows.
+ if (requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)
+ startLeDevicesDiscovery();
+ else if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod)
+ startClassicDevicesDiscovery();
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::stop()
+{
+ if (!active)
+ return;
+
+ pendingCancel = true;
+ pendingStart = false;
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::cancelDiscovery()
+{
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ active = false;
+ pendingCancel = false;
+ emit q->canceled();
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::restartDiscovery()
+{
+ pendingStart = false;
+ pendingCancel = false;
+ start(requestedMethods);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::finishDiscovery(QBluetoothDeviceDiscoveryAgent::Error errorCode, const QString &errorText)
+{
+ active = false;
+ pendingStart = false;
+ pendingCancel = false;
+ lastError = errorCode;
+ errorString = errorText;
+
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ if (errorCode == QBluetoothDeviceDiscoveryAgent::NoError)
+ emit q->finished();
+ else
+ emit q->error(lastError);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::startLeDevicesDiscovery()
+{
+ const auto threadWorker = threadWorkerLE;
+ QMetaObject::invokeMethod(threadWorkerLE, [threadWorker]()
+ {
+ DiscoveryResult result; // Do not use hSearch here!
+ result.systemErrorCode = NO_ERROR;
+ result.devices = enumerateLeDevices(&result.systemErrorCode);
+ emit threadWorker->discoveryCompleted(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery(const QVariant &res)
+{
+ if (pendingCancel && !pendingStart) {
+ cancelDiscovery();
+ } else if (pendingStart) {
+ restartDiscovery();
+ } else {
+ const DiscoveryResult result = res.value<DiscoveryResult>();
+ if (result.systemErrorCode == NO_ERROR || result.systemErrorCode == ERROR_NO_MORE_ITEMS) {
+ for (const QBluetoothDeviceInfo &device : result.devices)
+ processDiscoveredDevice(device);
+
+ // We run classic search at second, as it is slow on windows.
+ if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod)
+ startClassicDevicesDiscovery();
+ else
+ finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, qt_error_string(NO_ERROR));
+ } else {
+ const QBluetoothDeviceDiscoveryAgent::Error error = (result.systemErrorCode == ERROR_INVALID_HANDLE)
+ ? QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError
+ : QBluetoothDeviceDiscoveryAgent::InputOutputError;
+ finishDiscovery(error, qt_error_string(result.systemErrorCode));
+ }
+ }
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::startClassicDevicesDiscovery(Qt::HANDLE hSearch)
+{
+ const auto threadWorker = threadWorkerClassic;
+ QMetaObject::invokeMethod(threadWorker, [threadWorker, hSearch]()
+ {
+ DiscoveryResult result;
+ result.hSearch = hSearch;
+ result.systemErrorCode = NO_ERROR;
+
+ const QBluetoothDeviceInfo device = hSearch
+ ? findNextClassicDevice(&result.systemErrorCode, result.hSearch)
+ : findFirstClassicDevice(&result.systemErrorCode, &result.hSearch);
+
+ result.devices.append(device);
+ emit threadWorker->discoveryCompleted(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery(const QVariant &res)
+{
+ const DiscoveryResult result = res.value<DiscoveryResult>();
+ if (pendingCancel && !pendingStart) {
+ closeClassicSearch(result.hSearch);
+ cancelDiscovery();
+ } else if (pendingStart) {
+ closeClassicSearch(result.hSearch);
+ restartDiscovery();
+ } else {
+ if (result.systemErrorCode == ERROR_NO_MORE_ITEMS) {
+ closeClassicSearch(result.hSearch);
+ finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, QString());
+ } else if (result.systemErrorCode == NO_ERROR) {
+ if (result.hSearch) {
+ for (const QBluetoothDeviceInfo &device : result.devices)
+ processDiscoveredDevice(device);
+
+ startClassicDevicesDiscovery(result.hSearch);
+ } else {
+ finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, QString());
+ }
+ } else {
+ closeClassicSearch(result.hSearch);
+ const QBluetoothDeviceDiscoveryAgent::Error error = (result.systemErrorCode == ERROR_INVALID_HANDLE)
+ ? QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError
+ : QBluetoothDeviceDiscoveryAgent::InputOutputError;
+ finishDiscovery(error, qt_error_string(result.systemErrorCode));
+ }
+ }
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevice(
+ const QBluetoothDeviceInfo &foundDevice)
+{
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+
+ auto equalAddress = [foundDevice](const QBluetoothDeviceInfo &targetDevice) {
+ return foundDevice.address() == targetDevice.address(); };
+ auto end = discoveredDevices.end();
+ auto deviceIt = std::find_if(discoveredDevices.begin(), end, equalAddress);
+ if (deviceIt == end) {
+ qCDebug(QT_BT_WINDOWS) << "Found device: " << foundDevice.name() << foundDevice.address();
+ discoveredDevices.append(foundDevice);
+ emit q->deviceDiscovered(foundDevice);
+ } else {
+ qCDebug(QT_BT_WINDOWS) << "Updating device:" << deviceIt->name() << deviceIt->address();
+ // merge service uuids
+ QList<QBluetoothUuid> uuids = deviceIt->serviceUuids();
+ uuids.append(foundDevice.serviceUuids());
+ const QSet<QBluetoothUuid> uuidSet = uuids.toSet();
+ if (deviceIt->serviceUuids().count() != uuidSet.count())
+ deviceIt->setServiceUuids(uuidSet.toList().toVector());
+ if (deviceIt->coreConfigurations() != foundDevice.coreConfigurations())
+ deviceIt->setCoreConfigurations(
+ QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration);
+ }
+}
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(DiscoveryResult)
diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h
index 8f3e2b43..28e7ed53 100644
--- a/src/bluetooth/qbluetoothlocaldevice_p.h
+++ b/src/bluetooth/qbluetoothlocaldevice_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -223,6 +224,29 @@ private:
void initializeAdapter();
void initializeAdapterBluez5();
};
+
+#elif defined(QT_WIN_BLUETOOTH)
+
+class QBluetoothLocalDevicePrivate : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PUBLIC(QBluetoothLocalDevice)
+public:
+ QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q,
+ const QBluetoothAddress &address = QBluetoothAddress());
+
+ ~QBluetoothLocalDevicePrivate();
+ bool isValid() const;
+ void initialize(const QBluetoothAddress &address);
+
+ static QList<QBluetoothHostInfo> localAdapters();
+
+ QBluetoothAddress deviceAddress;
+ QString deviceName;
+ bool deviceValid;
+private:
+ QBluetoothLocalDevice *q_ptr;
+};
#elif defined(QT_WINRT_BLUETOOTH)
class QBluetoothLocalDevicePrivate : public QObject
{
diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp
new file mode 100644
index 00000000..cf17cad1
--- /dev/null
+++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 "qbluetoothlocaldevice.h"
+#include "qbluetoothaddress.h"
+
+#include "qbluetoothlocaldevice_p.h"
+
+#include <QtCore/QLoggingCategory>
+
+#include <qt_windows.h>
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothLocalDevicePrivate(this))
+{
+}
+
+QBluetoothLocalDevice::QBluetoothLocalDevice(
+ const QBluetoothAddress &address, QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothLocalDevicePrivate(this, address))
+{
+}
+
+QString QBluetoothLocalDevice::name() const
+{
+ Q_D(const QBluetoothLocalDevice);
+ return d->deviceName;
+}
+
+QBluetoothAddress QBluetoothLocalDevice::address() const
+{
+ Q_D(const QBluetoothLocalDevice);
+ return d->deviceAddress;
+}
+
+void QBluetoothLocalDevice::powerOn()
+{
+ if (hostMode() != HostPoweredOff)
+ return;
+
+ setHostMode(HostConnectable);
+}
+
+void QBluetoothLocalDevice::setHostMode(
+ QBluetoothLocalDevice::HostMode requestedMode)
+{
+ if (!isValid()) {
+ qCWarning(QT_BT_WINDOWS) << "The local device is not initialized correctly";
+ return;
+ }
+
+ if (requestedMode == HostDiscoverableLimitedInquiry)
+ requestedMode = HostDiscoverable;
+
+ if (requestedMode == hostMode())
+ return;
+
+ if (requestedMode == QBluetoothLocalDevice::HostPoweredOff) {
+ if (::BluetoothIsDiscoverable(nullptr)
+ && !::BluetoothEnableDiscovery(nullptr, FALSE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to disable the discoverable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ if (::BluetoothIsConnectable(nullptr)
+ && !::BluetoothEnableIncomingConnections(nullptr, FALSE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to disable the connectable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ } else if (requestedMode == QBluetoothLocalDevice::HostConnectable) {
+ if (!::BluetoothIsConnectable(nullptr)
+ && !::BluetoothEnableIncomingConnections(nullptr, TRUE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to enable the connectable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ } else if (requestedMode == QBluetoothLocalDevice::HostDiscoverable
+ || requestedMode == QBluetoothLocalDevice::HostDiscoverableLimitedInquiry) {
+ if (!::BluetoothIsConnectable(nullptr)
+ && !::BluetoothEnableIncomingConnections(nullptr, TRUE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to enable the connectable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ if (!::BluetoothEnableDiscovery(nullptr, TRUE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to enable the discoverable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ }
+
+ emit hostModeStateChanged(requestedMode);
+}
+
+QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const
+{
+ if (!isValid()) {
+ qCWarning(QT_BT_WINDOWS) << "The local device is not initialized correctly";
+ return HostPoweredOff;
+ }
+
+ if (::BluetoothIsDiscoverable(nullptr))
+ return HostDiscoverable;
+ if (::BluetoothIsConnectable(nullptr))
+ return HostConnectable;
+ return HostPoweredOff;
+}
+
+QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
+{
+ return QList<QBluetoothAddress>();
+}
+
+QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
+{
+ return QBluetoothLocalDevicePrivate::localAdapters();
+}
+
+void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing)
+{
+ Q_UNUSED(address);
+ Q_UNUSED(pairing);
+ Q_UNIMPLEMENTED();
+}
+
+QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(
+ const QBluetoothAddress &address) const
+{
+ Q_UNUSED(address);
+ Q_UNIMPLEMENTED();
+ return Unpaired;
+}
+
+void QBluetoothLocalDevice::pairingConfirmation(bool confirmation)
+{
+ Q_UNUSED(confirmation);
+ Q_UNIMPLEMENTED();
+}
+
+QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(
+ QBluetoothLocalDevice *q, const QBluetoothAddress &address)
+ : deviceValid(false)
+ , q_ptr(q)
+{
+ initialize(address);
+}
+
+QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate()
+{
+}
+
+bool QBluetoothLocalDevicePrivate::isValid() const
+{
+ return deviceValid;
+}
+
+void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address)
+{
+ Q_Q(QBluetoothLocalDevice);
+
+ const QList<QBluetoothHostInfo> adapterInfos = QBluetoothLocalDevicePrivate::localAdapters();
+ for (const QBluetoothHostInfo &adapterInfo : adapterInfos) {
+ if (address == QBluetoothAddress()
+ || address == adapterInfo.address()) {
+ deviceAddress = adapterInfo.address();
+ deviceName = adapterInfo.name();
+ deviceValid = true;
+ return;
+ }
+ }
+
+ qCWarning(QT_BT_WINDOWS) << "Unable to find classic local radio: " << address;
+ QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection,
+ Q_ARG(QBluetoothLocalDevice::Error,
+ QBluetoothLocalDevice::UnknownError));
+}
+
+QList<QBluetoothHostInfo> QBluetoothLocalDevicePrivate::localAdapters()
+{
+ BLUETOOTH_FIND_RADIO_PARAMS params;
+ ::ZeroMemory(&params, sizeof(params));
+ params.dwSize = sizeof(params);
+
+ QList<QBluetoothHostInfo> foundAdapters;
+
+ HANDLE hRadio = nullptr;
+ if (const HBLUETOOTH_RADIO_FIND hSearch = ::BluetoothFindFirstRadio(&params, &hRadio)) {
+ for (;;) {
+ BLUETOOTH_RADIO_INFO radio;
+ ::ZeroMemory(&radio, sizeof(radio));
+ radio.dwSize = sizeof(radio);
+
+ const DWORD retval = ::BluetoothGetRadioInfo(hRadio, &radio);
+ ::CloseHandle(hRadio);
+
+ if (retval != ERROR_SUCCESS)
+ break;
+
+ QBluetoothHostInfo adapterInfo;
+ adapterInfo.setAddress(QBluetoothAddress(radio.address.ullLong));
+ adapterInfo.setName(QString::fromWCharArray(radio.szName));
+
+ foundAdapters << adapterInfo;
+
+ if (!::BluetoothFindNextRadio(hSearch, &hRadio))
+ break;
+ }
+
+ ::BluetoothFindRadioClose(hSearch);
+ }
+
+ return foundAdapters;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserver.cpp b/src/bluetooth/qbluetoothserver.cpp
index 14b0796d..daed5dc2 100644
--- a/src/bluetooth/qbluetoothserver.cpp
+++ b/src/bluetooth/qbluetoothserver.cpp
@@ -167,9 +167,8 @@ QT_BEGIN_NAMESPACE
Constructs a bluetooth server with \a parent and \a serverType.
*/
QBluetoothServer::QBluetoothServer(QBluetoothServiceInfo::Protocol serverType, QObject *parent)
- : QObject(parent), d_ptr(new QBluetoothServerPrivate(serverType))
+ : QObject(parent), d_ptr(new QBluetoothServerPrivate(serverType, this))
{
- d_ptr->q_ptr = this;
}
/*!
diff --git a/src/bluetooth/qbluetoothserver_android.cpp b/src/bluetooth/qbluetoothserver_android.cpp
index 4c469c76..eed3a1ea 100644
--- a/src/bluetooth/qbluetoothserver_android.cpp
+++ b/src/bluetooth/qbluetoothserver_android.cpp
@@ -53,9 +53,10 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
QHash<QBluetoothServerPrivate*, int> __fakeServerPorts;
-QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType)
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType,
+ QBluetoothServer *parent)
: socket(0),maxPendingConnections(1), securityFlags(QBluetooth::NoSecurity), serverType(sType),
- m_lastError(QBluetoothServer::NoError)
+ m_lastError(QBluetoothServer::NoError), q_ptr(parent)
{
thread = new ServerAcceptanceThread();
thread->setMaxPendingConnections(maxPendingConnections);
diff --git a/src/bluetooth/qbluetoothserver_bluez.cpp b/src/bluetooth/qbluetoothserver_bluez.cpp
index 54bc85a0..a98f5398 100644
--- a/src/bluetooth/qbluetoothserver_bluez.cpp
+++ b/src/bluetooth/qbluetoothserver_bluez.cpp
@@ -66,9 +66,10 @@ QBluetoothSocket *QBluetoothServerPrivate::createSocketForServer(
return socket;
}
-QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType)
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType,
+ QBluetoothServer *parent)
: maxPendingConnections(1), securityFlags(QBluetooth::Authorization), serverType(sType),
- m_lastError(QBluetoothServer::NoError)
+ m_lastError(QBluetoothServer::NoError), q_ptr(parent)
{
if (sType == QBluetoothServiceInfo::RfcommProtocol)
socket = createSocketForServer(QBluetoothServiceInfo::RfcommProtocol);
diff --git a/src/bluetooth/qbluetoothserver_osx.mm b/src/bluetooth/qbluetoothserver_osx.mm
index 9674331d..83d7e060 100644
--- a/src/bluetooth/qbluetoothserver_osx.mm
+++ b/src/bluetooth/qbluetoothserver_osx.mm
@@ -87,12 +87,13 @@ typedef QMap<quint16, QBluetoothServerPrivate *>::iterator ServerMapIterator;
}
-QBluetoothServerPrivate::QBluetoothServerPrivate(ServiceInfo::Protocol type)
+QBluetoothServerPrivate::QBluetoothServerPrivate(ServiceInfo::Protocol type,
+ QBluetoothServer *parent)
: socket(nullptr),
maxPendingConnections(1),
securityFlags(QBluetooth::NoSecurity),
serverType(type),
- q_ptr(nullptr),
+ q_ptr(parent),
m_lastError(QBluetoothServer::NoError),
port(0)
{
diff --git a/src/bluetooth/qbluetoothserver_p.cpp b/src/bluetooth/qbluetoothserver_p.cpp
index 4f28c9b1..6657e151 100644
--- a/src/bluetooth/qbluetoothserver_p.cpp
+++ b/src/bluetooth/qbluetoothserver_p.cpp
@@ -46,8 +46,10 @@
QT_BEGIN_NAMESPACE
-QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType)
- : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError)
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType,
+ QBluetoothServer *parent)
+ : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError),
+ q_ptr(parent)
{
#ifndef QT_IOS_BLUETOOTH
printDummyWarning();
diff --git a/src/bluetooth/qbluetoothserver_p.h b/src/bluetooth/qbluetoothserver_p.h
index 76349de6..d14dc7b4 100644
--- a/src/bluetooth/qbluetoothserver_p.h
+++ b/src/bluetooth/qbluetoothserver_p.h
@@ -57,7 +57,7 @@
#include "qbluetoothserver.h"
#include "qbluetooth.h"
-#if QT_CONFIG(bluez)
+#if QT_CONFIG(bluez) || defined(QT_WIN_BLUETOOTH)
QT_FORWARD_DECLARE_CLASS(QSocketNotifier)
#endif
@@ -90,7 +90,6 @@ QT_BEGIN_NAMESPACE
class QBluetoothAddress;
class QBluetoothSocket;
-
class QBluetoothServer;
class QBluetoothServerPrivate
@@ -101,7 +100,7 @@ class QBluetoothServerPrivate
Q_DECLARE_PUBLIC(QBluetoothServer)
public:
- QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol serverType);
+ QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol serverType, QBluetoothServer *parent);
~QBluetoothServerPrivate();
#if QT_CONFIG(bluez)
@@ -111,6 +110,9 @@ public:
static QBluetoothSocket *createSocketForServer(
QBluetoothServiceInfo::Protocol socketType = QBluetoothServiceInfo::RfcommProtocol);
#endif
+#if defined(QT_WIN_BLUETOOTH)
+ void _q_newConnection();
+#endif
public:
QBluetoothSocket *socket;
@@ -124,7 +126,7 @@ protected:
private:
QBluetoothServer::Error m_lastError;
-#if QT_CONFIG(bluez)
+#if QT_CONFIG(bluez) || defined(QT_WIN_BLUETOOTH)
QSocketNotifier *socketNotifier = nullptr;
#elif defined(QT_ANDROID_BLUETOOTH)
ServerAcceptanceThread *thread;
diff --git a/src/bluetooth/qbluetoothserver_win.cpp b/src/bluetooth/qbluetoothserver_win.cpp
new file mode 100644
index 00000000..a57d39a5
--- /dev/null
+++ b/src/bluetooth/qbluetoothserver_win.cpp
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** 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 "qbluetoothserver.h"
+#include "qbluetoothserver_p.h"
+#include "qbluetoothsocket.h"
+#include "qbluetoothlocaldevice.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QSocketNotifier>
+
+#include <winsock2.h>
+#include <ws2bth.h>
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType,
+ QBluetoothServer *parent)
+ : maxPendingConnections(1), serverType(sType), q_ptr(parent),
+ m_lastError(QBluetoothServer::NoError)
+{
+ Q_Q(QBluetoothServer);
+ Q_ASSERT(sType == QBluetoothServiceInfo::RfcommProtocol);
+ socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, q);
+}
+
+QBluetoothServerPrivate::~QBluetoothServerPrivate()
+{
+}
+
+void QBluetoothServerPrivate::_q_newConnection()
+{
+ // disable socket notifier until application calls nextPendingConnection().
+ socketNotifier->setEnabled(false);
+
+ emit q_ptr->newConnection();
+}
+
+void QBluetoothServer::close()
+{
+ Q_D(QBluetoothServer);
+
+ delete d->socketNotifier;
+ d->socketNotifier = nullptr;
+
+ d->socket->close();
+}
+
+bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
+{
+ Q_D(QBluetoothServer);
+
+ if (d->serverType != QBluetoothServiceInfo::RfcommProtocol) {
+ qCWarning(QT_BT_WINDOWS) << "Protocol is not supported.";
+ d->m_lastError = QBluetoothServer::UnsupportedProtocolError;
+ emit error(d->m_lastError);
+ return false;
+ }
+
+ if (d->socket->state() == QBluetoothSocket::ListeningState) {
+ qCWarning(QT_BT_WINDOWS) << "Socket already in listen mode, close server first";
+ return false;
+ }
+
+ const QBluetoothLocalDevice device(address);
+ if (!device.isValid()) {
+ qCWarning(QT_BT_WINDOWS) << "Device does not support Bluetooth or"
+ << address.toString() << "is not a valid local adapter";
+ d->m_lastError = QBluetoothServer::UnknownError;
+ emit error(d->m_lastError);
+ return false;
+ }
+
+ const QBluetoothLocalDevice::HostMode hostMode = device.hostMode();
+ if (hostMode == QBluetoothLocalDevice::HostPoweredOff) {
+ d->m_lastError = QBluetoothServer::PoweredOffError;
+ emit error(d->m_lastError);
+ qCWarning(QT_BT_WINDOWS) << "Bluetooth device is powered off";
+ return false;
+ }
+
+ int sock = d->socket->socketDescriptor();
+ if (sock < 0) {
+ /* Negative socket descriptor is not always an error case.
+ * Another cause could be a call to close()/abort().
+ * Check whether we can recover by re-creating the socket.
+ */
+ delete d->socket;
+ d->socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this);
+ sock = d->socket->socketDescriptor();
+ if (sock < 0) {
+ d->m_lastError = InputOutputError;
+ emit error(d->m_lastError);
+ return false;
+ }
+ }
+
+ if (sock < 0)
+ return false;
+
+ SOCKADDR_BTH addr = {};
+ addr.addressFamily = AF_BTH;
+ addr.port = (port == 0) ? BT_PORT_ANY : port;
+ addr.btAddr = address.toUInt64();
+
+ if (::bind(sock, reinterpret_cast<sockaddr *>(&addr), sizeof(SOCKADDR_BTH)) < 0) {
+ if (errno == EADDRINUSE)
+ d->m_lastError = ServiceAlreadyRegisteredError;
+ else
+ d->m_lastError = InputOutputError;
+ emit error(d->m_lastError);
+ return false;
+ }
+
+ if (::listen(sock, d->maxPendingConnections) < 0) {
+ d->m_lastError = InputOutputError;
+ emit error(d->m_lastError);
+ return false;
+ }
+
+ d->socket->setSocketState(QBluetoothSocket::ListeningState);
+
+ if (!d->socketNotifier) {
+ d->socketNotifier = new QSocketNotifier(d->socket->socketDescriptor(),
+ QSocketNotifier::Read, this);
+ connect(d->socketNotifier, &QSocketNotifier::activated, this, [d](){
+ d->_q_newConnection();
+ });
+ }
+
+ return true;
+}
+
+void QBluetoothServer::setMaxPendingConnections(int numConnections)
+{
+ Q_UNUSED(numConnections);
+}
+
+bool QBluetoothServer::hasPendingConnections() const
+{
+ Q_D(const QBluetoothServer);
+
+ if (!d || !d->socketNotifier)
+ return false;
+
+ // if the socket notifier is disabled there is a pending connection waiting for us to accept.
+ return !d->socketNotifier->isEnabled();
+}
+
+QBluetoothSocket *QBluetoothServer::nextPendingConnection()
+{
+ Q_D(QBluetoothServer);
+
+ if (!hasPendingConnections())
+ return nullptr;
+
+ if (d->serverType != QBluetoothServiceInfo::RfcommProtocol)
+ return nullptr;
+
+ SOCKADDR_BTH addr = {};
+ int length = sizeof(SOCKADDR_BTH);
+ int pending = ::accept(d->socket->socketDescriptor(),
+ reinterpret_cast<sockaddr *>(&addr), &length);
+
+ QBluetoothSocket *newSocket = nullptr;
+
+ if (pending >= 0) {
+ newSocket = new QBluetoothSocket();
+ newSocket->setSocketDescriptor(pending, QBluetoothServiceInfo::RfcommProtocol);
+ }
+
+ d->socketNotifier->setEnabled(true);
+ return newSocket;
+}
+
+QBluetoothAddress QBluetoothServer::serverAddress() const
+{
+ Q_D(const QBluetoothServer);
+
+ return d->socket->localAddress();
+}
+
+quint16 QBluetoothServer::serverPort() const
+{
+ Q_D(const QBluetoothServer);
+
+ return d->socket->localPort();
+}
+
+void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security)
+{
+ Q_UNUSED(security);
+}
+
+QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const
+{
+ return QBluetooth::NoSecurity;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserver_winrt.cpp b/src/bluetooth/qbluetoothserver_winrt.cpp
index 3bcd6b33..b9d98eb7 100644
--- a/src/bluetooth/qbluetoothserver_winrt.cpp
+++ b/src/bluetooth/qbluetoothserver_winrt.cpp
@@ -70,8 +70,10 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
QHash<QBluetoothServerPrivate *, int> __fakeServerPorts;
-QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType)
- : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError), socket(0)
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType,
+ QBluetoothServer *parent)
+ : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError),
+ socket(0), q_ptr(parent)
{
#ifdef CLASSIC_APP_BUILD
CoInitialize(NULL);
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.h b/src/bluetooth/qbluetoothservicediscoveryagent.h
index 1db05f55..f1fa4640 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent.h
@@ -43,6 +43,7 @@
#include <QtBluetooth/qtbluetoothglobal.h>
#include <QtCore/QObject>
+#include <QtCore/QVariant>
#include <QtBluetooth/QBluetoothServiceInfo>
#include <QtBluetooth/QBluetoothUuid>
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
index c17221be..41410b70 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
@@ -72,7 +72,21 @@ class QXmlStreamReader;
QT_END_NAMESPACE
#endif
-#ifdef QT_WINRT_BLUETOOTH
+#ifdef QT_WIN_BLUETOOTH
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+class QThread;
+
+class ThreadWorkerFind : public QObject
+{
+ Q_OBJECT
+signals:
+ void findFinished(QVariant result);
+};
+QT_END_NAMESPACE
+
+#elif defined(QT_WINRT_BLUETOOTH)
#include <QtCore/QPointer>
#endif
@@ -96,7 +110,7 @@ class QWinRTBluetoothServiceDiscoveryWorker;
#endif
class QBluetoothServiceDiscoveryAgentPrivate
-#if defined QT_WINRT_BLUETOOTH
+#if defined QT_WINRT_BLUETOOTH || defined QT_WIN_BLUETOOTH
: public QObject
{
Q_OBJECT
@@ -157,6 +171,10 @@ public:
void _q_fetchUuidsTimeout();
void _q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state);
#endif
+#ifdef QT_WIN_BLUETOOTH
+ void _q_nextSdpScan(const QVariant &input);
+ bool serviceMatches(const QBluetoothServiceInfo &info);
+#endif
private:
void start(const QBluetoothAddress &address);
@@ -208,6 +226,15 @@ private:
QMap<QBluetoothAddress,QPair<QBluetoothDeviceInfo,QList<QBluetoothUuid> > > sdpCache;
#endif
+#ifdef QT_WIN_BLUETOOTH
+private:
+ bool pendingStop;
+ bool pendingFinish;
+
+ QThread *threadFind = nullptr;
+ ThreadWorkerFind *threadWorkerFind = nullptr;
+#endif
+
#ifdef QT_WINRT_BLUETOOTH
private slots:
void processFoundService(quint64 deviceAddress, const QBluetoothServiceInfo &info);
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp
new file mode 100644
index 00000000..c34443aa
--- /dev/null
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp
@@ -0,0 +1,437 @@
+/****************************************************************************
+**
+** 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 "qbluetoothservicediscoveryagent.h"
+#include "qbluetoothservicediscoveryagent_p.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QLibrary>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QThread>
+#include <QtCore/QUrl>
+
+#include <initguid.h>
+#include <winsock2.h>
+#include <qt_windows.h>
+
+#if defined(Q_CC_MINGW)
+// Workaround for MinGW headers declaring BluetoothSdpGetElementData incorrectly.
+# define BluetoothSdpGetElementData _BluetoothSdpGetElementData_notok
+# include <bluetoothapis.h>
+# undef BluetoothSdpGetElementData
+ extern "C" DWORD WINAPI BluetoothSdpGetElementData(LPBYTE, ULONG, PSDP_ELEMENT_DATA);
+#else
+# include <bluetoothapis.h>
+#endif
+
+#include <ws2bth.h>
+#include <iostream>
+
+QT_BEGIN_NAMESPACE
+
+struct FindServiceResult {
+ QBluetoothServiceInfo info;
+ Qt::HANDLE hSearch = INVALID_HANDLE_VALUE;
+ int systemError = NO_ERROR;
+};
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+static QList<QVariant> spdContainerToVariantList(LPBYTE containerStream, ULONG containerLength);
+
+static QVariant spdElementToVariant(const SDP_ELEMENT_DATA &element)
+{
+ QVariant variant;
+
+ switch (element.type) {
+ case SDP_TYPE_UINT: {
+ switch (element.specificType) {
+ case SDP_ST_UINT128:
+ //Not supported!!!
+ break;
+ case SDP_ST_UINT64:
+ variant = QVariant::fromValue<quint64>(element.data.uint64);
+ break;
+ case SDP_ST_UINT32:
+ variant = QVariant::fromValue<quint32>(element.data.uint32);
+ break;
+ case SDP_ST_UINT16:
+ variant = QVariant::fromValue<quint16>(element.data.uint16);
+ break;
+ case SDP_ST_UINT8:
+ variant = QVariant::fromValue<quint8>(element.data.uint8);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case SDP_TYPE_INT: {
+ switch (element.specificType) {
+ case SDP_ST_INT128: {
+ //Not supported!!!
+ break;
+ }
+ case SDP_ST_INT64:
+ variant = QVariant::fromValue<qint64>(element.data.int64);
+ break;
+ case SDP_ST_INT32:
+ variant = QVariant::fromValue<qint32>(element.data.int32);
+ break;
+ case SDP_ST_INT16:
+ variant = QVariant::fromValue<qint16>(element.data.int16);
+ break;
+ case SDP_ST_INT8:
+ variant = QVariant::fromValue<qint8>(element.data.int8);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case SDP_TYPE_UUID: {
+ switch (element.specificType) {
+ case SDP_ST_UUID128:
+ variant = QVariant::fromValue(QBluetoothUuid(element.data.uuid128));
+ break;
+ case SDP_ST_UUID32:
+ variant = QVariant::fromValue(QBluetoothUuid(quint32(element.data.uuid32)));
+ break;
+ case SDP_ST_UUID16:
+ variant = QVariant::fromValue(QBluetoothUuid(quint16(element.data.uuid16)));
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case SDP_TYPE_STRING: {
+ const QByteArray stringBuffer(reinterpret_cast<const char*>(element.data.string.value), element.data.string.length);
+ variant = QVariant::fromValue<QString>(QString::fromLocal8Bit(stringBuffer));
+ break;
+ }
+ case SDP_TYPE_URL: {
+ const QString urlString = QString::fromLocal8Bit(reinterpret_cast<const char*>(element.data.url.value),
+ int(element.data.url.length));
+ const QUrl url(urlString);
+ if (url.isValid())
+ variant = QVariant::fromValue<QUrl>(url);
+ break;
+ }
+ case SDP_TYPE_SEQUENCE: {
+ const QList<QVariant> sequenceList = spdContainerToVariantList(element.data.sequence.value,
+ element.data.sequence.length);
+ const QBluetoothServiceInfo::Sequence sequence(sequenceList);
+ variant = QVariant::fromValue(sequence);
+ break;
+ }
+ case SDP_TYPE_ALTERNATIVE: {
+ const QList<QVariant> alternativeList = spdContainerToVariantList(element.data.alternative.value,
+ element.data.alternative.length);
+ const QBluetoothServiceInfo::Alternative alternative(alternativeList);
+ variant = QVariant::fromValue(alternative);
+ break;
+ }
+ case SDP_TYPE_BOOLEAN:
+ variant = QVariant::fromValue<bool>(bool(element.data.booleanVal));
+ break;
+ case SDP_TYPE_NIL:
+ break;
+ default:
+ break;
+ }
+
+ return variant;
+}
+
+static QList<QVariant> spdContainerToVariantList(LPBYTE containerStream, ULONG containerLength)
+{
+ HBLUETOOTH_CONTAINER_ELEMENT iter = nullptr;
+ SDP_ELEMENT_DATA element = {};
+
+ QList<QVariant> sequence;
+
+ for (;;) {
+ const DWORD result = ::BluetoothSdpGetContainerElementData(containerStream,
+ containerLength,
+ &iter,
+ &element);
+
+ if (result == ERROR_SUCCESS) {
+ const QVariant variant = spdElementToVariant(element);
+ sequence.append(variant);
+ } else if (result == ERROR_NO_MORE_ITEMS) {
+ break;
+ } else if (result == ERROR_INVALID_PARAMETER) {
+ break;
+ }
+ }
+
+ return sequence;
+}
+
+#if defined(Q_CC_MINGW)
+# define SDP_CALLBACK
+#else
+# define SDP_CALLBACK QT_WIN_CALLBACK
+#endif
+static BOOL SDP_CALLBACK bluetoothSdpCallback(ULONG attributeId, LPBYTE valueStream, ULONG streamSize, LPVOID param)
+{
+ QBluetoothServiceInfo *result = static_cast<QBluetoothServiceInfo*>(param);
+
+ SDP_ELEMENT_DATA element = {};
+
+ if (::BluetoothSdpGetElementData(valueStream, streamSize, &element) == ERROR_SUCCESS) {
+ switch (element.type) {
+ case SDP_TYPE_UINT:
+ case SDP_TYPE_INT:
+ case SDP_TYPE_UUID:
+ case SDP_TYPE_STRING:
+ case SDP_TYPE_URL:
+ case SDP_TYPE_BOOLEAN:
+ case SDP_TYPE_SEQUENCE:
+ case SDP_TYPE_ALTERNATIVE: {
+ const QVariant variant = spdElementToVariant(element);
+
+ result->setAttribute(attributeId, variant);
+ break;
+ }
+ case SDP_TYPE_NIL:
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+enum {
+ WSAControlFlags = LUP_FLUSHCACHE
+ | LUP_RETURN_NAME
+ | LUP_RETURN_TYPE
+ | LUP_RETURN_ADDR
+ | LUP_RETURN_BLOB
+ | LUP_RETURN_COMMENT
+};
+
+static FindServiceResult findNextService(HANDLE hSearch)
+{
+ FindServiceResult result;
+ result.hSearch = hSearch;
+
+ QByteArray resultBuffer(2048, 0);
+ WSAQUERYSET *resultQuery = reinterpret_cast<WSAQUERYSET*>(resultBuffer.data());
+ DWORD resultBufferSize = DWORD(resultBuffer.size());
+ const int resultCode = ::WSALookupServiceNext(hSearch,
+ WSAControlFlags,
+ &resultBufferSize,
+ resultQuery);
+
+ if (resultCode == SOCKET_ERROR) {
+ result.systemError = ::WSAGetLastError();
+ if (result.systemError == WSA_E_NO_MORE)
+ ::WSALookupServiceEnd(hSearch);
+ return result;
+ }
+
+ if (resultQuery->lpBlob
+ && ::BluetoothSdpEnumAttributes(resultQuery->lpBlob->pBlobData,
+ resultQuery->lpBlob->cbSize,
+ bluetoothSdpCallback,
+ &result.info)) {
+ return result;
+ } else {
+ result.systemError = GetLastError();
+ }
+ return result;
+}
+
+static FindServiceResult findFirstService(const QBluetoothAddress &address)
+{
+ WSAData wsadata = {};
+ FindServiceResult result;
+
+ // IPv6 requires Winsock v2.0 or better.
+ if (::WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) {
+ result.systemError = ::WSAGetLastError();
+ return result;
+ }
+
+ const QString addressAsString = QStringLiteral("(%1)").arg(address.toString());
+ QVector<WCHAR> addressAsWChar(addressAsString.size() + 1);
+ addressAsString.toWCharArray(addressAsWChar.data());
+
+ GUID protocol = L2CAP_PROTOCOL_UUID; //Search for L2CAP and RFCOMM services
+
+ WSAQUERYSET serviceQuery = {};
+ serviceQuery.dwSize = sizeof(WSAQUERYSET);
+ serviceQuery.lpServiceClassId = &protocol;
+ serviceQuery.dwNameSpace = NS_BTH;
+ serviceQuery.dwNumberOfCsAddrs = 0;
+ serviceQuery.lpszContext = addressAsWChar.data();
+
+ HANDLE hSearch = nullptr;
+ const int resultCode = ::WSALookupServiceBegin(&serviceQuery,
+ WSAControlFlags,
+ &hSearch);
+ if (resultCode == SOCKET_ERROR) {
+ result.systemError = ::WSAGetLastError();
+ ::WSALookupServiceEnd(hSearch);
+ return result;
+ }
+ return findNextService(hSearch);
+}
+
+QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
+ QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter)
+ : error(QBluetoothServiceDiscoveryAgent::NoError),
+ state(Inactive),
+ deviceDiscoveryAgent(0),
+ mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery),
+ singleDevice(false),
+ pendingStop(false),
+ pendingFinish(false),
+ q_ptr(qp)
+{
+ Q_UNUSED(deviceAdapter);
+
+ threadFind = new QThread;
+ threadWorkerFind = new ThreadWorkerFind;
+ threadWorkerFind->moveToThread(threadFind);
+ connect(threadWorkerFind, &ThreadWorkerFind::findFinished, this, &QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan);
+ connect(threadFind, &QThread::finished, threadWorkerFind, &ThreadWorkerFind::deleteLater);
+ connect(threadFind, &QThread::finished, threadFind, &QThread::deleteLater);
+ threadFind->start();
+}
+
+QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate()
+{
+ if (pendingFinish)
+ stop();
+ if (threadFind)
+ threadFind->quit();
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address)
+{
+ if (!pendingFinish) {
+ pendingFinish = true;
+ pendingStop = false;
+
+ const auto threadWorker = threadWorkerFind;
+ QMetaObject::invokeMethod(threadWorkerFind, [threadWorker, address]()
+ {
+ const FindServiceResult result = findFirstService(address);
+ emit threadWorker->findFinished(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+ }
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::stop()
+{
+ pendingStop = true;
+}
+
+bool QBluetoothServiceDiscoveryAgentPrivate::serviceMatches(const QBluetoothServiceInfo &info)
+{
+ if (uuidFilter.isEmpty())
+ return true;
+
+ if (uuidFilter.contains(info.serviceUuid()))
+ return true;
+
+ const QList<QBluetoothUuid> serviceClassUuids = info.serviceClassUuids();
+ for (const QBluetoothUuid &uuid : serviceClassUuids)
+ if (uuidFilter.contains(uuid))
+ return true;
+
+ return false;
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &input)
+{
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ auto result = input.value<FindServiceResult>();
+
+ if (pendingStop) {
+ ::WSALookupServiceEnd(result.hSearch);
+ pendingStop = false;
+ pendingFinish = false;
+ emit q->canceled();
+ } else {
+ if (result.systemError == WSA_E_NO_MORE) {
+ result.systemError = NO_ERROR;
+ } else if (result.systemError != NO_ERROR) {
+ if (result.hSearch != INVALID_HANDLE_VALUE)
+ ::WSALookupServiceEnd(result.hSearch);
+ error = (result.systemError == ERROR_INVALID_HANDLE) ?
+ QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError
+ : QBluetoothServiceDiscoveryAgent::InputOutputError;
+ errorString = qt_error_string(result.systemError);
+ qCWarning(QT_BT_WINDOWS) << errorString;
+ emit q->error(this->error);
+ } else {
+
+ if (serviceMatches(result.info)) {
+ result.info.setDevice(discoveredDevices.at(0));
+ if (result.info.isValid()) {
+ if (!isDuplicatedService(result.info)) {
+ discoveredServices.append(result.info);
+ emit q->serviceDiscovered(result.info);
+ }
+ }
+ }
+
+ const auto threadWorker = threadWorkerFind;
+ const auto hSearch = result.hSearch;
+ QMetaObject::invokeMethod(threadWorkerFind, [threadWorker, hSearch]()
+ {
+ FindServiceResult result = findNextService(hSearch);
+ emit threadWorker->findFinished(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+ return;
+ }
+ pendingFinish = false;
+ _q_serviceDiscoveryFinished();
+ }
+}
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(FindServiceResult)
diff --git a/src/bluetooth/qbluetoothserviceinfo_p.h b/src/bluetooth/qbluetoothserviceinfo_p.h
index a0515b8c..3ed005e1 100644
--- a/src/bluetooth/qbluetoothserviceinfo_p.h
+++ b/src/bluetooth/qbluetoothserviceinfo_p.h
@@ -82,6 +82,11 @@ namespace ABI {
}
#endif
+#ifdef QT_WIN_BLUETOOTH
+#include <winsock2.h>
+#include <ws2bth.h>
+#endif
+
QT_BEGIN_NAMESPACE
class QBluetoothServiceInfo;
@@ -123,6 +128,14 @@ private:
bool writeSdpAttributes();
#endif
+#ifdef QT_WIN_BLUETOOTH
+ SOCKADDR_BTH sockaddr = {};
+ CSADDR_INFO addrinfo = {};
+ WSAQUERYSET regInfo = {};
+ QVector<WCHAR> serviceName;
+ QVector<WCHAR> serviceDescription;
+#endif
+
#if QT_OSX_BLUETOOTH
public:
bool registerService(const QBluetoothServiceInfo &info);
diff --git a/src/bluetooth/qbluetoothserviceinfo_win.cpp b/src/bluetooth/qbluetoothserviceinfo_win.cpp
new file mode 100644
index 00000000..1f1293ad
--- /dev/null
+++ b/src/bluetooth/qbluetoothserviceinfo_win.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** 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 "qbluetoothserviceinfo.h"
+#include "qbluetoothserviceinfo_p.h"
+#include "qbluetoothserver_p.h"
+#include "qbluetoothserver.h"
+
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate()
+ : registered(false)
+{
+}
+
+QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate()
+{
+}
+
+bool QBluetoothServiceInfoPrivate::isRegistered() const
+{
+ return registered;
+}
+
+bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAdapter)
+{
+ if (registered)
+ return false;
+
+ GUID serviceUuid = attributes.value(QBluetoothServiceInfo::ServiceId).value<QBluetoothUuid>();
+ const QString name = attributes.value(QBluetoothServiceInfo::ServiceName).toString();
+ const QString description = attributes.value(QBluetoothServiceInfo::ServiceDescription).toString();
+
+ ::memset(&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.addressFamily = AF_BTH;
+ sockaddr.port = serverChannel();
+ sockaddr.btAddr = localAdapter.toUInt64();
+
+ ::memset(&addrinfo, 0, sizeof(addrinfo));
+ addrinfo.LocalAddr.iSockaddrLength = sizeof(SOCKADDR_BTH);
+ addrinfo.LocalAddr.lpSockaddr = (LPSOCKADDR)&sockaddr;
+ addrinfo.RemoteAddr.iSockaddrLength = sizeof(SOCKADDR_BTH);
+ addrinfo.RemoteAddr.lpSockaddr = (LPSOCKADDR)&sockaddr;
+ addrinfo.iSocketType = SOCK_STREAM;
+ addrinfo.iProtocol = BTHPROTO_RFCOMM;
+
+ serviceName.resize(name.size() + 1);
+ name.toWCharArray(serviceName.data());
+ serviceName[name.size()] = WCHAR(0);
+ serviceDescription.resize(description.size() + 1);
+ description.toWCharArray(serviceDescription.data());
+ serviceDescription[description.size()] = WCHAR(0);
+
+ ::memset(&regInfo, 0, sizeof(regInfo));
+ regInfo.dwSize = sizeof(WSAQUERYSET);
+ regInfo.dwNameSpace = NS_BTH;
+ regInfo.dwNumberOfCsAddrs = 1;
+ regInfo.lpcsaBuffer = &addrinfo;
+ regInfo.lpszServiceInstanceName = serviceName.data();
+ regInfo.lpszComment = serviceDescription.data();
+ regInfo.lpServiceClassId = &serviceUuid;
+
+ if (::WSASetService(&regInfo, RNRSERVICE_REGISTER, 0) == SOCKET_ERROR)
+ return false;
+
+ registered = true;
+ return true;
+}
+
+bool QBluetoothServiceInfoPrivate::unregisterService()
+{
+ if (!registered)
+ return false;
+
+ if (::WSASetService(&regInfo, RNRSERVICE_DELETE, 0) == SOCKET_ERROR)
+ return false;
+
+ registered = false;
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp
index b1f75741..e4d85447 100644
--- a/src/bluetooth/qbluetoothsocket.cpp
+++ b/src/bluetooth/qbluetoothsocket.cpp
@@ -47,6 +47,8 @@
#include "qbluetoothsocket_android_p.h"
#elif defined(QT_WINRT_BLUETOOTH)
#include "qbluetoothsocket_winrt_p.h"
+#elif defined(QT_WIN_BLUETOOTH)
+#include "qbluetoothsocket_win_p.h"
#elif defined(QT_OSX_BLUETOOTH)
#include "qbluetoothsocket_osx_p.h"
#else
@@ -269,6 +271,8 @@ static QBluetoothSocketBasePrivate *createSocketPrivate()
return new QBluetoothSocketPrivateAndroid();
#elif defined(QT_WINRT_BLUETOOTH)
return new QBluetoothSocketPrivateWinRT();
+#elif defined(QT_WIN_BLUETOOTH)
+ return new QBluetoothSocketPrivateWin();
#elif defined(QT_OSX_BLUETOOTH)
return new QBluetoothSocketPrivate();
#else
diff --git a/src/bluetooth/qbluetoothsocket.h b/src/bluetooth/qbluetoothsocket.h
index 256a95dc..8d35f77e 100644
--- a/src/bluetooth/qbluetoothsocket.h
+++ b/src/bluetooth/qbluetoothsocket.h
@@ -68,6 +68,7 @@ class Q_BLUETOOTH_EXPORT QBluetoothSocket : public QIODevice
friend class QBluetoothSocketPrivateBluez;
friend class QBluetoothSocketPrivateBluezDBus;
friend class QBluetoothSocketPrivateDummy;
+ friend class QBluetoothSocketPrivateWin;
friend class QBluetoothSocketPrivateWinRT;
public:
diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp
new file mode 100644
index 00000000..83855323
--- /dev/null
+++ b/src/bluetooth/qbluetoothsocket_win.cpp
@@ -0,0 +1,597 @@
+/****************************************************************************
+**
+** 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 "qbluetoothsocket.h"
+#include "qbluetoothsocket_win_p.h"
+
+#include <QtCore/qloggingcategory.h>
+
+#include <QtBluetooth/qbluetoothdeviceinfo.h>
+#include <QtBluetooth/QBluetoothLocalDevice>
+#include <QtCore/QSocketNotifier>
+
+#include <winsock2.h>
+#include <ws2bth.h>
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+QBluetoothSocketPrivateWin::QBluetoothSocketPrivateWin()
+ : QBluetoothSocketBasePrivate()
+{
+ WSAData wsadata = {};
+ ::WSAStartup(MAKEWORD(2, 0), &wsadata);
+}
+
+QBluetoothSocketPrivateWin::~QBluetoothSocketPrivateWin()
+{
+ abort();
+}
+
+bool QBluetoothSocketPrivateWin::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (static_cast<SOCKET>(socket) != INVALID_SOCKET) {
+ if (socketType == type)
+ return true;
+ abort();
+ }
+ socketType = type;
+
+ if (type != QBluetoothServiceInfo::RfcommProtocol) {
+ socket = int(INVALID_SOCKET);
+ errorString = QBluetoothSocket::tr("Unsupported protocol. Win32 only supports RFCOMM sockets");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return false;
+ }
+
+ socket = ::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
+
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to create socket:" << error << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Failed to create socket");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+
+ if (!createNotifiers())
+ return false;
+
+ return true;
+}
+
+void QBluetoothSocketPrivateWin::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET && !ensureNativeSocket(socketType))
+ return;
+
+ if (!configureSecurity())
+ return;
+
+ SOCKADDR_BTH addr = {};
+ addr.addressFamily = AF_BTH;
+ addr.port = port;
+ addr.btAddr = address.toUInt64();
+
+ switch (socketType) {
+ case QBluetoothServiceInfo::RfcommProtocol:
+ addr.serviceClassId = RFCOMM_PROTOCOL_UUID;
+ break;
+ default:
+ errorString = QBluetoothSocket::tr("Socket type not handled: %1").arg(socketType);
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ connectWriteNotifier->setEnabled(true);
+ readNotifier->setEnabled(true);
+ exceptNotifier->setEnabled(true);
+
+ const int result = ::connect(socket, reinterpret_cast<sockaddr *>(&addr), sizeof(addr));
+
+ const int error = ::WSAGetLastError();
+ if (result != SOCKET_ERROR || error == WSAEWOULDBLOCK) {
+ q->setSocketState(QBluetoothSocket::ConnectingState);
+ q->setOpenMode(openMode);
+ } else {
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ }
+}
+
+void QBluetoothSocketPrivateWin::connectToService(
+ const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (q->state() != QBluetoothSocket::UnconnectedState
+ && q->state() != QBluetoothSocket::ServiceLookupState) {
+ //qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWIN::connectToService called on busy socket";
+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ // we are checking the service protocol and not socketType()
+ // socketType will change in ensureNativeSocket()
+ if (service.socketProtocol() != QBluetoothServiceInfo::RfcommProtocol) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocket::connectToService called with unsupported protocol";
+ errorString = QBluetoothSocket::tr("Socket type not supported");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ if (service.serverChannel() > 0) {
+ if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return;
+ }
+ connectToServiceHelper(service.device().address(), service.serverChannel(), openMode);
+ } else {
+ // try doing service discovery to see if we can find the socket
+ if (service.serviceUuid().isNull()
+ && !service.serviceClassUuids().contains(QBluetoothUuid::SerialPort)) {
+ qCWarning(QT_BT_WINDOWS) << "No port, no PSM, and no UUID provided. Unable to connect";
+ return;
+ }
+ qCDebug(QT_BT_WINDOWS) << "Need a port/psm, doing discovery";
+ q->doDeviceDiscovery(service, openMode);
+ }
+}
+
+void QBluetoothSocketPrivateWin::_q_writeNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state == QBluetoothSocket::ConnectingState) {
+ q->setSocketState(QBluetoothSocket::ConnectedState);
+ connectWriteNotifier->setEnabled(false);
+ } else {
+ if (txBuffer.isEmpty()) {
+ connectWriteNotifier->setEnabled(false);
+ return;
+ }
+
+ char buf[1024];
+ const int size = txBuffer.read(&buf[0], sizeof(buf));
+ const int writtenBytes = ::send(socket, &buf[0], size, 0);
+ if (writtenBytes == SOCKET_ERROR) {
+ // every other case returns error
+ const int error = ::WSAGetLastError();
+ errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error));
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ } else if (writtenBytes <= size) {
+ // add remainder back to buffer
+ const char *remainder = &buf[writtenBytes];
+ txBuffer.ungetBlock(remainder, size - writtenBytes);
+ if (writtenBytes > 0)
+ emit q->bytesWritten(writtenBytes);
+ } else {
+ errorString = QBluetoothSocket::tr("Logic error: more bytes sent than passed to ::send");
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ }
+
+ if (!txBuffer.isEmpty()) {
+ connectWriteNotifier->setEnabled(true);
+ } else if (state == QBluetoothSocket::ClosingState) {
+ connectWriteNotifier->setEnabled(false);
+ this->close();
+ }
+ }
+}
+
+void QBluetoothSocketPrivateWin::_q_readNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ char *writePointer = buffer.reserve(QPRIVATELINEARBUFFER_BUFFERSIZE);
+ const int bytesRead = ::recv(socket, writePointer, QPRIVATELINEARBUFFER_BUFFERSIZE, 0);
+ if (bytesRead == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ buffer.chop(QPRIVATELINEARBUFFER_BUFFERSIZE);
+ readNotifier->setEnabled(false);
+ connectWriteNotifier->setEnabled(false);
+ errorString = qt_error_string(error);
+ qCWarning(QT_BT_WINDOWS) << Q_FUNC_INFO << socket << "error:" << error << errorString;
+ switch (error) {
+ case WSAEHOSTDOWN:
+ q->setSocketError(QBluetoothSocket::HostNotFoundError);
+ break;
+ case WSAECONNRESET:
+ q->setSocketError(QBluetoothSocket::RemoteHostClosedError);
+ break;
+ default:
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ break;
+ }
+
+ q->disconnectFromService();
+ } else if (bytesRead == 0) {
+ q->setSocketError(QBluetoothSocket::RemoteHostClosedError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ } else {
+ const int unusedBytes = QPRIVATELINEARBUFFER_BUFFERSIZE - bytesRead;
+ buffer.chop(unusedBytes);
+ if (bytesRead > 0)
+ emit q->readyRead();
+ }
+}
+
+void QBluetoothSocketPrivateWin::_q_exceptNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ const int error = ::WSAGetLastError();
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+
+ if (state == QBluetoothSocket::ConnectingState)
+ abort();
+}
+
+void QBluetoothSocketPrivateWin::connectToService(
+ const QBluetoothAddress &address, const QBluetoothUuid &uuid,
+ QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (q->state() != QBluetoothSocket::UnconnectedState) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService called on busy socket";
+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService cannot "
+ "connect with 'UnknownProtocol' (type provided by given service)";
+ errorString = QBluetoothSocket::tr("Socket type not supported");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ QBluetoothServiceInfo service;
+ QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice);
+ service.setDevice(device);
+ service.setServiceUuid(uuid);
+ q->doDeviceDiscovery(service, openMode);
+}
+
+void QBluetoothSocketPrivateWin::connectToService(
+ const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService cannot "
+ "connect with 'UnknownProtocol' (type provided by given service)";
+ errorString = QBluetoothSocket::tr("Socket type not supported");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ if (q->state() != QBluetoothSocket::UnconnectedState) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService called on busy socket";
+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ q->setOpenMode(openMode);
+ connectToServiceHelper(address, port, openMode);
+}
+
+void QBluetoothSocketPrivateWin::abort()
+{
+ delete readNotifier;
+ readNotifier = nullptr;
+ delete connectWriteNotifier;
+ connectWriteNotifier = nullptr;
+ delete exceptNotifier;
+ exceptNotifier = nullptr;
+
+ // We don't transition through Closing for abort, so
+ // we don't call disconnectFromService or QBluetoothSocket::close
+ ::closesocket(socket);
+ socket = int(INVALID_SOCKET);
+
+ Q_Q(QBluetoothSocket);
+
+ const bool wasConnected = q->state() == QBluetoothSocket::ConnectedState;
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ if (wasConnected) {
+ q->setOpenMode(QIODevice::NotOpen);
+ emit q->readChannelFinished();
+ }
+}
+
+QString QBluetoothSocketPrivateWin::localName() const
+{
+ const QBluetoothAddress localAddr = localAddress();
+ if (localAddr == QBluetoothAddress())
+ return {};
+ const QBluetoothLocalDevice device(localAddr);
+ return device.name();
+}
+
+QBluetoothAddress QBluetoothSocketPrivateWin::localAddress() const
+{
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET)
+ return {};
+ SOCKADDR_BTH localAddr = {};
+ int localAddrLength = sizeof(localAddr);
+ const int localResult = ::getsockname(socket, reinterpret_cast<sockaddr *>(&localAddr), &localAddrLength);
+ if (localResult == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting local address" << error << qt_error_string(error);
+ return {};
+ }
+ return QBluetoothAddress(localAddr.btAddr);
+}
+
+quint16 QBluetoothSocketPrivateWin::localPort() const
+{
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET)
+ return {};
+ SOCKADDR_BTH localAddr = {};
+ int localAddrLength = sizeof(localAddr);
+ const int localResult = ::getsockname(socket, reinterpret_cast<sockaddr *>(&localAddr), &localAddrLength);
+ if (localResult == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting local port" << error << qt_error_string(error);
+ return {};
+ }
+ return localAddr.port;
+}
+
+QString QBluetoothSocketPrivateWin::peerName() const
+{
+ const QBluetoothAddress peerAddr = peerAddress();
+ if (peerAddr == QBluetoothAddress())
+ return {};
+ BLUETOOTH_DEVICE_INFO bdi = {};
+ bdi.dwSize = sizeof(bdi);
+ bdi.Address.ullLong = peerAddr.toUInt64();
+ const DWORD res = ::BluetoothGetDeviceInfo(nullptr, &bdi);
+ if (res != ERROR_SUCCESS) {
+ qCWarning(QT_BT_WINDOWS) << "Error calling BluetoothGetDeviceInfo" << res << qt_error_string(res);
+ return {};
+ }
+ return QString::fromWCharArray(&bdi.szName[0]);
+}
+
+QBluetoothAddress QBluetoothSocketPrivateWin::peerAddress() const
+{
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET)
+ return {};
+ SOCKADDR_BTH peerAddr = {};
+ int peerAddrLength = sizeof(peerAddr);
+ const int peerResult = ::getpeername(socket, reinterpret_cast<sockaddr *>(&peerAddr), &peerAddrLength);
+ if (peerResult == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error);
+ return {};
+ }
+ return QBluetoothAddress(peerAddr.btAddr);
+}
+
+quint16 QBluetoothSocketPrivateWin::peerPort() const
+{
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET)
+ return {};
+ SOCKADDR_BTH peerAddr = {};
+ int peerAddrLength = sizeof(peerAddr);
+ const int peerResult = ::getpeername(socket, reinterpret_cast<sockaddr *>(&peerAddr), &peerAddrLength);
+ if (peerResult == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error);
+ return {};
+ }
+ return peerAddr.port;
+}
+
+qint64 QBluetoothSocketPrivateWin::writeData(const char *data, qint64 maxSize)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot write while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ if (q->openMode() & QIODevice::Unbuffered) {
+ const int bytesWritten = ::send(socket, data, maxSize, 0);
+
+ if (bytesWritten == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error));
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ }
+
+ if (bytesWritten > 0)
+ emit q->bytesWritten(bytesWritten);
+
+ return bytesWritten;
+ } else {
+
+ if (!connectWriteNotifier)
+ return -1;
+
+ if (txBuffer.isEmpty()) {
+ connectWriteNotifier->setEnabled(true);
+ QMetaObject::invokeMethod(this, "_q_writeNotify", Qt::QueuedConnection);
+ }
+
+ char *txbuf = txBuffer.reserve(maxSize);
+ ::memcpy(txbuf, data, maxSize);
+
+ return maxSize;
+ }
+}
+
+qint64 QBluetoothSocketPrivateWin::readData(char *data, qint64 maxSize)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot read while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ const int bytesRead = buffer.read(data, maxSize);
+ return bytesRead;
+}
+
+void QBluetoothSocketPrivateWin::close()
+{
+ if (txBuffer.isEmpty())
+ abort();
+ else
+ connectWriteNotifier->setEnabled(true);
+}
+
+bool QBluetoothSocketPrivateWin::setSocketDescriptor(int socketDescriptor,
+ QBluetoothServiceInfo::Protocol protocol,
+ QBluetoothSocket::SocketState socketState,
+ QBluetoothSocket::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ abort();
+
+ socketType = protocol;
+ socket = socketDescriptor;
+
+ if (!createNotifiers())
+ return false;
+ q->setSocketState(socketState);
+ q->setOpenMode(openMode);
+ if (socketState == QBluetoothSocket::ConnectedState) {
+ connectWriteNotifier->setEnabled(true);
+ readNotifier->setEnabled(true);
+ exceptNotifier->setEnabled(true);
+ }
+
+ return true;
+}
+
+qint64 QBluetoothSocketPrivateWin::bytesAvailable() const
+{
+ return buffer.size();
+}
+
+bool QBluetoothSocketPrivateWin::canReadLine() const
+{
+ return buffer.canReadLine();
+}
+
+qint64 QBluetoothSocketPrivateWin::bytesToWrite() const
+{
+ return txBuffer.size();
+}
+
+bool QBluetoothSocketPrivateWin::createNotifiers()
+{
+ Q_Q(QBluetoothSocket);
+
+ ULONG mode = 1; // 1 to enable non-blocking socket
+ const int result = ::ioctlsocket(socket, FIONBIO, &mode);
+
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ qCWarning(QT_BT_WINDOWS) << "Error setting socket to non-blocking" << error << errorString;
+ abort();
+ return false;
+ }
+ readNotifier = new QSocketNotifier(socket, QSocketNotifier::Read);
+ QObject::connect(readNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_readNotify);
+ connectWriteNotifier = new QSocketNotifier(socket, QSocketNotifier::Write, q);
+ QObject::connect(connectWriteNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_writeNotify);
+ exceptNotifier = new QSocketNotifier(socket, QSocketNotifier::Exception, q);
+ QObject::connect(exceptNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_exceptNotify);
+
+ connectWriteNotifier->setEnabled(false);
+ readNotifier->setEnabled(false);
+ exceptNotifier->setEnabled(false);
+ return true;
+}
+
+bool QBluetoothSocketPrivateWin::configureSecurity()
+{
+ Q_Q(QBluetoothSocket);
+
+ if (secFlags & QBluetooth::Authorization) {
+ ULONG authenticate = TRUE;
+ const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_AUTHENTICATE, reinterpret_cast<const char*>(&authenticate), sizeof(authenticate));
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error;
+ qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot set connection security level");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+ }
+
+ if (secFlags & QBluetooth::Encryption) {
+ ULONG encrypt = TRUE;
+ const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_ENCRYPT, reinterpret_cast<const char*>(&encrypt), sizeof(encrypt));
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error;
+ qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot set connection security level");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+ }
+ return true;
+}
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothsocket_win_p.h b/src/bluetooth/qbluetoothsocket_win_p.h
new file mode 100644
index 00000000..77f4842e
--- /dev/null
+++ b/src/bluetooth/qbluetoothsocket_win_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** 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 QBLUETOOTHSOCKET_WIN_H
+#define QBLUETOOTHSOCKET_WIN_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 "qbluetoothsocket.h"
+#include "qbluetoothsocketbase_p.h"
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+
+class QBluetoothSocketPrivateWin final : public QBluetoothSocketBasePrivate
+{
+ Q_OBJECT
+ friend class QBluetoothServerPrivate;
+
+public:
+ QBluetoothSocketPrivateWin();
+ ~QBluetoothSocketPrivateWin() override;
+
+ void connectToServiceHelper(const QBluetoothAddress &address,
+ quint16 port,
+ QIODevice::OpenMode openMode) override;
+
+ void connectToService(const QBluetoothServiceInfo &service,
+ QIODevice::OpenMode openMode) override;
+ void connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid,
+ QIODevice::OpenMode openMode) override;
+ void connectToService(const QBluetoothAddress &address, quint16 port,
+ QIODevice::OpenMode openMode) override;
+
+ bool ensureNativeSocket(QBluetoothServiceInfo::Protocol type) override;
+
+ QString localName() const override;
+ QBluetoothAddress localAddress() const override;
+ quint16 localPort() const override;
+
+ QString peerName() const override;
+ QBluetoothAddress peerAddress() const override;
+ quint16 peerPort() const override;
+
+ void abort() override;
+ void close() override;
+
+ qint64 writeData(const char *data, qint64 maxSize) override;
+ qint64 readData(char *data, qint64 maxSize) override;
+
+ bool setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
+ QBluetoothSocket::SocketState socketState = QBluetoothSocket::ConnectedState,
+ QBluetoothSocket::OpenMode openMode = QBluetoothSocket::ReadWrite) override;
+
+ qint64 bytesAvailable() const override;
+ bool canReadLine() const override;
+ qint64 bytesToWrite() const override;
+
+private slots:
+ void _q_readNotify();
+ void _q_writeNotify();
+ void _q_exceptNotify();
+
+private:
+ bool createNotifiers();
+ bool configureSecurity();
+
+ QSocketNotifier *exceptNotifier = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QBLUETOOTHSOCKET_WIN_H
diff --git a/src/bluetooth/qbluetoothtransfermanager.cpp b/src/bluetooth/qbluetoothtransfermanager.cpp
index 37a3191a..d84f726c 100644
--- a/src/bluetooth/qbluetoothtransfermanager.cpp
+++ b/src/bluetooth/qbluetoothtransfermanager.cpp
@@ -121,8 +121,8 @@ QBluetoothTransferReply *QBluetoothTransferManager::put(const QBluetoothTransfer
connect(reply, SIGNAL(finished(QBluetoothTransferReply*)), this, SIGNAL(finished(QBluetoothTransferReply*)));
return reply;
#else
- // Android, iOS, and WinRT have no implementation
-#if !defined(QT_ANDROID_BLUETOOTH) && !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH)
+ // Android, iOS, and Win/WinRT have no implementation
+#if !defined(QT_ANDROID_BLUETOOTH) && !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH) && !defined(QT_WIN_BLUETOOTH)
printDummyWarning();
#endif
Q_UNUSED(request);
diff --git a/src/bluetooth/qbluetoothutils_win.cpp b/src/bluetooth/qbluetoothutils_win.cpp
index fa3127cb..a5151d82 100644
--- a/src/bluetooth/qbluetoothutils_win.cpp
+++ b/src/bluetooth/qbluetoothutils_win.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** 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.
diff --git a/src/bluetooth/qlowenergycharacteristic.h b/src/bluetooth/qlowenergycharacteristic.h
index d5d783c2..fe9b73fa 100644
--- a/src/bluetooth/qlowenergycharacteristic.h
+++ b/src/bluetooth/qlowenergycharacteristic.h
@@ -101,6 +101,7 @@ protected:
friend class QLowEnergyControllerPrivateBluez;
friend class QLowEnergyControllerPrivateBluezDBus;
friend class QLowEnergyControllerPrivateCommon;
+ friend class QLowEnergyControllerPrivateWin32;
friend class QLowEnergyControllerPrivateDarwin;
friend class QLowEnergyControllerPrivateWinRT;
friend class QLowEnergyControllerPrivateWinRTNew;
diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp
index d6af6999..bd263812 100644
--- a/src/bluetooth/qlowenergycontroller.cpp
+++ b/src/bluetooth/qlowenergycontroller.cpp
@@ -60,6 +60,8 @@
#if QT_CONFIG(winrt_btle_no_pairing)
#include "qlowenergycontroller_winrt_new_p.h"
#endif
+#elif defined(QT_WIN_BLUETOOTH)
+#include "qlowenergycontroller_win_p.h"
#elif defined(Q_OS_DARWIN)
#include "qlowenergycontroller_darwin_p.h"
#else
@@ -326,6 +328,9 @@ static QLowEnergyControllerPrivate *privateController(QLowEnergyController::Role
qCDebug(QT_BT_WINRT) << "Using pre 15063 low energy controller";
return new QLowEnergyControllerPrivateWinRT();
#endif
+#elif defined(QT_WIN_BLUETOOTH)
+ Q_UNUSED(role);
+ return new QLowEnergyControllerPrivateWin32();
#elif defined(Q_OS_DARWIN)
Q_UNUSED(role)
return new QLowEnergyControllerPrivateDarwin();
diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp
new file mode 100644
index 00000000..ced69685
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_win.cpp
@@ -0,0 +1,1356 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** 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_win_p.h"
+#include "qbluetoothdevicediscoveryagent_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QIODevice> // for open modes
+#include <QtCore/QEvent>
+#include <QtCore/QMutex>
+#include <QtCore/QThread>
+#include <QtCore/QDataStream>
+#include <QtCore/QCoreApplication>
+
+#include <algorithm> // for std::max
+
+#include <SetupAPI.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+Q_GLOBAL_STATIC(QLibrary, bluetoothapis)
+
+Q_GLOBAL_STATIC(QVector<QLowEnergyControllerPrivateWin32 *>, qControllers)
+static QMutex controllersGuard(QMutex::NonRecursive);
+
+const QEvent::Type CharactericticValueEventType = static_cast<QEvent::Type>(QEvent::User + 1);
+
+class CharactericticValueEvent : public QEvent
+{
+public:
+ explicit CharactericticValueEvent(const PBLUETOOTH_GATT_VALUE_CHANGED_EVENT gattValueChangedEvent)
+ : QEvent(CharactericticValueEventType)
+ , m_handle(0)
+ {
+ if (!gattValueChangedEvent || gattValueChangedEvent->CharacteristicValueDataSize == 0)
+ return;
+
+ m_handle = gattValueChangedEvent->ChangedAttributeHandle;
+
+ const PBTH_LE_GATT_CHARACTERISTIC_VALUE gattValue = gattValueChangedEvent->CharacteristicValue;
+ if (!gattValue)
+ return;
+
+ m_value = QByteArray(reinterpret_cast<const char *>(&gattValue->Data[0]),
+ int(gattValue->DataSize));
+ }
+
+ QByteArray m_value;
+ QLowEnergyHandle m_handle;
+};
+
+// Bit masks of ClientCharacteristicConfiguration value, see btle spec.
+namespace ClientCharacteristicConfigurationValue {
+enum { UseNotifications = 0x1, UseIndications = 0x2 };
+}
+
+static bool gattFunctionsResolved = false;
+
+static QBluetoothAddress getDeviceAddress(const QString &servicePath)
+{
+ const int firstbound = servicePath.lastIndexOf(QStringLiteral("_"));
+ const int lastbound = servicePath.indexOf(QLatin1Char('#'), firstbound);
+ const QString hex = servicePath.mid(firstbound + 1, lastbound - firstbound - 1);
+ bool ok = false;
+ return QBluetoothAddress(hex.toULongLong(&ok, 16));
+}
+
+static QString getServiceSystemPath(const QBluetoothAddress &deviceAddress,
+ const QBluetoothUuid &serviceUuid, int *systemErrorCode)
+{
+ const HDEVINFO deviceInfoSet = ::SetupDiGetClassDevs(
+ reinterpret_cast<const GUID *>(&serviceUuid),
+ nullptr,
+ nullptr,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+
+ if (deviceInfoSet == INVALID_HANDLE_VALUE) {
+ *systemErrorCode = int(::GetLastError());
+ return QString();
+ }
+
+ QString foundSystemPath;
+ DWORD index = 0;
+
+ for (;;) {
+ SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
+ ::ZeroMemory(&deviceInterfaceData, sizeof(deviceInterfaceData));
+ deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
+
+ if (!::SetupDiEnumDeviceInterfaces(
+ deviceInfoSet,
+ nullptr,
+ reinterpret_cast<const GUID *>(&serviceUuid),
+ index++,
+ &deviceInterfaceData)) {
+ *systemErrorCode = int(::GetLastError());
+ break;
+ }
+
+ DWORD deviceInterfaceDetailDataSize = 0;
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ deviceInfoSet,
+ &deviceInterfaceData,
+ nullptr,
+ deviceInterfaceDetailDataSize,
+ &deviceInterfaceDetailDataSize,
+ nullptr)) {
+ const int error = int(::GetLastError());
+ if (error != ERROR_INSUFFICIENT_BUFFER) {
+ *systemErrorCode = error;
+ break;
+ }
+ }
+
+ SP_DEVINFO_DATA deviceInfoData;
+ ::ZeroMemory(&deviceInfoData, sizeof(deviceInfoData));
+ deviceInfoData.cbSize = sizeof(deviceInfoData);
+
+ QByteArray deviceInterfaceDetailDataBuffer(
+ int(deviceInterfaceDetailDataSize), 0);
+
+ PSP_INTERFACE_DEVICE_DETAIL_DATA deviceInterfaceDetailData =
+ reinterpret_cast<PSP_INTERFACE_DEVICE_DETAIL_DATA>
+ (deviceInterfaceDetailDataBuffer.data());
+
+ deviceInterfaceDetailData->cbSize =
+ sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
+
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ deviceInfoSet,
+ &deviceInterfaceData,
+ deviceInterfaceDetailData,
+ DWORD(deviceInterfaceDetailDataBuffer.size()),
+ &deviceInterfaceDetailDataSize,
+ &deviceInfoData)) {
+ *systemErrorCode = int(::GetLastError());
+ break;
+ }
+
+ // We need to check on required device address which contains in a
+ // system path. As it is not enough to use only service UUID for this.
+ const auto candidateSystemPath = QString::fromWCharArray(deviceInterfaceDetailData->DevicePath);
+ const auto candidateDeviceAddress = getDeviceAddress(candidateSystemPath);
+ if (candidateDeviceAddress == deviceAddress) {
+ foundSystemPath = candidateSystemPath;
+ *systemErrorCode = NO_ERROR;
+ break;
+ }
+ }
+
+ ::SetupDiDestroyDeviceInfoList(deviceInfoSet);
+ return foundSystemPath;
+}
+
+static HANDLE openSystemDevice(
+ const QString &systemPath, QIODevice::OpenMode openMode, int *systemErrorCode)
+{
+ DWORD desiredAccess = 0;
+ DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+ if (openMode & QIODevice::ReadOnly) {
+ desiredAccess |= GENERIC_READ;
+ }
+
+ if (openMode & QIODevice::WriteOnly) {
+ desiredAccess |= GENERIC_WRITE;
+ shareMode &= ~DWORD(FILE_SHARE_WRITE);
+ }
+
+ const HANDLE hDevice = ::CreateFile(
+ reinterpret_cast<const wchar_t *>(systemPath.utf16()),
+ desiredAccess,
+ shareMode,
+ nullptr,
+ OPEN_EXISTING,
+ 0,
+ nullptr);
+
+ *systemErrorCode = (INVALID_HANDLE_VALUE == hDevice)
+ ? int(::GetLastError()) : NO_ERROR;
+ return hDevice;
+}
+
+static HANDLE openSystemService(const QBluetoothAddress &deviceAddress,
+ const QBluetoothUuid &service, QIODevice::OpenMode openMode, int *systemErrorCode)
+{
+ const QString serviceSystemPath = getServiceSystemPath(
+ deviceAddress, service, systemErrorCode);
+
+ if (*systemErrorCode != NO_ERROR)
+ return INVALID_HANDLE_VALUE;
+
+ const HANDLE hService = openSystemDevice(
+ serviceSystemPath, openMode, systemErrorCode);
+
+ if (*systemErrorCode != NO_ERROR)
+ return INVALID_HANDLE_VALUE;
+
+ return hService;
+}
+
+static void closeSystemDevice(HANDLE hDevice)
+{
+ if (hDevice && hDevice != INVALID_HANDLE_VALUE)
+ ::CloseHandle(hDevice);
+}
+
+static QVector<BTH_LE_GATT_SERVICE> enumeratePrimaryGattServices(
+ HANDLE hDevice, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QVector<BTH_LE_GATT_SERVICE>();
+ }
+
+ QVector<BTH_LE_GATT_SERVICE> foundServices;
+ USHORT servicesCount = 0;
+ for (;;) {
+ const HRESULT hr = ::BluetoothGATTGetServices(
+ hDevice,
+ servicesCount,
+ foundServices.isEmpty() ? nullptr : &foundServices[0],
+ &servicesCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return foundServices;
+ } else {
+ const int error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ foundServices.resize(servicesCount);
+ } else {
+ *systemErrorCode = error;
+ return QVector<BTH_LE_GATT_SERVICE>();
+ }
+ }
+ }
+}
+
+static QVector<BTH_LE_GATT_CHARACTERISTIC> enumerateGattCharacteristics(
+ HANDLE hService, PBTH_LE_GATT_SERVICE gattService, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QVector<BTH_LE_GATT_CHARACTERISTIC>();
+ }
+
+ QVector<BTH_LE_GATT_CHARACTERISTIC> foundCharacteristics;
+ USHORT characteristicsCount = 0;
+ for (;;) {
+ const HRESULT hr = ::BluetoothGATTGetCharacteristics(
+ hService,
+ gattService,
+ characteristicsCount,
+ foundCharacteristics.isEmpty() ? nullptr : &foundCharacteristics[0],
+ &characteristicsCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return foundCharacteristics;
+ } else {
+ const int error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ foundCharacteristics.resize(characteristicsCount);
+ } else {
+ *systemErrorCode = error;
+ return QVector<BTH_LE_GATT_CHARACTERISTIC>();
+ }
+ }
+ }
+}
+
+static QByteArray getGattCharacteristicValue(
+ HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QByteArray();
+ }
+
+ QByteArray valueBuffer;
+ USHORT valueBufferSize = 0;
+ for (;;) {
+ const auto valuePtr = valueBuffer.isEmpty()
+ ? nullptr
+ : reinterpret_cast<PBTH_LE_GATT_CHARACTERISTIC_VALUE>(valueBuffer.data());
+
+ const HRESULT hr = ::BluetoothGATTGetCharacteristicValue(
+ hService,
+ gattCharacteristic,
+ valueBufferSize,
+ valuePtr,
+ &valueBufferSize,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return QByteArray(reinterpret_cast<const char *>(&valuePtr->Data[0]),
+ int(valuePtr->DataSize));
+ } else {
+ const int error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ valueBuffer.resize(valueBufferSize);
+ valueBuffer.fill(0);
+ } else {
+ *systemErrorCode = error;
+ return QByteArray();
+ }
+ }
+ }
+}
+
+static void setGattCharacteristicValue(
+ HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic,
+ const QByteArray &value, DWORD flags, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return;
+ }
+
+ QByteArray valueBuffer;
+ QDataStream out(&valueBuffer, QIODevice::WriteOnly);
+ ULONG dataSize = ULONG(value.size());
+ out.writeRawData(reinterpret_cast<const char *>(&dataSize), sizeof(dataSize));
+ out.writeRawData(value.constData(), value.size());
+
+ BTH_LE_GATT_RELIABLE_WRITE_CONTEXT reliableWriteContext = 0;
+
+ const HRESULT hr = ::BluetoothGATTSetCharacteristicValue(
+ hService,
+ gattCharacteristic,
+ reinterpret_cast<PBTH_LE_GATT_CHARACTERISTIC_VALUE>(valueBuffer.data()),
+ reliableWriteContext,
+ flags);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+}
+
+static QVector<BTH_LE_GATT_DESCRIPTOR> enumerateGattDescriptors(
+ HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QVector<BTH_LE_GATT_DESCRIPTOR>();
+ }
+
+ QVector<BTH_LE_GATT_DESCRIPTOR> foundDescriptors;
+ USHORT descriptorsCount = 0;
+ for (;;) {
+ const HRESULT hr = ::BluetoothGATTGetDescriptors(
+ hService,
+ gattCharacteristic,
+ descriptorsCount,
+ foundDescriptors.isEmpty() ? nullptr : &foundDescriptors[0],
+ &descriptorsCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return foundDescriptors;
+ } else {
+ const int error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ foundDescriptors.resize(descriptorsCount);
+ } else {
+ *systemErrorCode = error;
+ return QVector<BTH_LE_GATT_DESCRIPTOR>();
+ }
+ }
+ }
+}
+
+static QByteArray getGattDescriptorValue(
+ HANDLE hService, PBTH_LE_GATT_DESCRIPTOR gattDescriptor, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QByteArray();
+ }
+
+ QByteArray valueBuffer;
+ USHORT valueBufferSize = 0;
+ for (;;) {
+ const auto valuePtr = valueBuffer.isEmpty()
+ ? nullptr
+ : reinterpret_cast<PBTH_LE_GATT_DESCRIPTOR_VALUE>(valueBuffer.data());
+
+ const HRESULT hr = ::BluetoothGATTGetDescriptorValue(
+ hService,
+ gattDescriptor,
+ valueBufferSize,
+ valuePtr,
+ &valueBufferSize,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ if (gattDescriptor->DescriptorType == CharacteristicUserDescription) {
+ QString valueString = QString::fromUtf16(reinterpret_cast<const ushort *>(&valuePtr->Data[0]),
+ valuePtr->DataSize/2);
+ return valueString.toUtf8();
+ }
+ return QByteArray(reinterpret_cast<const char *>(&valuePtr->Data[0]),
+ int(valuePtr->DataSize));
+ } else {
+ const int error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ valueBuffer.resize(valueBufferSize);
+ valueBuffer.fill(0);
+ } else {
+ *systemErrorCode = error;
+ return QByteArray();
+ }
+ }
+ }
+}
+
+static void setGattDescriptorValue(
+ HANDLE hService, PBTH_LE_GATT_DESCRIPTOR gattDescriptor,
+ QByteArray value, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return;
+ }
+
+ const int requiredValueBufferSize = int(sizeof(BTH_LE_GATT_DESCRIPTOR_VALUE))
+ + value.size();
+
+ QByteArray valueBuffer(requiredValueBufferSize, 0);
+
+ PBTH_LE_GATT_DESCRIPTOR_VALUE gattValue = reinterpret_cast<
+ PBTH_LE_GATT_DESCRIPTOR_VALUE>(valueBuffer.data());
+
+ gattValue->DescriptorType = gattDescriptor->DescriptorType;
+
+ if (gattValue->DescriptorType == ClientCharacteristicConfiguration) {
+ QDataStream in(value);
+ quint8 u;
+ in >> u;
+
+ // We need to setup appropriate fields that allow to subscribe for events.
+ gattValue->ClientCharacteristicConfiguration.IsSubscribeToNotification =
+ bool(u & ClientCharacteristicConfigurationValue::UseNotifications);
+ gattValue->ClientCharacteristicConfiguration.IsSubscribeToIndication =
+ bool(u & ClientCharacteristicConfigurationValue::UseIndications);
+ }
+
+ gattValue->DataSize = ULONG(value.size());
+ ::memcpy(gattValue->Data, value.constData(), size_t(value.size()));
+
+ const HRESULT hr = ::BluetoothGATTSetDescriptorValue(
+ hService,
+ gattDescriptor,
+ gattValue,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+}
+
+static void WINAPI eventChangedCallbackEntry(
+ BTH_LE_GATT_EVENT_TYPE eventType, PVOID eventOutParameter, PVOID context)
+{
+ if ((eventType != CharacteristicValueChangedEvent) || !eventOutParameter || !context)
+ return;
+
+ QMutexLocker locker(&controllersGuard);
+ const auto target = static_cast<QLowEnergyControllerPrivateWin32 *>(context);
+ if (!qControllers->contains(target))
+ return;
+
+ CharactericticValueEvent *e = new CharactericticValueEvent(
+ reinterpret_cast<const PBLUETOOTH_GATT_VALUE_CHANGED_EVENT>(eventOutParameter));
+
+ QCoreApplication::postEvent(target, e);
+}
+
+static HANDLE registerEvent(
+ HANDLE hService, BTH_LE_GATT_CHARACTERISTIC gattCharacteristic,
+ PVOID context, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return INVALID_HANDLE_VALUE;
+ }
+
+ HANDLE hEvent = INVALID_HANDLE_VALUE;
+
+ BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION registration;
+ ::ZeroMemory(&registration, sizeof(registration));
+ registration.NumCharacteristics = 1;
+ registration.Characteristics[0] = gattCharacteristic;
+
+ const HRESULT hr = ::BluetoothGATTRegisterEvent(
+ hService,
+ CharacteristicValueChangedEvent,
+ &registration,
+ eventChangedCallbackEntry,
+ context,
+ &hEvent,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+
+ return hEvent;
+}
+
+static void unregisterEvent(HANDLE hEvent, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return;
+ }
+
+ const HRESULT hr = ::BluetoothGATTUnregisterEvent(
+ hEvent,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+}
+
+static QBluetoothUuid qtBluetoothUuidFromNativeLeUuid(const BTH_LE_UUID &uuid)
+{
+ return uuid.IsShortUuid ? QBluetoothUuid(uuid.Value.ShortUuid)
+ : QBluetoothUuid(uuid.Value.LongUuid);
+}
+
+static BTH_LE_UUID nativeLeUuidFromQtBluetoothUuid(const QBluetoothUuid &uuid)
+{
+ BTH_LE_UUID gattUuid;
+ ::ZeroMemory(&gattUuid, sizeof(gattUuid));
+ if (uuid.minimumSize() == 2) {
+ gattUuid.IsShortUuid = TRUE;
+ gattUuid.Value.ShortUuid = USHORT(uuid.data1); // other fields should be empty!
+ } else {
+ gattUuid.Value.LongUuid = uuid;
+ }
+ return gattUuid;
+}
+
+static BTH_LE_GATT_CHARACTERISTIC recoverNativeLeGattCharacteristic(
+ QLowEnergyHandle serviceHandle, QLowEnergyHandle characteristicHandle,
+ const QLowEnergyServicePrivate::CharData &characteristicData)
+{
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic;
+
+ gattCharacteristic.ServiceHandle = serviceHandle;
+ gattCharacteristic.AttributeHandle = characteristicHandle;
+ gattCharacteristic.CharacteristicValueHandle = characteristicData.valueHandle;
+
+ gattCharacteristic.CharacteristicUuid = nativeLeUuidFromQtBluetoothUuid(
+ characteristicData.uuid);
+
+ gattCharacteristic.HasExtendedProperties = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::ExtendedProperty);
+ gattCharacteristic.IsBroadcastable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Broadcasting);
+ gattCharacteristic.IsIndicatable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Indicate);
+ gattCharacteristic.IsNotifiable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Notify);
+ gattCharacteristic.IsReadable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Read);
+ gattCharacteristic.IsSignedWritable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::WriteSigned);
+ gattCharacteristic.IsWritable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Write);
+ gattCharacteristic.IsWritableWithoutResponse = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::WriteNoResponse);
+
+ return gattCharacteristic;
+}
+
+static BTH_LE_GATT_DESCRIPTOR_TYPE nativeLeGattDescriptorTypeFromUuid(
+ const QBluetoothUuid &uuid)
+{
+ switch (uuid.toUInt16()) {
+ case QBluetoothUuid::CharacteristicExtendedProperties:
+ return CharacteristicExtendedProperties;
+ case QBluetoothUuid::CharacteristicUserDescription:
+ return CharacteristicUserDescription;
+ case QBluetoothUuid::ClientCharacteristicConfiguration:
+ return ClientCharacteristicConfiguration;
+ case QBluetoothUuid::ServerCharacteristicConfiguration:
+ return ServerCharacteristicConfiguration;
+ case QBluetoothUuid::CharacteristicPresentationFormat:
+ return CharacteristicFormat;
+ case QBluetoothUuid::CharacteristicAggregateFormat:
+ return CharacteristicAggregateFormat;
+ default:
+ return CustomDescriptor;
+ }
+}
+
+static BTH_LE_GATT_DESCRIPTOR recoverNativeLeGattDescriptor(
+ QLowEnergyHandle serviceHandle, QLowEnergyHandle characteristicHandle,
+ QLowEnergyHandle descriptorHandle,
+ const QLowEnergyServicePrivate::DescData &descriptorData)
+{
+ BTH_LE_GATT_DESCRIPTOR gattDescriptor;
+
+ gattDescriptor.ServiceHandle = serviceHandle;
+ gattDescriptor.CharacteristicHandle = characteristicHandle;
+ gattDescriptor.AttributeHandle = descriptorHandle;
+
+ gattDescriptor.DescriptorUuid = nativeLeUuidFromQtBluetoothUuid(
+ descriptorData.uuid);
+
+ gattDescriptor.DescriptorType = nativeLeGattDescriptorTypeFromUuid
+ (descriptorData.uuid);
+
+ return gattDescriptor;
+}
+
+void QLowEnergyControllerPrivateWin32::customEvent(QEvent *e)
+{
+ if (e->type() != CharactericticValueEventType)
+ return;
+
+ const CharactericticValueEvent *characteristicEvent
+ = static_cast<CharactericticValueEvent *>(e);
+
+ updateValueOfCharacteristic(characteristicEvent->m_handle,
+ characteristicEvent->m_value, false);
+
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(
+ characteristicEvent->m_handle);
+ if (service.isNull())
+ return;
+
+ const QLowEnergyCharacteristic ch(service, characteristicEvent->m_handle);
+ emit service->characteristicChanged(ch, characteristicEvent->m_value);
+}
+
+QLowEnergyControllerPrivateWin32::QLowEnergyControllerPrivateWin32()
+ : QLowEnergyControllerPrivate()
+{
+ QMutexLocker locker(&controllersGuard);
+ qControllers()->append(this);
+
+ gattFunctionsResolved = resolveFunctions(bluetoothapis());
+ if (!gattFunctionsResolved) {
+ qCWarning(QT_BT_WINDOWS) << "LE is not supported on this OS";
+ return;
+ }
+}
+
+QLowEnergyControllerPrivateWin32::~QLowEnergyControllerPrivateWin32()
+{
+ QMutexLocker locker(&controllersGuard);
+ qControllers()->removeAll(this);
+}
+
+void QLowEnergyControllerPrivateWin32::init()
+{
+}
+
+void QLowEnergyControllerPrivateWin32::connectToDevice()
+{
+ // required to pass unit test on default backend
+ if (remoteDevice.isNull()) {
+ qWarning() << "Invalid/null remote device address";
+ setError(QLowEnergyController::UnknownRemoteDeviceError);
+ return;
+ }
+
+ if (!deviceSystemPath.isEmpty()) {
+ qCDebug(QT_BT_WINDOWS) << "Already is connected";
+ return;
+ }
+
+ setState(QLowEnergyController::ConnectingState);
+
+ deviceSystemPath =
+ QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath(
+ remoteDevice);
+
+ if (deviceSystemPath.isEmpty()) {
+ qCWarning(QT_BT_WINDOWS) << qt_error_string(ERROR_PATH_NOT_FOUND);
+ setError(QLowEnergyController::UnknownRemoteDeviceError);
+ setState(QLowEnergyController::UnconnectedState);
+ return;
+ }
+
+ setState(QLowEnergyController::ConnectedState);
+
+ thread = new QThread;
+ threadWorker = new ThreadWorker;
+ threadWorker->moveToThread(thread);
+ connect(threadWorker, &ThreadWorker::jobFinished, this, &QLowEnergyControllerPrivateWin32::jobFinished);
+ connect(thread, &QThread::finished, threadWorker, &ThreadWorker::deleteLater);
+ connect(thread, &QThread::finished, thread, &QThread::deleteLater);
+ thread->start();
+
+ Q_Q(QLowEnergyController);
+ emit q->connected();
+}
+
+void QLowEnergyControllerPrivateWin32::disconnectFromDevice()
+{
+ if (deviceSystemPath.isEmpty()) {
+ qCDebug(QT_BT_WINDOWS) << "Already is disconnected";
+ return;
+ }
+
+ setState(QLowEnergyController::ClosingState);
+ deviceSystemPath.clear();
+ setState(QLowEnergyController::UnconnectedState);
+
+ if (thread) {
+ disconnect(threadWorker, &ThreadWorker::jobFinished, this, &QLowEnergyControllerPrivateWin32::jobFinished);
+ thread->quit();
+ thread = nullptr;
+ }
+
+ for (const auto &servicePrivate: serviceList)
+ closeSystemDevice(servicePrivate->hService);
+
+ Q_Q(QLowEnergyController);
+ emit q->disconnected();
+}
+
+void QLowEnergyControllerPrivateWin32::discoverServices()
+{
+ int systemErrorCode = NO_ERROR;
+
+ const HANDLE hDevice = openSystemDevice(
+ deviceSystemPath, QIODevice::ReadOnly, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << qt_error_string(systemErrorCode);
+ setError(QLowEnergyController::NetworkError);
+ setState(QLowEnergyController::ConnectedState);
+ return;
+ }
+
+ const QVector<BTH_LE_GATT_SERVICE> foundServices =
+ enumeratePrimaryGattServices(hDevice, &systemErrorCode);
+
+ closeSystemDevice(hDevice);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << qt_error_string(systemErrorCode);
+ setError(QLowEnergyController::NetworkError);
+ setState(QLowEnergyController::ConnectedState);
+ return;
+ }
+
+ setState(QLowEnergyController::DiscoveringState);
+
+ Q_Q(QLowEnergyController);
+
+ for (const BTH_LE_GATT_SERVICE &service : foundServices) {
+ const QBluetoothUuid uuid = qtBluetoothUuidFromNativeLeUuid(
+ service.ServiceUuid);
+ qCDebug(QT_BT_WINDOWS) << "Found uuid:" << uuid;
+
+ QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate();
+ priv->uuid = uuid;
+ priv->type = QLowEnergyService::PrimaryService;
+ priv->startHandle = service.AttributeHandle;
+ priv->setController(this);
+
+ QSharedPointer<QLowEnergyServicePrivate> pointer(priv);
+ serviceList.insert(uuid, pointer);
+
+ emit q->serviceDiscovered(uuid);
+ }
+
+ setState(QLowEnergyController::DiscoveredState);
+ emit q->discoveryFinished();
+}
+
+void QLowEnergyControllerPrivateWin32::discoverServiceDetails(
+ const QBluetoothUuid &service)
+{
+ if (!serviceList.contains(service)) {
+ qCWarning(QT_BT_WINDOWS) << "Discovery of unknown service" << service.toString()
+ << "not possible";
+ return;
+ }
+
+ const QSharedPointer<QLowEnergyServicePrivate> servicePrivate =
+ serviceList.value(service);
+
+ int systemErrorCode = NO_ERROR;
+
+ // Only open a service once and close it in the QLowEnergyServicePrivate destructor
+ if (!servicePrivate->hService || servicePrivate->hService == INVALID_HANDLE_VALUE) {
+ servicePrivate->hService = openSystemService(remoteDevice, service,
+ QIODevice::ReadOnly | QIODevice::WriteOnly,
+ &systemErrorCode);
+ if (systemErrorCode != NO_ERROR) {
+ servicePrivate->hService = openSystemService(remoteDevice, service,
+ QIODevice::ReadOnly,
+ &systemErrorCode);
+ }
+ }
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::UnknownError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+
+ // We assume that the service does not have any characteristics with descriptors.
+ servicePrivate->endHandle = servicePrivate->startHandle;
+
+ const QVector<BTH_LE_GATT_CHARACTERISTIC> foundCharacteristics =
+ enumerateGattCharacteristics(servicePrivate->hService, nullptr, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get characteristics for service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::CharacteristicReadError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+
+ for (const BTH_LE_GATT_CHARACTERISTIC &gattCharacteristic : foundCharacteristics) {
+ const QLowEnergyHandle characteristicHandle = gattCharacteristic.AttributeHandle;
+
+ QLowEnergyServicePrivate::CharData detailsData;
+
+ detailsData.hValueChangeEvent = nullptr;
+
+ detailsData.uuid = qtBluetoothUuidFromNativeLeUuid(
+ gattCharacteristic.CharacteristicUuid);
+ detailsData.valueHandle = gattCharacteristic.CharacteristicValueHandle;
+
+ QLowEnergyCharacteristic::PropertyTypes properties = QLowEnergyCharacteristic::Unknown;
+ if (gattCharacteristic.HasExtendedProperties)
+ properties |= QLowEnergyCharacteristic::ExtendedProperty;
+ if (gattCharacteristic.IsBroadcastable)
+ properties |= QLowEnergyCharacteristic::Broadcasting;
+ if (gattCharacteristic.IsIndicatable)
+ properties |= QLowEnergyCharacteristic::Indicate;
+ if (gattCharacteristic.IsNotifiable)
+ properties |= QLowEnergyCharacteristic::Notify;
+ if (gattCharacteristic.IsReadable)
+ properties |= QLowEnergyCharacteristic::Read;
+ if (gattCharacteristic.IsSignedWritable)
+ properties |= QLowEnergyCharacteristic::WriteSigned;
+ if (gattCharacteristic.IsWritable)
+ properties |= QLowEnergyCharacteristic::Write;
+ if (gattCharacteristic.IsWritableWithoutResponse)
+ properties |= QLowEnergyCharacteristic::WriteNoResponse;
+
+ detailsData.properties = properties;
+ detailsData.value = getGattCharacteristicValue(
+ servicePrivate->hService, const_cast<PBTH_LE_GATT_CHARACTERISTIC>(
+ &gattCharacteristic), &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ // We do not interrupt enumerating of characteristics
+ // if value can not be read
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for characteristic"
+ << detailsData.uuid.toString()
+ << "of the service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ }
+
+ // We assume that the characteristic has no any descriptors. So, the
+ // biggest characteristic + 1 will indicate an end handle of service.
+ servicePrivate->endHandle = std::max(
+ servicePrivate->endHandle,
+ QLowEnergyHandle(gattCharacteristic.AttributeHandle + 1));
+
+ const QVector<BTH_LE_GATT_DESCRIPTOR> foundDescriptors = enumerateGattDescriptors(
+ servicePrivate->hService, const_cast<PBTH_LE_GATT_CHARACTERISTIC>(
+ &gattCharacteristic), &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ if (systemErrorCode != ERROR_NOT_FOUND) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get descriptor for characteristic"
+ << detailsData.uuid.toString()
+ << "of the service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::DescriptorReadError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+ }
+
+ for (const BTH_LE_GATT_DESCRIPTOR &gattDescriptor : foundDescriptors) {
+ const QLowEnergyHandle descriptorHandle = gattDescriptor.AttributeHandle;
+
+ QLowEnergyServicePrivate::DescData data;
+ data.uuid = qtBluetoothUuidFromNativeLeUuid(
+ gattDescriptor.DescriptorUuid);
+
+ data.value = getGattDescriptorValue(servicePrivate->hService, const_cast<PBTH_LE_GATT_DESCRIPTOR>(
+ &gattDescriptor), &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for descriptor"
+ << data.uuid.toString()
+ << "for characteristic"
+ << detailsData.uuid.toString()
+ << "of the service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::DescriptorReadError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+
+ // Biggest descriptor will contain an end handle of service.
+ servicePrivate->endHandle = std::max(
+ servicePrivate->endHandle,
+ QLowEnergyHandle(gattDescriptor.AttributeHandle));
+
+ detailsData.descriptorList.insert(descriptorHandle, data);
+ }
+
+ servicePrivate->characteristicList.insert(characteristicHandle, detailsData);
+ }
+
+ servicePrivate->setState(QLowEnergyService::ServiceDiscovered);
+}
+
+void QLowEnergyControllerPrivateWin32::startAdvertising(const QLowEnergyAdvertisingParameters &, const QLowEnergyAdvertisingData &, const QLowEnergyAdvertisingData &)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWin32::stopAdvertising()
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWin32::requestConnectionUpdate(const QLowEnergyConnectionParameters &)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWin32::readCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+ if (!(charDetails.properties & QLowEnergyCharacteristic::Read)) {
+ // if this succeeds the device has a bug, char is advertised as
+ // non-readable. We try to be permissive and let the remote
+ // device answer to the read attempt
+ qCWarning(QT_BT_WINDOWS) << "Reading non-readable char" << charHandle;
+ }
+
+ ReadCharData data;
+ data.systemErrorCode = NO_ERROR;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicReadError);
+ return;
+ }
+
+ data.gattCharacteristic = recoverNativeLeGattCharacteristic(
+ service->startHandle, charHandle, charDetails);
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::ReadChar;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::writeCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QByteArray &newValue,
+ QLowEnergyService::WriteMode mode)
+{
+ Q_ASSERT(!service.isNull());
+
+ if (!service->characteristicList.contains(charHandle)) {
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+ WriteCharData data;
+ data.systemErrorCode = NO_ERROR;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+
+ data.gattCharacteristic = recoverNativeLeGattCharacteristic(
+ service->startHandle, charHandle, charDetails);
+
+ data.flags = (mode == QLowEnergyService::WriteWithResponse)
+ ? BLUETOOTH_GATT_FLAG_NONE
+ : BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE;
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::WriteChar;
+ data.newValue = newValue;
+ data.mode = mode;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job)
+{
+ switch (job.operation) {
+ case ThreadWorkerJob::WriteChar:
+ {
+ const WriteCharData data = job.data.value<WriteCharData>();
+ const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattCharacteristic.AttributeHandle);
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+
+ if (data.systemErrorCode != NO_ERROR) {
+ const QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle];
+ qCWarning(QT_BT_WINDOWS) << "Unable to set value for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+ updateValueOfCharacteristic(charHandle, data.newValue, false);
+
+ if (data.mode == QLowEnergyService::WriteWithResponse) {
+ const QLowEnergyCharacteristic ch = characteristicForHandle(charHandle);
+ emit service->characteristicWritten(ch, data.newValue);
+ }
+ }
+ break;
+ case ThreadWorkerJob::ReadChar:
+ {
+ const ReadCharData data = job.data.value<ReadCharData>();
+ const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattCharacteristic.AttributeHandle);
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+
+ if (data.systemErrorCode != NO_ERROR) {
+ const QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle];
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicReadError);
+ return;
+ }
+
+ updateValueOfCharacteristic(charHandle, data.value, false);
+
+ const QLowEnergyCharacteristic ch(service, charHandle);
+ emit service->characteristicRead(ch, data.value);
+ }
+ break;
+ case ThreadWorkerJob::WriteDescr:
+ {
+ WriteDescData data = job.data.value<WriteDescData>();
+ const QLowEnergyHandle descriptorHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.AttributeHandle);
+ const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.CharacteristicHandle);
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle];
+ const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle];
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to set value for descriptor"
+ << dscrDetails.uuid.toString()
+ << "for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+
+ if (data.gattDescriptor.DescriptorType == ClientCharacteristicConfiguration) {
+
+ QDataStream in(data.newValue);
+ quint8 u;
+ in >> u;
+
+ if (u & ClientCharacteristicConfigurationValue::UseNotifications
+ || u & ClientCharacteristicConfigurationValue::UseIndications) {
+ if (!charDetails.hValueChangeEvent) {
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic = recoverNativeLeGattCharacteristic(
+ service->startHandle, charHandle, charDetails);
+
+ // note: if the service handle is closed the event registration is no longer valid.
+ charDetails.hValueChangeEvent = registerEvent(
+ data.hService, gattCharacteristic, this, &data.systemErrorCode);
+ }
+ } else {
+ if (charDetails.hValueChangeEvent) {
+ unregisterEvent(charDetails.hValueChangeEvent, &data.systemErrorCode);
+ charDetails.hValueChangeEvent = nullptr;
+ }
+ }
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to subscribe events for descriptor"
+ << dscrDetails.uuid.toString()
+ << "for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+ }
+
+ updateValueOfDescriptor(charHandle, descriptorHandle, data.newValue, false);
+
+ const QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle);
+ emit service->descriptorWritten(dscr, data.newValue);
+ }
+ break;
+ case ThreadWorkerJob::ReadDescr:
+ {
+ ReadDescData data = job.data.value<ReadDescData>();
+ const QLowEnergyHandle descriptorHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.AttributeHandle);
+ const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.CharacteristicHandle);
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle];
+ const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle];
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for descriptor"
+ << dscrDetails.uuid.toString()
+ << "for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorReadError);
+ return;
+ }
+
+ updateValueOfDescriptor(charHandle, descriptorHandle, data.value, false);
+
+ QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle);
+ emit service->descriptorRead(dscr, data.value);
+ }
+ break;
+ }
+
+ QMetaObject::invokeMethod(threadWorker, "runPendingJob", Qt::QueuedConnection);
+}
+
+void QLowEnergyControllerPrivateWin32::readDescriptor(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+ if (!charDetails.descriptorList.contains(descriptorHandle))
+ return;
+
+ ReadDescData data;
+ data.systemErrorCode = NO_ERROR;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorReadError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::DescData &dscrDetails
+ = charDetails.descriptorList[descriptorHandle];
+
+ data.gattDescriptor = recoverNativeLeGattDescriptor(
+ service->startHandle, charHandle, descriptorHandle, dscrDetails);
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::ReadDescr;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::writeDescriptor(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+ if (!charDetails.descriptorList.contains(descriptorHandle))
+ return;
+
+ WriteDescData data;
+ data.systemErrorCode = NO_ERROR;
+ data.newValue = newValue;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::DescData &dscrDetails
+ = charDetails.descriptorList[descriptorHandle];
+
+ data.gattDescriptor = recoverNativeLeGattDescriptor(
+ service->startHandle, charHandle, descriptorHandle, dscrDetails);
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::WriteDescr;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::addToGenericAttributeList(const QLowEnergyServiceData &, QLowEnergyHandle)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void ThreadWorker::putJob(const ThreadWorkerJob &job)
+{
+ m_jobs.append(job);
+ if (m_jobs.count() == 1)
+ runPendingJob();
+}
+
+void ThreadWorker::runPendingJob()
+{
+ if (!m_jobs.count())
+ return;
+
+ ThreadWorkerJob job = m_jobs.first();
+
+ switch (job.operation) {
+ case ThreadWorkerJob::WriteChar:
+ {
+ WriteCharData data = job.data.value<WriteCharData>();
+ setGattCharacteristicValue(data.hService, &data.gattCharacteristic,
+ data.newValue, data.flags, &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ break;
+ case ThreadWorkerJob::ReadChar:
+ {
+ ReadCharData data = job.data.value<ReadCharData>();
+ data.value = getGattCharacteristicValue(
+ data.hService, &data.gattCharacteristic, &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ break;
+ case ThreadWorkerJob::WriteDescr:
+ {
+ WriteDescData data = job.data.value<WriteDescData>();
+ setGattDescriptorValue(data.hService, &data.gattDescriptor,
+ data.newValue, &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ break;
+ case ThreadWorkerJob::ReadDescr:
+ {
+ ReadDescData data = job.data.value<ReadDescData>();
+ data.value = getGattDescriptorValue(
+ data.hService,
+ const_cast<PBTH_LE_GATT_DESCRIPTOR>(&data.gattDescriptor),
+ &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ break;
+ }
+
+ m_jobs.removeFirst();
+ emit jobFinished(job);
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_win_p.h b/src/bluetooth/qlowenergycontroller_win_p.h
new file mode 100644
index 00000000..f8e3b10a
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_win_p.h
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** 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 QLOWENERGYCONTROLLERPRIVATE_WIN32_P_H
+#define QLOWENERGYCONTROLLERPRIVATE_WIN32_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 "qlowenergycontroller.h"
+#include "qlowenergycontrollerbase_p.h"
+
+#include <windows/qwinlowenergybluetooth_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QThread;
+class QLowEnergyControllerPrivateWin32;
+
+class ThreadWorkerJob
+{
+public:
+ enum Operation { WriteChar, ReadChar, WriteDescr, ReadDescr };
+ Operation operation;
+ QVariant data;
+};
+
+struct WriteCharData
+{
+ QByteArray newValue;
+ QLowEnergyService::WriteMode mode;
+ HANDLE hService;
+ DWORD flags;
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic;
+ int systemErrorCode;
+};
+
+struct ReadCharData
+{
+ QByteArray value;
+ HANDLE hService;
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic;
+ int systemErrorCode;
+};
+
+struct WriteDescData
+{
+ QByteArray newValue;
+ HANDLE hService;
+ BTH_LE_GATT_DESCRIPTOR gattDescriptor;
+ int systemErrorCode;
+};
+
+struct ReadDescData
+{
+ QByteArray value;
+ HANDLE hService;
+ BTH_LE_GATT_DESCRIPTOR gattDescriptor;
+ int systemErrorCode;
+};
+
+class ThreadWorker : public QObject
+{
+ Q_OBJECT
+public:
+ Q_INVOKABLE void putJob(const ThreadWorkerJob &job);
+ Q_INVOKABLE void runPendingJob();
+signals:
+ void jobFinished(const ThreadWorkerJob &job);
+private:
+ QVector<ThreadWorkerJob> m_jobs;
+};
+
+class QLowEnergyServiceData;
+
+extern void registerQLowEnergyControllerMetaType();
+
+class QLowEnergyControllerPrivateWin32 : public QLowEnergyControllerPrivate
+{
+ Q_OBJECT
+public:
+ QLowEnergyControllerPrivateWin32();
+ ~QLowEnergyControllerPrivateWin32();
+
+ 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;
+public slots:
+ void jobFinished(const ThreadWorkerJob &job);
+protected:
+ void customEvent(QEvent *e);
+private:
+ QThread *thread = nullptr;
+ ThreadWorker *threadWorker = nullptr;
+ QString deviceSystemPath;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(ThreadWorkerJob)
+Q_DECLARE_METATYPE(WriteCharData)
+Q_DECLARE_METATYPE(ReadCharData)
+Q_DECLARE_METATYPE(WriteDescData)
+Q_DECLARE_METATYPE(ReadDescData)
+
+#endif // QLOWENERGYCONTROLLERPRIVATE_WIN32__P_H
diff --git a/src/bluetooth/qlowenergydescriptor.h b/src/bluetooth/qlowenergydescriptor.h
index 84f48fbc..18bb53c0 100644
--- a/src/bluetooth/qlowenergydescriptor.h
+++ b/src/bluetooth/qlowenergydescriptor.h
@@ -83,6 +83,7 @@ protected:
friend class QLowEnergyControllerPrivateBluez;
friend class QLowEnergyControllerPrivateBluezDBus;
friend class QLowEnergyControllerPrivateCommon;
+ friend class QLowEnergyControllerPrivateWin32;
friend class QLowEnergyControllerPrivateDarwin;
friend class QLowEnergyControllerPrivateWinRT;
friend class QLowEnergyControllerPrivateWinRTNew;
diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h
index fb4163a0..226af145 100644
--- a/src/bluetooth/qlowenergyserviceprivate_p.h
+++ b/src/bluetooth/qlowenergyserviceprivate_p.h
@@ -60,6 +60,9 @@
#if defined(QT_ANDROID_BLUETOOTH)
#include <QtAndroidExtras/QAndroidJniObject>
#endif
+#if defined(QT_WIN_BLUETOOTH)
+#include <qt_windows.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -83,6 +86,9 @@ public:
QLowEnergyCharacteristic::PropertyTypes properties;
QByteArray value;
QHash<QLowEnergyHandle, DescData> descriptorList;
+#ifdef QT_WIN_BLUETOOTH
+ Qt::HANDLE hValueChangeEvent;
+#endif
};
enum GattAttributeTypes {
@@ -128,6 +134,9 @@ public:
// reference to the BluetoothGattService object
QAndroidJniObject androidService;
#endif
+#if defined(QT_WIN_BLUETOOTH)
+ Qt::HANDLE hService = nullptr;
+#endif
};
diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h
new file mode 100644
index 00000000..39b88a5f
--- /dev/null
+++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 QWINLOWENERGYBLUETOOTH_P_H
+#define QWINLOWENERGYBLUETOOTH_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/qlibrary.h>
+
+#include <qt_windows.h>
+
+#define WIN32_FROM_HRESULT(hr) \
+ (SUCCEEDED(hr) ? ERROR_SUCCESS : \
+ (HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : (hr)))
+
+#define BLUETOOTH_GATT_FLAG_NONE 0x00000000
+#define BLUETOOTH_GATT_FLAG_CONNECTION_ENCRYPTED 0x00000001
+#define BLUETOOTH_GATT_FLAG_CONNECTION_AUTHENTICATED 0x00000002
+#define BLUETOOTH_GATT_FLAG_FORCE_READ_FROM_DEVICE 0x00000004
+#define BLUETOOTH_GATT_FLAG_FORCE_READ_FROM_CACHE 0x00000008
+#define BLUETOOTH_GATT_FLAG_SIGNED_WRITE 0x00000010
+#define BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE 0x00000020
+#define BLUETOOTH_GATT_FLAG_RETURN_ALL 0x00000040
+
+typedef enum _BTH_LE_GATT_DESCRIPTOR_TYPE {
+ CharacteristicExtendedProperties,
+ CharacteristicUserDescription,
+ ClientCharacteristicConfiguration,
+ ServerCharacteristicConfiguration,
+ CharacteristicFormat,
+ CharacteristicAggregateFormat,
+ CustomDescriptor
+} BTH_LE_GATT_DESCRIPTOR_TYPE, *PBTH_LE_GATT_DESCRIPTOR_TYPE;
+
+typedef enum _BTH_LE_GATT_EVENT_TYPE {
+ CharacteristicValueChangedEvent
+} BTH_LE_GATT_EVENT_TYPE;
+
+typedef struct _BTH_LE_UUID {
+ BOOLEAN IsShortUuid;
+ union {
+ USHORT ShortUuid;
+ GUID LongUuid;
+ } Value;
+} BTH_LE_UUID, *PBTH_LE_UUID;
+
+typedef struct _BTH_LE_GATT_SERVICE {
+ BTH_LE_UUID ServiceUuid;
+ USHORT AttributeHandle;
+} BTH_LE_GATT_SERVICE, *PBTH_LE_GATT_SERVICE;
+
+typedef struct _BTH_LE_GATT_CHARACTERISTIC {
+ USHORT ServiceHandle;
+ BTH_LE_UUID CharacteristicUuid;
+ USHORT AttributeHandle;
+ USHORT CharacteristicValueHandle;
+ BOOLEAN IsBroadcastable;
+ BOOLEAN IsReadable;
+ BOOLEAN IsWritable;
+ BOOLEAN IsWritableWithoutResponse;
+ BOOLEAN IsSignedWritable;
+ BOOLEAN IsNotifiable;
+ BOOLEAN IsIndicatable;
+ BOOLEAN HasExtendedProperties;
+} BTH_LE_GATT_CHARACTERISTIC, *PBTH_LE_GATT_CHARACTERISTIC;
+
+typedef struct _BTH_LE_GATT_CHARACTERISTIC_VALUE {
+ ULONG DataSize;
+ UCHAR Data[1];
+} BTH_LE_GATT_CHARACTERISTIC_VALUE, *PBTH_LE_GATT_CHARACTERISTIC_VALUE;
+
+typedef struct _BTH_LE_GATT_DESCRIPTOR {
+ USHORT ServiceHandle;
+ USHORT CharacteristicHandle;
+ BTH_LE_GATT_DESCRIPTOR_TYPE DescriptorType;
+ BTH_LE_UUID DescriptorUuid;
+ USHORT AttributeHandle;
+} BTH_LE_GATT_DESCRIPTOR, *PBTH_LE_GATT_DESCRIPTOR;
+
+typedef struct _BTH_LE_GATT_DESCRIPTOR_VALUE {
+ BTH_LE_GATT_DESCRIPTOR_TYPE DescriptorType;
+ BTH_LE_UUID DescriptorUuid;
+ union {
+ struct {
+ BOOLEAN IsReliableWriteEnabled;
+ BOOLEAN IsAuxiliariesWritable;
+ } CharacteristicExtendedProperties;
+ struct {
+ BOOLEAN IsSubscribeToNotification;
+ BOOLEAN IsSubscribeToIndication;
+ } ClientCharacteristicConfiguration;
+ struct {
+ BOOLEAN IsBroadcast;
+ } ServerCharacteristicConfiguration;
+ struct {
+ UCHAR Format;
+ UCHAR Exponent;
+ BTH_LE_UUID Unit;
+ UCHAR NameSpace;
+ BTH_LE_UUID Description;
+ } CharacteristicFormat;
+ };
+ ULONG DataSize;
+ UCHAR Data[1];
+} BTH_LE_GATT_DESCRIPTOR_VALUE, *PBTH_LE_GATT_DESCRIPTOR_VALUE;
+
+typedef struct _BLUETOOTH_GATT_VALUE_CHANGED_EVENT {
+ USHORT ChangedAttributeHandle;
+ size_t CharacteristicValueDataSize;
+ PBTH_LE_GATT_CHARACTERISTIC_VALUE CharacteristicValue;
+} BLUETOOTH_GATT_VALUE_CHANGED_EVENT, *PBLUETOOTH_GATT_VALUE_CHANGED_EVENT;
+
+typedef struct _BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION {
+ USHORT NumCharacteristics;
+ BTH_LE_GATT_CHARACTERISTIC Characteristics[1];
+} BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION, *PBLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION;
+
+typedef VOID (CALLBACK *PFNBLUETOOTH_GATT_EVENT_CALLBACK)(
+ BTH_LE_GATT_EVENT_TYPE EventType,
+ PVOID EventOutParameter,
+ PVOID Context
+ );
+
+typedef ULONG64 BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, *PBTH_LE_GATT_RELIABLE_WRITE_CONTEXT;
+
+#define DEFINEFUNC(ret, func, ...) \
+ typedef ret (WINAPI *fp_##func)(__VA_ARGS__); \
+ static fp_##func func;
+
+#define RESOLVEFUNC(func) \
+ func = (fp_##func)resolveFunction(library, #func); \
+ if (!func) \
+ return false;
+
+DEFINEFUNC(HRESULT, BluetoothGATTGetServices, HANDLE, USHORT, PBTH_LE_GATT_SERVICE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetIncludedServices, HANDLE, PBTH_LE_GATT_SERVICE, USHORT, PBTH_LE_GATT_SERVICE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetCharacteristics, HANDLE, PBTH_LE_GATT_SERVICE, USHORT, PBTH_LE_GATT_CHARACTERISTIC, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetDescriptors, HANDLE, PBTH_LE_GATT_CHARACTERISTIC, USHORT, PBTH_LE_GATT_DESCRIPTOR, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetCharacteristicValue, HANDLE, PBTH_LE_GATT_CHARACTERISTIC, ULONG, PBTH_LE_GATT_CHARACTERISTIC_VALUE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetDescriptorValue, HANDLE, PBTH_LE_GATT_DESCRIPTOR, ULONG, PBTH_LE_GATT_DESCRIPTOR_VALUE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTBeginReliableWrite, HANDLE, PBTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTEndReliableWrite, HANDLE, BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTAbortReliableWrite, HANDLE, BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTSetCharacteristicValue, HANDLE, PBTH_LE_GATT_CHARACTERISTIC, PBTH_LE_GATT_CHARACTERISTIC_VALUE, BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTSetDescriptorValue, HANDLE, PBTH_LE_GATT_DESCRIPTOR, PBTH_LE_GATT_DESCRIPTOR_VALUE, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTRegisterEvent, HANDLE, BTH_LE_GATT_EVENT_TYPE, PVOID, PFNBLUETOOTH_GATT_EVENT_CALLBACK, PVOID, PHANDLE, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTUnregisterEvent, HANDLE, ULONG)
+
+static inline QFunctionPointer resolveFunction(QLibrary *library, const char *func)
+{
+ QFunctionPointer symbolFunctionPointer = library->resolve(func);
+ if (!symbolFunctionPointer)
+ qWarning("Cannot resolve '%s' in '%s'.", func, qPrintable(library->fileName()));
+ return symbolFunctionPointer;
+}
+
+static inline bool resolveFunctions(QLibrary *library)
+{
+ if (!library->isLoaded()) {
+ library->setFileName(QStringLiteral("bluetoothapis"));
+ if (!library->load()) {
+ qWarning("Unable to load '%s' library.", qPrintable(library->fileName()));
+ return false;
+ }
+ }
+
+ RESOLVEFUNC(BluetoothGATTGetServices)
+ RESOLVEFUNC(BluetoothGATTGetIncludedServices)
+ RESOLVEFUNC(BluetoothGATTGetCharacteristics)
+ RESOLVEFUNC(BluetoothGATTGetDescriptors)
+ RESOLVEFUNC(BluetoothGATTGetCharacteristicValue)
+ RESOLVEFUNC(BluetoothGATTGetDescriptorValue)
+ RESOLVEFUNC(BluetoothGATTBeginReliableWrite)
+ RESOLVEFUNC(BluetoothGATTEndReliableWrite)
+ RESOLVEFUNC(BluetoothGATTAbortReliableWrite)
+ RESOLVEFUNC(BluetoothGATTSetCharacteristicValue)
+ RESOLVEFUNC(BluetoothGATTSetDescriptorValue)
+ RESOLVEFUNC(BluetoothGATTRegisterEvent)
+ RESOLVEFUNC(BluetoothGATTUnregisterEvent)
+
+ return true;
+}
+
+#endif // QWINLOWENERGYBLUETOOTH_P_H
diff --git a/src/bluetooth/windows/windows.pri b/src/bluetooth/windows/windows.pri
new file mode 100644
index 00000000..bf35eaa4
--- /dev/null
+++ b/src/bluetooth/windows/windows.pri
@@ -0,0 +1,2 @@
+PRIVATE_HEADERS += \
+ windows/qwinlowenergybluetooth_p.h
diff --git a/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp b/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp
index ea1abef2..af7f0354 100644
--- a/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp
+++ b/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp
@@ -110,6 +110,9 @@ void tst_QBluetoothLocalDevice::tst_powerOn()
#ifdef Q_OS_OSX
QSKIP("Not possible on OS X");
#endif
+#ifdef Q_OS_WIN
+ QSKIP("Not possible on Windows");
+#endif
QBluetoothLocalDevice localDevice;
@@ -135,6 +138,9 @@ void tst_QBluetoothLocalDevice::tst_powerOff()
#ifdef Q_OS_OSX
QSKIP("Not possible on OS X");
#endif
+#ifdef Q_OS_WIN
+ QSKIP("Not possible on Windows");
+#endif
if (!QBluetoothLocalDevice::allDevices().count())
QSKIP("Skipping test due to missing Bluetooth device");
@@ -183,6 +189,9 @@ void tst_QBluetoothLocalDevice::tst_hostModes()
#ifdef Q_OS_OSX
QSKIP("Not possible on OS X");
#endif
+#ifdef Q_OS_WIN
+ QSKIP("Not possible on Windows");
+#endif
QFETCH(QBluetoothLocalDevice::HostMode, hostModeExpected);
QFETCH(bool, expectSignal);
@@ -340,6 +349,10 @@ void tst_QBluetoothLocalDevice::tst_pairDevice_data()
void tst_QBluetoothLocalDevice::tst_pairDevice()
{
+#ifdef Q_OS_WIN
+ QSKIP("Programmatic pairing not supported on Windows");
+#endif
+
QFETCH(QBluetoothAddress, deviceAddress);
QFETCH(QBluetoothLocalDevice::Pairing, pairingExpected);
QFETCH(int, pairingWaitTime);
diff --git a/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp b/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp
index 10c4bd3b..f89802d2 100644
--- a/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp
+++ b/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp
@@ -184,7 +184,7 @@ void tst_QBluetoothServiceInfo::tst_assignment_data()
bool l2cpSupported = true;
//some platforms don't support L2CP
-#ifdef QT_ANDROID_BLUETOOTH
+#if defined(QT_ANDROID_BLUETOOTH) || defined(Q_OS_WIN)
l2cpSupported = false;
#endif
QTest::newRow("assignment_data_l2cp")
diff --git a/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp b/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp
index 4e82aacd..5ebb0b1d 100644
--- a/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp
+++ b/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp
@@ -140,13 +140,18 @@ void tst_QLowEnergyDescriptor::initTestCase()
const QList<QLowEnergyCharacteristic> chars = leService->characteristics();
for (const QLowEnergyCharacteristic &ch : chars) {
- if (!ch.descriptors().isEmpty()) {
- globalService = leService;
- globalControl = controller;
- qWarning() << "Found service with descriptor" << remoteDeviceInfo.address()
- << globalService->serviceName() << globalService->serviceUuid();
- break;
+ const QList<QLowEnergyDescriptor> descriptors = ch.descriptors();
+ for (const QLowEnergyDescriptor &d : descriptors) {
+ if (!d.value().isEmpty()) {
+ globalService = leService;
+ globalControl = controller;
+ qWarning() << "Found service with descriptor" << remoteDeviceInfo.address()
+ << globalService->serviceName() << globalService->serviceUuid();
+ break;
+ }
}
+ if (globalControl)
+ break;
}
if (globalControl)
@@ -238,11 +243,19 @@ void tst_QLowEnergyDescriptor::tst_assignCompare()
QCOMPARE(target.uuid(), QBluetoothUuid());
QCOMPARE(target.value(), QByteArray());
+ int index = -1;
QList<QLowEnergyDescriptor> targets;
const QList<QLowEnergyCharacteristic> chars = globalService->characteristics();
for (const QLowEnergyCharacteristic &ch : chars) {
if (!ch.descriptors().isEmpty()) {
targets = ch.descriptors();
+ for (int i = 0; i < targets.size(); ++i) {
+ // try to get a descriptor we can read
+ if (targets[i].type() == QBluetoothUuid::CharacteristicUserDescription) {
+ index = i;
+ break;
+ }
+ }
break;
}
}
@@ -250,8 +263,10 @@ void tst_QLowEnergyDescriptor::tst_assignCompare()
if (targets.isEmpty())
QSKIP("No descriptor found despite prior indication.");
+ QVERIFY(index != -1);
+
// test assignment operator
- target = targets.first();
+ target = targets[index];
QVERIFY(target.isValid());
QVERIFY(target.type() != QBluetoothUuid::UnknownDescriptorType);
QVERIFY(!target.name().isEmpty());
@@ -259,26 +274,26 @@ void tst_QLowEnergyDescriptor::tst_assignCompare()
QVERIFY(!target.uuid().isNull());
QVERIFY(!target.value().isEmpty());
- QVERIFY(target == targets.first());
- QVERIFY(targets.first() == target);
- QVERIFY(!(target != targets.first()));
- QVERIFY(!(targets.first() != target));
+ QVERIFY(target == targets[index]);
+ QVERIFY(targets[index] == target);
+ QVERIFY(!(target != targets[index]));
+ QVERIFY(!(targets[index] != target));
- QCOMPARE(target.isValid(), targets.first().isValid());
- QCOMPARE(target.type(), targets.first().type());
- QCOMPARE(target.name(), targets.first().name());
- QCOMPARE(target.handle(), targets.first().handle());
- QCOMPARE(target.uuid(), targets.first().uuid());
- QCOMPARE(target.value(), targets.first().value());
+ QCOMPARE(target.isValid(), targets[index].isValid());
+ QCOMPARE(target.type(), targets[index].type());
+ QCOMPARE(target.name(), targets[index].name());
+ QCOMPARE(target.handle(), targets[index].handle());
+ QCOMPARE(target.uuid(), targets[index].uuid());
+ QCOMPARE(target.value(), targets[index].value());
// test copy constructor
QLowEnergyDescriptor copyConstructed(target);
- QCOMPARE(copyConstructed.isValid(), targets.first().isValid());
- QCOMPARE(copyConstructed.type(), targets.first().type());
- QCOMPARE(copyConstructed.name(), targets.first().name());
- QCOMPARE(copyConstructed.handle(), targets.first().handle());
- QCOMPARE(copyConstructed.uuid(), targets.first().uuid());
- QCOMPARE(copyConstructed.value(), targets.first().value());
+ QCOMPARE(copyConstructed.isValid(), targets[index].isValid());
+ QCOMPARE(copyConstructed.type(), targets[index].type());
+ QCOMPARE(copyConstructed.name(), targets[index].name());
+ QCOMPARE(copyConstructed.handle(), targets[index].handle());
+ QCOMPARE(copyConstructed.uuid(), targets[index].uuid());
+ QCOMPARE(copyConstructed.value(), targets[index].value());
QVERIFY(copyConstructed == target);
QVERIFY(target == copyConstructed);
@@ -300,18 +315,18 @@ void tst_QLowEnergyDescriptor::tst_assignCompare()
QVERIFY(!(invalid != target));
QVERIFY(!(target != invalid));
- QVERIFY(!(targets.first() == target));
- QVERIFY(!(target == targets.first()));
- QVERIFY(targets.first() != target);
- QVERIFY(target != targets.first());
+ QVERIFY(!(targets[index] == target));
+ QVERIFY(!(target == targets[index]));
+ QVERIFY(targets[index] != target);
+ QVERIFY(target != targets[index]);
if (targets.count() >= 2) {
- QLowEnergyDescriptor second = targets[1];
+ QLowEnergyDescriptor second = targets[(index+1)%2];
// at least two descriptors
- QVERIFY(!(targets.first() == second));
- QVERIFY(!(second == targets.first()));
- QVERIFY(targets.first() != second);
- QVERIFY(second != targets.first());
+ QVERIFY(!(targets[index] == second));
+ QVERIFY(!(second == targets[index]));
+ QVERIFY(targets[index] != second);
+ QVERIFY(second != targets[index]);
}
}