summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/bluetooth/bluetooth.pro19
-rw-r--r--src/bluetooth/qbluetooth.cpp2
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent.h1
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_p.h32
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp559
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_p.h25
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_win.cpp256
-rw-r--r--src/bluetooth/qbluetoothserver_win.cpp103
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent.h3
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_p.h18
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_win.cpp431
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_win.cpp63
-rw-r--r--src/bluetooth/qbluetoothsocket_win.cpp148
-rw-r--r--src/bluetooth/qlowenergycontroller_p.h6
-rw-r--r--src/bluetooth/qlowenergycontroller_win.cpp1194
-rw-r--r--src/bluetooth/qlowenergyserviceprivate_p.h3
-rw-r--r--src/bluetooth/windows/qwinlowenergybluetooth_p.h210
-rw-r--r--src/bluetooth/windows/windows.pri2
18 files changed, 3074 insertions, 1 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index 1ef7406b..e096374f 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -215,6 +215,25 @@ 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)
+
+ # remove dummy warning once platform port is complete
+ include(dummy/dummy.pri)
+
+ SOURCES += \
+ qbluetoothdevicediscoveryagent_win.cpp \
+ qbluetoothlocaldevice_win.cpp \
+ qbluetoothserviceinfo_win.cpp \
+ qbluetoothservicediscoveryagent_win.cpp \
+ qbluetoothsocket_win.cpp \
+ qbluetoothserver_win.cpp \
+ qlowenergycontroller_win.cpp
+
} else {
message("Unsupported Bluetooth platform, will not build a working QtBluetooth library.")
message("Either no Qt D-Bus found or no BlueZ headers available.")
diff --git a/src/bluetooth/qbluetooth.cpp b/src/bluetooth/qbluetooth.cpp
index 6eab11fb..7b5fd266 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")
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.h b/src/bluetooth/qbluetoothdevicediscoveryagent.h
index d3e41265..feb25324 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 243d7fd2..a1135bf1 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.
@@ -78,6 +79,10 @@ class QDBusVariant;
QT_END_NAMESPACE
#endif
+#ifdef QT_WIN_BLUETOOTH
+#include <QtConcurrent>
+#endif
+
QT_BEGIN_NAMESPACE
#ifdef QT_WINRT_BLUETOOTH
@@ -85,7 +90,7 @@ class QWinRTBluetoothDeviceDiscoveryWorker;
#endif
class QBluetoothDeviceDiscoveryAgentPrivate
-#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH)
+#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(QT_WIN_BLUETOOTH)
: public QObject
{
Q_OBJECT
@@ -159,6 +164,31 @@ 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();
+ void startClassicDevicesDiscovery(Qt::HANDLE hSearch = nullptr);
+ void completeClassicDevicesDiscovery();
+
+ void processDiscoveredDevice(const QBluetoothDeviceInfo &foundDevice);
+
+ QBluetoothAddress adapterAddress;
+ bool pendingCancel;
+ bool pendingStart;
+ bool active;
+
+ QFutureWatcher<QVariant> *classicScanWatcher;
+ QFutureWatcher<QVariant> *lowenergyScanWatcher;
+#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..9f05ffa1
--- /dev/null
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp
@@ -0,0 +1,559 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothdevicediscoveryagent.h"
+#include "qbluetoothuuid.h"
+#include "qbluetoothdevicediscoveryagent_p.h"
+#include "qbluetoothlocaldevice_p.h"
+
+#include <QtCore/qmutex.h>
+#include <QtConcurrent/QtConcurrent>
+
+#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() ? NULL : 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;
+ ::ZeroMemory(&searchParams, sizeof(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 = NULL;
+
+ BLUETOOTH_DEVICE_INFO deviceInfo;
+ ::ZeroMemory(&deviceInfo, sizeof(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 = ::GetLastError();
+ }
+
+ return foundDevice;
+}
+
+static QBluetoothDeviceInfo findNextClassicDevice(
+ DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND hSearch)
+{
+ BLUETOOTH_DEVICE_INFO deviceInfo;
+ ::ZeroMemory(&deviceInfo, sizeof(deviceInfo));
+ deviceInfo.dwSize = sizeof(deviceInfo);
+
+ QBluetoothDeviceInfo foundDevice;
+ if (!::BluetoothFindNextDevice(hSearch, &deviceInfo)) {
+ *systemErrorCode = ::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)
+{
+ const QUuid deviceInterfaceGuid("781aee18-7733-4ce4-add0-91f41c67b592");
+ const HDEVINFO hDeviceInfo = ::SetupDiGetClassDevs(
+ reinterpret_cast<const GUID *>(&deviceInterfaceGuid),
+ NULL,
+ 0,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+
+ if (hDeviceInfo == INVALID_HANDLE_VALUE) {
+ *systemErrorCode = ::GetLastError();
+ return QVector<QBluetoothDeviceInfo>();
+ }
+
+ QVector<QBluetoothDeviceInfo> foundDevices;
+ DWORD index = 0;
+
+ QVector<LeDeviceEntry> cachedEntries;
+
+ for (;;) {
+ SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
+ ::ZeroMemory(&deviceInterfaceData, sizeof(deviceInterfaceData));
+ deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
+
+ if (!::SetupDiEnumDeviceInterfaces(
+ hDeviceInfo,
+ NULL,
+ reinterpret_cast<const GUID *>(&deviceInterfaceGuid),
+ index++,
+ &deviceInterfaceData)) {
+ *systemErrorCode = ::GetLastError();
+ break;
+ }
+
+ DWORD deviceInterfaceDetailDataSize = 0;
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ hDeviceInfo,
+ &deviceInterfaceData,
+ NULL,
+ deviceInterfaceDetailDataSize,
+ &deviceInterfaceDetailDataSize,
+ NULL)) {
+ if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ *systemErrorCode = ::GetLastError();
+ break;
+ }
+ }
+
+ SP_DEVINFO_DATA deviceInfoData;
+ ::ZeroMemory(&deviceInfoData, sizeof(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 = ::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
+};
+
+static QVariant discoverLeDevicesStatic()
+{
+ DiscoveryResult result; // Do not use hSearch here!
+ result.systemErrorCode = NO_ERROR;
+ result.devices = enumerateLeDevices(&result.systemErrorCode);
+ return QVariant::fromValue(result);
+}
+
+static QVariant discoverClassicDevicesStatic(HBLUETOOTH_DEVICE_FIND 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);
+ return QVariant::fromValue(result);
+}
+
+QString QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath(
+ const QBluetoothAddress &deviceAddress)
+{
+ // update LE devices cache
+ DWORD dummyErrorCode;
+ enumerateLeDevices(&dummyErrorCode);
+
+ QMutexLocker locker(cachedLeDeviceEntriesGuard());
+ for (int i = 0; i < cachedLeDeviceEntries()->count(); ++i) {
+ if (cachedLeDeviceEntries()->at(i).deviceAddress == deviceAddress)
+ return cachedLeDeviceEntries()->at(i).devicePath;
+ }
+ return QString();
+}
+
+QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(
+ const QBluetoothAddress &deviceAdapter,
+ QBluetoothDeviceDiscoveryAgent *parent)
+ : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry)
+ , lastError(QBluetoothDeviceDiscoveryAgent::NoError)
+ , adapterAddress(deviceAdapter)
+ , pendingCancel(false)
+ , pendingStart(false)
+ , active(false)
+ , classicScanWatcher(nullptr)
+ , lowenergyScanWatcher(nullptr)
+ , lowEnergySearchTimeout(-1) // remains -1 -> timeout not supported
+ , q_ptr(parent)
+{
+}
+
+QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
+{
+ if (active)
+ stop();
+}
+
+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()
+{
+ if (!lowenergyScanWatcher) {
+ lowenergyScanWatcher = new QFutureWatcher<QVariant>(this);
+ connect(lowenergyScanWatcher, &QFutureWatcher<QVariant>::finished,
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery);
+ }
+ const QFuture<QVariant> future = QtConcurrent::run(discoverLeDevicesStatic);
+ lowenergyScanWatcher->setFuture(future);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery()
+{
+ if (pendingCancel && !pendingStart) {
+ cancelDiscovery();
+ } else if (pendingStart) {
+ restartDiscovery();
+ } else {
+ const DiscoveryResult result = lowenergyScanWatcher->result().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)
+{
+ if (!classicScanWatcher) {
+ classicScanWatcher = new QFutureWatcher<QVariant>(this);
+ connect(classicScanWatcher, &QFutureWatcher<QVariant>::finished,
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery);
+ }
+ const QFuture<QVariant> future = QtConcurrent::run(discoverClassicDevicesStatic, hSearch);
+ classicScanWatcher->setFuture(future);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery()
+{
+ const DiscoveryResult result = classicScanWatcher->result().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, qt_error_string(NO_ERROR));
+ } else if (result.systemErrorCode == NO_ERROR) {
+ if (result.hSearch) {
+ for (const QBluetoothDeviceInfo &device : result.devices)
+ processDiscoveredDevice(device);
+
+ startClassicDevicesDiscovery(result.hSearch);
+ } else {
+ finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, qt_error_string(NO_ERROR));
+ }
+ } 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) << "Emit: " << foundDevice.address();
+ discoveredDevices.append(foundDevice);
+ emit q->deviceDiscovered(foundDevice);
+ } else if (*deviceIt == foundDevice
+ || deviceIt->coreConfigurations() == foundDevice.coreConfigurations()) {
+ qCDebug(QT_BT_WINDOWS) << "Duplicate: " << foundDevice.address();
+ } else {
+ // We assume that if the existing device it is low energy, it means that
+ // the found device should be as classic, because it is impossible to get
+ // same low energy device.
+ if (deviceIt->coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration)
+ *deviceIt = foundDevice;
+
+ // We assume that it is impossible to have multiple devices with same core
+ // configurations, which have one address. This possible only in case a device
+ // provided both low energy and classic features at the same time.
+ deviceIt->setCoreConfigurations(QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration);
+ deviceIt->setCached(foundDevice.isCached());
+
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ qCDebug(QT_BT_WINDOWS) << "Updated: " << deviceIt->address();
+ emit q->deviceDiscovered(*deviceIt);
+ }
+}
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(DiscoveryResult)
diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h
index a52b683c..d0db3a1f 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.
@@ -207,6 +208,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
{
@@ -231,6 +255,7 @@ public:
private:
QBluetoothLocalDevice *q_ptr;
};
+
#elif !defined(QT_OSX_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..78b82a97
--- /dev/null
+++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp
@@ -0,0 +1,256 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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(NULL)
+ && !::BluetoothEnableDiscovery(NULL, FALSE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to disable the discoverable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ if (::BluetoothIsConnectable(NULL)
+ && !::BluetoothEnableIncomingConnections(NULL, FALSE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to disable the connectable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ } else if (requestedMode == QBluetoothLocalDevice::HostConnectable) {
+ if (::BluetoothIsDiscoverable(NULL)) {
+ if (!::BluetoothEnableDiscovery(NULL, FALSE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to disable the discoverable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ } else if (!::BluetoothEnableIncomingConnections(NULL, 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(NULL)
+ && !::BluetoothEnableIncomingConnections(NULL, TRUE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to enable the connectable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ if (!::BluetoothEnableDiscovery(NULL, 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(NULL))
+ return HostDiscoverable;
+ if (::BluetoothIsConnectable(NULL))
+ 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);
+}
+
+QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(
+ const QBluetoothAddress &address) const
+{
+ Q_UNUSED(address);
+ return Unpaired;
+}
+
+void QBluetoothLocalDevice::pairingConfirmation(bool confirmation)
+{
+ Q_UNUSED(confirmation);
+}
+
+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 = 0;
+ 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_win.cpp b/src/bluetooth/qbluetoothserver_win.cpp
new file mode 100644
index 00000000..86b520d6
--- /dev/null
+++ b/src/bluetooth/qbluetoothserver_win.cpp
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothserver.h"
+#include "qbluetoothserver_p.h"
+#include "qbluetoothsocket.h"
+
+QT_BEGIN_NAMESPACE
+
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType)
+ : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError)
+{
+ if (sType == QBluetoothServiceInfo::RfcommProtocol)
+ socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
+ else
+ socket = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol);
+}
+
+QBluetoothServerPrivate::~QBluetoothServerPrivate()
+{
+ delete socket;
+}
+
+void QBluetoothServer::close()
+{
+}
+
+bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
+{
+ Q_UNUSED(address);
+ Q_UNUSED(port);
+ Q_D(QBluetoothServer);
+ d->m_lastError = UnsupportedProtocolError;
+ emit error(d->m_lastError);
+ return false;
+}
+
+void QBluetoothServer::setMaxPendingConnections(int numConnections)
+{
+ Q_UNUSED(numConnections);
+}
+
+bool QBluetoothServer::hasPendingConnections() const
+{
+ return false;
+}
+
+QBluetoothSocket *QBluetoothServer::nextPendingConnection()
+{
+ return 0;
+}
+
+QBluetoothAddress QBluetoothServer::serverAddress() const
+{
+ return QBluetoothAddress();
+}
+
+quint16 QBluetoothServer::serverPort() const
+{
+ return 0;
+}
+
+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/qbluetoothservicediscoveryagent.h b/src/bluetooth/qbluetoothservicediscoveryagent.h
index 4a553c7d..4b1a72c1 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent.h
@@ -129,6 +129,9 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_fetchUuidsTimeout())
Q_PRIVATE_SLOT(d_func(), void _q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state))
#endif
+#ifdef QT_WIN_BLUETOOTH
+ Q_PRIVATE_SLOT(d_func(), void _q_nextSdpScan())
+#endif
};
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
index c4ea20a9..956ccabb 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
@@ -72,6 +72,10 @@ class QXmlStreamReader;
QT_END_NAMESPACE
#endif
+#ifdef QT_WIN_BLUETOOTH
+#include <QFutureWatcher>
+#endif
+
QT_BEGIN_NAMESPACE
class QBluetoothDeviceDiscoveryAgent;
@@ -145,6 +149,9 @@ public:
void _q_fetchUuidsTimeout();
void _q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state);
#endif
+#ifdef QT_WIN_BLUETOOTH
+ void _q_nextSdpScan();
+#endif
private:
void start(const QBluetoothAddress &address);
@@ -196,6 +203,17 @@ private:
QMap<QBluetoothAddress,QPair<QBluetoothDeviceInfo,QList<QBluetoothUuid> > > sdpCache;
#endif
+#ifdef QT_WIN_BLUETOOTH
+private:
+ int systemError;
+ bool pendingStop;
+ bool pendingFinish;
+
+ QFutureWatcher<QBluetoothServiceInfo> *searchWatcher;
+
+ Qt::HANDLE hSearch;
+#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..e8df3ebe
--- /dev/null
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp
@@ -0,0 +1,431 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothservicediscoveryagent.h"
+#include "qbluetoothservicediscoveryagent_p.h"
+
+#include <QtCore/QByteArray>
+#include <QtConcurrent>
+
+#include <initguid.h>
+#include <WinSock2.h>
+#include <qt_windows.h>
+#include <bluetoothapis.h>
+#include <ws2bth.h>
+
+Q_GLOBAL_STATIC(QLibrary, bluetoothapis)
+
+#define DEFINEFUNC(ret, func, ...) \
+ typedef ret (WINAPI *fp_##func)(__VA_ARGS__); \
+ static fp_##func p##func;
+
+#define RESOLVEFUNC(func) \
+ p##func = (fp_##func)resolveFunction(library, #func); \
+ if (!p##func) \
+ return false;
+
+DEFINEFUNC(DWORD, BluetoothSdpGetElementData, LPBYTE, ULONG, PSDP_ELEMENT_DATA)
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+static inline QFunctionPointer resolveFunction(QLibrary *library, const char *func)
+{
+ const 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(BluetoothSdpGetElementData)
+
+ return true;
+}
+
+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: {
+ //Not sure if this will work, might be evil
+ Q_ASSERT(sizeof(element.data.uuid128) == sizeof(quint128));
+
+ quint128 uuid128;
+ memcpy(&uuid128, &(element.data.uuid128), sizeof(quint128));
+
+ variant = QVariant::fromValue(QBluetoothUuid(uuid128));
+ }
+ case SDP_ST_UUID32:
+ variant = QVariant::fromValue(QBluetoothUuid(quint32(element.data.uuid32)));
+ case SDP_ST_UUID16:
+ variant = QVariant::fromValue(QBluetoothUuid(quint16(element.data.uuid16)));
+ 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 = NULL;
+ 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 (pBluetoothSdpGetElementData(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;
+}
+
+static void cleanupServiceDiscovery(HANDLE hSearch)
+{
+ if (hSearch != INVALID_HANDLE_VALUE)
+ WSALookupServiceEnd(hSearch);
+ WSACleanup();
+}
+
+enum {
+ WSAControlFlags = LUP_FLUSHCACHE
+ | LUP_RETURN_NAME
+ | LUP_RETURN_TYPE
+ | LUP_RETURN_ADDR
+ | LUP_RETURN_BLOB
+ | LUP_RETURN_COMMENT
+};
+
+static QBluetoothServiceInfo findNextService(HANDLE hSearch, int *systemError)
+{
+ QBluetoothServiceInfo result;
+
+ 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) {
+ *systemError = ::WSAGetLastError();
+ if (*systemError == WSA_E_NO_MORE)
+ cleanupServiceDiscovery(hSearch);
+ return QBluetoothServiceInfo();
+ }
+
+ if (resultQuery->lpBlob
+ && BluetoothSdpEnumAttributes(resultQuery->lpBlob->pBlobData,
+ resultQuery->lpBlob->cbSize,
+ bluetoothSdpCallback,
+ &result)) {
+ return result;
+ } else {
+ *systemError = GetLastError();
+ }
+
+ return result;
+}
+
+static QBluetoothServiceInfo findFirstService(LPHANDLE hSearch, const QBluetoothAddress &address, int *systemError)
+{
+ //### should we try for 2.2 on all platforms ??
+ WSAData wsadata;
+
+ // IPv6 requires Winsock v2.0 or better.
+ if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) {
+ *systemError = ::WSAGetLastError();
+ return QBluetoothServiceInfo();
+ }
+
+ const QString addressAsString = QStringLiteral("(%1)").arg(address.toString());
+
+ QVector<WCHAR> addressAsWChar(addressAsString.size());
+ addressAsString.toWCharArray(addressAsWChar.data());
+
+ GUID protocol = L2CAP_PROTOCOL_UUID; //Search for L2CAP and RFCOMM services
+
+ WSAQUERYSET serviceQuery;
+ ::ZeroMemory(&serviceQuery, sizeof(serviceQuery));
+ serviceQuery.dwSize = sizeof(WSAQUERYSET); //As specified by the windows documentation
+ serviceQuery.lpServiceClassId = &protocol; //The protocal of the service what is being queried
+ serviceQuery.dwNameSpace = NS_BTH; //As specified by the windows documentation
+ serviceQuery.dwNumberOfCsAddrs = 0; //As specified by the windows documentation
+ serviceQuery.lpszContext = addressAsWChar.data(); //The remote address that query will run on
+
+ const int resultCode = WSALookupServiceBegin(&serviceQuery,
+ WSAControlFlags,
+ hSearch);
+ if (resultCode == SOCKET_ERROR) {
+ *systemError = ::WSAGetLastError();
+ cleanupServiceDiscovery(hSearch);
+ return QBluetoothServiceInfo();
+ }
+ *systemError = NO_ERROR;
+ return findNextService(*hSearch, systemError);
+}
+
+QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
+ QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter)
+ : error(QBluetoothServiceDiscoveryAgent::NoError),
+ state(Inactive),
+ deviceDiscoveryAgent(0),
+ mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery),
+ singleDevice(false),
+ systemError(NO_ERROR),
+ pendingStop(false),
+ pendingFinish(false),
+ searchWatcher(Q_NULLPTR),
+ hSearch(INVALID_HANDLE_VALUE),
+ q_ptr(qp)
+{
+ Q_UNUSED(deviceAdapter);
+ resolveFunctions(bluetoothapis());
+ searchWatcher = new QFutureWatcher<QBluetoothServiceInfo>();
+}
+
+QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate()
+{
+ if (pendingFinish) {
+ stop();
+ searchWatcher->waitForFinished();
+ }
+ delete searchWatcher;
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address)
+{
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ if (!pendingFinish) {
+ pendingFinish = true;
+ pendingStop = false;
+
+ QObject::connect(searchWatcher, SIGNAL(finished()), q, SLOT(_q_nextSdpScan()), Qt::UniqueConnection);
+ const QFuture<QBluetoothServiceInfo> future =
+ QtConcurrent::run(&findFirstService,
+ &hSearch,
+ address,
+ &systemError);
+ searchWatcher->setFuture(future);
+ }
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::stop()
+{
+ pendingStop = true;
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan()
+{
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+
+ if (pendingStop) {
+ pendingStop = false;
+ pendingFinish = false;
+ emit q->canceled();
+ } else {
+ if (systemError == WSA_E_NO_MORE) {
+ systemError = NO_ERROR;
+ } else if (systemError != NO_ERROR) {
+ error = (systemError == ERROR_INVALID_HANDLE) ?
+ QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError
+ : QBluetoothServiceDiscoveryAgent::InputOutputError;
+ errorString = qt_error_string(systemError);
+ qCWarning(QT_BT_WINDOWS) << errorString;
+ emit q->error(this->error);
+ } else {
+ QBluetoothServiceInfo serviceInfo = searchWatcher->result();
+ serviceInfo.setDevice(discoveredDevices.at(0));
+ if (serviceInfo.isValid()) {
+ if (!isDuplicatedService(serviceInfo)) {
+ discoveredServices.append(serviceInfo);
+ emit q->serviceDiscovered(serviceInfo);
+ }
+ }
+ const QFuture<QBluetoothServiceInfo> future =
+ QtConcurrent::run(&findNextService, hSearch, &systemError);
+ searchWatcher->setFuture(future);
+ return;
+ }
+ hSearch = INVALID_HANDLE_VALUE;
+ pendingFinish = false;
+ _q_serviceDiscoveryFinished();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserviceinfo_win.cpp b/src/bluetooth/qbluetoothserviceinfo_win.cpp
new file mode 100644
index 00000000..8942149a
--- /dev/null
+++ b/src/bluetooth/qbluetoothserviceinfo_win.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothserviceinfo.h"
+#include "qbluetoothserviceinfo_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate()
+{
+}
+
+QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate()
+{
+}
+
+bool QBluetoothServiceInfoPrivate::isRegistered() const
+{
+ return false;
+}
+
+bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAdapter)
+{
+ Q_UNUSED(localAdapter);
+ return false;
+}
+
+bool QBluetoothServiceInfoPrivate::unregisterService()
+{
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp
new file mode 100644
index 00000000..e18c985b
--- /dev/null
+++ b/src/bluetooth/qbluetoothsocket_win.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothsocket.h"
+#include "qbluetoothsocket_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QBluetoothSocketPrivate::QBluetoothSocketPrivate()
+ : socket(-1),
+ socketType(QBluetoothServiceInfo::UnknownProtocol),
+ state(QBluetoothSocket::UnconnectedState),
+ socketError(QBluetoothSocket::NoSocketError)
+{
+}
+
+QBluetoothSocketPrivate::~QBluetoothSocketPrivate()
+{
+}
+
+bool QBluetoothSocketPrivate::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
+{
+ socketType = type;
+ return false;
+}
+
+void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+{
+ Q_UNUSED(openMode);
+ Q_UNUSED(address);
+ Q_UNUSED(port);
+}
+
+void QBluetoothSocketPrivate::abort()
+{
+}
+
+QString QBluetoothSocketPrivate::localName() const
+{
+ return QString();
+}
+
+QBluetoothAddress QBluetoothSocketPrivate::localAddress() const
+{
+ return QBluetoothAddress();
+}
+
+quint16 QBluetoothSocketPrivate::localPort() const
+{
+ return 0;
+}
+
+QString QBluetoothSocketPrivate::peerName() const
+{
+ return QString();
+}
+
+QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const
+{
+ return QBluetoothAddress();
+}
+
+quint16 QBluetoothSocketPrivate::peerPort() const
+{
+ return 0;
+}
+
+qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(maxSize);
+
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot write while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+ return -1;
+}
+
+qint64 QBluetoothSocketPrivate::readData(char *data, qint64 maxSize)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(maxSize);
+
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot write while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ return -1;
+}
+
+void QBluetoothSocketPrivate::close()
+{
+}
+
+bool QBluetoothSocketPrivate::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
+ QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
+{
+ Q_UNUSED(socketDescriptor);
+ Q_UNUSED(socketType)
+ Q_UNUSED(socketState);
+ Q_UNUSED(openMode);
+ return false;
+}
+
+qint64 QBluetoothSocketPrivate::bytesAvailable() const
+{
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h
index 6e866144..84d307c1 100644
--- a/src/bluetooth/qlowenergycontroller_p.h
+++ b/src/bluetooth/qlowenergycontroller_p.h
@@ -459,6 +459,12 @@ private:
void centralConnectionUpdated(QLowEnergyController::ControllerState newState,
QLowEnergyController::Error errorCode);
+#elif defined(QT_WIN_BLUETOOTH)
+protected:
+ void customEvent(QEvent *e);
+private:
+ QString deviceSystemPath;
+
#elif defined(QT_WINRT_BLUETOOTH)
private slots:
void characteristicChanged(int charHandle, const QByteArray &data);
diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp
new file mode 100644
index 00000000..89f46455
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_win.cpp
@@ -0,0 +1,1194 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlowenergycontroller_p.h"
+#include "qbluetoothdevicediscoveryagent_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QIODevice> // for open modes
+#include <QtCore/QEvent>
+#include <QtCore/QMutex>
+
+#include <algorithm> // for std::max
+
+#include <windows/qwinlowenergybluetooth_p.h>
+
+#include <setupapi.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+Q_GLOBAL_STATIC(QLibrary, bluetoothapis)
+
+Q_GLOBAL_STATIC(QVector<QLowEnergyControllerPrivate *>, 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]),
+ 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.indexOf(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),
+ NULL,
+ 0,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+
+ if (deviceInfoSet == INVALID_HANDLE_VALUE) {
+ *systemErrorCode = ::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,
+ NULL,
+ reinterpret_cast<const GUID *>(&serviceUuid),
+ index++,
+ &deviceInterfaceData)) {
+ *systemErrorCode = ::GetLastError();
+ break;
+ }
+
+ DWORD deviceInterfaceDetailDataSize = 0;
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ deviceInfoSet,
+ &deviceInterfaceData,
+ NULL,
+ deviceInterfaceDetailDataSize,
+ &deviceInterfaceDetailDataSize,
+ NULL)) {
+ if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ *systemErrorCode = ::GetLastError();
+ break;
+ }
+ }
+
+ SP_DEVINFO_DATA deviceInfoData;
+ ::ZeroMemory(&deviceInfoData, sizeof(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(
+ deviceInfoSet,
+ &deviceInterfaceData,
+ deviceInterfaceDetailData,
+ deviceInterfaceDetailDataBuffer.size(),
+ &deviceInterfaceDetailDataSize,
+ &deviceInfoData)) {
+ *systemErrorCode = ::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 = 0;
+
+ if (openMode & QIODevice::ReadOnly) {
+ desiredAccess |= GENERIC_READ;
+ shareMode |= FILE_SHARE_READ;
+ }
+
+ if (openMode & QIODevice::WriteOnly) {
+ desiredAccess |= GENERIC_WRITE;
+ shareMode |= FILE_SHARE_WRITE;
+ }
+
+ const HANDLE hDevice = ::CreateFile(
+ reinterpret_cast<const wchar_t *>(systemPath.utf16()),
+ desiredAccess,
+ shareMode,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+
+ *systemErrorCode = (INVALID_HANDLE_VALUE == hDevice)
+ ? ::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() ? NULL : &foundServices[0],
+ &servicesCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) {
+ foundServices.resize(servicesCount);
+ } else if (hr == S_OK) {
+ *systemErrorCode = NO_ERROR;
+ return foundServices;
+ } else {
+ *systemErrorCode = ::GetLastError();
+ 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() ? NULL : &foundCharacteristics[0],
+ &characteristicsCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) {
+ foundCharacteristics.resize(characteristicsCount);
+ } else if (hr == S_OK) {
+ *systemErrorCode = NO_ERROR;
+ return foundCharacteristics;
+ } else {
+ *systemErrorCode = ::GetLastError();
+ 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 HRESULT hr = ::BluetoothGATTGetCharacteristicValue(
+ hService,
+ gattCharacteristic,
+ valueBufferSize,
+ valueBuffer.isEmpty() ? NULL : reinterpret_cast<PBTH_LE_GATT_CHARACTERISTIC_VALUE>(valueBuffer.data()),
+ &valueBufferSize,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) {
+ valueBuffer.resize(valueBufferSize);
+ } else if (hr == S_OK) {
+ *systemErrorCode = NO_ERROR;
+ const PBTH_LE_GATT_CHARACTERISTIC_VALUE value = reinterpret_cast<
+ PBTH_LE_GATT_CHARACTERISTIC_VALUE>(valueBuffer.data());
+
+ return QByteArray(reinterpret_cast<const char *>(&value->Data[0]), value->DataSize);
+ } else {
+ *systemErrorCode = ::GetLastError();
+ 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 = 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;
+
+ if (::BluetoothGATTSetCharacteristicValue(
+ hService,
+ gattCharacteristic,
+ reinterpret_cast<PBTH_LE_GATT_CHARACTERISTIC_VALUE>(valueBuffer.data()),
+ reliableWriteContext,
+ flags) != S_OK) {
+ *systemErrorCode = ::GetLastError();
+ } else {
+ *systemErrorCode = NO_ERROR;
+ }
+}
+
+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() ? NULL : &foundDescriptors[0],
+ &descriptorsCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) {
+ foundDescriptors.resize(descriptorsCount);
+ } else if (hr == S_OK) {
+ *systemErrorCode = NO_ERROR;
+ return foundDescriptors;
+ } else {
+ *systemErrorCode = ::GetLastError();
+ 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 HRESULT hr = ::BluetoothGATTGetDescriptorValue(
+ hService,
+ gattDescriptor,
+ valueBufferSize,
+ valueBuffer.isEmpty() ? NULL : reinterpret_cast<PBTH_LE_GATT_DESCRIPTOR_VALUE>(valueBuffer.data()),
+ &valueBufferSize,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) {
+ valueBuffer.resize(valueBufferSize);
+ } else if (hr == S_OK) {
+ *systemErrorCode = NO_ERROR;
+ const PBTH_LE_GATT_DESCRIPTOR_VALUE value = reinterpret_cast<
+ PBTH_LE_GATT_DESCRIPTOR_VALUE>(valueBuffer.data());
+
+ return QByteArray(reinterpret_cast<const char *>(&value->Data[0]), value->DataSize);
+ } else {
+ *systemErrorCode = ::GetLastError();
+ 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 = 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(), value.size());
+
+ if (::BluetoothGATTSetDescriptorValue(
+ hService,
+ gattDescriptor,
+ gattValue,
+ BLUETOOTH_GATT_FLAG_NONE) != S_OK) {
+ *systemErrorCode = ::GetLastError();
+ return;
+ }
+
+ *systemErrorCode = NO_ERROR;
+}
+
+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<QLowEnergyControllerPrivate *>(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;
+ if (::BluetoothGATTRegisterEvent(
+ hService,
+ CharacteristicValueChangedEvent,
+ &registration,
+ eventChangedCallbackEntry,
+ context,
+ &hEvent,
+ BLUETOOTH_GATT_FLAG_NONE) != S_OK) {
+ *systemErrorCode = ::GetLastError();
+ } else {
+ *systemErrorCode = NO_ERROR;
+ }
+
+ return hEvent;
+}
+
+static void unregisterEvent(HANDLE hEvent, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return;
+ }
+
+ if (::BluetoothGATTUnregisterEvent(
+ hEvent,
+ BLUETOOTH_GATT_FLAG_NONE) != S_OK) {
+ *systemErrorCode = ::GetLastError();
+ } else {
+ *systemErrorCode = NO_ERROR;
+ }
+}
+
+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 = 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 QLowEnergyControllerPrivate::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);
+}
+
+QLowEnergyControllerPrivate::QLowEnergyControllerPrivate()
+ : QObject()
+ , state(QLowEnergyController::UnconnectedState)
+ , error(QLowEnergyController::NoError)
+{
+ QMutexLocker locker(&controllersGuard);
+ qControllers()->append(this);
+
+ gattFunctionsResolved = resolveFunctions(bluetoothapis());
+ if (!gattFunctionsResolved) {
+ qCWarning(QT_BT_WINDOWS) << "LE is not supported on this OS";
+ return;
+ }
+}
+
+QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate()
+{
+ QMutexLocker locker(&controllersGuard);
+ qControllers()->removeAll(this);
+}
+
+void QLowEnergyControllerPrivate::init()
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivate::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);
+
+ Q_Q(QLowEnergyController);
+ emit q->connected();
+}
+
+void QLowEnergyControllerPrivate::disconnectFromDevice()
+{
+ if (deviceSystemPath.isEmpty()) {
+ qCDebug(QT_BT_WINDOWS) << "Already is disconnected";
+ return;
+ }
+
+ setState(QLowEnergyController::ClosingState);
+ deviceSystemPath.clear();
+ setState(QLowEnergyController::UnconnectedState);
+
+ Q_Q(QLowEnergyController);
+ emit q->disconnected();
+}
+
+void QLowEnergyControllerPrivate::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 QLowEnergyControllerPrivate::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;
+
+ const HANDLE 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(hService, NULL, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ closeSystemDevice(hService);
+ 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 = NULL;
+
+ 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(
+ 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(
+ hService, const_cast<PBTH_LE_GATT_CHARACTERISTIC>(
+ &gattCharacteristic), &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ if (systemErrorCode != ERROR_NOT_FOUND) {
+ closeSystemDevice(hService);
+ 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(hService, const_cast<PBTH_LE_GATT_DESCRIPTOR>(
+ &gattDescriptor), &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ closeSystemDevice(hService);
+ 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);
+ }
+
+ closeSystemDevice(hService);
+
+ servicePrivate->setState(QLowEnergyService::ServiceDiscovered);
+}
+
+void QLowEnergyControllerPrivate::startAdvertising(const QLowEnergyAdvertisingParameters &, const QLowEnergyAdvertisingData &, const QLowEnergyAdvertisingData &)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivate::stopAdvertising()
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivate::requestConnectionUpdate(const QLowEnergyConnectionParameters &)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivate::readCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle)
+{
+ 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;
+ }
+
+ int systemErrorCode = NO_ERROR;
+
+ const HANDLE hService = openSystemService(
+ remoteDevice, service->uuid, QIODevice::ReadOnly, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicReadError);
+ return;
+ }
+
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic = recoverNativeLeGattCharacteristic(
+ service->startHandle, charHandle, charDetails);
+
+ const QByteArray characteristicValue = getGattCharacteristicValue(
+ hService, &gattCharacteristic, &systemErrorCode);
+ closeSystemDevice(hService);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicReadError);
+ return;
+ }
+
+ updateValueOfCharacteristic(charHandle, characteristicValue, false);
+
+ const QLowEnergyCharacteristic ch(service, charHandle);
+ emit service->characteristicRead(ch, characteristicValue);
+}
+
+void QLowEnergyControllerPrivate::writeCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QByteArray &newValue,
+ QLowEnergyService::WriteMode mode)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ int systemErrorCode = NO_ERROR;
+
+ const HANDLE hService = openSystemService(
+ remoteDevice, service->uuid, QIODevice::ReadWrite, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic = recoverNativeLeGattCharacteristic(
+ service->startHandle, charHandle, charDetails);
+
+ const DWORD flags = (mode == QLowEnergyService::WriteWithResponse)
+ ? BLUETOOTH_GATT_FLAG_NONE
+ : BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE;
+
+ // TODO: If a device is not connected, this function will block
+ // for some time. So, need to re-implement of writeCharacteristic()
+ // with use QFutureWatcher.
+ setGattCharacteristicValue(hService, &gattCharacteristic,
+ newValue, flags, &systemErrorCode);
+ closeSystemDevice(hService);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to set value for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+ updateValueOfCharacteristic(charHandle, newValue, false);
+
+ if (mode == QLowEnergyService::WriteWithResponse) {
+ const QLowEnergyCharacteristic ch(service, charHandle);
+ emit service->characteristicWritten(ch, newValue);
+ }
+}
+
+void QLowEnergyControllerPrivate::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;
+
+ int systemErrorCode = NO_ERROR;
+
+ const HANDLE hService = openSystemService(
+ remoteDevice, service->uuid, QIODevice::ReadOnly, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorReadError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::DescData &dscrDetails
+ = charDetails.descriptorList[descriptorHandle];
+
+ BTH_LE_GATT_DESCRIPTOR gattDescriptor = recoverNativeLeGattDescriptor(
+ service->startHandle, charHandle, descriptorHandle, dscrDetails);
+
+ const QByteArray value = getGattDescriptorValue(
+ hService, const_cast<PBTH_LE_GATT_DESCRIPTOR>(
+ &gattDescriptor), &systemErrorCode);
+ closeSystemDevice(hService);
+
+ if (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(systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorReadError);
+ return;
+ }
+
+ updateValueOfDescriptor(charHandle, descriptorHandle, value, false);
+
+ QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle);
+ emit service->descriptorRead(dscr, value);
+}
+
+void QLowEnergyControllerPrivate::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;
+
+ int systemErrorCode = NO_ERROR;
+
+ const HANDLE hService = openSystemService(
+ remoteDevice, service->uuid, QIODevice::ReadWrite, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::DescData &dscrDetails
+ = charDetails.descriptorList[descriptorHandle];
+
+ BTH_LE_GATT_DESCRIPTOR gattDescriptor = recoverNativeLeGattDescriptor(
+ service->startHandle, charHandle, descriptorHandle, dscrDetails);
+
+ setGattDescriptorValue(hService, &gattDescriptor, newValue, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ closeSystemDevice(hService);
+ 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(systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+
+ if (gattDescriptor.DescriptorType == ClientCharacteristicConfiguration) {
+
+ QDataStream in(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);
+
+ charDetails.hValueChangeEvent = registerEvent(
+ hService, gattCharacteristic, this, &systemErrorCode);
+ }
+ } else {
+ if (charDetails.hValueChangeEvent) {
+ unregisterEvent(charDetails.hValueChangeEvent, &systemErrorCode);
+ charDetails.hValueChangeEvent = NULL;
+ }
+ }
+
+ closeSystemDevice(hService);
+
+ if (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(systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+ } else {
+ closeSystemDevice(hService);
+ }
+
+ updateValueOfDescriptor(charHandle, descriptorHandle, newValue, false);
+
+ const QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle);
+ emit service->descriptorWritten(dscr, newValue);
+}
+
+void QLowEnergyControllerPrivate::addToGenericAttributeList(const QLowEnergyServiceData &, QLowEnergyHandle)
+{
+ Q_UNIMPLEMENTED();
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h
index 7727b583..26ca56f6 100644
--- a/src/bluetooth/qlowenergyserviceprivate_p.h
+++ b/src/bluetooth/qlowenergyserviceprivate_p.h
@@ -83,6 +83,9 @@ public:
QLowEnergyCharacteristic::PropertyTypes properties;
QByteArray value;
QHash<QLowEnergyHandle, DescData> descriptorList;
+#ifdef QT_WIN_BLUETOOTH
+ Qt::HANDLE hValueChangeEvent;
+#endif
};
enum GattAttributeTypes {
diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h
new file mode 100644
index 00000000..c12ea4ae
--- /dev/null
+++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINLOWENERGYBLUETOOTH_P_H
+#define QWINLOWENERGYBLUETOOTH_P_H
+
+#include <QtCore/qlibrary.h>
+
+#include <qt_windows.h>
+
+#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