From 98a7d7cac2be779befd12396a531d29a316ef804 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 16 Jul 2014 21:00:56 +0400 Subject: Add a stub modules to compile on Windows It is the first commit which prepares possibility of build for Windows; still there is no Windows-specific implementation of bluetooth classes. Tested build with Qt5 (MinGW). Change-Id: I70d8c5cff4ac4e8b9192ab8556d81aad8432394a Reviewed-by: Alex Blasche --- src/bluetooth/bluetooth.pro | 10 ++ .../qbluetoothdevicediscoveryagent_win.cpp | 82 +++++++++++ src/bluetooth/qbluetoothlocaldevice_win.cpp | 112 ++++++++++++++ src/bluetooth/qbluetoothserver_win.cpp | 111 ++++++++++++++ .../qbluetoothservicediscoveryagent_win.cpp | 68 +++++++++ src/bluetooth/qbluetoothserviceinfo_win.cpp | 71 +++++++++ src/bluetooth/qbluetoothsocket_win.cpp | 164 +++++++++++++++++++++ 7 files changed, 618 insertions(+) create mode 100644 src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp create mode 100644 src/bluetooth/qbluetoothlocaldevice_win.cpp create mode 100644 src/bluetooth/qbluetoothserver_win.cpp create mode 100644 src/bluetooth/qbluetoothservicediscoveryagent_win.cpp create mode 100644 src/bluetooth/qbluetoothserviceinfo_win.cpp create mode 100644 src/bluetooth/qbluetoothsocket_win.cpp diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index 95c7ae19..b02e9e37 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -117,6 +117,16 @@ config_bluez:qtHaveModule(dbus) { qbluetoothsocket_android.cpp \ qbluetoothserver_android.cpp +} else:win32 { + + SOURCES += \ + qbluetoothdevicediscoveryagent_win.cpp \ + qbluetoothlocaldevice_win.cpp \ + qbluetoothserviceinfo_win.cpp \ + qbluetoothservicediscoveryagent_win.cpp \ + qbluetoothsocket_win.cpp \ + qbluetoothserver_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.") diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp new file mode 100644 index 00000000..863ae74b --- /dev/null +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbluetoothdevicediscoveryagent.h" +#include "qbluetoothdevicediscoveryagent_p.h" +#include "qbluetoothaddress.h" +#include "qbluetoothuuid.h" + +#define QT_DEVICEDISCOVERY_DEBUG + +QT_BEGIN_NAMESPACE + +QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( + const QBluetoothAddress &deviceAdapter, + QBluetoothDeviceDiscoveryAgent *parent) + : q_ptr(parent) +{ + Q_UNUSED(deviceAdapter); + inquiryType = QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry; + lastError = QBluetoothDeviceDiscoveryAgent::NoError; +} + +QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() +{ +} + +bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const +{ + return false; +} + +void QBluetoothDeviceDiscoveryAgentPrivate::start() +{ + Q_Q(QBluetoothDeviceDiscoveryAgent); + lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError; + errorString = QBluetoothDeviceDiscoveryAgent::tr("No Bluetooth device available"); + emit q->error(QBluetoothDeviceDiscoveryAgent::InputOutputError); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::stop() +{ +} + +QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp new file mode 100644 index 00000000..6fedafc7 --- /dev/null +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbluetoothlocaldevice.h" +#include "qbluetoothaddress.h" + +QT_BEGIN_NAMESPACE + +QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) : + QObject(parent), + d_ptr(0) +{ +} + +QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &, QObject *parent) : + QObject(parent), + d_ptr(0) +{ +} + +QString QBluetoothLocalDevice::name() const +{ + return QString(); +} + +QBluetoothAddress QBluetoothLocalDevice::address() const +{ + return QBluetoothAddress(); +} + +void QBluetoothLocalDevice::powerOn() +{ +} + +void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode) +{ + Q_UNUSED(mode); +} + +QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const +{ + return HostPoweredOff; +} + +QList QBluetoothLocalDevice::connectedDevices() const +{ + return QList(); +} + +QList QBluetoothLocalDevice::allDevices() +{ + QList localDevices; + return localDevices; +} + +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); +} + +QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothserver_win.cpp b/src/bluetooth/qbluetoothserver_win.cpp new file mode 100644 index 00000000..d9efcf2f --- /dev/null +++ b/src/bluetooth/qbluetoothserver_win.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp new file mode 100644 index 00000000..86874a6c --- /dev/null +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbluetoothservicediscoveryagent.h" +#include "qbluetoothservicediscoveryagent_p.h" + +QT_BEGIN_NAMESPACE + +QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(const QBluetoothAddress &deviceAdapter) + : error(QBluetoothServiceDiscoveryAgent::NoError), state(Inactive), + deviceDiscoveryAgent(0), mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), + singleDevice(false) +{ + Q_UNUSED(deviceAdapter); +} + +QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate() +{ +} + +void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address) +{ + Q_UNUSED(address); +} + +void QBluetoothServiceDiscoveryAgentPrivate::stop() +{ +} + +QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothserviceinfo_win.cpp b/src/bluetooth/qbluetoothserviceinfo_win.cpp new file mode 100644 index 00000000..e5f2f4e5 --- /dev/null +++ b/src/bluetooth/qbluetoothserviceinfo_win.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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..6e78be9e --- /dev/null +++ b/src/bluetooth/qbluetoothsocket_win.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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::_q_writeNotify() +{ +} + +void QBluetoothSocketPrivate::_q_readNotify() +{ +} + +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 -- cgit v1.2.3 From 12881369cddf906a94bff8e5f2520d32369476a4 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 17 Jul 2014 13:31:01 +0400 Subject: Implement QBluetoothLocalDevice::allDevices() method Implemented enumeration of local Bluetooth devices on Windows. Tested on Windows 8.1 32 bit with several USB Bluetooth dongles, based on "BCM920702" chip (from the Broadcom Corp) and on "CSR8510 A10" chip (from the Cambridge Silicon Radio Ltd), using Qt5 (MinGW). Important note: For some unknown reason Windows refuses to correctly work with more than one USB Bluetooth dongle at the same time. When the second dongle is inserted the device manager displays "This device can't start.(Code 10)". This issue can even be reproduced when the two USB devices have an identical chip, or two devices on different chips are plugged in. Others combinations of devices and their quantities (more than two) were not checked. Thus, the current implementation only works with one local USB Bluetooth device. Change-Id: Ie84b02acbb2b7700016343438e2f068dd631497b Reviewed-by: Alex Blasche --- src/bluetooth/bluetooth.pro | 4 ++- src/bluetooth/qbluetoothlocaldevice_win.cpp | 38 ++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index b02e9e37..84b1e294 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -117,7 +117,7 @@ config_bluez:qtHaveModule(dbus) { qbluetoothsocket_android.cpp \ qbluetoothserver_android.cpp -} else:win32 { +} else:win32:!winrt:!wince { SOURCES += \ qbluetoothdevicediscoveryagent_win.cpp \ @@ -127,6 +127,8 @@ config_bluez:qtHaveModule(dbus) { qbluetoothsocket_win.cpp \ qbluetoothserver_win.cpp + LIBS += -lbthprops + } else { message("Unsupported Bluetooth platform, will not build a working QtBluetooth library.") message("Either no Qt D-Bus found or no BlueZ headers.") diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp index 6fedafc7..452d4575 100644 --- a/src/bluetooth/qbluetoothlocaldevice_win.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -42,6 +43,9 @@ #include "qbluetoothlocaldevice.h" #include "qbluetoothaddress.h" +#include +#include + QT_BEGIN_NAMESPACE QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) : @@ -87,7 +91,39 @@ QList QBluetoothLocalDevice::connectedDevices() const QList QBluetoothLocalDevice::allDevices() { + BLUETOOTH_FIND_RADIO_PARAMS findRadioParams; + ::ZeroMemory(&findRadioParams, sizeof(findRadioParams)); + findRadioParams.dwSize = sizeof(findRadioParams); + + HANDLE radioHandle = NULL; + const HBLUETOOTH_RADIO_FIND radioFindHandle = ::BluetoothFindFirstRadio(&findRadioParams, + &radioHandle); + if (!radioFindHandle) + return QList(); + QList localDevices; + + forever { + BLUETOOTH_RADIO_INFO radioInfo; + ::ZeroMemory(&radioInfo, sizeof(radioInfo)); + radioInfo.dwSize = sizeof(radioInfo); + + const DWORD retval = ::BluetoothGetRadioInfo(radioHandle, &radioInfo); + ::CloseHandle(radioHandle); + + if (retval != ERROR_SUCCESS) + break; + + QBluetoothHostInfo localDevice; + localDevice.setAddress(QBluetoothAddress(radioInfo.address.ullLong)); + localDevice.setName(QString::fromWCharArray(radioInfo.szName)); + localDevices.append(localDevice); + + if (!::BluetoothFindNextRadio(radioFindHandle, &radioHandle)) + break; + } + + ::BluetoothFindRadioClose(radioFindHandle); return localDevices; } -- cgit v1.2.3 From 7f1ed68c12c193561bd016da091d9ffff0b7724b Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 17 Jul 2014 18:34:13 +0400 Subject: Implement the initialization of QBluetoothLocalDevice The instance of the local device is initialized via its bluetooth address. * If the constructor is passed an empty address, the class will be initialized using properties of first found local Bluetooth device in the system. * If the constructor is passed the specific address, it will enumerated all local Bluetooth devices and compares the Bluetooth address of each device. In case none of the addresses match, QBluetoothLocalDevice::isValid() returns false. Change-Id: Ib88df0403aa5d227ec6b75a4afcbf2f8675f05a8 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetooth.cpp | 4 +- src/bluetooth/qbluetoothlocaldevice_p.h | 29 +++++++++- src/bluetooth/qbluetoothlocaldevice_win.cpp | 86 +++++++++++++++++++++++++++-- 3 files changed, 111 insertions(+), 8 deletions(-) diff --git a/src/bluetooth/qbluetooth.cpp b/src/bluetooth/qbluetooth.cpp index de1e5629..d991808b 100644 --- a/src/bluetooth/qbluetooth.cpp +++ b/src/bluetooth/qbluetooth.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -80,5 +81,6 @@ 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_QNX, "qt.bluetooth.qnx") +Q_LOGGING_CATEGORY(QT_BT_WINDOWS, "qt.bluetooth.windows") QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h index 55086012..383a1bb8 100644 --- a/src/bluetooth/qbluetoothlocaldevice_p.h +++ b/src/bluetooth/qbluetoothlocaldevice_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -78,6 +79,10 @@ QT_END_NAMESPACE #include #endif +#ifdef Q_OS_WIN32 +#include +#endif + QT_BEGIN_NAMESPACE class QBluetoothAddress; @@ -234,6 +239,28 @@ private: bool isValidDevice; QList connectedDevicesSet; }; + +#elif defined(Q_OS_WIN32) + +class QBluetoothLocalDevicePrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(QBluetoothLocalDevice) +public: + QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, const QBluetoothAddress &address); + ~QBluetoothLocalDevicePrivate(); + + void initialize(const QBluetoothAddress &address); + + bool isValid() const; + +private: + QBluetoothLocalDevice *q_ptr; + HANDLE deviceHandle; + QString deviceName; + QBluetoothAddress deviceAddress; +}; + #else class QBluetoothLocalDevicePrivate : public QObject { diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp index 452d4575..2ce8e0af 100644 --- a/src/bluetooth/qbluetoothlocaldevice_win.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -43,31 +43,38 @@ #include "qbluetoothlocaldevice.h" #include "qbluetoothaddress.h" -#include +#include "qbluetoothlocaldevice_p.h" + +#include + #include QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) + QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) : QObject(parent), - d_ptr(0) + d_ptr(new QBluetoothLocalDevicePrivate(this, QBluetoothAddress())) { } -QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &, QObject *parent) : +QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent) : QObject(parent), - d_ptr(0) + d_ptr(new QBluetoothLocalDevicePrivate(this, address)) { } QString QBluetoothLocalDevice::name() const { - return QString(); + Q_D(const QBluetoothLocalDevice); + return d->deviceName; } QBluetoothAddress QBluetoothLocalDevice::address() const { - return QBluetoothAddress(); + Q_D(const QBluetoothLocalDevice); + return d->deviceAddress; } void QBluetoothLocalDevice::powerOn() @@ -145,4 +152,71 @@ void QBluetoothLocalDevice::pairingConfirmation(bool confirmation) Q_UNUSED(confirmation); } +QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, + const QBluetoothAddress &address) + : q_ptr(q) + , deviceHandle(NULL) +{ + initialize(address); +} + + +QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate() +{ + if (isValid()) + ::CloseHandle(deviceHandle); +} + +void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address) +{ + BLUETOOTH_FIND_RADIO_PARAMS findRadioParams; + ::ZeroMemory(&findRadioParams, sizeof(findRadioParams)); + findRadioParams.dwSize = sizeof(findRadioParams); + + HANDLE radioHandle = NULL; + const HBLUETOOTH_RADIO_FIND radioFindHandle = ::BluetoothFindFirstRadio(&findRadioParams, + &radioHandle); + if (!radioFindHandle) { + qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); + return; + } + + forever { + BLUETOOTH_RADIO_INFO radioInfo; + ::ZeroMemory(&radioInfo, sizeof(radioInfo)); + radioInfo.dwSize = sizeof(radioInfo); + + const DWORD retval = ::BluetoothGetRadioInfo(radioHandle, &radioInfo); + if (retval != ERROR_SUCCESS) { + ::CloseHandle(radioHandle); + qCWarning(QT_BT_WINDOWS) << qt_error_string(retval); + break; + } + + if (address.isNull() || (address == QBluetoothAddress(radioInfo.address.ullLong))) { + deviceAddress = QBluetoothAddress(radioInfo.address.ullLong); + deviceName = QString::fromWCharArray(radioInfo.szName); + deviceHandle = radioHandle; + break; + } + + ::CloseHandle(radioHandle); + + if (!::BluetoothFindNextRadio(radioFindHandle, &radioHandle)) { + const DWORD nativeError = ::GetLastError(); + if (nativeError != ERROR_NO_MORE_ITEMS) + qCWarning(QT_BT_WINDOWS) << qt_error_string(nativeError); + break; + } + } + + if (!::BluetoothFindRadioClose(radioFindHandle)) + qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); +} + +bool QBluetoothLocalDevicePrivate::isValid() const +{ + return deviceHandle && (deviceHandle != INVALID_HANDLE_VALUE); +} + QT_END_NAMESPACE -- cgit v1.2.3 From c7f99c0200e8ddf68b432735dd690525a1c54b74 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 17 Jul 2014 19:30:03 +0400 Subject: Implement the host modes for QBluetoothLocalDevice Change-Id: I313c8143dc4b54f5ee4aab8465514aefcbb5f789 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothlocaldevice_win.cpp | 72 ++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp index 2ce8e0af..0234a95a 100644 --- a/src/bluetooth/qbluetoothlocaldevice_win.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -79,15 +79,83 @@ QBluetoothAddress QBluetoothLocalDevice::address() const void QBluetoothLocalDevice::powerOn() { + if (hostMode() != HostPoweredOff) + return; + + setHostMode(HostConnectable); } -void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode) +void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode requestedMode) { - Q_UNUSED(mode); + Q_D(QBluetoothLocalDevice); + + 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(d->deviceHandle) + && !::BluetoothEnableDiscovery(d->deviceHandle, FALSE)) { + qCWarning(QT_BT_WINDOWS) << "Unable to disable the discoverable mode"; + emit error(QBluetoothLocalDevice::UnknownError); + return; + } + if (::BluetoothIsConnectable(d->deviceHandle) + && !::BluetoothEnableIncomingConnections(d->deviceHandle, FALSE)) { + qCWarning(QT_BT_WINDOWS) << "Unable to disable the connectable mode"; + emit error(QBluetoothLocalDevice::UnknownError); + return; + } + } else if (requestedMode == QBluetoothLocalDevice::HostConnectable) { + if (::BluetoothIsDiscoverable(d->deviceHandle)) { + if (!::BluetoothEnableDiscovery(d->deviceHandle, FALSE)) { + qCWarning(QT_BT_WINDOWS) << "Unable to disable the discoverable mode"; + emit error(QBluetoothLocalDevice::UnknownError); + return; + } + } else if (!::BluetoothEnableIncomingConnections(d->deviceHandle, 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(d->deviceHandle) + && !::BluetoothEnableIncomingConnections(d->deviceHandle, TRUE)) { + qCWarning(QT_BT_WINDOWS) << "Unable to enable the connectable mode"; + emit error(QBluetoothLocalDevice::UnknownError); + return; + } + if (!::BluetoothEnableDiscovery(d->deviceHandle, TRUE)) { + qCWarning(QT_BT_WINDOWS) << "Unable to enable the discoverable mode"; + emit error(QBluetoothLocalDevice::UnknownError); + return; + } + } + + emit hostModeStateChanged(requestedMode); } QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const { + Q_D(const QBluetoothLocalDevice); + + if (!isValid()) { + qCWarning(QT_BT_WINDOWS) << "The local device is not initialized correctly"; + return HostPoweredOff; + } + + if (::BluetoothIsDiscoverable(d->deviceHandle)) + return HostDiscoverable; + if (::BluetoothIsConnectable(d->deviceHandle)) + return HostConnectable; return HostPoweredOff; } -- cgit v1.2.3 From f71fda5702e6f1a76cdf52401af15599cca0735c Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Fri, 18 Jul 2014 19:43:29 +0400 Subject: Share the native private code of QBluetoothLocalDevice It makes sense to share the private code for QBluetoothLocalDevice with other classes. For this purpose the private class QBluetoothLocalDevicePrivate inherits from the new QBluetoothLocalDevicePrivateData class. Now QBluetoothLocalDevicePrivateData can be used by QBluetoothServiceDiscoveryAgentPrivate too. It reduces code duplication then searching for the handle of a local device with a certain address. Change-Id: I0bea137b231553364af9658d7d2a409fef4f40bb Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothlocaldevice_p.h | 21 ++++++++++++--------- src/bluetooth/qbluetoothlocaldevice_win.cpp | 13 +++++++------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h index 383a1bb8..08ad1c54 100644 --- a/src/bluetooth/qbluetoothlocaldevice_p.h +++ b/src/bluetooth/qbluetoothlocaldevice_p.h @@ -242,23 +242,26 @@ private: #elif defined(Q_OS_WIN32) -class QBluetoothLocalDevicePrivate : public QObject +class QBluetoothLocalDevicePrivateData +{ +public: + QBluetoothLocalDevicePrivateData(const QBluetoothAddress &address); + bool isValid() const; + + HANDLE deviceHandle; + QString deviceName; + QBluetoothAddress deviceAddress; +}; + +class QBluetoothLocalDevicePrivate : public QBluetoothLocalDevicePrivateData { - Q_OBJECT Q_DECLARE_PUBLIC(QBluetoothLocalDevice) public: QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, const QBluetoothAddress &address); ~QBluetoothLocalDevicePrivate(); - void initialize(const QBluetoothAddress &address); - - bool isValid() const; - private: QBluetoothLocalDevice *q_ptr; - HANDLE deviceHandle; - QString deviceName; - QBluetoothAddress deviceAddress; }; #else diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp index 0234a95a..476e5c65 100644 --- a/src/bluetooth/qbluetoothlocaldevice_win.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -222,20 +222,20 @@ void QBluetoothLocalDevice::pairingConfirmation(bool confirmation) QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, const QBluetoothAddress &address) - : q_ptr(q) - , deviceHandle(NULL) + : QBluetoothLocalDevicePrivateData(address) + , q_ptr(q) { - initialize(address); } - QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate() { if (isValid()) ::CloseHandle(deviceHandle); } -void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address) +QBluetoothLocalDevicePrivateData::QBluetoothLocalDevicePrivateData( + const QBluetoothAddress &address) + : deviceHandle(INVALID_HANDLE_VALUE) { BLUETOOTH_FIND_RADIO_PARAMS findRadioParams; ::ZeroMemory(&findRadioParams, sizeof(findRadioParams)); @@ -282,9 +282,10 @@ void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address) qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); } -bool QBluetoothLocalDevicePrivate::isValid() const +bool QBluetoothLocalDevicePrivateData::isValid() const { return deviceHandle && (deviceHandle != INVALID_HANDLE_VALUE); } + QT_END_NAMESPACE -- cgit v1.2.3 From fc4ed403dc20507d3c3f137b4acb957e2eef3c41 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Sun, 20 Jul 2014 16:20:03 +0400 Subject: Implement the QBluetoothDeviceDiscoveryAgent class on Windows Change-Id: I74eaf9acedbcd5b601ff3a6feef21869dbbf9bd9 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent.h | 7 +- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 30 +++- .../qbluetoothdevicediscoveryagent_win.cpp | 183 +++++++++++++++++++-- 3 files changed, 207 insertions(+), 13 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.h b/src/bluetooth/qbluetoothdevicediscoveryagent.h index ba675d90..89937769 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent.h @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -113,6 +114,10 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_PropertiesChanged(const QString &interface, const QVariantMap &changed_properties, const QStringList &invalidated_properties)) Q_PRIVATE_SLOT(d_func(), void _q_extendedDeviceDiscoveryTimeout()) #endif + +#ifdef Q_OS_WIN32 + Q_PRIVATE_SLOT(d_func(), void _q_handleFindResult()) +#endif }; QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index c4e804d3..c6199d62 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -71,6 +72,12 @@ QT_END_NAMESPACE #include #endif +#ifdef Q_OS_WIN32 +#include +#include "qbluetoothlocaldevice_p.h" +#include +#endif + QT_BEGIN_NAMESPACE class QBluetoothDeviceDiscoveryAgentPrivate @@ -78,6 +85,9 @@ class QBluetoothDeviceDiscoveryAgentPrivate : public QObject { Q_OBJECT +#elif defined(Q_OS_WIN32) + : public QBluetoothLocalDevicePrivateData +{ #else { #endif @@ -105,6 +115,10 @@ public: void _q_extendedDeviceDiscoveryTimeout(); #endif +#ifdef Q_OS_WIN32 + void _q_handleFindResult(); +#endif + private: QList discoveredDevices; QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType; @@ -166,6 +180,20 @@ private: bool isFinished; #endif +#ifdef Q_OS_WIN32 + void processDiscoveredDevices(const BLUETOOTH_DEVICE_INFO &info); + void handleErrors(DWORD errorCode); + bool isRunning() const; + + static QVariant findFirstDevice(HANDLE radioHandle); + static QVariant findNextDevice(HBLUETOOTH_DEVICE_FIND findHandle); + static void findClose(HBLUETOOTH_DEVICE_FIND findHandle); + + QFutureWatcher *findWatcher; + bool pendingCancel; + bool pendingStart; +#endif + QBluetoothDeviceDiscoveryAgent *q_ptr; }; diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 863ae74b..94c62ba1 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -48,35 +49,195 @@ QT_BEGIN_NAMESPACE +struct NativeFindResult +{ + NativeFindResult(); + + BLUETOOTH_DEVICE_INFO deviceInfo; + HBLUETOOTH_DEVICE_FIND findHandle; + DWORD errorCode; +}; +Q_DECLARE_METATYPE(NativeFindResult) + +NativeFindResult::NativeFindResult() + : findHandle(NULL) + , errorCode(ERROR_SUCCESS) +{ + ::ZeroMemory(&deviceInfo, sizeof(deviceInfo)); + deviceInfo.dwSize = sizeof(deviceInfo); +} + QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( - const QBluetoothAddress &deviceAdapter, - QBluetoothDeviceDiscoveryAgent *parent) - : q_ptr(parent) + const QBluetoothAddress &deviceAdapter, + QBluetoothDeviceDiscoveryAgent *parent) + : QBluetoothLocalDevicePrivateData(deviceAdapter) + , inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry) + , lastError(QBluetoothDeviceDiscoveryAgent::NoError) + , findWatcher(0) + , pendingCancel(false) + , pendingStart(false) + , q_ptr(parent) { - Q_UNUSED(deviceAdapter); - inquiryType = QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry; - lastError = QBluetoothDeviceDiscoveryAgent::NoError; } QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() { + if (isRunning()) { + stop(); + findWatcher->waitForFinished(); + } } bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const { - return false; + if (pendingStart) + return true; + if (pendingCancel) + return false; + + return isRunning(); } void QBluetoothDeviceDiscoveryAgentPrivate::start() { Q_Q(QBluetoothDeviceDiscoveryAgent); - lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError; - errorString = QBluetoothDeviceDiscoveryAgent::tr("No Bluetooth device available"); - emit q->error(QBluetoothDeviceDiscoveryAgent::InputOutputError); + + if (pendingCancel == true) { + pendingStart = true; + return; + } + + discoveredDevices.clear(); + + if (!findWatcher) { + findWatcher = new QFutureWatcher(q); + QObject::connect(findWatcher, SIGNAL(finished()), q, SLOT(_q_handleFindResult())); + } + + const QFuture future = QtConcurrent::run(findFirstDevice, deviceHandle); + findWatcher->setFuture(future); } void QBluetoothDeviceDiscoveryAgentPrivate::stop() { + if (!isRunning()) + return; + + pendingCancel = true; + pendingStart = false; +} + +void QBluetoothDeviceDiscoveryAgentPrivate::_q_handleFindResult() +{ + Q_Q(QBluetoothDeviceDiscoveryAgent); + + const QVariant result = findWatcher->result(); + const NativeFindResult nativeFindResult = result.value(); + + if (nativeFindResult.errorCode == ERROR_SUCCESS + || nativeFindResult.errorCode == ERROR_NO_MORE_ITEMS) { + + if (pendingCancel && !pendingStart) { + emit q->canceled(); + pendingCancel = false; + } else if (pendingStart) { + pendingCancel = false; + pendingStart = false; + start(); + } else { + if (nativeFindResult.errorCode == ERROR_NO_MORE_ITEMS) { + emit q->finished(); + } else { + processDiscoveredDevices(nativeFindResult.deviceInfo); + const QFuture future = + QtConcurrent::run(findNextDevice, nativeFindResult.findHandle); + findWatcher->setFuture(future); + return; + } + } + + } else { + handleErrors(nativeFindResult.errorCode); + pendingCancel = false; + pendingStart = false; + } + + findClose(nativeFindResult.findHandle); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevices( + const BLUETOOTH_DEVICE_INFO &info) +{ + Q_Q(QBluetoothDeviceDiscoveryAgent); + + QBluetoothDeviceInfo deviceInfo( + QBluetoothAddress(info.Address.ullLong), + QString::fromWCharArray(info.szName), + info.ulClassofDevice); + + if (info.fRemembered) + deviceInfo.setCached(true); + + discoveredDevices.append(deviceInfo); + emit q->deviceDiscovered(deviceInfo); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::handleErrors(DWORD errorCode) +{ + Q_Q(QBluetoothDeviceDiscoveryAgent); + + lastError = (errorCode == ERROR_INVALID_HANDLE) ? + QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError + : QBluetoothDeviceDiscoveryAgent::InputOutputError; + errorString = qt_error_string(errorCode); + emit q->error(lastError); +} + +bool QBluetoothDeviceDiscoveryAgentPrivate::isRunning() const +{ + return findWatcher && findWatcher->isRunning(); +} + +QVariant QBluetoothDeviceDiscoveryAgentPrivate::findFirstDevice(HANDLE radioHandle) +{ + 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 = radioHandle; + + NativeFindResult nativeFindResult; + nativeFindResult.findHandle = ::BluetoothFindFirstDevice( + &searchParams, &nativeFindResult.deviceInfo); + if (!nativeFindResult.findHandle) + nativeFindResult.errorCode = ::GetLastError(); + + QVariant result; + result.setValue(nativeFindResult); + return result; +} + +QVariant QBluetoothDeviceDiscoveryAgentPrivate::findNextDevice(HBLUETOOTH_DEVICE_FIND findHandle) +{ + NativeFindResult nativeFindResult; + nativeFindResult.findHandle = findHandle; + if (!::BluetoothFindNextDevice(findHandle, &nativeFindResult.deviceInfo)) + nativeFindResult.errorCode = ::GetLastError(); + + QVariant result; + result.setValue(nativeFindResult); + return result; +} + +void QBluetoothDeviceDiscoveryAgentPrivate::findClose(HBLUETOOTH_DEVICE_FIND findHandle) +{ + if (findHandle) + ::BluetoothFindDeviceClose(findHandle); } QT_END_NAMESPACE -- cgit v1.2.3 From 4fc7f500adbb4316a0f92424364876ec714eeb8e Mon Sep 17 00:00:00 2001 From: David Weisgerber Date: Fri, 22 Aug 2014 13:28:19 +0200 Subject: Moved Q_DECLARE_METATYPE outside of Qt namespace Added test if bluetooth device present in start sequence of device discovery Change-Id: I87db065a38bc5cb8e8acb603d48e5856d3bc7080 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 94c62ba1..bdfb0237 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -57,7 +57,6 @@ struct NativeFindResult HBLUETOOTH_DEVICE_FIND findHandle; DWORD errorCode; }; -Q_DECLARE_METATYPE(NativeFindResult) NativeFindResult::NativeFindResult() : findHandle(NULL) @@ -109,6 +108,13 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start() discoveredDevices.clear(); + if (isValid() == false) { + lastError = QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError; + errorString = qt_error_string(ERROR_INVALID_HANDLE); + emit q->error(lastError); + return; + } + if (!findWatcher) { findWatcher = new QFutureWatcher(q); QObject::connect(findWatcher, SIGNAL(finished()), q, SLOT(_q_handleFindResult())); @@ -241,3 +247,5 @@ void QBluetoothDeviceDiscoveryAgentPrivate::findClose(HBLUETOOTH_DEVICE_FIND fin } QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QT_PREPEND_NAMESPACE(NativeFindResult)) -- cgit v1.2.3 From 0d73e3fa1c893c9b27b9a6eda7ab3359f4dac7ff Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Fri, 12 Sep 2014 14:21:59 +0200 Subject: Make Windows compile Those two functions were removed in 5.4. However the windows port had already added its windows specific version of the same class. Therefore they still existed after the merge. Change-Id: I4b66e776496518f5f2ffce6dba72e8b62a051740 Reviewed-by: Lars Knoll --- src/bluetooth/qbluetoothsocket_win.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp index 6e78be9e..670f5aac 100644 --- a/src/bluetooth/qbluetoothsocket_win.cpp +++ b/src/bluetooth/qbluetoothsocket_win.cpp @@ -69,14 +69,6 @@ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, Q_UNUSED(port); } -void QBluetoothSocketPrivate::_q_writeNotify() -{ -} - -void QBluetoothSocketPrivate::_q_readNotify() -{ -} - void QBluetoothSocketPrivate::abort() { } -- cgit v1.2.3 From 11095f3504515f0ec2a754f64f97719340247505 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 19 Nov 2014 20:01:15 +0300 Subject: Refactor of code for "classic" part on Windows The previous code is based on the assumption where we can use each of local radio separatelly to discovery of remote devices. Windows API allows to use a handle of separately local bluetooth adapter to operate with its power, to start/stop detection of remote devices through it and so on. But in this API there is no opportunity to enumerate services and attributes of the given remote device via the concrete local adapter. Therefore now to discovery of remote devices are used all local bluetooth adapters in system. Also, the power and the host modes management now are belongs to all local adapters at once. Tested on Windows 8 with the USB CSR8510 A10 adapter Change-Id: I949b112158a575f5b563a78163c1e3990c952ada Reviewed-by: Alex Blasche --- src/bluetooth/bluetooth.pro | 2 + src/bluetooth/qbluetoothdevicediscoveryagent.h | 4 - src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 34 ++-- .../qbluetoothdevicediscoveryagent_win.cpp | 209 ++++++++++----------- src/bluetooth/qbluetoothlocaldevice_p.h | 24 ++- src/bluetooth/qbluetoothlocaldevice_win.cpp | 166 +++++++--------- src/bluetooth/windows/qwinclassicbluetooth.cpp | 136 ++++++++++++++ src/bluetooth/windows/qwinclassicbluetooth_p.h | 77 ++++++++ src/bluetooth/windows/windows.pri | 5 + 9 files changed, 411 insertions(+), 246 deletions(-) create mode 100644 src/bluetooth/windows/qwinclassicbluetooth.cpp create mode 100644 src/bluetooth/windows/qwinclassicbluetooth_p.h create mode 100644 src/bluetooth/windows/windows.pri diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index afa0b9fa..fe00eec0 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -198,6 +198,8 @@ config_bluez:qtHaveModule(dbus) { SOURCES -= qbluetoothdevicediscoveryagent.cpp } else:win32:!winrt:!wince { + include(windows/windows.pri) + SOURCES += \ qbluetoothdevicediscoveryagent_win.cpp \ qbluetoothlocaldevice_win.cpp \ diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.h b/src/bluetooth/qbluetoothdevicediscoveryagent.h index e9c0a4ed..8a375096 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent.h @@ -107,10 +107,6 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_PropertiesChanged(const QString &interface, const QVariantMap &changed_properties, const QStringList &invalidated_properties)) Q_PRIVATE_SLOT(d_func(), void _q_extendedDeviceDiscoveryTimeout()) #endif - -#ifdef Q_OS_WIN32 - Q_PRIVATE_SLOT(d_func(), void _q_handleFindResult()) -#endif }; QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index f224fe41..096f013c 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -78,20 +78,16 @@ QT_END_NAMESPACE #ifdef Q_OS_WIN32 #include -#include "qbluetoothlocaldevice_p.h" -#include +#include "windows/qwinclassicbluetooth_p.h" #endif QT_BEGIN_NAMESPACE class QBluetoothDeviceDiscoveryAgentPrivate -#if defined(QT_QNX_BLUETOOTH) || defined(QT_ANDROID_BLUETOOTH) +#if defined(QT_QNX_BLUETOOTH) || defined(QT_ANDROID_BLUETOOTH) || defined(Q_OS_WIN32) : public QObject { Q_OBJECT -#elif defined(Q_OS_WIN32) - : public QBluetoothLocalDevicePrivateData -{ #else { #endif @@ -119,10 +115,6 @@ public: void _q_extendedDeviceDiscoveryTimeout(); #endif -#ifdef Q_OS_WIN32 - void _q_handleFindResult(); -#endif - private: QList discoveredDevices; QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType; @@ -191,17 +183,25 @@ private: #endif #ifdef Q_OS_WIN32 - void processDiscoveredDevices(const BLUETOOTH_DEVICE_INFO &info); - void handleErrors(DWORD errorCode); - bool isRunning() const; +private slots: + void classicDeviceDiscovered(); + +private: + void initialize(const QBluetoothAddress &deviceAdapter); + + bool isClassicAdapterValid(const QBluetoothAddress &deviceAdapter); + void startDiscoveryForFirstClassicDevice(); + void startDiscoveryForNextClassicDevice(HBLUETOOTH_DEVICE_FIND hSearch); + void completeClassicDiscovery(HBLUETOOTH_DEVICE_FIND hSearch); + void acceptDiscoveredClassicDevice(const BLUETOOTH_DEVICE_INFO &device); - static QVariant findFirstDevice(HANDLE radioHandle); - static QVariant findNextDevice(HBLUETOOTH_DEVICE_FIND findHandle); - static void findClose(HBLUETOOTH_DEVICE_FIND findHandle); + void setError(DWORD error, const QString &str = QString()); - QFutureWatcher *findWatcher; + QFutureWatcher *classicDiscoveryWatcher; bool pendingCancel; bool pendingStart; + bool isClassicActive; + bool isClassicValid; #endif QBluetoothDeviceDiscoveryAgent *q_ptr; diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index bdfb0237..53e4d5aa 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -45,45 +45,30 @@ #include "qbluetoothaddress.h" #include "qbluetoothuuid.h" -#define QT_DEVICEDISCOVERY_DEBUG - QT_BEGIN_NAMESPACE -struct NativeFindResult -{ - NativeFindResult(); - - BLUETOOTH_DEVICE_INFO deviceInfo; - HBLUETOOTH_DEVICE_FIND findHandle; - DWORD errorCode; -}; - -NativeFindResult::NativeFindResult() - : findHandle(NULL) - , errorCode(ERROR_SUCCESS) -{ - ::ZeroMemory(&deviceInfo, sizeof(deviceInfo)); - deviceInfo.dwSize = sizeof(deviceInfo); -} +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( const QBluetoothAddress &deviceAdapter, QBluetoothDeviceDiscoveryAgent *parent) - : QBluetoothLocalDevicePrivateData(deviceAdapter) - , inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry) + : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry) , lastError(QBluetoothDeviceDiscoveryAgent::NoError) - , findWatcher(0) + , classicDiscoveryWatcher(0) , pendingCancel(false) , pendingStart(false) + , isClassicActive(false) + , isClassicValid(false) , q_ptr(parent) { + initialize(deviceAdapter); } QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() { - if (isRunning()) { + if (isClassicActive) { stop(); - findWatcher->waitForFinished(); + classicDiscoveryWatcher->waitForFinished(); } } @@ -94,12 +79,16 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const if (pendingCancel) return false; - return isRunning(); + return isClassicActive; } void QBluetoothDeviceDiscoveryAgentPrivate::start() { - Q_Q(QBluetoothDeviceDiscoveryAgent); + if (!isClassicValid) { + setError(ERROR_INVALID_HANDLE, + QBluetoothDeviceDiscoveryAgent::tr("Passed address is not a local device.")); + return; + } if (pendingCancel == true) { pendingStart = true; @@ -108,40 +97,27 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start() discoveredDevices.clear(); - if (isValid() == false) { - lastError = QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError; - errorString = qt_error_string(ERROR_INVALID_HANDLE); - emit q->error(lastError); - return; - } - - if (!findWatcher) { - findWatcher = new QFutureWatcher(q); - QObject::connect(findWatcher, SIGNAL(finished()), q, SLOT(_q_handleFindResult())); - } - - const QFuture future = QtConcurrent::run(findFirstDevice, deviceHandle); - findWatcher->setFuture(future); + startDiscoveryForFirstClassicDevice(); } void QBluetoothDeviceDiscoveryAgentPrivate::stop() { - if (!isRunning()) + if (!isClassicActive) return; pendingCancel = true; pendingStart = false; } -void QBluetoothDeviceDiscoveryAgentPrivate::_q_handleFindResult() +void QBluetoothDeviceDiscoveryAgentPrivate::classicDeviceDiscovered() { Q_Q(QBluetoothDeviceDiscoveryAgent); - const QVariant result = findWatcher->result(); - const NativeFindResult nativeFindResult = result.value(); + const WinClassicBluetooth::RemoteDeviceDiscoveryResult result = + classicDiscoveryWatcher->result(); - if (nativeFindResult.errorCode == ERROR_SUCCESS - || nativeFindResult.errorCode == ERROR_NO_MORE_ITEMS) { + if (result.error == ERROR_SUCCESS + || result.error == ERROR_NO_MORE_ITEMS) { if (pendingCancel && !pendingStart) { emit q->canceled(); @@ -151,101 +127,116 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_handleFindResult() pendingStart = false; start(); } else { - if (nativeFindResult.errorCode == ERROR_NO_MORE_ITEMS) { - emit q->finished(); - } else { - processDiscoveredDevices(nativeFindResult.deviceInfo); - const QFuture future = - QtConcurrent::run(findNextDevice, nativeFindResult.findHandle); - findWatcher->setFuture(future); + if (result.error != ERROR_NO_MORE_ITEMS) { + acceptDiscoveredClassicDevice(result.device); + startDiscoveryForNextClassicDevice(result.hSearch); return; } } } else { - handleErrors(nativeFindResult.errorCode); + setError(result.error); pendingCancel = false; pendingStart = false; } - findClose(nativeFindResult.findHandle); + completeClassicDiscovery(result.hSearch); } -void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevices( - const BLUETOOTH_DEVICE_INFO &info) +void QBluetoothDeviceDiscoveryAgentPrivate::initialize( + const QBluetoothAddress &deviceAdapter) { - Q_Q(QBluetoothDeviceDiscoveryAgent); + isClassicValid = isClassicAdapterValid(deviceAdapter); +} - QBluetoothDeviceInfo deviceInfo( - QBluetoothAddress(info.Address.ullLong), - QString::fromWCharArray(info.szName), - info.ulClassofDevice); +bool QBluetoothDeviceDiscoveryAgentPrivate::isClassicAdapterValid( + const QBluetoothAddress &deviceAdapter) +{ + const WinClassicBluetooth::LocalRadiosDiscoveryResult result = + WinClassicBluetooth::enumerateLocalRadios(); - if (info.fRemembered) - deviceInfo.setCached(true); + if (result.error != NO_ERROR + && result.error != ERROR_NO_MORE_ITEMS) { + qCWarning(QT_BT_WINDOWS) << "Occurred error during search of classic local radios"; + return false; + } else if (result.radios.isEmpty()) { + qCWarning(QT_BT_WINDOWS) << "No any classic local radio found"; + return false; + } - discoveredDevices.append(deviceInfo); - emit q->deviceDiscovered(deviceInfo); + foreach (const BLUETOOTH_RADIO_INFO &radio, result.radios) { + if (deviceAdapter == QBluetoothAddress() + || deviceAdapter == QBluetoothAddress(radio.address.ullLong)) { + return true; + } + } + + qCWarning(QT_BT_WINDOWS) << "No matching for classic local radio:" << deviceAdapter; + return false; } -void QBluetoothDeviceDiscoveryAgentPrivate::handleErrors(DWORD errorCode) +void QBluetoothDeviceDiscoveryAgentPrivate::startDiscoveryForFirstClassicDevice() { - Q_Q(QBluetoothDeviceDiscoveryAgent); + isClassicActive = true; - lastError = (errorCode == ERROR_INVALID_HANDLE) ? - QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError - : QBluetoothDeviceDiscoveryAgent::InputOutputError; - errorString = qt_error_string(errorCode); - emit q->error(lastError); + if (!classicDiscoveryWatcher) { + classicDiscoveryWatcher = new QFutureWatcher< + WinClassicBluetooth::RemoteDeviceDiscoveryResult>(this); + QObject::connect(classicDiscoveryWatcher, SIGNAL(finished()), + this, SLOT(classicDeviceDiscovered())); + } + + const QFuture future = + QtConcurrent::run(WinClassicBluetooth::startDiscoveryOfFirstRemoteDevice); + classicDiscoveryWatcher->setFuture(future); } -bool QBluetoothDeviceDiscoveryAgentPrivate::isRunning() const +void QBluetoothDeviceDiscoveryAgentPrivate::startDiscoveryForNextClassicDevice( + HBLUETOOTH_DEVICE_FIND hSearch) { - return findWatcher && findWatcher->isRunning(); + Q_ASSERT(classicDiscoveryWatcher); + + const QFuture future = + QtConcurrent::run(WinClassicBluetooth::startDiscoveryOfNextRemoteDevice, hSearch); + classicDiscoveryWatcher->setFuture(future); } -QVariant QBluetoothDeviceDiscoveryAgentPrivate::findFirstDevice(HANDLE radioHandle) +void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDiscovery( + 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 = radioHandle; - - NativeFindResult nativeFindResult; - nativeFindResult.findHandle = ::BluetoothFindFirstDevice( - &searchParams, &nativeFindResult.deviceInfo); - if (!nativeFindResult.findHandle) - nativeFindResult.errorCode = ::GetLastError(); - - QVariant result; - result.setValue(nativeFindResult); - return result; + Q_Q(QBluetoothDeviceDiscoveryAgent); + + WinClassicBluetooth::cancelRemoteDevicesDiscovery(hSearch); + isClassicActive = false; + emit q->finished(); } -QVariant QBluetoothDeviceDiscoveryAgentPrivate::findNextDevice(HBLUETOOTH_DEVICE_FIND findHandle) +void QBluetoothDeviceDiscoveryAgentPrivate::acceptDiscoveredClassicDevice( + const BLUETOOTH_DEVICE_INFO &device) { - NativeFindResult nativeFindResult; - nativeFindResult.findHandle = findHandle; - if (!::BluetoothFindNextDevice(findHandle, &nativeFindResult.deviceInfo)) - nativeFindResult.errorCode = ::GetLastError(); - - QVariant result; - result.setValue(nativeFindResult); - return result; + Q_Q(QBluetoothDeviceDiscoveryAgent); + + QBluetoothDeviceInfo deviceInfo( + QBluetoothAddress(device.Address.ullLong), + QString::fromWCharArray(device.szName), + device.ulClassofDevice); + + if (device.fRemembered) + deviceInfo.setCached(true); + + discoveredDevices.append(deviceInfo); + emit q->deviceDiscovered(deviceInfo); } -void QBluetoothDeviceDiscoveryAgentPrivate::findClose(HBLUETOOTH_DEVICE_FIND findHandle) +void QBluetoothDeviceDiscoveryAgentPrivate::setError(DWORD error, const QString &str) { - if (findHandle) - ::BluetoothFindDeviceClose(findHandle); + Q_Q(QBluetoothDeviceDiscoveryAgent); + + lastError = (error == ERROR_INVALID_HANDLE) ? + QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError + : QBluetoothDeviceDiscoveryAgent::InputOutputError; + errorString = str.isEmpty() ? qt_error_string(error) : str; + emit q->error(lastError); } QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QT_PREPEND_NAMESPACE(NativeFindResult)) diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h index c490ca85..3ed8ca62 100644 --- a/src/bluetooth/qbluetoothlocaldevice_p.h +++ b/src/bluetooth/qbluetoothlocaldevice_p.h @@ -83,7 +83,7 @@ QT_END_NAMESPACE #endif #ifdef Q_OS_WIN32 -#include +#include "windows/qwinclassicbluetooth_p.h" #endif QT_BEGIN_NAMESPACE @@ -245,23 +245,21 @@ private: #elif defined(Q_OS_WIN32) -class QBluetoothLocalDevicePrivateData +class QBluetoothLocalDevicePrivate : public QObject { + Q_OBJECT + Q_DECLARE_PUBLIC(QBluetoothLocalDevice) public: - QBluetoothLocalDevicePrivateData(const QBluetoothAddress &address); + QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, + const QBluetoothAddress &address = QBluetoothAddress()); + + ~QBluetoothLocalDevicePrivate(); bool isValid() const; + void initialize(const QBluetoothAddress &address); - HANDLE deviceHandle; - QString deviceName; QBluetoothAddress deviceAddress; -}; - -class QBluetoothLocalDevicePrivate : public QBluetoothLocalDevicePrivateData -{ - Q_DECLARE_PUBLIC(QBluetoothLocalDevice) -public: - QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, const QBluetoothAddress &address); - ~QBluetoothLocalDevicePrivate(); + QString deviceName; + bool deviceValid; private: QBluetoothLocalDevice *q_ptr; diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp index 476e5c65..a2dd14e6 100644 --- a/src/bluetooth/qbluetoothlocaldevice_win.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -47,19 +47,18 @@ #include -#include - QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) : QObject(parent), - d_ptr(new QBluetoothLocalDevicePrivate(this, QBluetoothAddress())) + d_ptr(new QBluetoothLocalDevicePrivate(this)) { } -QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent) : +QBluetoothLocalDevice::QBluetoothLocalDevice( + const QBluetoothAddress &address, QObject *parent) : QObject(parent), d_ptr(new QBluetoothLocalDevicePrivate(this, address)) { @@ -85,10 +84,9 @@ void QBluetoothLocalDevice::powerOn() setHostMode(HostConnectable); } -void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode requestedMode) +void QBluetoothLocalDevice::setHostMode( + QBluetoothLocalDevice::HostMode requestedMode) { - Q_D(QBluetoothLocalDevice); - if (!isValid()) { qCWarning(QT_BT_WINDOWS) << "The local device is not initialized correctly"; return; @@ -101,39 +99,39 @@ void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode requeste return; if (requestedMode == QBluetoothLocalDevice::HostPoweredOff) { - if (::BluetoothIsDiscoverable(d->deviceHandle) - && !::BluetoothEnableDiscovery(d->deviceHandle, FALSE)) { + if (::BluetoothIsDiscoverable(NULL) + && !::BluetoothEnableDiscovery(NULL, FALSE)) { qCWarning(QT_BT_WINDOWS) << "Unable to disable the discoverable mode"; emit error(QBluetoothLocalDevice::UnknownError); return; } - if (::BluetoothIsConnectable(d->deviceHandle) - && !::BluetoothEnableIncomingConnections(d->deviceHandle, FALSE)) { + 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(d->deviceHandle)) { - if (!::BluetoothEnableDiscovery(d->deviceHandle, FALSE)) { + 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(d->deviceHandle, TRUE)) { + } 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(d->deviceHandle) - && !::BluetoothEnableIncomingConnections(d->deviceHandle, TRUE)) { + if (!::BluetoothIsConnectable(NULL) + && !::BluetoothEnableIncomingConnections(NULL, TRUE)) { qCWarning(QT_BT_WINDOWS) << "Unable to enable the connectable mode"; emit error(QBluetoothLocalDevice::UnknownError); return; } - if (!::BluetoothEnableDiscovery(d->deviceHandle, TRUE)) { + if (!::BluetoothEnableDiscovery(NULL, TRUE)) { qCWarning(QT_BT_WINDOWS) << "Unable to enable the discoverable mode"; emit error(QBluetoothLocalDevice::UnknownError); return; @@ -145,16 +143,14 @@ void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode requeste QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const { - Q_D(const QBluetoothLocalDevice); - if (!isValid()) { qCWarning(QT_BT_WINDOWS) << "The local device is not initialized correctly"; return HostPoweredOff; } - if (::BluetoothIsDiscoverable(d->deviceHandle)) + if (::BluetoothIsDiscoverable(NULL)) return HostDiscoverable; - if (::BluetoothIsConnectable(d->deviceHandle)) + if (::BluetoothIsConnectable(NULL)) return HostConnectable; return HostPoweredOff; } @@ -166,40 +162,17 @@ QList QBluetoothLocalDevice::connectedDevices() const QList QBluetoothLocalDevice::allDevices() { - BLUETOOTH_FIND_RADIO_PARAMS findRadioParams; - ::ZeroMemory(&findRadioParams, sizeof(findRadioParams)); - findRadioParams.dwSize = sizeof(findRadioParams); - - HANDLE radioHandle = NULL; - const HBLUETOOTH_RADIO_FIND radioFindHandle = ::BluetoothFindFirstRadio(&findRadioParams, - &radioHandle); - if (!radioFindHandle) - return QList(); - - QList localDevices; - - forever { - BLUETOOTH_RADIO_INFO radioInfo; - ::ZeroMemory(&radioInfo, sizeof(radioInfo)); - radioInfo.dwSize = sizeof(radioInfo); + const WinClassicBluetooth::LocalRadiosDiscoveryResult result = + WinClassicBluetooth::enumerateLocalRadios(); - const DWORD retval = ::BluetoothGetRadioInfo(radioHandle, &radioInfo); - ::CloseHandle(radioHandle); - - if (retval != ERROR_SUCCESS) - break; - - QBluetoothHostInfo localDevice; - localDevice.setAddress(QBluetoothAddress(radioInfo.address.ullLong)); - localDevice.setName(QString::fromWCharArray(radioInfo.szName)); - localDevices.append(localDevice); - - if (!::BluetoothFindNextRadio(radioFindHandle, &radioHandle)) - break; + QList devices; + foreach (const BLUETOOTH_RADIO_INFO &radio, result.radios) { + QBluetoothHostInfo device; + device.setAddress(QBluetoothAddress(radio.address.ullLong)); + device.setName(QString::fromWCharArray(radio.szName)); + devices.append(device); } - - ::BluetoothFindRadioClose(radioFindHandle); - return localDevices; + return devices; } void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing) @@ -220,72 +193,59 @@ void QBluetoothLocalDevice::pairingConfirmation(bool confirmation) Q_UNUSED(confirmation); } -QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, - const QBluetoothAddress &address) - : QBluetoothLocalDevicePrivateData(address) +QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate( + QBluetoothLocalDevice *q, const QBluetoothAddress &address) + : deviceValid(false) , q_ptr(q) { + initialize(address); } QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate() { - if (isValid()) - ::CloseHandle(deviceHandle); } -QBluetoothLocalDevicePrivateData::QBluetoothLocalDevicePrivateData( - const QBluetoothAddress &address) - : deviceHandle(INVALID_HANDLE_VALUE) +bool QBluetoothLocalDevicePrivate::isValid() const { - BLUETOOTH_FIND_RADIO_PARAMS findRadioParams; - ::ZeroMemory(&findRadioParams, sizeof(findRadioParams)); - findRadioParams.dwSize = sizeof(findRadioParams); - - HANDLE radioHandle = NULL; - const HBLUETOOTH_RADIO_FIND radioFindHandle = ::BluetoothFindFirstRadio(&findRadioParams, - &radioHandle); - if (!radioFindHandle) { - qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); - return; - } + return deviceValid; +} - forever { - BLUETOOTH_RADIO_INFO radioInfo; - ::ZeroMemory(&radioInfo, sizeof(radioInfo)); - radioInfo.dwSize = sizeof(radioInfo); +void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address) +{ + Q_Q(QBluetoothLocalDevice); - const DWORD retval = ::BluetoothGetRadioInfo(radioHandle, &radioInfo); - if (retval != ERROR_SUCCESS) { - ::CloseHandle(radioHandle); - qCWarning(QT_BT_WINDOWS) << qt_error_string(retval); - break; - } + const WinClassicBluetooth::LocalRadiosDiscoveryResult result = + WinClassicBluetooth::enumerateLocalRadios(); - if (address.isNull() || (address == QBluetoothAddress(radioInfo.address.ullLong))) { - deviceAddress = QBluetoothAddress(radioInfo.address.ullLong); - deviceName = QString::fromWCharArray(radioInfo.szName); - deviceHandle = radioHandle; - break; - } - - ::CloseHandle(radioHandle); + if (result.error != NO_ERROR + && result.error != ERROR_NO_MORE_ITEMS) { + qCWarning(QT_BT_WINDOWS) << qt_error_string(result.error); + QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection, + Q_ARG(QBluetoothLocalDevice::Error, + QBluetoothLocalDevice::UnknownError)); + return; + } else if (result.radios.isEmpty()) { + qCWarning(QT_BT_WINDOWS) << "No any classic local radio found"; + QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection, + Q_ARG(QBluetoothLocalDevice::Error, + QBluetoothLocalDevice::UnknownError)); + return; + } - if (!::BluetoothFindNextRadio(radioFindHandle, &radioHandle)) { - const DWORD nativeError = ::GetLastError(); - if (nativeError != ERROR_NO_MORE_ITEMS) - qCWarning(QT_BT_WINDOWS) << qt_error_string(nativeError); - break; + foreach (const BLUETOOTH_RADIO_INFO &radio, result.radios) { + if (address == QBluetoothAddress() + || address == QBluetoothAddress(radio.address.ullLong)) { + deviceAddress = QBluetoothAddress(radio.address.ullLong); + deviceName = QString::fromWCharArray(radio.szName); + deviceValid = true; + return; } } - if (!::BluetoothFindRadioClose(radioFindHandle)) - qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); -} - -bool QBluetoothLocalDevicePrivateData::isValid() const -{ - return deviceHandle && (deviceHandle != INVALID_HANDLE_VALUE); + qCWarning(QT_BT_WINDOWS) << "Unable to find classic local radio: " << address; + QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection, + Q_ARG(QBluetoothLocalDevice::Error, + QBluetoothLocalDevice::UnknownError)); } - QT_END_NAMESPACE diff --git a/src/bluetooth/windows/qwinclassicbluetooth.cpp b/src/bluetooth/windows/qwinclassicbluetooth.cpp new file mode 100644 index 00000000..31212d8e --- /dev/null +++ b/src/bluetooth/windows/qwinclassicbluetooth.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwinclassicbluetooth_p.h" + +QT_BEGIN_NAMESPACE + +namespace WinClassicBluetooth { + +LocalRadiosDiscoveryResult::LocalRadiosDiscoveryResult() + : error(NO_ERROR) +{ +} + +RemoteDeviceDiscoveryResult::RemoteDeviceDiscoveryResult() + : hSearch(0) + , error(NO_ERROR) +{ + ::ZeroMemory(&device, sizeof(device)); + device.dwSize = sizeof(device); +} + +LocalRadiosDiscoveryResult enumerateLocalRadios() +{ + BLUETOOTH_FIND_RADIO_PARAMS params; + ::ZeroMemory(¶ms, sizeof(params)); + params.dwSize = sizeof(params); + + HANDLE hRadio = 0; + const HBLUETOOTH_RADIO_FIND hSearch = + ::BluetoothFindFirstRadio(¶ms, &hRadio); + + LocalRadiosDiscoveryResult result; + + if (!hSearch) { + result.error = ::GetLastError(); + return result; + } + + forever { + BLUETOOTH_RADIO_INFO radio; + ::ZeroMemory(&radio, sizeof(radio)); + radio.dwSize = sizeof(radio); + + const DWORD retval = ::BluetoothGetRadioInfo(hRadio, &radio); + ::CloseHandle(hRadio); + + if (retval != ERROR_SUCCESS) { + result.error = ::GetLastError(); + break; + } + + result.radios.append(radio); + + if (!::BluetoothFindNextRadio(hSearch, &hRadio)) { + result.error = ::GetLastError(); + break; + } + } + + ::BluetoothFindRadioClose(hSearch); + return result; +} + +RemoteDeviceDiscoveryResult startDiscoveryOfFirstRemoteDevice() +{ + 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; + + RemoteDeviceDiscoveryResult result; + result.hSearch = ::BluetoothFindFirstDevice( + &searchParams, &result.device); + + if (!result.hSearch) + result.error = ::GetLastError(); + return result; +} + +RemoteDeviceDiscoveryResult startDiscoveryOfNextRemoteDevice( + HBLUETOOTH_DEVICE_FIND hSearch) +{ + RemoteDeviceDiscoveryResult result; + result.hSearch = hSearch; + if (!::BluetoothFindNextDevice(hSearch, &result.device)) + result.error = ::GetLastError(); + return result; +} + +void cancelRemoteDevicesDiscovery(HBLUETOOTH_DEVICE_FIND hSearch) +{ + if (hSearch) + ::BluetoothFindDeviceClose(hSearch); +} + +} // namespace WinClassicBluetooth + +QT_END_NAMESPACE diff --git a/src/bluetooth/windows/qwinclassicbluetooth_p.h b/src/bluetooth/windows/qwinclassicbluetooth_p.h new file mode 100644 index 00000000..9c0955bf --- /dev/null +++ b/src/bluetooth/windows/qwinclassicbluetooth_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINCLASSICBLUETOOTH_P_H +#define QWINCLASSICBLUETOOTH_P_H + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace WinClassicBluetooth { + +struct LocalRadiosDiscoveryResult +{ + LocalRadiosDiscoveryResult(); + QList radios; + DWORD error; +}; + +struct RemoteDeviceDiscoveryResult +{ + RemoteDeviceDiscoveryResult(); + BLUETOOTH_DEVICE_INFO device; + HBLUETOOTH_DEVICE_FIND hSearch; + DWORD error; +}; + +LocalRadiosDiscoveryResult enumerateLocalRadios(); + +RemoteDeviceDiscoveryResult startDiscoveryOfFirstRemoteDevice(); +RemoteDeviceDiscoveryResult startDiscoveryOfNextRemoteDevice(HBLUETOOTH_DEVICE_FIND hSearch); +void cancelRemoteDevicesDiscovery(HBLUETOOTH_DEVICE_FIND hSearch); + +} // namespace WinClassicBluetooth + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(WinClassicBluetooth::LocalRadiosDiscoveryResult) +Q_DECLARE_METATYPE(WinClassicBluetooth::RemoteDeviceDiscoveryResult) + +#endif // QWINCLASSICBLUETOOTH_P_H diff --git a/src/bluetooth/windows/windows.pri b/src/bluetooth/windows/windows.pri new file mode 100644 index 00000000..f08e0497 --- /dev/null +++ b/src/bluetooth/windows/windows.pri @@ -0,0 +1,5 @@ +PRIVATE_HEADERS += \ + windows/qwinclassicbluetooth_p.h + +SOURCES += \ + windows/qwinclassicbluetooth.cpp -- cgit v1.2.3 From 02b415fe5d538645e95896eb70c9d38628388841 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Fri, 14 Nov 2014 20:02:16 +0300 Subject: Add discovering for BLE devices on Windows BLE devices are supported in Windows 8 and above. Windows has not public API to discovering/pairing of BLE devices. A user shall do it by means of standard "bluetooth" application which are inbox into Windows. Only after that there is an opportunity to display the discovered devices. Change-Id: Idd3d2949456a32c8c333744205755853aef80422 Reviewed-by: Alex Blasche --- src/bluetooth/bluetooth.pro | 2 +- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 23 ++ .../qbluetoothdevicediscoveryagent_win.cpp | 215 ++++++++++++++--- src/bluetooth/windows/qwinlowenergybluetooth.cpp | 264 +++++++++++++++++++++ src/bluetooth/windows/qwinlowenergybluetooth_p.h | 74 ++++++ src/bluetooth/windows/windows.pri | 6 +- 6 files changed, 551 insertions(+), 33 deletions(-) create mode 100644 src/bluetooth/windows/qwinlowenergybluetooth.cpp create mode 100644 src/bluetooth/windows/qwinlowenergybluetooth_p.h diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index fe00eec0..db8d2e21 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -209,7 +209,7 @@ config_bluez:qtHaveModule(dbus) { qbluetoothserver_win.cpp \ qlowenergycontroller_p.cpp - LIBS += -lbthprops + LIBS += -lbthprops -lsetupapi } else { message("Unsupported Bluetooth platform, will not build a working QtBluetooth library.") diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index 096f013c..cb247943 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -79,6 +79,7 @@ QT_END_NAMESPACE #ifdef Q_OS_WIN32 #include #include "windows/qwinclassicbluetooth_p.h" +#include "windows/qwinlowenergybluetooth_p.h" #endif QT_BEGIN_NAMESPACE @@ -185,6 +186,7 @@ private: #ifdef Q_OS_WIN32 private slots: void classicDeviceDiscovered(); + void lowEnergyDeviceDiscovered(); private: void initialize(const QBluetoothAddress &deviceAdapter); @@ -195,13 +197,34 @@ private: void completeClassicDiscovery(HBLUETOOTH_DEVICE_FIND hSearch); void acceptDiscoveredClassicDevice(const BLUETOOTH_DEVICE_INFO &device); + bool isLowEnergyAdapterValid(const QBluetoothAddress &deviceAdapter); + void startDiscoveryForLowEnergyDevices(); + void completeLowEnergyDiscovery(); + void acceptDiscoveredLowEnergyDevice(const WinLowEnergyBluetooth::DeviceInfo &device); + + void processDuplicates(const QBluetoothDeviceInfo &foundDevice); + void setError(DWORD error, const QString &str = QString()); + bool isDiscoveredSuccessfully(int systemError) const; + + bool canBeCanceled() const; + void cancel(); + + bool canBePendingStarted() const; + void prepareToPendingStart(); + + void finalize(); + void drop(int systemError); + QFutureWatcher *classicDiscoveryWatcher; + QFutureWatcher *lowEnergyDiscoveryWatcher; bool pendingCancel; bool pendingStart; bool isClassicActive; bool isClassicValid; + bool isLowEnergyActive; + bool isLowEnergyValid; #endif QBluetoothDeviceDiscoveryAgent *q_ptr; diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 53e4d5aa..5a550e57 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -55,10 +55,13 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry) , lastError(QBluetoothDeviceDiscoveryAgent::NoError) , classicDiscoveryWatcher(0) + , lowEnergyDiscoveryWatcher(0) , pendingCancel(false) , pendingStart(false) , isClassicActive(false) , isClassicValid(false) + , isLowEnergyActive(false) + , isLowEnergyValid(false) , q_ptr(parent) { initialize(deviceAdapter); @@ -66,10 +69,14 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() { - if (isClassicActive) { + if (isClassicActive || isLowEnergyActive) stop(); + + if (classicDiscoveryWatcher) classicDiscoveryWatcher->waitForFinished(); - } + + if (lowEnergyDiscoveryWatcher) + lowEnergyDiscoveryWatcher->waitForFinished(); } bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const @@ -79,12 +86,12 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const if (pendingCancel) return false; - return isClassicActive; + return isClassicActive || isLowEnergyActive; } void QBluetoothDeviceDiscoveryAgentPrivate::start() { - if (!isClassicValid) { + if (!isClassicValid && !isLowEnergyValid) { setError(ERROR_INVALID_HANDLE, QBluetoothDeviceDiscoveryAgent::tr("Passed address is not a local device.")); return; @@ -97,12 +104,16 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start() discoveredDevices.clear(); - startDiscoveryForFirstClassicDevice(); + if (isClassicValid) + startDiscoveryForFirstClassicDevice(); + + if (isLowEnergyValid) + startDiscoveryForLowEnergyDevices(); } void QBluetoothDeviceDiscoveryAgentPrivate::stop() { - if (!isClassicActive) + if (!isClassicActive && !isLowEnergyActive) return; pendingCancel = true; @@ -111,21 +122,15 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop() void QBluetoothDeviceDiscoveryAgentPrivate::classicDeviceDiscovered() { - Q_Q(QBluetoothDeviceDiscoveryAgent); - const WinClassicBluetooth::RemoteDeviceDiscoveryResult result = classicDiscoveryWatcher->result(); - if (result.error == ERROR_SUCCESS - || result.error == ERROR_NO_MORE_ITEMS) { + if (isDiscoveredSuccessfully(result.error)) { - if (pendingCancel && !pendingStart) { - emit q->canceled(); - pendingCancel = false; - } else if (pendingStart) { - pendingCancel = false; - pendingStart = false; - start(); + if (canBeCanceled()) { + cancel(); + } else if (canBePendingStarted()) { + prepareToPendingStart(); } else { if (result.error != ERROR_NO_MORE_ITEMS) { acceptDiscoveredClassicDevice(result.device); @@ -135,18 +140,40 @@ void QBluetoothDeviceDiscoveryAgentPrivate::classicDeviceDiscovered() } } else { - setError(result.error); - pendingCancel = false; - pendingStart = false; + drop(result.error); } completeClassicDiscovery(result.hSearch); } +void QBluetoothDeviceDiscoveryAgentPrivate::lowEnergyDeviceDiscovered() +{ + const WinLowEnergyBluetooth::DeviceDiscoveryResult result = + lowEnergyDiscoveryWatcher->result(); + + if (isDiscoveredSuccessfully(result.error)) { + + if (canBeCanceled()) { + cancel(); + } else if (canBePendingStarted()) { + prepareToPendingStart(); + } else { + foreach (const WinLowEnergyBluetooth::DeviceInfo &deviceInfo, result.devices) + acceptDiscoveredLowEnergyDevice(deviceInfo); + } + + } else { + drop(result.error); + } + + completeLowEnergyDiscovery(); +} + void QBluetoothDeviceDiscoveryAgentPrivate::initialize( const QBluetoothAddress &deviceAdapter) { isClassicValid = isClassicAdapterValid(deviceAdapter); + isLowEnergyValid = isLowEnergyAdapterValid(deviceAdapter); } bool QBluetoothDeviceDiscoveryAgentPrivate::isClassicAdapterValid( @@ -155,8 +182,7 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isClassicAdapterValid( const WinClassicBluetooth::LocalRadiosDiscoveryResult result = WinClassicBluetooth::enumerateLocalRadios(); - if (result.error != NO_ERROR - && result.error != ERROR_NO_MORE_ITEMS) { + if (!isDiscoveredSuccessfully(result.error)) { qCWarning(QT_BT_WINDOWS) << "Occurred error during search of classic local radios"; return false; } else if (result.radios.isEmpty()) { @@ -166,7 +192,7 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isClassicAdapterValid( foreach (const BLUETOOTH_RADIO_INFO &radio, result.radios) { if (deviceAdapter == QBluetoothAddress() - || deviceAdapter == QBluetoothAddress(radio.address.ullLong)) { + || deviceAdapter == QBluetoothAddress(radio.address.ullLong)) { return true; } } @@ -204,18 +230,14 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startDiscoveryForNextClassicDevice( void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDiscovery( HBLUETOOTH_DEVICE_FIND hSearch) { - Q_Q(QBluetoothDeviceDiscoveryAgent); - WinClassicBluetooth::cancelRemoteDevicesDiscovery(hSearch); isClassicActive = false; - emit q->finished(); + finalize(); } void QBluetoothDeviceDiscoveryAgentPrivate::acceptDiscoveredClassicDevice( const BLUETOOTH_DEVICE_INFO &device) { - Q_Q(QBluetoothDeviceDiscoveryAgent); - QBluetoothDeviceInfo deviceInfo( QBluetoothAddress(device.Address.ullLong), QString::fromWCharArray(device.szName), @@ -224,8 +246,89 @@ void QBluetoothDeviceDiscoveryAgentPrivate::acceptDiscoveredClassicDevice( if (device.fRemembered) deviceInfo.setCached(true); - discoveredDevices.append(deviceInfo); - emit q->deviceDiscovered(deviceInfo); + processDuplicates(deviceInfo); +} + +bool QBluetoothDeviceDiscoveryAgentPrivate::isLowEnergyAdapterValid( + const QBluetoothAddress &deviceAdapter) +{ + Q_UNUSED(deviceAdapter); + + // We can not detect an address of local BLE adapter, + // but we can detect that some BLE adapter is present. + return WinLowEnergyBluetooth::hasLocalRadio(); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::startDiscoveryForLowEnergyDevices() +{ + isLowEnergyActive = true; + + if (!lowEnergyDiscoveryWatcher) { + lowEnergyDiscoveryWatcher = new QFutureWatcher< + WinLowEnergyBluetooth::DeviceDiscoveryResult>(this); + QObject::connect(lowEnergyDiscoveryWatcher, SIGNAL(finished()), + this, SLOT(lowEnergyDeviceDiscovered())); + } + + const QFuture future = + QtConcurrent::run(WinLowEnergyBluetooth::startDiscoveryOfRemoteDevices); + lowEnergyDiscoveryWatcher->setFuture(future); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::completeLowEnergyDiscovery() +{ + isLowEnergyActive = false; + finalize(); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::acceptDiscoveredLowEnergyDevice( + const WinLowEnergyBluetooth::DeviceInfo &device) +{ + QBluetoothDeviceInfo deviceInfo(device.address, device.name, 0); + deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration); + deviceInfo.setCached(true); + + processDuplicates(deviceInfo); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::processDuplicates( + const QBluetoothDeviceInfo &foundDevice) +{ + Q_Q(QBluetoothDeviceDiscoveryAgent); + + for (int i = 0; i < discoveredDevices.size(); i++) { + QBluetoothDeviceInfo mergedDevice = discoveredDevices[i]; + if (mergedDevice.address() == foundDevice.address()) { + if (mergedDevice == foundDevice + || mergedDevice.coreConfigurations() == foundDevice.coreConfigurations()) { + qCDebug(QT_BT_WINDOWS) << "Duplicate: " << foundDevice.address(); + return; + } + + // 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 (mergedDevice.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) + mergedDevice = 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. + mergedDevice.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration); + mergedDevice.setCached(foundDevice.isCached()); + + discoveredDevices.replace(i, mergedDevice); + Q_Q(QBluetoothDeviceDiscoveryAgent); + qCDebug(QT_BT_WINDOWS) << "Updated: " << mergedDevice.address(); + + emit q->deviceDiscovered(mergedDevice); + return; + } + } + + qCDebug(QT_BT_WINDOWS) << "Emit: " << foundDevice.address(); + discoveredDevices.append(foundDevice); + emit q->deviceDiscovered(foundDevice); } void QBluetoothDeviceDiscoveryAgentPrivate::setError(DWORD error, const QString &str) @@ -239,4 +342,56 @@ void QBluetoothDeviceDiscoveryAgentPrivate::setError(DWORD error, const QString emit q->error(lastError); } +bool QBluetoothDeviceDiscoveryAgentPrivate::isDiscoveredSuccessfully( + int systemError) const +{ + return systemError == NO_ERROR || systemError == ERROR_NO_MORE_ITEMS; +} + +bool QBluetoothDeviceDiscoveryAgentPrivate::canBeCanceled() const +{ + if (isClassicActive || isLowEnergyActive) + return false; + return pendingCancel && !pendingStart; +} + +void QBluetoothDeviceDiscoveryAgentPrivate::cancel() +{ + Q_Q(QBluetoothDeviceDiscoveryAgent); + + emit q->canceled(); + pendingCancel = false; +} + +bool QBluetoothDeviceDiscoveryAgentPrivate::canBePendingStarted() const +{ + if (isClassicActive || isLowEnergyActive) + return false; + return pendingStart; +} + +void QBluetoothDeviceDiscoveryAgentPrivate::prepareToPendingStart() +{ + pendingCancel = false; + pendingStart = false; + start(); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::finalize() +{ + Q_Q(QBluetoothDeviceDiscoveryAgent); + + if (isClassicActive || isLowEnergyActive) + return; + + emit q->finished(); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::drop(int systemError) +{ + setError(systemError); + pendingCancel = false; + pendingStart = false; +} + QT_END_NAMESPACE diff --git a/src/bluetooth/windows/qwinlowenergybluetooth.cpp b/src/bluetooth/windows/qwinlowenergybluetooth.cpp new file mode 100644 index 00000000..f4f34e42 --- /dev/null +++ b/src/bluetooth/windows/qwinlowenergybluetooth.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwinlowenergybluetooth_p.h" + +#include + +QT_BEGIN_NAMESPACE + +namespace WinLowEnergyBluetooth { + +static QString deviceRegistryProperty( + HDEVINFO deviceInfoHandle, + const PSP_DEVINFO_DATA deviceInfoData, + DWORD registryProperty) +{ + DWORD propertyRegDataType = 0; + DWORD propertyBufferSize = 0; + QByteArray propertyBuffer; + + while (!::SetupDiGetDeviceRegistryProperty( + deviceInfoHandle, + deviceInfoData, + registryProperty, + &propertyRegDataType, + reinterpret_cast(propertyBuffer.data()), + propertyBuffer.size(), + &propertyBufferSize)) { + + const DWORD error = ::GetLastError(); + + if (error != ERROR_INSUFFICIENT_BUFFER + || (propertyRegDataType != REG_SZ + && propertyRegDataType != REG_EXPAND_SZ)) { + return QString(); + } + + propertyBuffer.fill(0, propertyBufferSize * sizeof(WCHAR)); + } + + return QString::fromWCharArray( + reinterpret_cast(propertyBuffer.constData())); +} + +static QString deviceFriendlyName( + HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData) +{ + return deviceRegistryProperty( + deviceInfoSet, deviceInfoData, SPDRP_FRIENDLYNAME); +} + +static QString deviceService( + HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData) +{ + return deviceRegistryProperty( + deviceInfoSet, deviceInfoData, SPDRP_SERVICE); +} + +static QString deviceSystemPath( + const PSP_INTERFACE_DEVICE_DETAIL_DATA detailData) +{ + return QString::fromWCharArray(detailData->DevicePath); +} + +static QBluetoothAddress parseRemoteAddress(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)); +} + +DeviceInfo::DeviceInfo( + const QBluetoothAddress &address, + const QString &name, + const QString &systemPath) + : address(address) + , name(name) + , systemPath(systemPath) +{ +} + +DeviceDiscoveryResult::DeviceDiscoveryResult() + : error(NO_ERROR) +{ +} + +static DeviceDiscoveryResult availableSystemInterfaces( + const GUID &deviceInterface) +{ + const DWORD flags = DIGCF_PRESENT | DIGCF_DEVICEINTERFACE; + const HDEVINFO deviceInfoHandle = ::SetupDiGetClassDevs( + &deviceInterface, 0, 0, flags); + + DeviceDiscoveryResult result; + + if (deviceInfoHandle == INVALID_HANDLE_VALUE) { + result.error = ::GetLastError(); + return result; + } + + DWORD index = 0; + + forever { + + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + ::ZeroMemory(&deviceInterfaceData, sizeof(deviceInterfaceData)); + deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); + + if (!::SetupDiEnumDeviceInterfaces( + deviceInfoHandle, + NULL, + &deviceInterface, + index++, + &deviceInterfaceData)) { + + result.error = ::GetLastError(); + break; + } + + DWORD deviceInterfaceDetailDataSize = 0; + if (!::SetupDiGetDeviceInterfaceDetail( + deviceInfoHandle, + &deviceInterfaceData, + NULL, + deviceInterfaceDetailDataSize, + &deviceInterfaceDetailDataSize, + NULL)) { + + const DWORD error = ::GetLastError(); + if (error != ERROR_INSUFFICIENT_BUFFER) { + result.error = ::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 + (deviceInterfaceDetailDataBuffer.data()); + + deviceInterfaceDetailData->cbSize = + sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA); + + if (!::SetupDiGetDeviceInterfaceDetail( + deviceInfoHandle, + &deviceInterfaceData, + deviceInterfaceDetailData, + deviceInterfaceDetailDataBuffer.size(), + &deviceInterfaceDetailDataSize, + &deviceInfoData)) { + result.error = ::GetLastError(); + break; + } + + const QString systemPath = + deviceSystemPath(deviceInterfaceDetailData); + + const QBluetoothAddress address = parseRemoteAddress(systemPath); + if (address.isNull()) + continue; + + const QString friendlyName = + deviceFriendlyName(deviceInfoHandle, &deviceInfoData); + + const DeviceInfo info(address, friendlyName, systemPath); + result.devices.append(info); + + } + + ::SetupDiDestroyDeviceInfoList(deviceInfoHandle); + return result; +} + +static QStringList availableSystemServices(const GUID &deviceInterface) +{ + const DWORD flags = DIGCF_PRESENT; + const HDEVINFO deviceInfoHandle = ::SetupDiGetClassDevs( + &deviceInterface, NULL, 0, flags); + + if (deviceInfoHandle == INVALID_HANDLE_VALUE) + return QStringList(); + + SP_DEVINFO_DATA deviceInfoData; + ::memset(&deviceInfoData, 0, sizeof(deviceInfoData)); + deviceInfoData.cbSize = sizeof(deviceInfoData); + + DWORD index = 0; + QStringList result; + + while (::SetupDiEnumDeviceInfo( + deviceInfoHandle, index++, &deviceInfoData)) { + + const QString service = deviceService(deviceInfoHandle, &deviceInfoData); + if (service.isEmpty()) + continue; + + result.append(service); + } + + ::SetupDiDestroyDeviceInfoList(deviceInfoHandle); + return result; +} + +DeviceDiscoveryResult startDiscoveryOfRemoteDevices() +{ + return availableSystemInterfaces( + QUuid("781aee18-7733-4ce4-add0-91f41c67b592")); +} + +bool hasLocalRadio() +{ + const QStringList systemServices = + availableSystemServices( + QUuid("e0cbf06c-cd8b-4647-bb8a-263b43f0f974")); + + return systemServices.contains(QStringLiteral("BthLEEnum")); +} + +} // namespace WinLowEnergyBluetooth + +QT_END_NAMESPACE diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h new file mode 100644 index 00000000..aa9127a4 --- /dev/null +++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINLOWENERGYBLUETOOTH_P_H +#define QWINLOWENERGYBLUETOOTH_P_H + +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace WinLowEnergyBluetooth { + +struct DeviceInfo +{ + DeviceInfo(const QBluetoothAddress &address, + const QString &name, + const QString &systemPath); + QBluetoothAddress address; + QString name; + QString systemPath; +}; + +struct DeviceDiscoveryResult +{ + DeviceDiscoveryResult(); + QList devices; + DWORD error; +}; + +bool hasLocalRadio(); + +DeviceDiscoveryResult startDiscoveryOfRemoteDevices(); + +} // namespace WinLowEnergyBluetooth + +QT_END_NAMESPACE + +#endif // QWINLOWENERGYBLUETOOTH_P_H diff --git a/src/bluetooth/windows/windows.pri b/src/bluetooth/windows/windows.pri index f08e0497..84e7200a 100644 --- a/src/bluetooth/windows/windows.pri +++ b/src/bluetooth/windows/windows.pri @@ -1,5 +1,7 @@ PRIVATE_HEADERS += \ - windows/qwinclassicbluetooth_p.h + windows/qwinclassicbluetooth_p.h \ + windows/qwinlowenergybluetooth_p.h SOURCES += \ - windows/qwinclassicbluetooth.cpp + windows/qwinclassicbluetooth.cpp \ + windows/qwinlowenergybluetooth.cpp -- cgit v1.2.3 From ee9a7a91abda21d66a3162da1273bbe1e69adf23 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Fri, 5 Dec 2014 13:50:18 +0300 Subject: Add native wrapper for BLE API on Windows Wrapper trying to resolve of the BLE functions from the "bluetoothapis.dll" library in runtime. It is necessary by the following reasons: 1) MinGW has not necessary headers. 2) MSVC has incorrect sequence of headers that lead to compilation errors. 3) BLE supports beginning from Windows 8 and above, thus the function isSupported() just returns false for lower versions of Windows. Change-Id: I19254779227e045241f14b152dcb5fe5ac0ccd5e Reviewed-by: Alex Blasche --- src/bluetooth/windows/qwinlowenergybluetooth.cpp | 68 ++++++++++++++ src/bluetooth/windows/qwinlowenergybluetooth_p.h | 111 +++++++++++++++++++++++ 2 files changed, 179 insertions(+) diff --git a/src/bluetooth/windows/qwinlowenergybluetooth.cpp b/src/bluetooth/windows/qwinlowenergybluetooth.cpp index f4f34e42..275c5f64 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth.cpp +++ b/src/bluetooth/windows/qwinlowenergybluetooth.cpp @@ -35,11 +35,73 @@ #include "qwinlowenergybluetooth_p.h" #include +#include +#include QT_BEGIN_NAMESPACE namespace WinLowEnergyBluetooth { +#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; +} + +Q_GLOBAL_STATIC(QLibrary, bluetoothapis) + static QString deviceRegistryProperty( HDEVINFO deviceInfoHandle, const PSP_DEVINFO_DATA deviceInfoData, @@ -250,6 +312,12 @@ DeviceDiscoveryResult startDiscoveryOfRemoteDevices() QUuid("781aee18-7733-4ce4-add0-91f41c67b592")); } +bool isSupported() +{ + static bool resolved = resolveFunctions(bluetoothapis()); + return resolved; +} + bool hasLocalRadio() { const QStringList systemServices = diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h index aa9127a4..d7987966 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth_p.h +++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h @@ -46,6 +46,116 @@ QT_BEGIN_NAMESPACE namespace WinLowEnergyBluetooth { +#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; + struct DeviceInfo { DeviceInfo(const QBluetoothAddress &address, @@ -63,6 +173,7 @@ struct DeviceDiscoveryResult DWORD error; }; +bool isSupported(); bool hasLocalRadio(); DeviceDiscoveryResult startDiscoveryOfRemoteDevices(); -- cgit v1.2.3 From fd96b0aa5ae37188f63e6bd1503024c1b5b3cf60 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Mon, 8 Dec 2014 19:47:03 +0300 Subject: Add discovering of primary GATT services on Windows It is still partially implemented where are discovered only the primary services UUID's. Change-Id: I2765a8f83be4716cfe875a2f7a3593ba1af211a4 Reviewed-by: Alex Blasche --- src/bluetooth/bluetooth.pro | 2 +- src/bluetooth/qlowenergycontroller_p.h | 17 ++ src/bluetooth/qlowenergycontroller_win.cpp | 249 +++++++++++++++++++++++ src/bluetooth/windows/qwinlowenergybluetooth.cpp | 31 +++ src/bluetooth/windows/qwinlowenergybluetooth_p.h | 9 + 5 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 src/bluetooth/qlowenergycontroller_win.cpp diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index db8d2e21..334ff600 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -207,7 +207,7 @@ config_bluez:qtHaveModule(dbus) { qbluetoothservicediscoveryagent_win.cpp \ qbluetoothsocket_win.cpp \ qbluetoothserver_win.cpp \ - qlowenergycontroller_p.cpp + qlowenergycontroller_win.cpp LIBS += -lbthprops -lsetupapi diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h index 6234b57f..273ccf84 100644 --- a/src/bluetooth/qlowenergycontroller_p.h +++ b/src/bluetooth/qlowenergycontroller_p.h @@ -65,6 +65,12 @@ QT_END_NAMESPACE #include "android/lowenergynotificationhub_p.h" #endif +#if defined(Q_OS_WIN32) +#include +#include +#include "windows/qwinlowenergybluetooth_p.h" +#endif + QT_BEGIN_NAMESPACE #if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE) @@ -211,6 +217,17 @@ private slots: void descriptorWritten(int descHandle, const QByteArray &data, QLowEnergyService::ServiceError errorCode); void characteristicChanged(int charHandle, const QByteArray &data); + +#elif defined(Q_OS_WIN32) +private slots: + void primaryServicesDiscoveryCompleted(); + +private: + void startDiscoveryOfPrimaryServices(); + bool isConnected() const; + + HANDLE hRemoteDevice; + QFutureWatcher *primaryServicesDiscoveryWatcher; #endif private: QLowEnergyController *q_ptr; diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp new file mode 100644 index 00000000..508e8e53 --- /dev/null +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlowenergycontroller_p.h" +#include + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) + +QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() + : QObject(), + state(QLowEnergyController::UnconnectedState), + error(QLowEnergyController::NoError), + hRemoteDevice(INVALID_HANDLE_VALUE), + primaryServicesDiscoveryWatcher(0) +{ +} + +QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() +{ +} + +void QLowEnergyControllerPrivate::connectToDevice() +{ + Q_Q(QLowEnergyController); + + // required to pass unit test on default backend + if (remoteDevice.isNull()) { + qWarning() << "Invalid/null remote device address"; + setError(QLowEnergyController::UnknownRemoteDeviceError); + return; + } + + if (!WinLowEnergyBluetooth::isSupported()) { + qWarning() << "Low energy is not supported by OS"; + setError(QLowEnergyController::UnknownError); + return; + } + + if (isConnected()) { + qCDebug(QT_BT_WINDOWS) << "Already is connected"; + return; + } + + setState(QLowEnergyController::ConnectingState); + + const WinLowEnergyBluetooth::DeviceDiscoveryResult result = + WinLowEnergyBluetooth::startDiscoveryOfRemoteDevices(); + + if (result.error != NO_ERROR + && result.error != ERROR_NO_MORE_ITEMS) { + qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); + setError(QLowEnergyController::UnknownRemoteDeviceError); + setState(QLowEnergyController::UnconnectedState); + return; + } + + foreach (const WinLowEnergyBluetooth::DeviceInfo &info, result.devices) { + + if (info.address != remoteDevice) + continue; + + const DWORD desiredAccess = GENERIC_READ | GENERIC_WRITE; + const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + + hRemoteDevice = ::CreateFile( + reinterpret_cast(info.systemPath.utf16()), + desiredAccess, + shareMode, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (hRemoteDevice == INVALID_HANDLE_VALUE) { + qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + return; + } + + setState(QLowEnergyController::ConnectedState); + emit q->connected(); + return; + } + + qCWarning(QT_BT_WINDOWS) << qt_error_string(ERROR_FILE_NOT_FOUND); + setError(QLowEnergyController::UnknownRemoteDeviceError); + setState(QLowEnergyController::UnconnectedState); +} + +void QLowEnergyControllerPrivate::disconnectFromDevice() +{ + Q_Q(QLowEnergyController); + + if (!WinLowEnergyBluetooth::isSupported()) { + qWarning() << "Low energy is not supported by OS"; + setError(QLowEnergyController::UnknownError); + return; + } + + if (!isConnected()) { + qCDebug(QT_BT_WINDOWS) << "Already is disconnected"; + return; + } + + setState(QLowEnergyController::ClosingState); + + if (!::CloseHandle(hRemoteDevice)) + qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); + + hRemoteDevice = INVALID_HANDLE_VALUE; + setState(QLowEnergyController::UnconnectedState); + emit q->disconnected(); +} + +void QLowEnergyControllerPrivate::discoverServices() +{ + if (!WinLowEnergyBluetooth::isSupported()) { + qWarning() << "Low energy is not supported by OS"; + setError(QLowEnergyController::UnknownError); + return; + } + + startDiscoveryOfPrimaryServices(); +} + +void QLowEnergyControllerPrivate::discoverServiceDetails( + const QBluetoothUuid &/*service*/) +{ + +} + +void QLowEnergyControllerPrivate::writeCharacteristic( + const QSharedPointer /*service*/, + const QLowEnergyHandle /*charHandle*/, + const QByteArray &/*newValue*/, + bool /*writeWithResponse*/) +{ + +} + +void QLowEnergyControllerPrivate::writeDescriptor( + const QSharedPointer /*service*/, + const QLowEnergyHandle /*charHandle*/, + const QLowEnergyHandle /*descriptorHandle*/, + const QByteArray &/*newValue*/) +{ + +} + +void QLowEnergyControllerPrivate::primaryServicesDiscoveryCompleted() +{ + Q_Q(QLowEnergyController); + + const WinLowEnergyBluetooth::ServicesDiscoveryResult result = + primaryServicesDiscoveryWatcher->result(); + + if (result.error != NO_ERROR) { + qCWarning(QT_BT_WINDOWS) << qt_error_string(result.error); + setError(QLowEnergyController::UnknownError); + return; + } + + foreach (const WinLowEnergyBluetooth::BTH_LE_GATT_SERVICE &service, + result.services) { + + const QBluetoothUuid uuid( + service.ServiceUuid.IsShortUuid + ? QBluetoothUuid(service.ServiceUuid.Value.ShortUuid) + : QBluetoothUuid(service.ServiceUuid.Value.LongUuid)); + + 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 pointer(priv); + + serviceList.insert(uuid, pointer); + emit q->serviceDiscovered(uuid); + } + + setState(QLowEnergyController::DiscoveredState); + emit q->discoveryFinished(); +} + +void QLowEnergyControllerPrivate::startDiscoveryOfPrimaryServices() +{ + if (!primaryServicesDiscoveryWatcher) { + primaryServicesDiscoveryWatcher = new QFutureWatcher< + WinLowEnergyBluetooth::ServicesDiscoveryResult>(this); + + QObject::connect(primaryServicesDiscoveryWatcher, SIGNAL(finished()), + this, SLOT(primaryServicesDiscoveryCompleted())); + } + + if (primaryServicesDiscoveryWatcher->isRunning()) + return; + + const QFuture future = + QtConcurrent::run( + WinLowEnergyBluetooth::startDiscoveryOfPrimaryServices, + hRemoteDevice); + + primaryServicesDiscoveryWatcher->setFuture(future); +} + +bool QLowEnergyControllerPrivate::isConnected() const +{ + return hRemoteDevice && (hRemoteDevice != INVALID_HANDLE_VALUE); +} + +QT_END_NAMESPACE diff --git a/src/bluetooth/windows/qwinlowenergybluetooth.cpp b/src/bluetooth/windows/qwinlowenergybluetooth.cpp index 275c5f64..5fe4e16b 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth.cpp +++ b/src/bluetooth/windows/qwinlowenergybluetooth.cpp @@ -182,6 +182,11 @@ DeviceDiscoveryResult::DeviceDiscoveryResult() { } +ServicesDiscoveryResult::ServicesDiscoveryResult() + : error(NO_ERROR) +{ +} + static DeviceDiscoveryResult availableSystemInterfaces( const GUID &deviceInterface) { @@ -312,6 +317,32 @@ DeviceDiscoveryResult startDiscoveryOfRemoteDevices() QUuid("781aee18-7733-4ce4-add0-91f41c67b592")); } +ServicesDiscoveryResult startDiscoveryOfPrimaryServices( + HANDLE hDevice) +{ + ServicesDiscoveryResult result; + USHORT servicesCount = 0; + forever { + const HRESULT hr = BluetoothGATTGetServices( + hDevice, + result.services.count(), + result.services.isEmpty() ? NULL : &result.services[0], + &servicesCount, + BLUETOOTH_GATT_FLAG_NONE); + + if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { + result.services.resize(servicesCount); + } else if (hr == S_OK) { + break; + } else { + result.error = ::GetLastError(); + result.services.clear(); + break; + } + } + return result; +} + bool isSupported() { static bool resolved = resolveFunctions(bluetoothapis()); diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h index d7987966..cf83f331 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth_p.h +++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h @@ -36,6 +36,7 @@ #define QWINLOWENERGYBLUETOOTH_P_H #include +#include #include @@ -173,10 +174,18 @@ struct DeviceDiscoveryResult DWORD error; }; +struct ServicesDiscoveryResult +{ + ServicesDiscoveryResult(); + QVector services; + DWORD error; +}; + bool isSupported(); bool hasLocalRadio(); DeviceDiscoveryResult startDiscoveryOfRemoteDevices(); +ServicesDiscoveryResult startDiscoveryOfPrimaryServices(HANDLE hDevice); } // namespace WinLowEnergyBluetooth -- cgit v1.2.3 From e9be1330bad04c6afbc6894b621b68f3b32b696b Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Fri, 27 Feb 2015 10:56:30 +0100 Subject: Update Digia headers to QtCompany headers Change-Id: I07b67d4bd879994a480a70f4776d68256f51d736 Reviewed-by: Denis Shienkov Reviewed-by: Alex Blasche --- .../qbluetoothdevicediscoveryagent_win.cpp | 36 ++++++++------------ src/bluetooth/qbluetoothlocaldevice_win.cpp | 36 ++++++++------------ src/bluetooth/qbluetoothserver_win.cpp | 36 ++++++++------------ .../qbluetoothservicediscoveryagent_win.cpp | 38 +++++++++------------- src/bluetooth/qbluetoothserviceinfo_win.cpp | 38 +++++++++------------- src/bluetooth/qbluetoothsocket_win.cpp | 36 ++++++++------------ src/bluetooth/qlowenergycontroller_win.cpp | 15 ++++----- src/bluetooth/windows/qwinclassicbluetooth.cpp | 14 ++++---- src/bluetooth/windows/qwinclassicbluetooth_p.h | 14 ++++---- src/bluetooth/windows/qwinlowenergybluetooth.cpp | 14 ++++---- src/bluetooth/windows/qwinlowenergybluetooth_p.h | 14 ++++---- 11 files changed, 121 insertions(+), 170 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 5a550e57..78640ee2 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -1,41 +1,33 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 The Qt Company Ltd. ** Copyright (C) 2014 Denis Shienkov -** Contact: http://www.qt-project.org/legal +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL$ +** $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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** 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. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** 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. ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp index a2dd14e6..4b1c252d 100644 --- a/src/bluetooth/qbluetoothlocaldevice_win.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -1,41 +1,33 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 The Qt Company Ltd. ** Copyright (C) 2014 Denis Shienkov -** Contact: http://www.qt-project.org/legal +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL$ +** $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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** 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. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** 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. ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/bluetooth/qbluetoothserver_win.cpp b/src/bluetooth/qbluetoothserver_win.cpp index d9efcf2f..86b520d6 100644 --- a/src/bluetooth/qbluetoothserver_win.cpp +++ b/src/bluetooth/qbluetoothserver_win.cpp @@ -1,40 +1,32 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** 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:LGPL$ +** $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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** 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. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** 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. ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index 86874a6c..738fe739 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -1,40 +1,32 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** 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:LGPL$ +** $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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** 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. ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/bluetooth/qbluetoothserviceinfo_win.cpp b/src/bluetooth/qbluetoothserviceinfo_win.cpp index e5f2f4e5..8942149a 100644 --- a/src/bluetooth/qbluetoothserviceinfo_win.cpp +++ b/src/bluetooth/qbluetoothserviceinfo_win.cpp @@ -1,40 +1,32 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** 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:LGPL$ +** $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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** 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. ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp index 670f5aac..e18c985b 100644 --- a/src/bluetooth/qbluetoothsocket_win.cpp +++ b/src/bluetooth/qbluetoothsocket_win.cpp @@ -1,40 +1,32 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** 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:LGPL$ +** $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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** 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. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** 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. ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 508e8e53..6e387d21 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -1,8 +1,8 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 The Qt Company Ltd. ** Copyright (C) 2014 Denis Shienkov -** Contact: http://www.qt-project.org/legal +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** @@ -11,9 +11,9 @@ ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 @@ -24,14 +24,13 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** 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 diff --git a/src/bluetooth/windows/qwinclassicbluetooth.cpp b/src/bluetooth/windows/qwinclassicbluetooth.cpp index 31212d8e..44a73c9e 100644 --- a/src/bluetooth/windows/qwinclassicbluetooth.cpp +++ b/src/bluetooth/windows/qwinclassicbluetooth.cpp @@ -1,8 +1,8 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 The Qt Company Ltd. ** Copyright (C) 2014 Denis Shienkov -** Contact: http://www.qt-project.org/legal +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** @@ -11,9 +11,9 @@ ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 @@ -24,8 +24,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** 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$ diff --git a/src/bluetooth/windows/qwinclassicbluetooth_p.h b/src/bluetooth/windows/qwinclassicbluetooth_p.h index 9c0955bf..87c4093c 100644 --- a/src/bluetooth/windows/qwinclassicbluetooth_p.h +++ b/src/bluetooth/windows/qwinclassicbluetooth_p.h @@ -1,8 +1,8 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 The Qt Company Ltd. ** Copyright (C) 2014 Denis Shienkov -** Contact: http://www.qt-project.org/legal +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** @@ -11,9 +11,9 @@ ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 @@ -24,8 +24,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** 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$ diff --git a/src/bluetooth/windows/qwinlowenergybluetooth.cpp b/src/bluetooth/windows/qwinlowenergybluetooth.cpp index 5fe4e16b..eeb8ae33 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth.cpp +++ b/src/bluetooth/windows/qwinlowenergybluetooth.cpp @@ -1,8 +1,8 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 The Qt Company Ltd. ** Copyright (C) 2014 Denis Shienkov -** Contact: http://www.qt-project.org/legal +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** @@ -11,9 +11,9 @@ ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 @@ -24,8 +24,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** 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$ diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h index cf83f331..08a99b38 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth_p.h +++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h @@ -1,8 +1,8 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 The Qt Company Ltd. ** Copyright (C) 2014 Denis Shienkov -** Contact: http://www.qt-project.org/legal +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** @@ -11,9 +11,9 @@ ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 @@ -24,8 +24,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** 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$ -- cgit v1.2.3 From fba05edde4fd6699da731ffc05660364df613502 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Mon, 18 May 2015 15:32:28 +0200 Subject: Add new readChar/readDesc symbols for Windows Change-Id: I40058079da4b19d93298b4630ac23652f2fad4fa Reviewed-by: Timur Pocheptsov --- src/bluetooth/qlowenergycontroller_win.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 6e387d21..dd54b202 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -162,6 +162,13 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( } +void QLowEnergyControllerPrivate::readCharacteristic( + const QSharedPointer /*service*/, + const QLowEnergyHandle /*charHandle*/) +{ + +} + void QLowEnergyControllerPrivate::writeCharacteristic( const QSharedPointer /*service*/, const QLowEnergyHandle /*charHandle*/, @@ -171,6 +178,14 @@ void QLowEnergyControllerPrivate::writeCharacteristic( } +void QLowEnergyControllerPrivate::readDescriptor( + const QSharedPointer /*service*/, + const QLowEnergyHandle /*charHandle*/, + const QLowEnergyHandle /*descriptorHandle*/) +{ + +} + void QLowEnergyControllerPrivate::writeDescriptor( const QSharedPointer /*service*/, const QLowEnergyHandle /*charHandle*/, -- cgit v1.2.3 From 3e7d0c6b62d2fd6acd2c8a64b8e007729843e088 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Mon, 28 Sep 2015 20:20:31 +0300 Subject: Windows: Use the new signsl/slot syntax Change-Id: I9ff77b2a245e41257521d1bac7474864c90dedaf Reviewed-by: Oliver Wolff --- src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp | 8 ++++---- src/bluetooth/qlowenergycontroller_win.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 78640ee2..05f7091d 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -200,8 +200,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startDiscoveryForFirstClassicDevice( if (!classicDiscoveryWatcher) { classicDiscoveryWatcher = new QFutureWatcher< WinClassicBluetooth::RemoteDeviceDiscoveryResult>(this); - QObject::connect(classicDiscoveryWatcher, SIGNAL(finished()), - this, SLOT(classicDeviceDiscovered())); + connect(classicDiscoveryWatcher, &QFutureWatcher::finished, + this, &QBluetoothDeviceDiscoveryAgentPrivate::classicDeviceDiscovered); } const QFuture future = @@ -258,8 +258,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startDiscoveryForLowEnergyDevices() if (!lowEnergyDiscoveryWatcher) { lowEnergyDiscoveryWatcher = new QFutureWatcher< WinLowEnergyBluetooth::DeviceDiscoveryResult>(this); - QObject::connect(lowEnergyDiscoveryWatcher, SIGNAL(finished()), - this, SLOT(lowEnergyDeviceDiscovered())); + connect(lowEnergyDiscoveryWatcher, &QFutureWatcher::finished, + this, &QBluetoothDeviceDiscoveryAgentPrivate::lowEnergyDeviceDiscovered); } const QFuture future = diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index dd54b202..8342e3ce 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -240,8 +240,8 @@ void QLowEnergyControllerPrivate::startDiscoveryOfPrimaryServices() primaryServicesDiscoveryWatcher = new QFutureWatcher< WinLowEnergyBluetooth::ServicesDiscoveryResult>(this); - QObject::connect(primaryServicesDiscoveryWatcher, SIGNAL(finished()), - this, SLOT(primaryServicesDiscoveryCompleted())); + connect(primaryServicesDiscoveryWatcher, &QFutureWatcher::finished, + this, &QLowEnergyControllerPrivate::primaryServicesDiscoveryCompleted); } if (primaryServicesDiscoveryWatcher->isRunning()) -- cgit v1.2.3 From 797624ac0c5f0a7ee3dd0a938454d55cb8786373 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Mon, 28 Sep 2015 21:15:28 +0300 Subject: Windows: Simplify code related to enumerating of a local radios It is the first stage to cleaning of the 'windows' directory. A shared code related to enumerating of a local adapters are moved to the QBluetoothLocalDevicePrivate class. Change-Id: I208b79c29cd1f906b6fed627d25c7b1310718581 Reviewed-by: Oliver Wolff Reviewed-by: Alex Blasche --- .../qbluetoothdevicediscoveryagent_win.cpp | 16 +---- src/bluetooth/qbluetoothlocaldevice_p.h | 6 +- src/bluetooth/qbluetoothlocaldevice_win.cpp | 78 +++++++++++++--------- src/bluetooth/windows/qwinclassicbluetooth.cpp | 42 ------------ src/bluetooth/windows/qwinclassicbluetooth_p.h | 2 - 5 files changed, 50 insertions(+), 94 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 05f7091d..937bc2a4 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -36,6 +36,7 @@ #include "qbluetoothdevicediscoveryagent_p.h" #include "qbluetoothaddress.h" #include "qbluetoothuuid.h" +#include "qbluetoothlocaldevice_p.h" QT_BEGIN_NAMESPACE @@ -171,20 +172,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::initialize( bool QBluetoothDeviceDiscoveryAgentPrivate::isClassicAdapterValid( const QBluetoothAddress &deviceAdapter) { - const WinClassicBluetooth::LocalRadiosDiscoveryResult result = - WinClassicBluetooth::enumerateLocalRadios(); - - if (!isDiscoveredSuccessfully(result.error)) { - qCWarning(QT_BT_WINDOWS) << "Occurred error during search of classic local radios"; - return false; - } else if (result.radios.isEmpty()) { - qCWarning(QT_BT_WINDOWS) << "No any classic local radio found"; - return false; - } - - foreach (const BLUETOOTH_RADIO_INFO &radio, result.radios) { + foreach (const QBluetoothHostInfo &adapterInfo, QBluetoothLocalDevicePrivate::localAdapters()) { if (deviceAdapter == QBluetoothAddress() - || deviceAdapter == QBluetoothAddress(radio.address.ullLong)) { + || deviceAdapter == adapterInfo.address()) { return true; } } diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h index 844be01b..66f5c1c0 100644 --- a/src/bluetooth/qbluetoothlocaldevice_p.h +++ b/src/bluetooth/qbluetoothlocaldevice_p.h @@ -79,10 +79,6 @@ QT_END_NAMESPACE #include #endif -#ifdef Q_OS_WIN32 -#include "windows/qwinclassicbluetooth_p.h" -#endif - QT_BEGIN_NAMESPACE class QBluetoothAddress; @@ -218,6 +214,8 @@ public: bool isValid() const; void initialize(const QBluetoothAddress &address); + static QList localAdapters(); + QBluetoothAddress deviceAddress; QString deviceName; bool deviceValid; diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp index 4b1c252d..2ccf2b70 100644 --- a/src/bluetooth/qbluetoothlocaldevice_win.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -39,6 +39,9 @@ #include +#include +#include + QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) @@ -154,17 +157,7 @@ QList QBluetoothLocalDevice::connectedDevices() const QList QBluetoothLocalDevice::allDevices() { - const WinClassicBluetooth::LocalRadiosDiscoveryResult result = - WinClassicBluetooth::enumerateLocalRadios(); - - QList devices; - foreach (const BLUETOOTH_RADIO_INFO &radio, result.radios) { - QBluetoothHostInfo device; - device.setAddress(QBluetoothAddress(radio.address.ullLong)); - device.setName(QString::fromWCharArray(radio.szName)); - devices.append(device); - } - return devices; + return QBluetoothLocalDevicePrivate::localAdapters(); } void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing) @@ -206,29 +199,11 @@ void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address) { Q_Q(QBluetoothLocalDevice); - const WinClassicBluetooth::LocalRadiosDiscoveryResult result = - WinClassicBluetooth::enumerateLocalRadios(); - - if (result.error != NO_ERROR - && result.error != ERROR_NO_MORE_ITEMS) { - qCWarning(QT_BT_WINDOWS) << qt_error_string(result.error); - QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection, - Q_ARG(QBluetoothLocalDevice::Error, - QBluetoothLocalDevice::UnknownError)); - return; - } else if (result.radios.isEmpty()) { - qCWarning(QT_BT_WINDOWS) << "No any classic local radio found"; - QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection, - Q_ARG(QBluetoothLocalDevice::Error, - QBluetoothLocalDevice::UnknownError)); - return; - } - - foreach (const BLUETOOTH_RADIO_INFO &radio, result.radios) { + foreach (const QBluetoothHostInfo &adapterInfo, QBluetoothLocalDevicePrivate::localAdapters()) { if (address == QBluetoothAddress() - || address == QBluetoothAddress(radio.address.ullLong)) { - deviceAddress = QBluetoothAddress(radio.address.ullLong); - deviceName = QString::fromWCharArray(radio.szName); + || address == adapterInfo.address()) { + deviceAddress = adapterInfo.address(); + deviceName = adapterInfo.name(); deviceValid = true; return; } @@ -240,4 +215,41 @@ void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address) QBluetoothLocalDevice::UnknownError)); } +QList QBluetoothLocalDevicePrivate::localAdapters() +{ + BLUETOOTH_FIND_RADIO_PARAMS params; + ::ZeroMemory(¶ms, sizeof(params)); + params.dwSize = sizeof(params); + + QList foundAdapters; + + HANDLE hRadio = 0; + if (const HBLUETOOTH_RADIO_FIND hSearch = ::BluetoothFindFirstRadio(¶ms, &hRadio)) { + forever { + 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/windows/qwinclassicbluetooth.cpp b/src/bluetooth/windows/qwinclassicbluetooth.cpp index 44a73c9e..88313ed7 100644 --- a/src/bluetooth/windows/qwinclassicbluetooth.cpp +++ b/src/bluetooth/windows/qwinclassicbluetooth.cpp @@ -51,48 +51,6 @@ RemoteDeviceDiscoveryResult::RemoteDeviceDiscoveryResult() device.dwSize = sizeof(device); } -LocalRadiosDiscoveryResult enumerateLocalRadios() -{ - BLUETOOTH_FIND_RADIO_PARAMS params; - ::ZeroMemory(¶ms, sizeof(params)); - params.dwSize = sizeof(params); - - HANDLE hRadio = 0; - const HBLUETOOTH_RADIO_FIND hSearch = - ::BluetoothFindFirstRadio(¶ms, &hRadio); - - LocalRadiosDiscoveryResult result; - - if (!hSearch) { - result.error = ::GetLastError(); - return result; - } - - forever { - BLUETOOTH_RADIO_INFO radio; - ::ZeroMemory(&radio, sizeof(radio)); - radio.dwSize = sizeof(radio); - - const DWORD retval = ::BluetoothGetRadioInfo(hRadio, &radio); - ::CloseHandle(hRadio); - - if (retval != ERROR_SUCCESS) { - result.error = ::GetLastError(); - break; - } - - result.radios.append(radio); - - if (!::BluetoothFindNextRadio(hSearch, &hRadio)) { - result.error = ::GetLastError(); - break; - } - } - - ::BluetoothFindRadioClose(hSearch); - return result; -} - RemoteDeviceDiscoveryResult startDiscoveryOfFirstRemoteDevice() { BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams; diff --git a/src/bluetooth/windows/qwinclassicbluetooth_p.h b/src/bluetooth/windows/qwinclassicbluetooth_p.h index 87c4093c..2ba255ce 100644 --- a/src/bluetooth/windows/qwinclassicbluetooth_p.h +++ b/src/bluetooth/windows/qwinclassicbluetooth_p.h @@ -61,8 +61,6 @@ struct RemoteDeviceDiscoveryResult DWORD error; }; -LocalRadiosDiscoveryResult enumerateLocalRadios(); - RemoteDeviceDiscoveryResult startDiscoveryOfFirstRemoteDevice(); RemoteDeviceDiscoveryResult startDiscoveryOfNextRemoteDevice(HBLUETOOTH_DEVICE_FIND hSearch); void cancelRemoteDevicesDiscovery(HBLUETOOTH_DEVICE_FIND hSearch); -- cgit v1.2.3 From c18927e87e65eefccd8cda9e6671d234e95d387c Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Tue, 13 Oct 2015 17:18:15 +0300 Subject: Windows: Refactor code related to discovering of a remote devices * There is no need to do parallel scan for LE and Classic devices. It is enough to do it one after another by using one QFutureWatcher instead of two QFutureWatchers. * Now the qwinclassicbluetooth(h).cpp files are deleted from the 'windows' directory, and its related code is moved into QBluetoothDeviceDiscoveryAgentPrivate. Change-Id: I2acf102c3a8d313d078b351e9a2ce54ebca79dee Reviewed-by: Timur Pocheptsov --- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 48 +- .../qbluetoothdevicediscoveryagent_win.cpp | 556 ++++++++++++--------- src/bluetooth/qlowenergycontroller_win.cpp | 57 +-- src/bluetooth/windows/qwinclassicbluetooth.cpp | 94 ---- src/bluetooth/windows/qwinclassicbluetooth_p.h | 75 --- src/bluetooth/windows/qwinlowenergybluetooth.cpp | 150 +----- src/bluetooth/windows/qwinlowenergybluetooth_p.h | 18 - src/bluetooth/windows/windows.pri | 2 - 8 files changed, 363 insertions(+), 637 deletions(-) delete mode 100644 src/bluetooth/windows/qwinclassicbluetooth.cpp delete mode 100644 src/bluetooth/windows/qwinclassicbluetooth_p.h diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index 0d220ef9..d7da8e98 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -75,8 +75,6 @@ QT_END_NAMESPACE #ifdef Q_OS_WIN32 #include -#include "windows/qwinclassicbluetooth_p.h" -#include "windows/qwinlowenergybluetooth_p.h" #endif QT_BEGIN_NAMESPACE @@ -157,47 +155,23 @@ private: #endif #ifdef Q_OS_WIN32 +public: + typedef void* SearchHandle; + static QString discoveredLeDeviceSystemPath(const QBluetoothAddress &deviceAddress); + private slots: - void classicDeviceDiscovered(); - void lowEnergyDeviceDiscovered(); + void taskFinished(); private: - void initialize(const QBluetoothAddress &deviceAdapter); - - bool isClassicAdapterValid(const QBluetoothAddress &deviceAdapter); - void startDiscoveryForFirstClassicDevice(); - void startDiscoveryForNextClassicDevice(HBLUETOOTH_DEVICE_FIND hSearch); - void completeClassicDiscovery(HBLUETOOTH_DEVICE_FIND hSearch); - void acceptDiscoveredClassicDevice(const BLUETOOTH_DEVICE_INFO &device); - - bool isLowEnergyAdapterValid(const QBluetoothAddress &deviceAdapter); - void startDiscoveryForLowEnergyDevices(); - void completeLowEnergyDiscovery(); - void acceptDiscoveredLowEnergyDevice(const WinLowEnergyBluetooth::DeviceInfo &device); - - void processDuplicates(const QBluetoothDeviceInfo &foundDevice); - - void setError(DWORD error, const QString &str = QString()); - - bool isDiscoveredSuccessfully(int systemError) const; - - bool canBeCanceled() const; - void cancel(); - - bool canBePendingStarted() const; - void prepareToPendingStart(); - - void finalize(); - void drop(int systemError); + void processDiscoveredDevice(const QBluetoothDeviceInfo &foundDevice); - QFutureWatcher *classicDiscoveryWatcher; - QFutureWatcher *lowEnergyDiscoveryWatcher; + QBluetoothAddress adapterAddress; bool pendingCancel; bool pendingStart; - bool isClassicActive; - bool isClassicValid; - bool isLowEnergyActive; - bool isLowEnergyValid; + QFutureWatcher *scanWatcher; + bool active; + int systemErrorCode; + SearchHandle searchHandle; #endif QBluetoothDeviceDiscoveryAgent *q_ptr; diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 937bc2a4..78626e85 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -33,253 +33,414 @@ ****************************************************************************/ #include "qbluetoothdevicediscoveryagent.h" -#include "qbluetoothdevicediscoveryagent_p.h" -#include "qbluetoothaddress.h" #include "qbluetoothuuid.h" +#include "qbluetoothdevicediscoveryagent_p.h" #include "qbluetoothlocaldevice_p.h" +#include + +#include +#include +#include + QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) -QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( - const QBluetoothAddress &deviceAdapter, - QBluetoothDeviceDiscoveryAgent *parent) - : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry) - , lastError(QBluetoothDeviceDiscoveryAgent::NoError) - , classicDiscoveryWatcher(0) - , lowEnergyDiscoveryWatcher(0) - , pendingCancel(false) - , pendingStart(false) - , isClassicActive(false) - , isClassicValid(false) - , isLowEnergyActive(false) - , isLowEnergyValid(false) - , q_ptr(parent) -{ - initialize(deviceAdapter); -} +struct LeDeviceEntry { + LeDeviceEntry(const QString &path, const QBluetoothAddress &address) + : devicePath(path), deviceAddress(address) {} + QString devicePath; + QBluetoothAddress deviceAddress; +}; -QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() +Q_GLOBAL_STATIC(QList, cachedLeDeviceEntries) +Q_GLOBAL_STATIC(QMutex, cachedLeDeviceEntriesGuard) + +static QString devicePropertyString( + HDEVINFO hDeviceInfo, + const PSP_DEVINFO_DATA deviceInfoData, + DWORD registryProperty) { - if (isClassicActive || isLowEnergyActive) - stop(); + DWORD propertyRegDataType = 0; + DWORD propertyBufferSize = 0; + QByteArray propertyBuffer; + + while (!::SetupDiGetDeviceRegistryProperty( + hDeviceInfo, + deviceInfoData, + registryProperty, + &propertyRegDataType, + propertyBuffer.isEmpty() ? NULL : reinterpret_cast(propertyBuffer.data()), + propertyBuffer.size(), + &propertyBufferSize)) { + + const DWORD error = ::GetLastError(); + if (error != ERROR_INSUFFICIENT_BUFFER + || (propertyRegDataType != REG_SZ + && propertyRegDataType != REG_EXPAND_SZ)) { + return QString(); + } - if (classicDiscoveryWatcher) - classicDiscoveryWatcher->waitForFinished(); + // add +2 byte to allow to successfully convert to qstring + propertyBuffer.fill(0, propertyBufferSize + sizeof(wchar_t)); + } - if (lowEnergyDiscoveryWatcher) - lowEnergyDiscoveryWatcher->waitForFinished(); + return QString::fromWCharArray(reinterpret_cast( + propertyBuffer.constData())); } -bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const +static QString deviceName(HDEVINFO hDeviceInfo, PSP_DEVINFO_DATA deviceInfoData) { - if (pendingStart) - return true; - if (pendingCancel) - return false; - - return isClassicActive || isLowEnergyActive; + return devicePropertyString(hDeviceInfo, deviceInfoData, SPDRP_FRIENDLYNAME); } -void QBluetoothDeviceDiscoveryAgentPrivate::start() +static QString deviceSystemPath(const PSP_INTERFACE_DEVICE_DETAIL_DATA detailData) { - if (!isClassicValid && !isLowEnergyValid) { - setError(ERROR_INVALID_HANDLE, - QBluetoothDeviceDiscoveryAgent::tr("Passed address is not a local device.")); - return; - } - - if (pendingCancel == true) { - pendingStart = true; - return; - } - - discoveredDevices.clear(); - - if (isClassicValid) - startDiscoveryForFirstClassicDevice(); - - if (isLowEnergyValid) - startDiscoveryForLowEnergyDevices(); + return QString::fromWCharArray(detailData->DevicePath); } -void QBluetoothDeviceDiscoveryAgentPrivate::stop() +static QBluetoothAddress deviceAddress(const QString &devicePath) { - if (!isClassicActive && !isLowEnergyActive) - return; - - pendingCancel = true; - pendingStart = false; + 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)); } -void QBluetoothDeviceDiscoveryAgentPrivate::classicDeviceDiscovered() +static QBluetoothDeviceInfo createClassicDeviceInfo(const BLUETOOTH_DEVICE_INFO &foundDevice) { - const WinClassicBluetooth::RemoteDeviceDiscoveryResult result = - classicDiscoveryWatcher->result(); - - if (isDiscoveredSuccessfully(result.error)) { + QBluetoothDeviceInfo deviceInfo( + QBluetoothAddress(foundDevice.Address.ullLong), + QString::fromWCharArray(foundDevice.szName), + foundDevice.ulClassofDevice); - if (canBeCanceled()) { - cancel(); - } else if (canBePendingStarted()) { - prepareToPendingStart(); - } else { - if (result.error != ERROR_NO_MORE_ITEMS) { - acceptDiscoveredClassicDevice(result.device); - startDiscoveryForNextClassicDevice(result.hSearch); - return; - } - } + if (foundDevice.fRemembered) + deviceInfo.setCached(true); + return deviceInfo; +} +static QBluetoothDeviceInfo findFirstClassicDevice( + int *systemErrorCode, HBLUETOOTH_DEVICE_FIND *searchHandle) +{ + 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) { + *searchHandle = hFind; + *systemErrorCode = NO_ERROR; + foundDevice = createClassicDeviceInfo(deviceInfo); } else { - drop(result.error); + *systemErrorCode = ::GetLastError(); } - completeClassicDiscovery(result.hSearch); + return foundDevice; } -void QBluetoothDeviceDiscoveryAgentPrivate::lowEnergyDeviceDiscovered() +static QBluetoothDeviceInfo findNextClassicDevice( + int *systemErrorCode, HBLUETOOTH_DEVICE_FIND searchHandle) { - const WinLowEnergyBluetooth::DeviceDiscoveryResult result = - lowEnergyDiscoveryWatcher->result(); - - if (isDiscoveredSuccessfully(result.error)) { - - if (canBeCanceled()) { - cancel(); - } else if (canBePendingStarted()) { - prepareToPendingStart(); - } else { - foreach (const WinLowEnergyBluetooth::DeviceInfo &deviceInfo, result.devices) - acceptDiscoveredLowEnergyDevice(deviceInfo); - } + BLUETOOTH_DEVICE_INFO deviceInfo; + ::ZeroMemory(&deviceInfo, sizeof(deviceInfo)); + deviceInfo.dwSize = sizeof(deviceInfo); + QBluetoothDeviceInfo foundDevice; + if (!::BluetoothFindNextDevice(searchHandle, &deviceInfo)) { + *systemErrorCode = ::GetLastError(); } else { - drop(result.error); + *systemErrorCode = NO_ERROR; + foundDevice = createClassicDeviceInfo(deviceInfo); } - completeLowEnergyDiscovery(); + return foundDevice; } -void QBluetoothDeviceDiscoveryAgentPrivate::initialize( - const QBluetoothAddress &deviceAdapter) +static void closeClassicSearch(HBLUETOOTH_DEVICE_FIND *searchHandle) { - isClassicValid = isClassicAdapterValid(deviceAdapter); - isLowEnergyValid = isLowEnergyAdapterValid(deviceAdapter); + if (searchHandle && *searchHandle) { + ::BluetoothFindDeviceClose(*searchHandle); + *searchHandle = 0; + } } -bool QBluetoothDeviceDiscoveryAgentPrivate::isClassicAdapterValid( - const QBluetoothAddress &deviceAdapter) +static QList enumerateLeDevices( + int *systemErrorCode) { - foreach (const QBluetoothHostInfo &adapterInfo, QBluetoothLocalDevicePrivate::localAdapters()) { - if (deviceAdapter == QBluetoothAddress() - || deviceAdapter == adapterInfo.address()) { - return true; - } + const QUuid deviceInterfaceGuid("781aee18-7733-4ce4-add0-91f41c67b592"); + const HDEVINFO hDeviceInfo = ::SetupDiGetClassDevs( + reinterpret_cast(&deviceInterfaceGuid), + NULL, + 0, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + if (hDeviceInfo == INVALID_HANDLE_VALUE) { + *systemErrorCode = ::GetLastError(); + return QList(); } - qCWarning(QT_BT_WINDOWS) << "No matching for classic local radio:" << deviceAdapter; - return false; -} + QList foundDevices; + DWORD index = 0; -void QBluetoothDeviceDiscoveryAgentPrivate::startDiscoveryForFirstClassicDevice() -{ - isClassicActive = true; + QList cachedEntries; - if (!classicDiscoveryWatcher) { - classicDiscoveryWatcher = new QFutureWatcher< - WinClassicBluetooth::RemoteDeviceDiscoveryResult>(this); - connect(classicDiscoveryWatcher, &QFutureWatcher::finished, - this, &QBluetoothDeviceDiscoveryAgentPrivate::classicDeviceDiscovered); + forever { + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + ::ZeroMemory(&deviceInterfaceData, sizeof(deviceInterfaceData)); + deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); + + if (!::SetupDiEnumDeviceInterfaces( + hDeviceInfo, + NULL, + reinterpret_cast(&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 + (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); } - const QFuture future = - QtConcurrent::run(WinClassicBluetooth::startDiscoveryOfFirstRemoteDevice); - classicDiscoveryWatcher->setFuture(future); + QMutexLocker locker(cachedLeDeviceEntriesGuard()); + cachedLeDeviceEntries()->swap(cachedEntries); + + ::SetupDiDestroyDeviceInfoList(hDeviceInfo); + return foundDevices; } -void QBluetoothDeviceDiscoveryAgentPrivate::startDiscoveryForNextClassicDevice( - HBLUETOOTH_DEVICE_FIND hSearch) +QString QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath( + const QBluetoothAddress &deviceAddress) { - Q_ASSERT(classicDiscoveryWatcher); - - const QFuture future = - QtConcurrent::run(WinClassicBluetooth::startDiscoveryOfNextRemoteDevice, hSearch); - classicDiscoveryWatcher->setFuture(future); + // update LE devices cache + int 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(); } -void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDiscovery( - HBLUETOOTH_DEVICE_FIND hSearch) +QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( + const QBluetoothAddress &deviceAdapter, + QBluetoothDeviceDiscoveryAgent *parent) + : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry) + , lastError(QBluetoothDeviceDiscoveryAgent::NoError) + , adapterAddress(deviceAdapter) + , pendingCancel(false) + , pendingStart(false) + , scanWatcher(Q_NULLPTR) + , active(false) + , systemErrorCode(NO_ERROR) + , searchHandle(0) + , q_ptr(parent) { - WinClassicBluetooth::cancelRemoteDevicesDiscovery(hSearch); - isClassicActive = false; - finalize(); + scanWatcher = new QFutureWatcher(this); + connect(scanWatcher, &QFutureWatcher::finished, + this, &QBluetoothDeviceDiscoveryAgentPrivate::taskFinished); } -void QBluetoothDeviceDiscoveryAgentPrivate::acceptDiscoveredClassicDevice( - const BLUETOOTH_DEVICE_INFO &device) +QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() { - QBluetoothDeviceInfo deviceInfo( - QBluetoothAddress(device.Address.ullLong), - QString::fromWCharArray(device.szName), - device.ulClassofDevice); - - if (device.fRemembered) - deviceInfo.setCached(true); + if (active) + stop(); - processDuplicates(deviceInfo); + scanWatcher->waitForFinished(); } -bool QBluetoothDeviceDiscoveryAgentPrivate::isLowEnergyAdapterValid( - const QBluetoothAddress &deviceAdapter) +bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const { - Q_UNUSED(deviceAdapter); - - // We can not detect an address of local BLE adapter, - // but we can detect that some BLE adapter is present. - return WinLowEnergyBluetooth::hasLocalRadio(); + if (pendingStart) + return true; + if (pendingCancel) + return false; + return active; } -void QBluetoothDeviceDiscoveryAgentPrivate::startDiscoveryForLowEnergyDevices() +void QBluetoothDeviceDiscoveryAgentPrivate::start() { - isLowEnergyActive = true; + if (pendingCancel == true) { + pendingStart = true; + return; + } + + const QList foundLocalAdapters = + QBluetoothLocalDevicePrivate::localAdapters(); - if (!lowEnergyDiscoveryWatcher) { - lowEnergyDiscoveryWatcher = new QFutureWatcher< - WinLowEnergyBluetooth::DeviceDiscoveryResult>(this); - connect(lowEnergyDiscoveryWatcher, &QFutureWatcher::finished, - this, &QBluetoothDeviceDiscoveryAgentPrivate::lowEnergyDeviceDiscovered); + 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. + bool foundLocalAdapter = false; + foreach (const QBluetoothHostInfo &adapterInfo, foundLocalAdapters) { + if (adapterAddress == QBluetoothAddress() || adapterAddress == adapterInfo.address()) { + foundLocalAdapter = true; + break; + } } - const QFuture future = - QtConcurrent::run(WinLowEnergyBluetooth::startDiscoveryOfRemoteDevices); - lowEnergyDiscoveryWatcher->setFuture(future); + if (!foundLocalAdapter) { + 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; + + // Start scan for first classic device. + const QFuture future = QtConcurrent::run( + findFirstClassicDevice, &systemErrorCode, &searchHandle); + scanWatcher->setFuture(future); } -void QBluetoothDeviceDiscoveryAgentPrivate::completeLowEnergyDiscovery() +void QBluetoothDeviceDiscoveryAgentPrivate::stop() { - isLowEnergyActive = false; - finalize(); + if (!active) + return; + + pendingCancel = true; + pendingStart = false; } -void QBluetoothDeviceDiscoveryAgentPrivate::acceptDiscoveredLowEnergyDevice( - const WinLowEnergyBluetooth::DeviceInfo &device) +void QBluetoothDeviceDiscoveryAgentPrivate::taskFinished() { - QBluetoothDeviceInfo deviceInfo(device.address, device.name, 0); - deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration); - deviceInfo.setCached(true); + Q_Q(QBluetoothDeviceDiscoveryAgent); - processDuplicates(deviceInfo); + if (pendingCancel && !pendingStart) { + closeClassicSearch(&searchHandle); + active = false; + pendingCancel = false; + emit q->canceled(); + } else if (pendingStart) { + closeClassicSearch(&searchHandle); + pendingStart = pendingCancel = false; + start(); + } else { + if (systemErrorCode == ERROR_NO_MORE_ITEMS) { + closeClassicSearch(&searchHandle); + // Enumerate LE devices. + const QList foundDevices = + enumerateLeDevices(&systemErrorCode); + if (systemErrorCode == ERROR_NO_MORE_ITEMS) { + foreach (const QBluetoothDeviceInfo &foundDevice, foundDevices) + processDiscoveredDevice(foundDevice); + active = false; + emit q->finished(); + } else { + pendingStart = pendingCancel = false; + active = false; + lastError = (systemErrorCode == ERROR_INVALID_HANDLE) ? + QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError + : QBluetoothDeviceDiscoveryAgent::InputOutputError; + errorString = qt_error_string(systemErrorCode); + emit q->error(lastError); + } + } else if (systemErrorCode == NO_ERROR) { + processDiscoveredDevice(scanWatcher->result()); + // Start scan for next classic device. + const QFuture future = QtConcurrent::run( + findNextClassicDevice, &systemErrorCode, searchHandle); + scanWatcher->setFuture(future); + } else { + closeClassicSearch(&searchHandle); + pendingStart = pendingCancel = false; + active = false; + lastError = (systemErrorCode == ERROR_INVALID_HANDLE) ? + QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError + : QBluetoothDeviceDiscoveryAgent::InputOutputError; + errorString = qt_error_string(systemErrorCode); + emit q->error(lastError); + } + } } -void QBluetoothDeviceDiscoveryAgentPrivate::processDuplicates( +void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevice( const QBluetoothDeviceInfo &foundDevice) { Q_Q(QBluetoothDeviceDiscoveryAgent); for (int i = 0; i < discoveredDevices.size(); i++) { QBluetoothDeviceInfo mergedDevice = discoveredDevices[i]; + if (mergedDevice.address() == foundDevice.address()) { if (mergedDevice == foundDevice || mergedDevice.coreConfigurations() == foundDevice.coreConfigurations()) { @@ -313,67 +474,4 @@ void QBluetoothDeviceDiscoveryAgentPrivate::processDuplicates( emit q->deviceDiscovered(foundDevice); } -void QBluetoothDeviceDiscoveryAgentPrivate::setError(DWORD error, const QString &str) -{ - Q_Q(QBluetoothDeviceDiscoveryAgent); - - lastError = (error == ERROR_INVALID_HANDLE) ? - QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError - : QBluetoothDeviceDiscoveryAgent::InputOutputError; - errorString = str.isEmpty() ? qt_error_string(error) : str; - emit q->error(lastError); -} - -bool QBluetoothDeviceDiscoveryAgentPrivate::isDiscoveredSuccessfully( - int systemError) const -{ - return systemError == NO_ERROR || systemError == ERROR_NO_MORE_ITEMS; -} - -bool QBluetoothDeviceDiscoveryAgentPrivate::canBeCanceled() const -{ - if (isClassicActive || isLowEnergyActive) - return false; - return pendingCancel && !pendingStart; -} - -void QBluetoothDeviceDiscoveryAgentPrivate::cancel() -{ - Q_Q(QBluetoothDeviceDiscoveryAgent); - - emit q->canceled(); - pendingCancel = false; -} - -bool QBluetoothDeviceDiscoveryAgentPrivate::canBePendingStarted() const -{ - if (isClassicActive || isLowEnergyActive) - return false; - return pendingStart; -} - -void QBluetoothDeviceDiscoveryAgentPrivate::prepareToPendingStart() -{ - pendingCancel = false; - pendingStart = false; - start(); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::finalize() -{ - Q_Q(QBluetoothDeviceDiscoveryAgent); - - if (isClassicActive || isLowEnergyActive) - return; - - emit q->finished(); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::drop(int systemError) -{ - setError(systemError); - pendingCancel = false; - pendingStart = false; -} - QT_END_NAMESPACE diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 8342e3ce..ed33c5c8 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -31,7 +31,10 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + #include "qlowenergycontroller_p.h" +#include "qbluetoothdevicediscoveryagent_p.h" + #include QT_BEGIN_NAMESPACE @@ -75,49 +78,29 @@ void QLowEnergyControllerPrivate::connectToDevice() setState(QLowEnergyController::ConnectingState); - const WinLowEnergyBluetooth::DeviceDiscoveryResult result = - WinLowEnergyBluetooth::startDiscoveryOfRemoteDevices(); - - if (result.error != NO_ERROR - && result.error != ERROR_NO_MORE_ITEMS) { - qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); - setError(QLowEnergyController::UnknownRemoteDeviceError); - setState(QLowEnergyController::UnconnectedState); - return; - } - - foreach (const WinLowEnergyBluetooth::DeviceInfo &info, result.devices) { - - if (info.address != remoteDevice) - continue; - - const DWORD desiredAccess = GENERIC_READ | GENERIC_WRITE; - const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + const QString deviceSystemPath = QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath(remoteDevice); - hRemoteDevice = ::CreateFile( - reinterpret_cast(info.systemPath.utf16()), - desiredAccess, - shareMode, - NULL, - OPEN_EXISTING, - 0, - NULL); + const DWORD desiredAccess = GENERIC_READ | GENERIC_WRITE; + const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; - if (hRemoteDevice == INVALID_HANDLE_VALUE) { - qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); - setError(QLowEnergyController::ConnectionError); - setState(QLowEnergyController::UnconnectedState); - return; - } + hRemoteDevice = ::CreateFile( + reinterpret_cast(deviceSystemPath.utf16()), + desiredAccess, + shareMode, + NULL, + OPEN_EXISTING, + 0, + NULL); - setState(QLowEnergyController::ConnectedState); - emit q->connected(); + if (hRemoteDevice == INVALID_HANDLE_VALUE) { + qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); return; } - qCWarning(QT_BT_WINDOWS) << qt_error_string(ERROR_FILE_NOT_FOUND); - setError(QLowEnergyController::UnknownRemoteDeviceError); - setState(QLowEnergyController::UnconnectedState); + setState(QLowEnergyController::ConnectedState); + emit q->connected(); } void QLowEnergyControllerPrivate::disconnectFromDevice() diff --git a/src/bluetooth/windows/qwinclassicbluetooth.cpp b/src/bluetooth/windows/qwinclassicbluetooth.cpp deleted file mode 100644 index 88313ed7..00000000 --- a/src/bluetooth/windows/qwinclassicbluetooth.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Copyright (C) 2014 Denis Shienkov -** 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 "qwinclassicbluetooth_p.h" - -QT_BEGIN_NAMESPACE - -namespace WinClassicBluetooth { - -LocalRadiosDiscoveryResult::LocalRadiosDiscoveryResult() - : error(NO_ERROR) -{ -} - -RemoteDeviceDiscoveryResult::RemoteDeviceDiscoveryResult() - : hSearch(0) - , error(NO_ERROR) -{ - ::ZeroMemory(&device, sizeof(device)); - device.dwSize = sizeof(device); -} - -RemoteDeviceDiscoveryResult startDiscoveryOfFirstRemoteDevice() -{ - 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; - - RemoteDeviceDiscoveryResult result; - result.hSearch = ::BluetoothFindFirstDevice( - &searchParams, &result.device); - - if (!result.hSearch) - result.error = ::GetLastError(); - return result; -} - -RemoteDeviceDiscoveryResult startDiscoveryOfNextRemoteDevice( - HBLUETOOTH_DEVICE_FIND hSearch) -{ - RemoteDeviceDiscoveryResult result; - result.hSearch = hSearch; - if (!::BluetoothFindNextDevice(hSearch, &result.device)) - result.error = ::GetLastError(); - return result; -} - -void cancelRemoteDevicesDiscovery(HBLUETOOTH_DEVICE_FIND hSearch) -{ - if (hSearch) - ::BluetoothFindDeviceClose(hSearch); -} - -} // namespace WinClassicBluetooth - -QT_END_NAMESPACE diff --git a/src/bluetooth/windows/qwinclassicbluetooth_p.h b/src/bluetooth/windows/qwinclassicbluetooth_p.h deleted file mode 100644 index 2ba255ce..00000000 --- a/src/bluetooth/windows/qwinclassicbluetooth_p.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Copyright (C) 2014 Denis Shienkov -** 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 QWINCLASSICBLUETOOTH_P_H -#define QWINCLASSICBLUETOOTH_P_H - -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace WinClassicBluetooth { - -struct LocalRadiosDiscoveryResult -{ - LocalRadiosDiscoveryResult(); - QList radios; - DWORD error; -}; - -struct RemoteDeviceDiscoveryResult -{ - RemoteDeviceDiscoveryResult(); - BLUETOOTH_DEVICE_INFO device; - HBLUETOOTH_DEVICE_FIND hSearch; - DWORD error; -}; - -RemoteDeviceDiscoveryResult startDiscoveryOfFirstRemoteDevice(); -RemoteDeviceDiscoveryResult startDiscoveryOfNextRemoteDevice(HBLUETOOTH_DEVICE_FIND hSearch); -void cancelRemoteDevicesDiscovery(HBLUETOOTH_DEVICE_FIND hSearch); - -} // namespace WinClassicBluetooth - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(WinClassicBluetooth::LocalRadiosDiscoveryResult) -Q_DECLARE_METATYPE(WinClassicBluetooth::RemoteDeviceDiscoveryResult) - -#endif // QWINCLASSICBLUETOOTH_P_H diff --git a/src/bluetooth/windows/qwinlowenergybluetooth.cpp b/src/bluetooth/windows/qwinlowenergybluetooth.cpp index eeb8ae33..0b917bd3 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth.cpp +++ b/src/bluetooth/windows/qwinlowenergybluetooth.cpp @@ -102,6 +102,11 @@ static inline bool resolveFunctions(QLibrary *library) Q_GLOBAL_STATIC(QLibrary, bluetoothapis) +ServicesDiscoveryResult::ServicesDiscoveryResult() + : error(NO_ERROR) +{ +} + static QString deviceRegistryProperty( HDEVINFO deviceInfoHandle, const PSP_DEVINFO_DATA deviceInfoData, @@ -135,13 +140,6 @@ static QString deviceRegistryProperty( reinterpret_cast(propertyBuffer.constData())); } -static QString deviceFriendlyName( - HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData) -{ - return deviceRegistryProperty( - deviceInfoSet, deviceInfoData, SPDRP_FRIENDLYNAME); -} - static QString deviceService( HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData) { @@ -149,138 +147,6 @@ static QString deviceService( deviceInfoSet, deviceInfoData, SPDRP_SERVICE); } -static QString deviceSystemPath( - const PSP_INTERFACE_DEVICE_DETAIL_DATA detailData) -{ - return QString::fromWCharArray(detailData->DevicePath); -} - -static QBluetoothAddress parseRemoteAddress(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)); -} - -DeviceInfo::DeviceInfo( - const QBluetoothAddress &address, - const QString &name, - const QString &systemPath) - : address(address) - , name(name) - , systemPath(systemPath) -{ -} - -DeviceDiscoveryResult::DeviceDiscoveryResult() - : error(NO_ERROR) -{ -} - -ServicesDiscoveryResult::ServicesDiscoveryResult() - : error(NO_ERROR) -{ -} - -static DeviceDiscoveryResult availableSystemInterfaces( - const GUID &deviceInterface) -{ - const DWORD flags = DIGCF_PRESENT | DIGCF_DEVICEINTERFACE; - const HDEVINFO deviceInfoHandle = ::SetupDiGetClassDevs( - &deviceInterface, 0, 0, flags); - - DeviceDiscoveryResult result; - - if (deviceInfoHandle == INVALID_HANDLE_VALUE) { - result.error = ::GetLastError(); - return result; - } - - DWORD index = 0; - - forever { - - SP_DEVICE_INTERFACE_DATA deviceInterfaceData; - ::ZeroMemory(&deviceInterfaceData, sizeof(deviceInterfaceData)); - deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); - - if (!::SetupDiEnumDeviceInterfaces( - deviceInfoHandle, - NULL, - &deviceInterface, - index++, - &deviceInterfaceData)) { - - result.error = ::GetLastError(); - break; - } - - DWORD deviceInterfaceDetailDataSize = 0; - if (!::SetupDiGetDeviceInterfaceDetail( - deviceInfoHandle, - &deviceInterfaceData, - NULL, - deviceInterfaceDetailDataSize, - &deviceInterfaceDetailDataSize, - NULL)) { - - const DWORD error = ::GetLastError(); - if (error != ERROR_INSUFFICIENT_BUFFER) { - result.error = ::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 - (deviceInterfaceDetailDataBuffer.data()); - - deviceInterfaceDetailData->cbSize = - sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA); - - if (!::SetupDiGetDeviceInterfaceDetail( - deviceInfoHandle, - &deviceInterfaceData, - deviceInterfaceDetailData, - deviceInterfaceDetailDataBuffer.size(), - &deviceInterfaceDetailDataSize, - &deviceInfoData)) { - result.error = ::GetLastError(); - break; - } - - const QString systemPath = - deviceSystemPath(deviceInterfaceDetailData); - - const QBluetoothAddress address = parseRemoteAddress(systemPath); - if (address.isNull()) - continue; - - const QString friendlyName = - deviceFriendlyName(deviceInfoHandle, &deviceInfoData); - - const DeviceInfo info(address, friendlyName, systemPath); - result.devices.append(info); - - } - - ::SetupDiDestroyDeviceInfoList(deviceInfoHandle); - return result; -} - static QStringList availableSystemServices(const GUID &deviceInterface) { const DWORD flags = DIGCF_PRESENT; @@ -311,12 +177,6 @@ static QStringList availableSystemServices(const GUID &deviceInterface) return result; } -DeviceDiscoveryResult startDiscoveryOfRemoteDevices() -{ - return availableSystemInterfaces( - QUuid("781aee18-7733-4ce4-add0-91f41c67b592")); -} - ServicesDiscoveryResult startDiscoveryOfPrimaryServices( HANDLE hDevice) { diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h index 08a99b38..adc8650e 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth_p.h +++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h @@ -157,23 +157,6 @@ typedef VOID (CALLBACK *PFNBLUETOOTH_GATT_EVENT_CALLBACK)( typedef ULONG64 BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, *PBTH_LE_GATT_RELIABLE_WRITE_CONTEXT; -struct DeviceInfo -{ - DeviceInfo(const QBluetoothAddress &address, - const QString &name, - const QString &systemPath); - QBluetoothAddress address; - QString name; - QString systemPath; -}; - -struct DeviceDiscoveryResult -{ - DeviceDiscoveryResult(); - QList devices; - DWORD error; -}; - struct ServicesDiscoveryResult { ServicesDiscoveryResult(); @@ -184,7 +167,6 @@ struct ServicesDiscoveryResult bool isSupported(); bool hasLocalRadio(); -DeviceDiscoveryResult startDiscoveryOfRemoteDevices(); ServicesDiscoveryResult startDiscoveryOfPrimaryServices(HANDLE hDevice); } // namespace WinLowEnergyBluetooth diff --git a/src/bluetooth/windows/windows.pri b/src/bluetooth/windows/windows.pri index 84e7200a..019d67e4 100644 --- a/src/bluetooth/windows/windows.pri +++ b/src/bluetooth/windows/windows.pri @@ -1,7 +1,5 @@ PRIVATE_HEADERS += \ - windows/qwinclassicbluetooth_p.h \ windows/qwinlowenergybluetooth_p.h SOURCES += \ - windows/qwinclassicbluetooth.cpp \ windows/qwinlowenergybluetooth.cpp -- cgit v1.2.3 From e624e7e63c6ac2b330720fea465b05190c70023b Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 14 Oct 2015 12:41:24 +0300 Subject: Windows: Removal of dead code Change-Id: I6d9c681dec978e507373c703784111cdf6808d2f Reviewed-by: Timur Pocheptsov --- src/bluetooth/windows/qwinlowenergybluetooth.cpp | 79 ------------------------ src/bluetooth/windows/qwinlowenergybluetooth_p.h | 1 - 2 files changed, 80 deletions(-) diff --git a/src/bluetooth/windows/qwinlowenergybluetooth.cpp b/src/bluetooth/windows/qwinlowenergybluetooth.cpp index 0b917bd3..8033a121 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth.cpp +++ b/src/bluetooth/windows/qwinlowenergybluetooth.cpp @@ -107,76 +107,6 @@ ServicesDiscoveryResult::ServicesDiscoveryResult() { } -static QString deviceRegistryProperty( - HDEVINFO deviceInfoHandle, - const PSP_DEVINFO_DATA deviceInfoData, - DWORD registryProperty) -{ - DWORD propertyRegDataType = 0; - DWORD propertyBufferSize = 0; - QByteArray propertyBuffer; - - while (!::SetupDiGetDeviceRegistryProperty( - deviceInfoHandle, - deviceInfoData, - registryProperty, - &propertyRegDataType, - reinterpret_cast(propertyBuffer.data()), - propertyBuffer.size(), - &propertyBufferSize)) { - - const DWORD error = ::GetLastError(); - - if (error != ERROR_INSUFFICIENT_BUFFER - || (propertyRegDataType != REG_SZ - && propertyRegDataType != REG_EXPAND_SZ)) { - return QString(); - } - - propertyBuffer.fill(0, propertyBufferSize * sizeof(WCHAR)); - } - - return QString::fromWCharArray( - reinterpret_cast(propertyBuffer.constData())); -} - -static QString deviceService( - HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData) -{ - return deviceRegistryProperty( - deviceInfoSet, deviceInfoData, SPDRP_SERVICE); -} - -static QStringList availableSystemServices(const GUID &deviceInterface) -{ - const DWORD flags = DIGCF_PRESENT; - const HDEVINFO deviceInfoHandle = ::SetupDiGetClassDevs( - &deviceInterface, NULL, 0, flags); - - if (deviceInfoHandle == INVALID_HANDLE_VALUE) - return QStringList(); - - SP_DEVINFO_DATA deviceInfoData; - ::memset(&deviceInfoData, 0, sizeof(deviceInfoData)); - deviceInfoData.cbSize = sizeof(deviceInfoData); - - DWORD index = 0; - QStringList result; - - while (::SetupDiEnumDeviceInfo( - deviceInfoHandle, index++, &deviceInfoData)) { - - const QString service = deviceService(deviceInfoHandle, &deviceInfoData); - if (service.isEmpty()) - continue; - - result.append(service); - } - - ::SetupDiDestroyDeviceInfoList(deviceInfoHandle); - return result; -} - ServicesDiscoveryResult startDiscoveryOfPrimaryServices( HANDLE hDevice) { @@ -209,15 +139,6 @@ bool isSupported() return resolved; } -bool hasLocalRadio() -{ - const QStringList systemServices = - availableSystemServices( - QUuid("e0cbf06c-cd8b-4647-bb8a-263b43f0f974")); - - return systemServices.contains(QStringLiteral("BthLEEnum")); -} - } // namespace WinLowEnergyBluetooth QT_END_NAMESPACE diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h index adc8650e..c591a93d 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth_p.h +++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h @@ -165,7 +165,6 @@ struct ServicesDiscoveryResult }; bool isSupported(); -bool hasLocalRadio(); ServicesDiscoveryResult startDiscoveryOfPrimaryServices(HANDLE hDevice); -- cgit v1.2.3 From 4cb5575e9ce38f74c471bbecde139bf4f83fa22f Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 14 Oct 2015 18:49:15 +0300 Subject: Windows: Refactor code related to a services discovering * We do not need QtConcurrent for a services discovering because they returns from a cache immediately. * We don't need to hold the opened device all the time after connection to it. It is enough to open a device only for a some time to request of services. * Now the windows/qwinlowenergybluetooth_p.h file contains only resolved system LE functions and data types. Change-Id: I248666d78cc9141bf987987dfd5dd9280decf4b8 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_p.h | 15 +- src/bluetooth/qlowenergycontroller_win.cpp | 241 ++++++++++++----------- src/bluetooth/windows/qwinlowenergybluetooth.cpp | 144 -------------- src/bluetooth/windows/qwinlowenergybluetooth_p.h | 77 ++++++-- src/bluetooth/windows/windows.pri | 3 - 5 files changed, 185 insertions(+), 295 deletions(-) delete mode 100644 src/bluetooth/windows/qwinlowenergybluetooth.cpp diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h index 6d81316d..fde057cf 100644 --- a/src/bluetooth/qlowenergycontroller_p.h +++ b/src/bluetooth/qlowenergycontroller_p.h @@ -65,12 +65,6 @@ QT_END_NAMESPACE #include "android/lowenergynotificationhub_p.h" #endif -#if defined(Q_OS_WIN32) -#include -#include -#include "windows/qwinlowenergybluetooth_p.h" -#endif - QT_BEGIN_NAMESPACE #if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE) @@ -227,15 +221,8 @@ private slots: void characteristicChanged(int charHandle, const QByteArray &data); void serviceError(int attributeHandle, QLowEnergyService::ServiceError errorCode); #elif defined(Q_OS_WIN32) -private slots: - void primaryServicesDiscoveryCompleted(); - private: - void startDiscoveryOfPrimaryServices(); - bool isConnected() const; - - HANDLE hRemoteDevice; - QFutureWatcher *primaryServicesDiscoveryWatcher; + QString deviceSystemPath; #endif private: QLowEnergyController *q_ptr; diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index ed33c5c8..0f629a0b 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -37,17 +37,80 @@ #include +#include + QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) +Q_GLOBAL_STATIC(QLibrary, bluetoothapis) + +static bool gattFunctionsResolved = false; + +static HANDLE openSystemDevice(const QString &systemPath, int *systemErrorCode) +{ + const DWORD desiredAccess = GENERIC_READ | GENERIC_WRITE; + const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + const HANDLE serviceHandle = ::CreateFile( + reinterpret_cast(systemPath.utf16()), + desiredAccess, + shareMode, + NULL, + OPEN_EXISTING, + 0, + NULL); + + *systemErrorCode = (INVALID_HANDLE_VALUE == serviceHandle) + ? ::GetLastError() : NO_ERROR; + return serviceHandle; +} + +static void closeSystemDevice(HANDLE deviceHandle) +{ + if (deviceHandle && deviceHandle != INVALID_HANDLE_VALUE) + ::CloseHandle(deviceHandle); +} + +static QVector enumeratePrimaryGattServices( + HANDLE deviceHandle, int *systemErrorCode) +{ + if (!gattFunctionsResolved) { + *systemErrorCode = ERROR_NOT_SUPPORTED; + return QVector(); + } + + QVector foundServices; + USHORT servicesCount = 0; + forever { + const HRESULT hr = ::BluetoothGATTGetServices( + deviceHandle, + 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(); + } + } +} + QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() - : QObject(), - state(QLowEnergyController::UnconnectedState), - error(QLowEnergyController::NoError), - hRemoteDevice(INVALID_HANDLE_VALUE), - primaryServicesDiscoveryWatcher(0) + : QObject() + , state(QLowEnergyController::UnconnectedState) + , error(QLowEnergyController::NoError) { + gattFunctionsResolved = resolveFunctions(bluetoothapis()); + if (!gattFunctionsResolved) { + qCWarning(QT_BT_WINDOWS) << "LE is not supported on this OS"; + return; + } } QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() @@ -56,8 +119,6 @@ QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() void QLowEnergyControllerPrivate::connectToDevice() { - Q_Q(QLowEnergyController); - // required to pass unit test on default backend if (remoteDevice.isNull()) { qWarning() << "Invalid/null remote device address"; @@ -65,78 +126,97 @@ void QLowEnergyControllerPrivate::connectToDevice() return; } - if (!WinLowEnergyBluetooth::isSupported()) { - qWarning() << "Low energy is not supported by OS"; - setError(QLowEnergyController::UnknownError); - return; - } - - if (isConnected()) { + if (!deviceSystemPath.isEmpty()) { qCDebug(QT_BT_WINDOWS) << "Already is connected"; return; } setState(QLowEnergyController::ConnectingState); - const QString deviceSystemPath = QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath(remoteDevice); - - const DWORD desiredAccess = GENERIC_READ | GENERIC_WRITE; - const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; - - hRemoteDevice = ::CreateFile( - reinterpret_cast(deviceSystemPath.utf16()), - desiredAccess, - shareMode, - NULL, - OPEN_EXISTING, - 0, - NULL); + deviceSystemPath = + QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath( + remoteDevice); - if (hRemoteDevice == INVALID_HANDLE_VALUE) { - qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); - setError(QLowEnergyController::ConnectionError); + 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() { - Q_Q(QLowEnergyController); - - if (!WinLowEnergyBluetooth::isSupported()) { - qWarning() << "Low energy is not supported by OS"; - setError(QLowEnergyController::UnknownError); - return; - } - - if (!isConnected()) { + if (deviceSystemPath.isEmpty()) { qCDebug(QT_BT_WINDOWS) << "Already is disconnected"; return; } setState(QLowEnergyController::ClosingState); - - if (!::CloseHandle(hRemoteDevice)) - qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); - - hRemoteDevice = INVALID_HANDLE_VALUE; + deviceSystemPath.clear(); setState(QLowEnergyController::UnconnectedState); + + Q_Q(QLowEnergyController); emit q->disconnected(); } void QLowEnergyControllerPrivate::discoverServices() { - if (!WinLowEnergyBluetooth::isSupported()) { - qWarning() << "Low energy is not supported by OS"; - setError(QLowEnergyController::UnknownError); + int systemErrorCode = NO_ERROR; + + const HANDLE deviceHandle = openSystemDevice(deviceSystemPath, &systemErrorCode); + + if (systemErrorCode != NO_ERROR) { + qCWarning(QT_BT_WINDOWS) << qt_error_string(systemErrorCode); + setError(QLowEnergyController::NetworkError); + setState(QLowEnergyController::ConnectedState); + return; + } + + const QVector foundServices = + enumeratePrimaryGattServices(deviceHandle, &systemErrorCode); + + closeSystemDevice(deviceHandle); + + if (systemErrorCode != NO_ERROR) { + qCWarning(QT_BT_WINDOWS) << qt_error_string(systemErrorCode); + setError(QLowEnergyController::NetworkError); + setState(QLowEnergyController::ConnectedState); return; } - startDiscoveryOfPrimaryServices(); + setState(QLowEnergyController::DiscoveringState); + + Q_Q(QLowEnergyController); + + foreach (const BTH_LE_GATT_SERVICE &service, foundServices) { + + const QBluetoothUuid uuid( + service.ServiceUuid.IsShortUuid + ? QBluetoothUuid(service.ServiceUuid.Value.ShortUuid) + : QBluetoothUuid(service.ServiceUuid.Value.LongUuid)); + + 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 pointer(priv); + serviceList.insert(uuid, pointer); + + emit q->serviceDiscovered(uuid); + } + + setState(QLowEnergyController::DiscoveredState); + emit q->discoveryFinished(); } void QLowEnergyControllerPrivate::discoverServiceDetails( @@ -178,69 +258,4 @@ void QLowEnergyControllerPrivate::writeDescriptor( } -void QLowEnergyControllerPrivate::primaryServicesDiscoveryCompleted() -{ - Q_Q(QLowEnergyController); - - const WinLowEnergyBluetooth::ServicesDiscoveryResult result = - primaryServicesDiscoveryWatcher->result(); - - if (result.error != NO_ERROR) { - qCWarning(QT_BT_WINDOWS) << qt_error_string(result.error); - setError(QLowEnergyController::UnknownError); - return; - } - - foreach (const WinLowEnergyBluetooth::BTH_LE_GATT_SERVICE &service, - result.services) { - - const QBluetoothUuid uuid( - service.ServiceUuid.IsShortUuid - ? QBluetoothUuid(service.ServiceUuid.Value.ShortUuid) - : QBluetoothUuid(service.ServiceUuid.Value.LongUuid)); - - 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 pointer(priv); - - serviceList.insert(uuid, pointer); - emit q->serviceDiscovered(uuid); - } - - setState(QLowEnergyController::DiscoveredState); - emit q->discoveryFinished(); -} - -void QLowEnergyControllerPrivate::startDiscoveryOfPrimaryServices() -{ - if (!primaryServicesDiscoveryWatcher) { - primaryServicesDiscoveryWatcher = new QFutureWatcher< - WinLowEnergyBluetooth::ServicesDiscoveryResult>(this); - - connect(primaryServicesDiscoveryWatcher, &QFutureWatcher::finished, - this, &QLowEnergyControllerPrivate::primaryServicesDiscoveryCompleted); - } - - if (primaryServicesDiscoveryWatcher->isRunning()) - return; - - const QFuture future = - QtConcurrent::run( - WinLowEnergyBluetooth::startDiscoveryOfPrimaryServices, - hRemoteDevice); - - primaryServicesDiscoveryWatcher->setFuture(future); -} - -bool QLowEnergyControllerPrivate::isConnected() const -{ - return hRemoteDevice && (hRemoteDevice != INVALID_HANDLE_VALUE); -} - QT_END_NAMESPACE diff --git a/src/bluetooth/windows/qwinlowenergybluetooth.cpp b/src/bluetooth/windows/qwinlowenergybluetooth.cpp deleted file mode 100644 index 8033a121..00000000 --- a/src/bluetooth/windows/qwinlowenergybluetooth.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Copyright (C) 2014 Denis Shienkov -** 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 "qwinlowenergybluetooth_p.h" - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace WinLowEnergyBluetooth { - -#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; -} - -Q_GLOBAL_STATIC(QLibrary, bluetoothapis) - -ServicesDiscoveryResult::ServicesDiscoveryResult() - : error(NO_ERROR) -{ -} - -ServicesDiscoveryResult startDiscoveryOfPrimaryServices( - HANDLE hDevice) -{ - ServicesDiscoveryResult result; - USHORT servicesCount = 0; - forever { - const HRESULT hr = BluetoothGATTGetServices( - hDevice, - result.services.count(), - result.services.isEmpty() ? NULL : &result.services[0], - &servicesCount, - BLUETOOTH_GATT_FLAG_NONE); - - if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { - result.services.resize(servicesCount); - } else if (hr == S_OK) { - break; - } else { - result.error = ::GetLastError(); - result.services.clear(); - break; - } - } - return result; -} - -bool isSupported() -{ - static bool resolved = resolveFunctions(bluetoothapis()); - return resolved; -} - -} // namespace WinLowEnergyBluetooth - -QT_END_NAMESPACE diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h index c591a93d..c12ea4ae 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth_p.h +++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h @@ -35,17 +35,9 @@ #ifndef QWINLOWENERGYBLUETOOTH_P_H #define QWINLOWENERGYBLUETOOTH_P_H -#include -#include - -#include +#include #include -#include - -QT_BEGIN_NAMESPACE - -namespace WinLowEnergyBluetooth { #define BLUETOOTH_GATT_FLAG_NONE 0x00000000 #define BLUETOOTH_GATT_FLAG_CONNECTION_ENCRYPTED 0x00000001 @@ -157,19 +149,62 @@ typedef VOID (CALLBACK *PFNBLUETOOTH_GATT_EVENT_CALLBACK)( typedef ULONG64 BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, *PBTH_LE_GATT_RELIABLE_WRITE_CONTEXT; -struct ServicesDiscoveryResult +#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) { - ServicesDiscoveryResult(); - QVector services; - DWORD error; -}; - -bool isSupported(); + QFunctionPointer symbolFunctionPointer = library->resolve(func); + if (!symbolFunctionPointer) + qWarning("Cannot resolve '%s' in '%s'.", func, qPrintable(library->fileName())); + return symbolFunctionPointer; +} -ServicesDiscoveryResult startDiscoveryOfPrimaryServices(HANDLE hDevice); - -} // namespace WinLowEnergyBluetooth - -QT_END_NAMESPACE +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 index 019d67e4..bf35eaa4 100644 --- a/src/bluetooth/windows/windows.pri +++ b/src/bluetooth/windows/windows.pri @@ -1,5 +1,2 @@ PRIVATE_HEADERS += \ windows/qwinlowenergybluetooth_p.h - -SOURCES += \ - windows/qwinlowenergybluetooth.cpp -- cgit v1.2.3 From 7b2c03595cfc476477e166188f74ade382330e00 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Fri, 16 Oct 2015 16:57:53 +0300 Subject: Windows: Implement service details discovering Commit adds basic implementation for obtaining detail information about the LE services. All information gets from the cache therefore there is no need for separate threads. Note: Discovering of an included services is not implemented yet. Change-Id: I0e885c54ddf34cf50cc5d2ad7883a12c59aafb6c Reviewed-by: Timur Pocheptsov Reviewed-by: Denis Shienkov Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_win.cpp | 369 ++++++++++++++++++++++++++++- 1 file changed, 364 insertions(+), 5 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 0f629a0b..2a4a1d19 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -36,9 +36,12 @@ #include "qbluetoothdevicediscoveryagent_p.h" #include +#include // for open modes #include +#include + QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) @@ -47,10 +50,101 @@ Q_GLOBAL_STATIC(QLibrary, bluetoothapis) static bool gattFunctionsResolved = false; -static HANDLE openSystemDevice(const QString &systemPath, int *systemErrorCode) +static QString getServiceSystemPath(const QBluetoothUuid &serviceUuid, int *systemErrorCode) +{ + const HDEVINFO deviceInfoSet = ::SetupDiGetClassDevs( + reinterpret_cast(&serviceUuid), + NULL, + 0, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + if (deviceInfoSet == INVALID_HANDLE_VALUE) { + *systemErrorCode = ::GetLastError(); + return QString(); + } + + QString foundSystemPath; + DWORD index = 0; + + forever { + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + ::ZeroMemory(&deviceInterfaceData, sizeof(deviceInterfaceData)); + deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); + + if (!::SetupDiEnumDeviceInterfaces( + deviceInfoSet, + NULL, + reinterpret_cast(&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 + (deviceInterfaceDetailDataBuffer.data()); + + deviceInterfaceDetailData->cbSize = + sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA); + + if (!::SetupDiGetDeviceInterfaceDetail( + deviceInfoSet, + &deviceInterfaceData, + deviceInterfaceDetailData, + deviceInterfaceDetailDataBuffer.size(), + &deviceInterfaceDetailDataSize, + &deviceInfoData)) { + *systemErrorCode = ::GetLastError(); + break; + } + + foundSystemPath = QString::fromWCharArray(deviceInterfaceDetailData->DevicePath); + *systemErrorCode = NO_ERROR; + break; + } + + ::SetupDiDestroyDeviceInfoList(deviceInfoSet); + return foundSystemPath; +} + +static HANDLE openSystemDevice( + const QString &systemPath, QIODevice::OpenMode openMode, int *systemErrorCode) { - const DWORD desiredAccess = GENERIC_READ | GENERIC_WRITE; - const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + 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 serviceHandle = ::CreateFile( reinterpret_cast(systemPath.utf16()), desiredAccess, @@ -101,6 +195,136 @@ static QVector enumeratePrimaryGattServices( } } +static QVector enumerateGattCharacteristics( + HANDLE serviceHandle, PBTH_LE_GATT_SERVICE gattService, int *systemErrorCode) +{ + if (!gattFunctionsResolved) { + *systemErrorCode = ERROR_NOT_SUPPORTED; + return QVector(); + } + + QVector foundCharacteristics; + USHORT characteristicsCount = 0; + forever { + const HRESULT hr = ::BluetoothGATTGetCharacteristics( + serviceHandle, + 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(); + } + } +} + +static QByteArray getGattCharacteristicValue( + HANDLE serviceHandle, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode) +{ + if (!gattFunctionsResolved) { + *systemErrorCode = ERROR_NOT_SUPPORTED; + return QByteArray(); + } + + QByteArray valueBuffer; + USHORT valueBufferSize = 0; + forever { + const HRESULT hr = ::BluetoothGATTGetCharacteristicValue( + serviceHandle, + gattCharacteristic, + valueBufferSize, + valueBuffer.isEmpty() ? NULL : reinterpret_cast(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(&value->Data[0]), value->DataSize); + } else { + *systemErrorCode = ::GetLastError(); + return QByteArray(); + } + } +} + +static QVector enumerateGattDescriptors( + HANDLE serviceHandle, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode) +{ + if (!gattFunctionsResolved) { + *systemErrorCode = ERROR_NOT_SUPPORTED; + return QVector(); + } + + QVector foundDescriptors; + USHORT descriptorsCount = 0; + forever { + const HRESULT hr = ::BluetoothGATTGetDescriptors( + serviceHandle, + 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(); + } + } +} + +static QByteArray getGattDescriptorValue( + HANDLE serviceHandle, PBTH_LE_GATT_DESCRIPTOR gattDescriptor, int *systemErrorCode) +{ + if (!gattFunctionsResolved) { + *systemErrorCode = ERROR_NOT_SUPPORTED; + return QByteArray(); + } + + QByteArray valueBuffer; + USHORT valueBufferSize = 0; + forever { + const HRESULT hr = ::BluetoothGATTGetDescriptorValue( + serviceHandle, + gattDescriptor, + valueBufferSize, + valueBuffer.isEmpty() ? NULL : reinterpret_cast(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(&value->Data[0]), value->DataSize); + } else { + *systemErrorCode = ::GetLastError(); + return QByteArray(); + } + } +} + QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() : QObject() , state(QLowEnergyController::UnconnectedState) @@ -169,7 +393,8 @@ void QLowEnergyControllerPrivate::discoverServices() { int systemErrorCode = NO_ERROR; - const HANDLE deviceHandle = openSystemDevice(deviceSystemPath, &systemErrorCode); + const HANDLE deviceHandle = openSystemDevice( + deviceSystemPath, QIODevice::ReadOnly, &systemErrorCode); if (systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << qt_error_string(systemErrorCode); @@ -220,9 +445,143 @@ void QLowEnergyControllerPrivate::discoverServices() } void QLowEnergyControllerPrivate::discoverServiceDetails( - const QBluetoothUuid &/*service*/) + const QBluetoothUuid &service) { + if (!serviceList.contains(service)) { + qCWarning(QT_BT_WINDOWS) << "Discovery of unknown service" << service.toString() + << "not possible"; + return; + } + + QSharedPointer servicePrivate = + serviceList.value(service); + + int systemErrorCode = NO_ERROR; + + const QString serviceSystemPath = getServiceSystemPath(service, &systemErrorCode); + + if (systemErrorCode != NO_ERROR) { + qCWarning(QT_BT_WINDOWS) << "Unable to find service" << service.toString() + << "path :" << qt_error_string(systemErrorCode); + servicePrivate->setError(QLowEnergyService::UnknownError); + servicePrivate->setState(QLowEnergyService::DiscoveryRequired); + } + + const HANDLE serviceHandle = openSystemDevice + (serviceSystemPath, 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); + } + + const QVector foundCharacteristics = + enumerateGattCharacteristics(serviceHandle, NULL, &systemErrorCode); + + if (systemErrorCode != NO_ERROR) { + closeSystemDevice(serviceHandle); + 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; + } + + foreach (const BTH_LE_GATT_CHARACTERISTIC &gattCharacteristic, foundCharacteristics) { + const QLowEnergyHandle characteristicHandle = gattCharacteristic.AttributeHandle; + + QLowEnergyServicePrivate::CharData detailsData; + detailsData.uuid = QBluetoothUuid( + gattCharacteristic.CharacteristicUuid.IsShortUuid + ? QBluetoothUuid(gattCharacteristic.CharacteristicUuid.Value.ShortUuid) + : QBluetoothUuid(gattCharacteristic.CharacteristicUuid.Value.LongUuid)); + 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( + serviceHandle, const_cast( + &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); + } + + const QVector foundDescriptors = enumerateGattDescriptors( + serviceHandle, const_cast( + &gattCharacteristic), &systemErrorCode); + + if (systemErrorCode != NO_ERROR) { + if (systemErrorCode != ERROR_NOT_FOUND) { + closeSystemDevice(serviceHandle); + 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; + } + } + + foreach (const BTH_LE_GATT_DESCRIPTOR &gattDescriptor, foundDescriptors) { + const QLowEnergyHandle descriptorHandle = gattDescriptor.AttributeHandle; + + QLowEnergyServicePrivate::DescData data; + data.uuid = QBluetoothUuid( + gattDescriptor.DescriptorUuid.IsShortUuid + ? QBluetoothUuid(gattDescriptor.DescriptorUuid.Value.ShortUuid) + : QBluetoothUuid(gattDescriptor.DescriptorUuid.Value.LongUuid)); + + data.value = getGattDescriptorValue(serviceHandle, const_cast( + &gattDescriptor), &systemErrorCode); + + if (systemErrorCode != NO_ERROR) { + closeSystemDevice(serviceHandle); + 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; + } + + detailsData.descriptorList.insert(descriptorHandle, data); + } + + servicePrivate->characteristicList.insert(characteristicHandle, detailsData); + } + + closeSystemDevice(serviceHandle); + servicePrivate->setState(QLowEnergyService::ServiceDiscovered); } void QLowEnergyControllerPrivate::readCharacteristic( -- cgit v1.2.3 From aee7e789db94960e1de16830c112ba09b5ab730e Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 22 Oct 2015 19:13:40 +0300 Subject: Windows: Introduce a function for QBluetoothUuid creation It makes sense to introduce a common function which converts the native BTH_LE_UUID to the QBluetoothUuid (this used for services, characteristics and descriptors). Change-Id: Ic6eb5230d22426d5980af90f5d849c015e3b7926 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_win.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 2a4a1d19..cc0e7010 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -325,6 +325,12 @@ static QByteArray getGattDescriptorValue( } } +static QBluetoothUuid qtBluetoothUuidFromNativeLeUuid(const BTH_LE_UUID &uuid) +{ + return uuid.IsShortUuid ? QBluetoothUuid(uuid.Value.ShortUuid) + : QBluetoothUuid(uuid.Value.LongUuid); +} + QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() : QObject() , state(QLowEnergyController::UnconnectedState) @@ -420,12 +426,8 @@ void QLowEnergyControllerPrivate::discoverServices() Q_Q(QLowEnergyController); foreach (const BTH_LE_GATT_SERVICE &service, foundServices) { - - const QBluetoothUuid uuid( - service.ServiceUuid.IsShortUuid - ? QBluetoothUuid(service.ServiceUuid.Value.ShortUuid) - : QBluetoothUuid(service.ServiceUuid.Value.LongUuid)); - + const QBluetoothUuid uuid = qtBluetoothUuidFromNativeLeUuid( + service.ServiceUuid); qCDebug(QT_BT_WINDOWS) << "Found uuid:" << uuid; QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate(); @@ -493,10 +495,8 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( const QLowEnergyHandle characteristicHandle = gattCharacteristic.AttributeHandle; QLowEnergyServicePrivate::CharData detailsData; - detailsData.uuid = QBluetoothUuid( - gattCharacteristic.CharacteristicUuid.IsShortUuid - ? QBluetoothUuid(gattCharacteristic.CharacteristicUuid.Value.ShortUuid) - : QBluetoothUuid(gattCharacteristic.CharacteristicUuid.Value.LongUuid)); + detailsData.uuid = qtBluetoothUuidFromNativeLeUuid( + gattCharacteristic.CharacteristicUuid); detailsData.valueHandle = gattCharacteristic.CharacteristicValueHandle; QLowEnergyCharacteristic::PropertyTypes properties = QLowEnergyCharacteristic::Unknown; @@ -552,10 +552,8 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( const QLowEnergyHandle descriptorHandle = gattDescriptor.AttributeHandle; QLowEnergyServicePrivate::DescData data; - data.uuid = QBluetoothUuid( - gattDescriptor.DescriptorUuid.IsShortUuid - ? QBluetoothUuid(gattDescriptor.DescriptorUuid.Value.ShortUuid) - : QBluetoothUuid(gattDescriptor.DescriptorUuid.Value.LongUuid)); + data.uuid = qtBluetoothUuidFromNativeLeUuid( + gattDescriptor.DescriptorUuid); data.value = getGattDescriptorValue(serviceHandle, const_cast( &gattDescriptor), &systemErrorCode); -- cgit v1.2.3 From 84dfae733088c771c3ddbdca7820d0a07a801abe Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Fri, 23 Oct 2015 11:41:09 +0300 Subject: Add missed returns from the service details discovery function In case of error we need to return from the function immediatelly. Change-Id: Ic54431a8aed5beabd97ad85b122e64ecadf60c13 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_win.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index cc0e7010..a8ef3005 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -467,6 +467,7 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( << "path :" << qt_error_string(systemErrorCode); servicePrivate->setError(QLowEnergyService::UnknownError); servicePrivate->setState(QLowEnergyService::DiscoveryRequired); + return; } const HANDLE serviceHandle = openSystemDevice @@ -477,6 +478,7 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( << ":" << qt_error_string(systemErrorCode); servicePrivate->setError(QLowEnergyService::UnknownError); servicePrivate->setState(QLowEnergyService::DiscoveryRequired); + return; } const QVector foundCharacteristics = -- cgit v1.2.3 From c7f7d4169f0d913d909b78bff2b5aaa1beb760fd Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Fri, 23 Oct 2015 12:03:09 +0300 Subject: Windows: Introduce a function to open the system service It is reasonable to move both getServiceSystemPath() and openSystemDevice() functions into openSystemService() function that can be used for the services details discovering and also for the reading or writing of characteristics and descriptors. Change-Id: I3b9f9587887067adbaf4316a3865ae8c83dce683 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_win.cpp | 48 +++++++++++++++++------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index a8ef3005..f85b507f 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -159,6 +159,24 @@ static HANDLE openSystemDevice( return serviceHandle; } +static HANDLE openSystemService( + const QBluetoothUuid &service, QIODevice::OpenMode openMode, int *systemErrorCode) +{ + const QString serviceSystemPath = getServiceSystemPath( + 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 deviceHandle) { if (deviceHandle && deviceHandle != INVALID_HANDLE_VALUE) @@ -460,18 +478,8 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( int systemErrorCode = NO_ERROR; - const QString serviceSystemPath = getServiceSystemPath(service, &systemErrorCode); - - if (systemErrorCode != NO_ERROR) { - qCWarning(QT_BT_WINDOWS) << "Unable to find service" << service.toString() - << "path :" << qt_error_string(systemErrorCode); - servicePrivate->setError(QLowEnergyService::UnknownError); - servicePrivate->setState(QLowEnergyService::DiscoveryRequired); - return; - } - - const HANDLE serviceHandle = openSystemDevice - (serviceSystemPath, QIODevice::ReadOnly, &systemErrorCode); + const HANDLE hService = openSystemService( + service, QIODevice::ReadOnly, &systemErrorCode); if (systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service.toString() @@ -482,10 +490,10 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( } const QVector foundCharacteristics = - enumerateGattCharacteristics(serviceHandle, NULL, &systemErrorCode); + enumerateGattCharacteristics(hService, NULL, &systemErrorCode); if (systemErrorCode != NO_ERROR) { - closeSystemDevice(serviceHandle); + closeSystemDevice(hService); qCWarning(QT_BT_WINDOWS) << "Unable to get characteristics for service" << service.toString() << ":" << qt_error_string(systemErrorCode); servicePrivate->setError(QLowEnergyService::CharacteristicReadError); @@ -521,7 +529,7 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( detailsData.properties = properties; detailsData.value = getGattCharacteristicValue( - serviceHandle, const_cast( + hService, const_cast( &gattCharacteristic), &systemErrorCode); if (systemErrorCode != NO_ERROR) { @@ -534,12 +542,12 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( } const QVector foundDescriptors = enumerateGattDescriptors( - serviceHandle, const_cast( + hService, const_cast( &gattCharacteristic), &systemErrorCode); if (systemErrorCode != NO_ERROR) { if (systemErrorCode != ERROR_NOT_FOUND) { - closeSystemDevice(serviceHandle); + closeSystemDevice(hService); qCWarning(QT_BT_WINDOWS) << "Unable to get descriptor for characteristic" << detailsData.uuid.toString() << "of the service" << service.toString() @@ -557,11 +565,11 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( data.uuid = qtBluetoothUuidFromNativeLeUuid( gattDescriptor.DescriptorUuid); - data.value = getGattDescriptorValue(serviceHandle, const_cast( + data.value = getGattDescriptorValue(hService, const_cast( &gattDescriptor), &systemErrorCode); if (systemErrorCode != NO_ERROR) { - closeSystemDevice(serviceHandle); + closeSystemDevice(hService); qCWarning(QT_BT_WINDOWS) << "Unable to get value for descriptor" << data.uuid.toString() << "for characteristic" @@ -579,7 +587,7 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( servicePrivate->characteristicList.insert(characteristicHandle, detailsData); } - closeSystemDevice(serviceHandle); + closeSystemDevice(hService); servicePrivate->setState(QLowEnergyService::ServiceDiscovered); } -- cgit v1.2.3 From c85dcd7102fca2cde3837f36e6f27346a4564e27 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Fri, 23 Oct 2015 12:26:21 +0300 Subject: Windows: Rename variables which is the system handles Now the system handles have the 'h' prefix (e.g. hService), not to confuse with the others variables which contains the 'Handle' suffix (e.g. with the serviceHandle and so on). Change-Id: I3b79c5323bce043c93326edf6d660ea4bde8522a Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 2 +- .../qbluetoothdevicediscoveryagent_win.cpp | 30 ++++++++--------- src/bluetooth/qlowenergycontroller_win.cpp | 38 +++++++++++----------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index d7da8e98..81015544 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -171,7 +171,7 @@ private: QFutureWatcher *scanWatcher; bool active; int systemErrorCode; - SearchHandle searchHandle; + SearchHandle hSearch; #endif QBluetoothDeviceDiscoveryAgent *q_ptr; diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 78626e85..ff09878e 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -122,7 +122,7 @@ static QBluetoothDeviceInfo createClassicDeviceInfo(const BLUETOOTH_DEVICE_INFO } static QBluetoothDeviceInfo findFirstClassicDevice( - int *systemErrorCode, HBLUETOOTH_DEVICE_FIND *searchHandle) + int *systemErrorCode, HBLUETOOTH_DEVICE_FIND *hSearch) { BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams; ::ZeroMemory(&searchParams, sizeof(searchParams)); @@ -144,7 +144,7 @@ static QBluetoothDeviceInfo findFirstClassicDevice( QBluetoothDeviceInfo foundDevice; if (hFind) { - *searchHandle = hFind; + *hSearch = hFind; *systemErrorCode = NO_ERROR; foundDevice = createClassicDeviceInfo(deviceInfo); } else { @@ -155,14 +155,14 @@ static QBluetoothDeviceInfo findFirstClassicDevice( } static QBluetoothDeviceInfo findNextClassicDevice( - int *systemErrorCode, HBLUETOOTH_DEVICE_FIND searchHandle) + int *systemErrorCode, HBLUETOOTH_DEVICE_FIND hSearch) { BLUETOOTH_DEVICE_INFO deviceInfo; ::ZeroMemory(&deviceInfo, sizeof(deviceInfo)); deviceInfo.dwSize = sizeof(deviceInfo); QBluetoothDeviceInfo foundDevice; - if (!::BluetoothFindNextDevice(searchHandle, &deviceInfo)) { + if (!::BluetoothFindNextDevice(hSearch, &deviceInfo)) { *systemErrorCode = ::GetLastError(); } else { *systemErrorCode = NO_ERROR; @@ -172,11 +172,11 @@ static QBluetoothDeviceInfo findNextClassicDevice( return foundDevice; } -static void closeClassicSearch(HBLUETOOTH_DEVICE_FIND *searchHandle) +static void closeClassicSearch(HBLUETOOTH_DEVICE_FIND *hSearch) { - if (searchHandle && *searchHandle) { - ::BluetoothFindDeviceClose(*searchHandle); - *searchHandle = 0; + if (hSearch && *hSearch) { + ::BluetoothFindDeviceClose(*hSearch); + *hSearch = 0; } } @@ -301,7 +301,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( , scanWatcher(Q_NULLPTR) , active(false) , systemErrorCode(NO_ERROR) - , searchHandle(0) + , hSearch(0) , q_ptr(parent) { scanWatcher = new QFutureWatcher(this); @@ -368,7 +368,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start() // Start scan for first classic device. const QFuture future = QtConcurrent::run( - findFirstClassicDevice, &systemErrorCode, &searchHandle); + findFirstClassicDevice, &systemErrorCode, &hSearch); scanWatcher->setFuture(future); } @@ -386,17 +386,17 @@ void QBluetoothDeviceDiscoveryAgentPrivate::taskFinished() Q_Q(QBluetoothDeviceDiscoveryAgent); if (pendingCancel && !pendingStart) { - closeClassicSearch(&searchHandle); + closeClassicSearch(&hSearch); active = false; pendingCancel = false; emit q->canceled(); } else if (pendingStart) { - closeClassicSearch(&searchHandle); + closeClassicSearch(&hSearch); pendingStart = pendingCancel = false; start(); } else { if (systemErrorCode == ERROR_NO_MORE_ITEMS) { - closeClassicSearch(&searchHandle); + closeClassicSearch(&hSearch); // Enumerate LE devices. const QList foundDevices = enumerateLeDevices(&systemErrorCode); @@ -418,10 +418,10 @@ void QBluetoothDeviceDiscoveryAgentPrivate::taskFinished() processDiscoveredDevice(scanWatcher->result()); // Start scan for next classic device. const QFuture future = QtConcurrent::run( - findNextClassicDevice, &systemErrorCode, searchHandle); + findNextClassicDevice, &systemErrorCode, hSearch); scanWatcher->setFuture(future); } else { - closeClassicSearch(&searchHandle); + closeClassicSearch(&hSearch); pendingStart = pendingCancel = false; active = false; lastError = (systemErrorCode == ERROR_INVALID_HANDLE) ? diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index f85b507f..ee488e6f 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -145,7 +145,7 @@ static HANDLE openSystemDevice( shareMode |= FILE_SHARE_WRITE; } - const HANDLE serviceHandle = ::CreateFile( + const HANDLE hDevice = ::CreateFile( reinterpret_cast(systemPath.utf16()), desiredAccess, shareMode, @@ -154,9 +154,9 @@ static HANDLE openSystemDevice( 0, NULL); - *systemErrorCode = (INVALID_HANDLE_VALUE == serviceHandle) + *systemErrorCode = (INVALID_HANDLE_VALUE == hDevice) ? ::GetLastError() : NO_ERROR; - return serviceHandle; + return hDevice; } static HANDLE openSystemService( @@ -177,14 +177,14 @@ static HANDLE openSystemService( return hService; } -static void closeSystemDevice(HANDLE deviceHandle) +static void closeSystemDevice(HANDLE hDevice) { - if (deviceHandle && deviceHandle != INVALID_HANDLE_VALUE) - ::CloseHandle(deviceHandle); + if (hDevice && hDevice != INVALID_HANDLE_VALUE) + ::CloseHandle(hDevice); } static QVector enumeratePrimaryGattServices( - HANDLE deviceHandle, int *systemErrorCode) + HANDLE hDevice, int *systemErrorCode) { if (!gattFunctionsResolved) { *systemErrorCode = ERROR_NOT_SUPPORTED; @@ -195,7 +195,7 @@ static QVector enumeratePrimaryGattServices( USHORT servicesCount = 0; forever { const HRESULT hr = ::BluetoothGATTGetServices( - deviceHandle, + hDevice, servicesCount, foundServices.isEmpty() ? NULL : &foundServices[0], &servicesCount, @@ -214,7 +214,7 @@ static QVector enumeratePrimaryGattServices( } static QVector enumerateGattCharacteristics( - HANDLE serviceHandle, PBTH_LE_GATT_SERVICE gattService, int *systemErrorCode) + HANDLE hService, PBTH_LE_GATT_SERVICE gattService, int *systemErrorCode) { if (!gattFunctionsResolved) { *systemErrorCode = ERROR_NOT_SUPPORTED; @@ -225,7 +225,7 @@ static QVector enumerateGattCharacteristics( USHORT characteristicsCount = 0; forever { const HRESULT hr = ::BluetoothGATTGetCharacteristics( - serviceHandle, + hService, gattService, characteristicsCount, foundCharacteristics.isEmpty() ? NULL : &foundCharacteristics[0], @@ -245,7 +245,7 @@ static QVector enumerateGattCharacteristics( } static QByteArray getGattCharacteristicValue( - HANDLE serviceHandle, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode) + HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode) { if (!gattFunctionsResolved) { *systemErrorCode = ERROR_NOT_SUPPORTED; @@ -256,7 +256,7 @@ static QByteArray getGattCharacteristicValue( USHORT valueBufferSize = 0; forever { const HRESULT hr = ::BluetoothGATTGetCharacteristicValue( - serviceHandle, + hService, gattCharacteristic, valueBufferSize, valueBuffer.isEmpty() ? NULL : reinterpret_cast(valueBuffer.data()), @@ -279,7 +279,7 @@ static QByteArray getGattCharacteristicValue( } static QVector enumerateGattDescriptors( - HANDLE serviceHandle, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode) + HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode) { if (!gattFunctionsResolved) { *systemErrorCode = ERROR_NOT_SUPPORTED; @@ -290,7 +290,7 @@ static QVector enumerateGattDescriptors( USHORT descriptorsCount = 0; forever { const HRESULT hr = ::BluetoothGATTGetDescriptors( - serviceHandle, + hService, gattCharacteristic, descriptorsCount, foundDescriptors.isEmpty() ? NULL : &foundDescriptors[0], @@ -310,7 +310,7 @@ static QVector enumerateGattDescriptors( } static QByteArray getGattDescriptorValue( - HANDLE serviceHandle, PBTH_LE_GATT_DESCRIPTOR gattDescriptor, int *systemErrorCode) + HANDLE hService, PBTH_LE_GATT_DESCRIPTOR gattDescriptor, int *systemErrorCode) { if (!gattFunctionsResolved) { *systemErrorCode = ERROR_NOT_SUPPORTED; @@ -321,7 +321,7 @@ static QByteArray getGattDescriptorValue( USHORT valueBufferSize = 0; forever { const HRESULT hr = ::BluetoothGATTGetDescriptorValue( - serviceHandle, + hService, gattDescriptor, valueBufferSize, valueBuffer.isEmpty() ? NULL : reinterpret_cast(valueBuffer.data()), @@ -417,7 +417,7 @@ void QLowEnergyControllerPrivate::discoverServices() { int systemErrorCode = NO_ERROR; - const HANDLE deviceHandle = openSystemDevice( + const HANDLE hDevice = openSystemDevice( deviceSystemPath, QIODevice::ReadOnly, &systemErrorCode); if (systemErrorCode != NO_ERROR) { @@ -428,9 +428,9 @@ void QLowEnergyControllerPrivate::discoverServices() } const QVector foundServices = - enumeratePrimaryGattServices(deviceHandle, &systemErrorCode); + enumeratePrimaryGattServices(hDevice, &systemErrorCode); - closeSystemDevice(deviceHandle); + closeSystemDevice(hDevice); if (systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << qt_error_string(systemErrorCode); -- cgit v1.2.3 From 15b795c62681aa3cb5a0187434503b83a439e390 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Fri, 23 Oct 2015 16:38:33 +0300 Subject: Windows: Implement the characteristic reading Change-Id: Iab82f9492fdb3243f7c49970539709ea8d913901 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_win.cpp | 93 +++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index ee488e6f..d2374f96 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -349,6 +349,52 @@ static QBluetoothUuid qtBluetoothUuidFromNativeLeUuid(const BTH_LE_UUID &uuid) : 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; +} + QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() : QObject() , state(QLowEnergyController::UnconnectedState) @@ -593,10 +639,53 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( } void QLowEnergyControllerPrivate::readCharacteristic( - const QSharedPointer /*service*/, - const QLowEnergyHandle /*charHandle*/) + const QSharedPointer 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( + 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); + + 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); + QLowEnergyCharacteristic ch(service, charHandle); + emit service->characteristicRead(ch, characteristicValue); } void QLowEnergyControllerPrivate::writeCharacteristic( -- cgit v1.2.3 From 9cbd63e8155779f866da1154ed6016423bbe5265 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Fri, 23 Oct 2015 19:05:57 +0300 Subject: Windows: Implement the characteristic writing Writing function can be blocked some time if a remote device is not connected or go out from the range. Change-Id: Ia887dccf4c2c61693b04c35f2c092ac3b11e596a Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_win.cpp | 83 ++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index d2374f96..0518177c 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -278,6 +278,35 @@ static QByteArray getGattCharacteristicValue( } } +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(&dataSize), sizeof(dataSize)); + out.writeRawData(value.constData(), value.size()); + + BTH_LE_GATT_RELIABLE_WRITE_CONTEXT reliableWriteContext = 0; + + if (::BluetoothGATTSetCharacteristicValue( + hService, + gattCharacteristic, + reinterpret_cast(valueBuffer.data()), + reliableWriteContext, + flags) != S_OK) { + *systemErrorCode = ::GetLastError(); + } else { + *systemErrorCode = NO_ERROR; + } +} + static QVector enumerateGattDescriptors( HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode) { @@ -689,12 +718,58 @@ void QLowEnergyControllerPrivate::readCharacteristic( } void QLowEnergyControllerPrivate::writeCharacteristic( - const QSharedPointer /*service*/, - const QLowEnergyHandle /*charHandle*/, - const QByteArray &/*newValue*/, - bool /*writeWithResponse*/) + const QSharedPointer service, + const QLowEnergyHandle charHandle, + const QByteArray &newValue, + bool writeWithResponse) { + Q_ASSERT(!service.isNull()); + if (!service->characteristicList.contains(charHandle)) + return; + + int systemErrorCode = NO_ERROR; + + const HANDLE hService = openSystemService( + 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 = 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); + + 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 (writeWithResponse) { + QLowEnergyCharacteristic ch(service, charHandle); + emit service->characteristicWritten(ch, newValue); + } } void QLowEnergyControllerPrivate::readDescriptor( -- cgit v1.2.3 From c7c4fa2823de1fc6795e95efd49a0c7ed6248b42 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 28 Oct 2015 13:50:38 +0300 Subject: Windows: Detect the end handle of a service Change-Id: I03f02c376f2cd9e83b7e2559c2becb80ff307435 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_win.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 0518177c..b126a954 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -38,6 +38,8 @@ #include #include // for open modes +#include // for std::max + #include #include @@ -564,6 +566,9 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( return; } + // We assume that the service does not have any characteristics with descriptors. + servicePrivate->endHandle = servicePrivate->startHandle; + const QVector foundCharacteristics = enumerateGattCharacteristics(hService, NULL, &systemErrorCode); @@ -616,6 +621,12 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( << ":" << 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 foundDescriptors = enumerateGattDescriptors( hService, const_cast( &gattCharacteristic), &systemErrorCode); @@ -656,6 +667,11 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( return; } + // Biggest descriptor will contain an end handle of service. + servicePrivate->endHandle = std::max( + servicePrivate->endHandle, + QLowEnergyHandle(gattDescriptor.AttributeHandle)); + detailsData.descriptorList.insert(descriptorHandle, data); } -- cgit v1.2.3 From 3e6f15d111984536b0299a8b47caf0779ea52730 Mon Sep 17 00:00:00 2001 From: Eric Lemanissier Date: Thu, 22 Oct 2015 16:39:10 +0200 Subject: Bluetooth - service discovery on Windows Based on previous change by Philip Schuchardt Change-Id: If27e1fce63f2a832d19965219f1a5567f76ab770 Reviewed-by: Alex Blasche --- src/bluetooth/bluetooth.pro | 5 +- src/bluetooth/qbluetoothservicediscoveryagent.h | 3 + src/bluetooth/qbluetoothservicediscoveryagent_p.h | 20 ++ .../qbluetoothservicediscoveryagent_win.cpp | 377 ++++++++++++++++++++- 4 files changed, 398 insertions(+), 7 deletions(-) diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index 01cec7ec..ca78afee 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -181,6 +181,8 @@ config_bluez:qtHaveModule(dbus) { SOURCES -= qlowenergyservice.cpp SOURCES -= qlowenergycontroller.cpp } else:win32:!winrt:!wince { + DEFINES += QT_WIN_BLUETOOTH + LIBS += -lbthprops -lws2_32 -lsetupapi include(windows/windows.pri) @@ -192,9 +194,6 @@ config_bluez:qtHaveModule(dbus) { qbluetoothsocket_win.cpp \ qbluetoothserver_win.cpp \ qlowenergycontroller_win.cpp - - LIBS += -lbthprops -lsetupapi - } else { message("Unsupported Bluetooth platform, will not build a working QtBluetooth library.") message("Either no Qt D-Bus found or no BlueZ headers.") diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.h b/src/bluetooth/qbluetoothservicediscoveryagent.h index 984a4084..58bc5fb4 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent.h @@ -123,6 +123,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 c4d5017a..c9620ffd 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h @@ -66,6 +66,10 @@ class QXmlStreamReader; QT_END_NAMESPACE #endif +#ifdef QT_WIN_BLUETOOTH +#include +#endif + QT_BEGIN_NAMESPACE class QBluetoothDeviceDiscoveryAgent; @@ -128,6 +132,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); @@ -179,6 +186,19 @@ private: QMap > > sdpCache; #endif +#ifdef QT_WIN_BLUETOOTH +public: + typedef void* SearchHandle; +private: + int systemError; + bool pendingStop; + bool pendingFinish; + + QFutureWatcher *searchWatcher; + + SearchHandle hSearch; +#endif + protected: QBluetoothServiceDiscoveryAgent *q_ptr; }; diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index 738fe739..4e8bdac0 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -34,27 +34,396 @@ #include "qbluetoothservicediscoveryagent.h" #include "qbluetoothservicediscoveryagent_p.h" +#include +#include + +#include +#include +#include +#include +#include + +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) +{ + 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 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(element.data.uint64); + break; + case SDP_ST_UINT32: + variant = QVariant::fromValue(element.data.uint32); + break; + case SDP_ST_UINT16: + variant = QVariant::fromValue(element.data.uint16); + break; + case SDP_ST_UINT8: + variant = QVariant::fromValue(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(element.data.int64); + break; + case SDP_ST_INT32: + variant = QVariant::fromValue(element.data.int32); + break; + case SDP_ST_INT16: + variant = QVariant::fromValue(element.data.int16); + break; + case SDP_ST_INT8: + variant = QVariant::fromValue(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: { + QByteArray stringBuffer(reinterpret_cast(element.data.string.value), element.data.string.length); + variant = QVariant::fromValue(QString::fromLocal8Bit(stringBuffer)); + break; + } + case SDP_TYPE_URL: { + QString urlString = QString::fromLocal8Bit(reinterpret_cast(element.data.url.value), + (int)element.data.url.length); + QUrl url(urlString); + if (url.isValid()) + variant = QVariant::fromValue(url); + break; + } + case SDP_TYPE_SEQUENCE: { + QList sequenceList = spdContainerToVariantList(element.data.sequence.value, + element.data.sequence.length); + QBluetoothServiceInfo::Sequence sequence(sequenceList); + variant = QVariant::fromValue(sequence); + break; + } + case SDP_TYPE_ALTERNATIVE: { + QList alternativeList = spdContainerToVariantList(element.data.alternative.value, + element.data.alternative.length); + QBluetoothServiceInfo::Alternative alternative(alternativeList); + variant = QVariant::fromValue(alternative); + break; + } + case SDP_TYPE_BOOLEAN: + variant = QVariant::fromValue((bool)element.data.booleanVal); + break; + case SDP_TYPE_NIL: + break; + default: + break; + } + + return variant; +} + +static QList spdContainerToVariantList(LPBYTE containerStream, ULONG containerLength) +{ + HBLUETOOTH_CONTAINER_ELEMENT iter = NULL; + SDP_ELEMENT_DATA element; + + QList sequence; + + forever { + 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(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(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 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(const QBluetoothAddress &deviceAdapter) - : error(QBluetoothServiceDiscoveryAgent::NoError), state(Inactive), - deviceDiscoveryAgent(0), mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), - singleDevice(false) + : 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_UNUSED(deviceAdapter); + resolveFunctions(bluetoothapis()); + searchWatcher = new QFutureWatcher(); } QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate() { + if (pendingFinish) { + stop(); + searchWatcher->waitForFinished(); + } + delete searchWatcher; } void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address) { - Q_UNUSED(address); + Q_Q(QBluetoothServiceDiscoveryAgent); + if (!pendingFinish) { + pendingFinish = true; + pendingStop = false; + + QObject::connect(searchWatcher, SIGNAL(finished()), q, SLOT(_q_nextSdpScan()), Qt::UniqueConnection); + const QFuture 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 future = + QtConcurrent::run(&findNextService, hSearch, &systemError); + searchWatcher->setFuture(future); + return; + } + hSearch = INVALID_HANDLE_VALUE; + pendingFinish = false; + _q_serviceDiscoveryFinished(); + } } QT_END_NAMESPACE -- cgit v1.2.3 From ef0342fdbfbf26a4c872deac9eac0ab244a199af Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 28 Oct 2015 19:49:31 +0300 Subject: Windows: Implement the descriptor reading and writing Also is supported the notification when characteristic value changes. Change-Id: Ic50ea7e6bf53db60180cbe2fcd72439b048747ef Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_p.h | 2 + src/bluetooth/qlowenergycontroller_win.cpp | 340 ++++++++++++++++++++++++++++- src/bluetooth/qlowenergyserviceprivate_p.h | 3 + 3 files changed, 338 insertions(+), 7 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h index fde057cf..95a8beec 100644 --- a/src/bluetooth/qlowenergycontroller_p.h +++ b/src/bluetooth/qlowenergycontroller_p.h @@ -221,6 +221,8 @@ private slots: void characteristicChanged(int charHandle, const QByteArray &data); void serviceError(int attributeHandle, QLowEnergyService::ServiceError errorCode); #elif defined(Q_OS_WIN32) +protected: + void customEvent(QEvent *e); private: QString deviceSystemPath; #endif diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index b126a954..adc7a233 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -37,6 +37,7 @@ #include #include // for open modes +#include #include // for std::max @@ -50,6 +51,37 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) Q_GLOBAL_STATIC(QLibrary, bluetoothapis) +const QEvent::Type CharactericticValueEventType = static_cast(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(&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 QString getServiceSystemPath(const QBluetoothUuid &serviceUuid, int *systemErrorCode) @@ -374,6 +406,111 @@ static QByteArray getGattDescriptorValue( } } +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; + + CharactericticValueEvent *e = new CharactericticValueEvent( + reinterpret_cast(eventOutParameter)); + + QCoreApplication::postEvent(static_cast(context), 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(®istration, sizeof(registration)); + registration.NumCharacteristics = 1; + registration.Characteristics[0] = gattCharacteristic; + if (::BluetoothGATTRegisterEvent( + hService, + CharacteristicValueChangedEvent, + ®istration, + 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) @@ -426,6 +563,67 @@ static BTH_LE_GATT_CHARACTERISTIC recoverNativeLeGattCharacteristic( 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(e); + + updateValueOfCharacteristic(characteristicEvent->m_handle, + characteristicEvent->m_value, false); + + QSharedPointer service = serviceForHandle( + characteristicEvent->m_handle); + if (service.isNull()) + return; + + QLowEnergyCharacteristic ch(service, characteristicEvent->m_handle); + emit service->characteristicChanged(ch, characteristicEvent->m_value); +} + QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() : QObject() , state(QLowEnergyController::UnconnectedState) @@ -585,6 +783,9 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( const QLowEnergyHandle characteristicHandle = gattCharacteristic.AttributeHandle; QLowEnergyServicePrivate::CharData detailsData; + + detailsData.hValueChangeEvent = NULL; + detailsData.uuid = qtBluetoothUuidFromNativeLeUuid( gattCharacteristic.CharacteristicUuid); detailsData.valueHandle = gattCharacteristic.CharacteristicValueHandle; @@ -789,20 +990,145 @@ void QLowEnergyControllerPrivate::writeCharacteristic( } void QLowEnergyControllerPrivate::readDescriptor( - const QSharedPointer /*service*/, - const QLowEnergyHandle /*charHandle*/, - const QLowEnergyHandle /*descriptorHandle*/) + const QSharedPointer 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( + 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( + &gattDescriptor), &systemErrorCode); + + if (systemErrorCode != NO_ERROR) { + closeSystemDevice(hService); + 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 /*service*/, - const QLowEnergyHandle /*charHandle*/, - const QLowEnergyHandle /*descriptorHandle*/, - const QByteArray &/*newValue*/) + const QSharedPointer 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( + 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; + } + } + + if (systemErrorCode != NO_ERROR) { + closeSystemDevice(hService); + 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; + } + } + + updateValueOfDescriptor(charHandle, descriptorHandle, newValue, false); + QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle); + emit service->descriptorWritten(dscr, newValue); } QT_END_NAMESPACE diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h index 0232b912..51c92045 100644 --- a/src/bluetooth/qlowenergyserviceprivate_p.h +++ b/src/bluetooth/qlowenergyserviceprivate_p.h @@ -62,6 +62,9 @@ public: QLowEnergyCharacteristic::PropertyTypes properties; QByteArray value; QHash descriptorList; +#ifdef Q_OS_WIN32 + Qt::HANDLE hValueChangeEvent; +#endif }; enum GattAttributeTypes { -- cgit v1.2.3 From 4baa345ffd7d7d826c3d40a37bc3d4fb0c48c9aa Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Tue, 17 Nov 2015 12:24:31 +0300 Subject: Windows: Use QT_WIN_BLUETOOTH macro where it is possible Change-Id: I77811a65b530c8994b622d4d18718c50ec629684 Reviewed-by: Alex Blasche Reviewed-by: Timur Pocheptsov --- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 6 +++--- src/bluetooth/qbluetoothlocaldevice_p.h | 2 +- src/bluetooth/qlowenergycontroller_p.h | 2 +- src/bluetooth/qlowenergyserviceprivate_p.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index 81015544..cf4fd0c5 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -73,14 +73,14 @@ class QDBusVariant; QT_END_NAMESPACE #endif -#ifdef Q_OS_WIN32 +#ifdef QT_WIN_BLUETOOTH #include #endif QT_BEGIN_NAMESPACE class QBluetoothDeviceDiscoveryAgentPrivate -#if defined(QT_ANDROID_BLUETOOTH) || defined(Q_OS_WIN32) +#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WIN_BLUETOOTH) : public QObject { Q_OBJECT @@ -154,7 +154,7 @@ private: QTimer extendedDiscoveryTimer; #endif -#ifdef Q_OS_WIN32 +#ifdef QT_WIN_BLUETOOTH public: typedef void* SearchHandle; static QString discoveredLeDeviceSystemPath(const QBluetoothAddress &deviceAddress); diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h index 66f5c1c0..4a6aa529 100644 --- a/src/bluetooth/qbluetoothlocaldevice_p.h +++ b/src/bluetooth/qbluetoothlocaldevice_p.h @@ -200,7 +200,7 @@ private: void initializeAdapterBluez5(); }; -#elif defined(Q_OS_WIN32) +#elif defined(QT_WIN_BLUETOOTH) class QBluetoothLocalDevicePrivate : public QObject { diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h index 95a8beec..ca99ae58 100644 --- a/src/bluetooth/qlowenergycontroller_p.h +++ b/src/bluetooth/qlowenergycontroller_p.h @@ -220,7 +220,7 @@ private slots: QLowEnergyService::ServiceError errorCode); void characteristicChanged(int charHandle, const QByteArray &data); void serviceError(int attributeHandle, QLowEnergyService::ServiceError errorCode); -#elif defined(Q_OS_WIN32) +#elif defined(QT_WIN_BLUETOOTH) protected: void customEvent(QEvent *e); private: diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h index 51c92045..50a258d5 100644 --- a/src/bluetooth/qlowenergyserviceprivate_p.h +++ b/src/bluetooth/qlowenergyserviceprivate_p.h @@ -62,7 +62,7 @@ public: QLowEnergyCharacteristic::PropertyTypes properties; QByteArray value; QHash descriptorList; -#ifdef Q_OS_WIN32 +#ifdef QT_WIN_BLUETOOTH Qt::HANDLE hValueChangeEvent; #endif }; -- cgit v1.2.3 From 49875b3f1165a78a05fca4f3b3e27dfec4001ab0 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Tue, 17 Nov 2015 12:34:01 +0300 Subject: Windows: Use Qt::HANDLE instead of typedef void* Change-Id: I4f2194501f652c38cff43ac1716a5853cd1e1b13 Reviewed-by: Alex Blasche Reviewed-by: Timur Pocheptsov --- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 3 +-- src/bluetooth/qbluetoothservicediscoveryagent_p.h | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index cf4fd0c5..16ebe63c 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -156,7 +156,6 @@ private: #ifdef QT_WIN_BLUETOOTH public: - typedef void* SearchHandle; static QString discoveredLeDeviceSystemPath(const QBluetoothAddress &deviceAddress); private slots: @@ -171,7 +170,7 @@ private: QFutureWatcher *scanWatcher; bool active; int systemErrorCode; - SearchHandle hSearch; + Qt::HANDLE hSearch; #endif QBluetoothDeviceDiscoveryAgent *q_ptr; diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h index c9620ffd..86eccb12 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h @@ -187,8 +187,6 @@ private: #endif #ifdef QT_WIN_BLUETOOTH -public: - typedef void* SearchHandle; private: int systemError; bool pendingStop; @@ -196,7 +194,7 @@ private: QFutureWatcher *searchWatcher; - SearchHandle hSearch; + Qt::HANDLE hSearch; #endif protected: -- cgit v1.2.3 From c1698e56f0f988564d6e86426a45fdcf4e653a1e Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Tue, 16 May 2017 14:20:35 +0300 Subject: Ignore tst_discoveryTimeout() auto test Change-Id: Ia13e05cda2ee740ce71d2d9d6e1bcbdffabd029c Reviewed-by: Oliver Wolff Reviewed-by: Alex Blasche --- tests/auto/qbluetoothdevicediscoveryagent/BLACKLIST | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/auto/qbluetoothdevicediscoveryagent/BLACKLIST diff --git a/tests/auto/qbluetoothdevicediscoveryagent/BLACKLIST b/tests/auto/qbluetoothdevicediscoveryagent/BLACKLIST new file mode 100644 index 00000000..ff395711 --- /dev/null +++ b/tests/auto/qbluetoothdevicediscoveryagent/BLACKLIST @@ -0,0 +1,3 @@ +# Remove it after fixing qbluetoothdevicediscoveryagent +[tst_discoveryTimeout] +windows -- cgit v1.2.3 From d86490915aaa42c04d4dc1ad866e5bb1836d4272 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 1 Jun 2017 16:25:17 +0300 Subject: Initialize lowEnergySearchTimeout with -1 .. that allows to pass the tst_discoveryTimeout() test. Change-Id: I1af53c8186151fcb6017b07b28f4b8355d91d072 Reviewed-by: Oliver Wolff Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp | 1 + tests/auto/qbluetoothdevicediscoveryagent/BLACKLIST | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 tests/auto/qbluetoothdevicediscoveryagent/BLACKLIST diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index d6f60ad3..4728faf7 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -302,6 +302,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( , active(false) , systemErrorCode(NO_ERROR) , hSearch(0) + , lowEnergySearchTimeout(-1) // remains -1 -> timeout not supported , q_ptr(parent) { scanWatcher = new QFutureWatcher(this); diff --git a/tests/auto/qbluetoothdevicediscoveryagent/BLACKLIST b/tests/auto/qbluetoothdevicediscoveryagent/BLACKLIST deleted file mode 100644 index ff395711..00000000 --- a/tests/auto/qbluetoothdevicediscoveryagent/BLACKLIST +++ /dev/null @@ -1,3 +0,0 @@ -# Remove it after fixing qbluetoothdevicediscoveryagent -[tst_discoveryTimeout] -windows -- cgit v1.2.3 From 78e0a1c6a5823a76bb85f04724dd4b1df80a8c44 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 1 Jun 2017 16:36:30 +0300 Subject: Replace 'forever' macro with 'for (;;)' statement Change-Id: I402b856be06db0e55ec2efb21fc6cb8f35e57a0c Reviewed-by: Oliver Wolff --- src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp | 2 +- src/bluetooth/qbluetoothlocaldevice_win.cpp | 2 +- src/bluetooth/qbluetoothservicediscoveryagent_win.cpp | 2 +- src/bluetooth/qlowenergycontroller_win.cpp | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 4728faf7..967aec8e 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -200,7 +200,7 @@ static QList enumerateLeDevices( QList cachedEntries; - forever { + for (;;) { SP_DEVICE_INTERFACE_DATA deviceInterfaceData; ::ZeroMemory(&deviceInterfaceData, sizeof(deviceInterfaceData)); deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp index 2ccf2b70..816a3a4b 100644 --- a/src/bluetooth/qbluetoothlocaldevice_win.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -225,7 +225,7 @@ QList QBluetoothLocalDevicePrivate::localAdapters() HANDLE hRadio = 0; if (const HBLUETOOTH_RADIO_FIND hSearch = ::BluetoothFindFirstRadio(¶ms, &hRadio)) { - forever { + for (;;) { BLUETOOTH_RADIO_INFO radio; ::ZeroMemory(&radio, sizeof(radio)); radio.dwSize = sizeof(radio); diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index b92025ee..6e9dab96 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -201,7 +201,7 @@ static QList spdContainerToVariantList(LPBYTE containerStream, ULONG c QList sequence; - forever { + for (;;) { DWORD result = BluetoothSdpGetContainerElementData(containerStream, containerLength, &iter, diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index a71f9327..f01b99d7 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -100,7 +100,7 @@ static QString getServiceSystemPath(const QBluetoothUuid &serviceUuid, int *syst QString foundSystemPath; DWORD index = 0; - forever { + for (;;) { SP_DEVICE_INTERFACE_DATA deviceInterfaceData; ::ZeroMemory(&deviceInterfaceData, sizeof(deviceInterfaceData)); deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); @@ -227,7 +227,7 @@ static QVector enumeratePrimaryGattServices( QVector foundServices; USHORT servicesCount = 0; - forever { + for (;;) { const HRESULT hr = ::BluetoothGATTGetServices( hDevice, servicesCount, @@ -257,7 +257,7 @@ static QVector enumerateGattCharacteristics( QVector foundCharacteristics; USHORT characteristicsCount = 0; - forever { + for (;;) { const HRESULT hr = ::BluetoothGATTGetCharacteristics( hService, gattService, @@ -288,7 +288,7 @@ static QByteArray getGattCharacteristicValue( QByteArray valueBuffer; USHORT valueBufferSize = 0; - forever { + for (;;) { const HRESULT hr = ::BluetoothGATTGetCharacteristicValue( hService, gattCharacteristic, @@ -351,7 +351,7 @@ static QVector enumerateGattDescriptors( QVector foundDescriptors; USHORT descriptorsCount = 0; - forever { + for (;;) { const HRESULT hr = ::BluetoothGATTGetDescriptors( hService, gattCharacteristic, @@ -382,7 +382,7 @@ static QByteArray getGattDescriptorValue( QByteArray valueBuffer; USHORT valueBufferSize = 0; - forever { + for (;;) { const HRESULT hr = ::BluetoothGATTGetDescriptorValue( hService, gattDescriptor, -- cgit v1.2.3 From f02932a14e902a5a4cd7f3fe34c85ddb636e6044 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 1 Jun 2017 16:50:02 +0300 Subject: Use std::find_if to check for local adapter address Change-Id: I53af8c2a5dc848d1f90221d036c59f17c44921dc Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 967aec8e..9f3eb84c 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -356,15 +356,13 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent } // Check for the local adapter address. - bool foundLocalAdapter = false; - foreach (const QBluetoothHostInfo &adapterInfo, foundLocalAdapters) { - if (adapterAddress == QBluetoothAddress() || adapterAddress == adapterInfo.address()) { - foundLocalAdapter = true; - break; - } - } - - if (!foundLocalAdapter) { + 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."); -- cgit v1.2.3 From 5c62cfd89db15229fa9a96825c04ad6b6fd08db9 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 1 Jun 2017 16:56:23 +0300 Subject: Replace 'foreach' macro with range-based 'for' Change-Id: I7010ff54a146a9d70029e2607b415e0e1dcc1d78 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp | 5 ++--- src/bluetooth/qbluetoothlocaldevice_win.cpp | 3 ++- src/bluetooth/qlowenergycontroller_win.cpp | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 9f3eb84c..7f0fe728 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -406,10 +406,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::taskFinished() if (systemErrorCode == ERROR_NO_MORE_ITEMS) { closeClassicSearch(&hSearch); // Enumerate LE devices. - const QList foundDevices = - enumerateLeDevices(&systemErrorCode); + const QList foundDevices = enumerateLeDevices(&systemErrorCode); if (systemErrorCode == ERROR_NO_MORE_ITEMS) { - foreach (const QBluetoothDeviceInfo &foundDevice, foundDevices) + for (const QBluetoothDeviceInfo &foundDevice : foundDevices) processDiscoveredDevice(foundDevice); active = false; emit q->finished(); diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp index 816a3a4b..78b82a97 100644 --- a/src/bluetooth/qbluetoothlocaldevice_win.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -199,7 +199,8 @@ void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address) { Q_Q(QBluetoothLocalDevice); - foreach (const QBluetoothHostInfo &adapterInfo, QBluetoothLocalDevicePrivate::localAdapters()) { + const QList adapterInfos = QBluetoothLocalDevicePrivate::localAdapters(); + for (const QBluetoothHostInfo &adapterInfo : adapterInfos) { if (address == QBluetoothAddress() || address == adapterInfo.address()) { deviceAddress = adapterInfo.address(); diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index f01b99d7..494d715c 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -723,7 +723,7 @@ void QLowEnergyControllerPrivate::discoverServices() Q_Q(QLowEnergyController); - foreach (const BTH_LE_GATT_SERVICE &service, foundServices) { + for (const BTH_LE_GATT_SERVICE &service : foundServices) { const QBluetoothUuid uuid = qtBluetoothUuidFromNativeLeUuid( service.ServiceUuid); qCDebug(QT_BT_WINDOWS) << "Found uuid:" << uuid; @@ -784,7 +784,7 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( return; } - foreach (const BTH_LE_GATT_CHARACTERISTIC &gattCharacteristic, foundCharacteristics) { + for (const BTH_LE_GATT_CHARACTERISTIC &gattCharacteristic : foundCharacteristics) { const QLowEnergyHandle characteristicHandle = gattCharacteristic.AttributeHandle; QLowEnergyServicePrivate::CharData detailsData; @@ -850,7 +850,7 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( } } - foreach (const BTH_LE_GATT_DESCRIPTOR &gattDescriptor, foundDescriptors) { + for (const BTH_LE_GATT_DESCRIPTOR &gattDescriptor : foundDescriptors) { const QLowEnergyHandle descriptorHandle = gattDescriptor.AttributeHandle; QLowEnergyServicePrivate::DescData data; -- cgit v1.2.3 From c38583ec73e8490b6373e9ac42503d41b4f28665 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 1 Jun 2017 17:06:24 +0300 Subject: Use 'const' keyword a bit more Change-Id: If735ef8abf793fed46c43f692d413e76013a899e Reviewed-by: Oliver Wolff Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothservicediscoveryagent_win.cpp | 18 +++++++++--------- src/bluetooth/qlowenergycontroller_win.cpp | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index 6e9dab96..e8df3ebe 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -62,7 +62,7 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) static inline QFunctionPointer resolveFunction(QLibrary *library, const char *func) { - QFunctionPointer symbolFunctionPointer = library->resolve(func); + const QFunctionPointer symbolFunctionPointer = library->resolve(func); if (!symbolFunctionPointer) qWarning("Cannot resolve '%s' in '%s'.", func, qPrintable(library->fileName())); return symbolFunctionPointer; @@ -156,29 +156,29 @@ static QVariant spdElementToVariant(const SDP_ELEMENT_DATA &element) break; } case SDP_TYPE_STRING: { - QByteArray stringBuffer(reinterpret_cast(element.data.string.value), element.data.string.length); + const QByteArray stringBuffer(reinterpret_cast(element.data.string.value), element.data.string.length); variant = QVariant::fromValue(QString::fromLocal8Bit(stringBuffer)); break; } case SDP_TYPE_URL: { - QString urlString = QString::fromLocal8Bit(reinterpret_cast(element.data.url.value), + const QString urlString = QString::fromLocal8Bit(reinterpret_cast(element.data.url.value), (int)element.data.url.length); - QUrl url(urlString); + const QUrl url(urlString); if (url.isValid()) variant = QVariant::fromValue(url); break; } case SDP_TYPE_SEQUENCE: { - QList sequenceList = spdContainerToVariantList(element.data.sequence.value, + const QList sequenceList = spdContainerToVariantList(element.data.sequence.value, element.data.sequence.length); - QBluetoothServiceInfo::Sequence sequence(sequenceList); + const QBluetoothServiceInfo::Sequence sequence(sequenceList); variant = QVariant::fromValue(sequence); break; } case SDP_TYPE_ALTERNATIVE: { - QList alternativeList = spdContainerToVariantList(element.data.alternative.value, + const QList alternativeList = spdContainerToVariantList(element.data.alternative.value, element.data.alternative.length); - QBluetoothServiceInfo::Alternative alternative(alternativeList); + const QBluetoothServiceInfo::Alternative alternative(alternativeList); variant = QVariant::fromValue(alternative); break; } @@ -202,7 +202,7 @@ static QList spdContainerToVariantList(LPBYTE containerStream, ULONG c QList sequence; for (;;) { - DWORD result = BluetoothSdpGetContainerElementData(containerStream, + const DWORD result = BluetoothSdpGetContainerElementData(containerStream, containerLength, &iter, &element); diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 494d715c..b22e64d1 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -615,12 +615,12 @@ void QLowEnergyControllerPrivate::customEvent(QEvent *e) updateValueOfCharacteristic(characteristicEvent->m_handle, characteristicEvent->m_value, false); - QSharedPointer service = serviceForHandle( + const QSharedPointer service = serviceForHandle( characteristicEvent->m_handle); if (service.isNull()) return; - QLowEnergyCharacteristic ch(service, characteristicEvent->m_handle); + const QLowEnergyCharacteristic ch(service, characteristicEvent->m_handle); emit service->characteristicChanged(ch, characteristicEvent->m_value); } @@ -753,7 +753,7 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( return; } - QSharedPointer servicePrivate = + const QSharedPointer servicePrivate = serviceList.value(service); int systemErrorCode = NO_ERROR; @@ -950,7 +950,7 @@ void QLowEnergyControllerPrivate::readCharacteristic( updateValueOfCharacteristic(charHandle, characteristicValue, false); - QLowEnergyCharacteristic ch(service, charHandle); + const QLowEnergyCharacteristic ch(service, charHandle); emit service->characteristicRead(ch, characteristicValue); } @@ -1004,7 +1004,7 @@ void QLowEnergyControllerPrivate::writeCharacteristic( updateValueOfCharacteristic(charHandle, newValue, false); if (mode == QLowEnergyService::WriteWithResponse) { - QLowEnergyCharacteristic ch(service, charHandle); + const QLowEnergyCharacteristic ch(service, charHandle); emit service->characteristicWritten(ch, newValue); } } @@ -1147,7 +1147,7 @@ void QLowEnergyControllerPrivate::writeDescriptor( updateValueOfDescriptor(charHandle, descriptorHandle, newValue, false); - QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle); + const QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle); emit service->descriptorWritten(dscr, newValue); } -- cgit v1.2.3 From 50d1b99a89ed65d80a60a8d2f1c91c5ab4b80371 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 8 Jun 2017 09:55:41 +0300 Subject: Get rid of QList in favor to QVector where possible Change-Id: I872e7d6bc4371beed0b80eb1ebb374d2c48c9167 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 7f0fe728..108f41a6 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -48,13 +48,11 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) struct LeDeviceEntry { - LeDeviceEntry(const QString &path, const QBluetoothAddress &address) - : devicePath(path), deviceAddress(address) {} QString devicePath; QBluetoothAddress deviceAddress; }; -Q_GLOBAL_STATIC(QList, cachedLeDeviceEntries) +Q_GLOBAL_STATIC(QVector, cachedLeDeviceEntries) Q_GLOBAL_STATIC(QMutex, cachedLeDeviceEntriesGuard) static QString devicePropertyString( @@ -180,7 +178,7 @@ static void closeClassicSearch(HBLUETOOTH_DEVICE_FIND *hSearch) } } -static QList enumerateLeDevices( +static QVector enumerateLeDevices( int *systemErrorCode) { const QUuid deviceInterfaceGuid("781aee18-7733-4ce4-add0-91f41c67b592"); @@ -192,13 +190,13 @@ static QList enumerateLeDevices( if (hDeviceInfo == INVALID_HANDLE_VALUE) { *systemErrorCode = ::GetLastError(); - return QList(); + return QVector(); } - QList foundDevices; + QVector foundDevices; DWORD index = 0; - QList cachedEntries; + QVector cachedEntries; for (;;) { SP_DEVICE_INTERFACE_DATA deviceInterfaceData; @@ -265,7 +263,7 @@ static QList enumerateLeDevices( deviceInfo.setCached(true); foundDevices << deviceInfo; - cachedEntries << LeDeviceEntry(systemPath, address); + cachedEntries << LeDeviceEntry{systemPath, address}; } QMutexLocker locker(cachedLeDeviceEntriesGuard()); @@ -406,7 +404,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::taskFinished() if (systemErrorCode == ERROR_NO_MORE_ITEMS) { closeClassicSearch(&hSearch); // Enumerate LE devices. - const QList foundDevices = enumerateLeDevices(&systemErrorCode); + const QVector foundDevices = enumerateLeDevices(&systemErrorCode); if (systemErrorCode == ERROR_NO_MORE_ITEMS) { for (const QBluetoothDeviceInfo &foundDevice : foundDevices) processDiscoveredDevice(foundDevice); -- cgit v1.2.3 From bfeed6bbcdddc7988e5bef13a36998eae084d7b4 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 1 Jun 2017 17:22:05 +0300 Subject: Fix possible leaking of system handles Change-Id: Ib702e7aa95a26212e1713b514adb5e6c81e9bbd7 Reviewed-by: Oliver Wolff --- src/bluetooth/qlowenergycontroller_win.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index b22e64d1..02b03297 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -938,6 +938,7 @@ void QLowEnergyControllerPrivate::readCharacteristic( const QByteArray characteristicValue = getGattCharacteristicValue( hService, &gattCharacteristic, &systemErrorCode); + closeSystemDevice(hService); if (systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to get value for characteristic" @@ -991,6 +992,7 @@ void QLowEnergyControllerPrivate::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" @@ -1044,9 +1046,9 @@ void QLowEnergyControllerPrivate::readDescriptor( const QByteArray value = getGattDescriptorValue( hService, const_cast( &gattDescriptor), &systemErrorCode); + closeSystemDevice(hService); if (systemErrorCode != NO_ERROR) { - closeSystemDevice(hService); qCWarning(QT_BT_WINDOWS) << "Unable to get value for descriptor" << dscrDetails.uuid.toString() << "for characteristic" @@ -1132,8 +1134,9 @@ void QLowEnergyControllerPrivate::writeDescriptor( } } + closeSystemDevice(hService); + if (systemErrorCode != NO_ERROR) { - closeSystemDevice(hService); qCWarning(QT_BT_WINDOWS) << "Unable to subscribe events for descriptor" << dscrDetails.uuid.toString() << "for characteristic" @@ -1143,6 +1146,8 @@ void QLowEnergyControllerPrivate::writeDescriptor( service->setError(QLowEnergyService::DescriptorWriteError); return; } + } else { + closeSystemDevice(hService); } updateValueOfDescriptor(charHandle, descriptorHandle, newValue, false); -- cgit v1.2.3 From 4301d682e05c678c378864cb48da489203f785e2 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 1 Jun 2017 17:42:42 +0300 Subject: Pass a device address to correctly search its service system path We need to check on a device address from the returned system path and to return matched path. Otherwise always returns a first found system path for devices with same service UUID. Change-Id: Iccfae4d7b19451b4fa19fce9bbd1ab9b749cd5bb Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_win.cpp | 38 +++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 02b03297..e7967dc4 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -84,7 +84,17 @@ enum { UseNotifications = 0x1, UseIndications = 0x2 }; static bool gattFunctionsResolved = false; -static QString getServiceSystemPath(const QBluetoothUuid &serviceUuid, int *systemErrorCode) +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(&serviceUuid), @@ -154,9 +164,15 @@ static QString getServiceSystemPath(const QBluetoothUuid &serviceUuid, int *syst break; } - foundSystemPath = QString::fromWCharArray(deviceInterfaceDetailData->DevicePath); - *systemErrorCode = NO_ERROR; - 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); @@ -193,11 +209,11 @@ static HANDLE openSystemDevice( return hDevice; } -static HANDLE openSystemService( +static HANDLE openSystemService(const QBluetoothAddress &deviceAddress, const QBluetoothUuid &service, QIODevice::OpenMode openMode, int *systemErrorCode) { const QString serviceSystemPath = getServiceSystemPath( - service, systemErrorCode); + deviceAddress, service, systemErrorCode); if (*systemErrorCode != NO_ERROR) return INVALID_HANDLE_VALUE; @@ -759,7 +775,7 @@ void QLowEnergyControllerPrivate::discoverServiceDetails( int systemErrorCode = NO_ERROR; const HANDLE hService = openSystemService( - service, QIODevice::ReadOnly, &systemErrorCode); + remoteDevice, service, QIODevice::ReadOnly, &systemErrorCode); if (systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service.toString() @@ -924,7 +940,7 @@ void QLowEnergyControllerPrivate::readCharacteristic( int systemErrorCode = NO_ERROR; const HANDLE hService = openSystemService( - service->uuid, QIODevice::ReadOnly, &systemErrorCode); + remoteDevice, service->uuid, QIODevice::ReadOnly, &systemErrorCode); if (systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() @@ -968,7 +984,7 @@ void QLowEnergyControllerPrivate::writeCharacteristic( int systemErrorCode = NO_ERROR; const HANDLE hService = openSystemService( - service->uuid, QIODevice::ReadWrite, &systemErrorCode); + remoteDevice, service->uuid, QIODevice::ReadWrite, &systemErrorCode); if (systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() @@ -1028,7 +1044,7 @@ void QLowEnergyControllerPrivate::readDescriptor( int systemErrorCode = NO_ERROR; const HANDLE hService = openSystemService( - service->uuid, QIODevice::ReadOnly, &systemErrorCode); + remoteDevice, service->uuid, QIODevice::ReadOnly, &systemErrorCode); if (systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() @@ -1083,7 +1099,7 @@ void QLowEnergyControllerPrivate::writeDescriptor( int systemErrorCode = NO_ERROR; const HANDLE hService = openSystemService( - service->uuid, QIODevice::ReadWrite, &systemErrorCode); + remoteDevice, service->uuid, QIODevice::ReadWrite, &systemErrorCode); if (systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() -- cgit v1.2.3 From 63ec6a9501b86946ca5c7b9c2176b764f55a82d9 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 1 Jun 2017 18:06:24 +0300 Subject: Fix crash at access to unexistent controller object Sometimes are possible a situations when triggered event callback function contains a pointer with a context of unexistent controller's object (f.e. this controller can be already deleted). That causes a crash at trying to access by this pointer. Solution is to append a pointer of each created object (or to remove a pointer of each destroyed object) to (from) some guarded vector of pointers. And then to check every received context with pointers inside of this vector. If a context does not contains in a vector, then skip any actions with this context. Change-Id: I94e237c41028724307f58bb09235299eae15d6b5 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_win.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index e7967dc4..89f46455 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -38,6 +38,7 @@ #include #include // for open modes #include +#include #include // for std::max @@ -51,6 +52,9 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) Q_GLOBAL_STATIC(QLibrary, bluetoothapis) +Q_GLOBAL_STATIC(QVector, qControllers) +static QMutex controllersGuard(QMutex::NonRecursive); + const QEvent::Type CharactericticValueEventType = static_cast(QEvent::User + 1); class CharactericticValueEvent : public QEvent @@ -474,10 +478,15 @@ static void WINAPI eventChangedCallbackEntry( if ((eventType != CharacteristicValueChangedEvent) || !eventOutParameter || !context) return; + QMutexLocker locker(&controllersGuard); + const auto target = static_cast(context); + if (!qControllers->contains(target)) + return; + CharactericticValueEvent *e = new CharactericticValueEvent( reinterpret_cast(eventOutParameter)); - QCoreApplication::postEvent(static_cast(context), e); + QCoreApplication::postEvent(target, e); } static HANDLE registerEvent( @@ -645,6 +654,9 @@ QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() , 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"; @@ -654,6 +666,8 @@ QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() { + QMutexLocker locker(&controllersGuard); + qControllers()->removeAll(this); } void QLowEnergyControllerPrivate::init() -- cgit v1.2.3 From 6c4bd9551755dac834b107202df5d95b346f0b79 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Tue, 13 Jun 2017 13:06:01 +0300 Subject: Fix classic and LE devices discovery Devices discovery was broken due to merging from 5.9 branch. Where were introduced new QBluetoothDeviceDiscoveryAgent::DiscoveryMethods feature. But now, this code is refactored and adjusted in completelly asynchronous manner. Change-Id: I9b7c4cbe27066ed05d1ed4546f71ea559544f6f5 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 18 +- .../qbluetoothdevicediscoveryagent_win.cpp | 213 ++++++++++++++------- 2 files changed, 160 insertions(+), 71 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index 4d0e24eb..9eadfae4 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -168,19 +168,25 @@ private: public: static QString discoveredLeDeviceSystemPath(const QBluetoothAddress &deviceAddress); -private slots: - void taskFinished(); - 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; - QFutureWatcher *scanWatcher; bool active; - int systemErrorCode; - Qt::HANDLE hSearch; + + QFutureWatcher *classicScanWatcher; + QFutureWatcher *lowenergyScanWatcher; #endif #ifdef QT_WINRT_BLUETOOTH diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 108f41a6..0e6e1343 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -38,6 +38,7 @@ #include "qbluetoothlocaldevice_p.h" #include +#include #include #include @@ -114,13 +115,15 @@ static QBluetoothDeviceInfo createClassicDeviceInfo(const BLUETOOTH_DEVICE_INFO QString::fromWCharArray(foundDevice.szName), foundDevice.ulClassofDevice); + deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration); + if (foundDevice.fRemembered) deviceInfo.setCached(true); return deviceInfo; } static QBluetoothDeviceInfo findFirstClassicDevice( - int *systemErrorCode, HBLUETOOTH_DEVICE_FIND *hSearch) + DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND *hSearch) { BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams; ::ZeroMemory(&searchParams, sizeof(searchParams)); @@ -153,7 +156,7 @@ static QBluetoothDeviceInfo findFirstClassicDevice( } static QBluetoothDeviceInfo findNextClassicDevice( - int *systemErrorCode, HBLUETOOTH_DEVICE_FIND hSearch) + DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND hSearch) { BLUETOOTH_DEVICE_INFO deviceInfo; ::ZeroMemory(&deviceInfo, sizeof(deviceInfo)); @@ -170,16 +173,14 @@ static QBluetoothDeviceInfo findNextClassicDevice( return foundDevice; } -static void closeClassicSearch(HBLUETOOTH_DEVICE_FIND *hSearch) +static void closeClassicSearch(HBLUETOOTH_DEVICE_FIND hSearch) { - if (hSearch && *hSearch) { - ::BluetoothFindDeviceClose(*hSearch); - *hSearch = 0; - } + if (hSearch) + ::BluetoothFindDeviceClose(hSearch); } static QVector enumerateLeDevices( - int *systemErrorCode) + DWORD *systemErrorCode) { const QUuid deviceInterfaceGuid("781aee18-7733-4ce4-add0-91f41c67b592"); const HDEVINFO hDeviceInfo = ::SetupDiGetClassDevs( @@ -273,11 +274,39 @@ static QVector enumerateLeDevices( return foundDevices; } +struct DiscoveryResult { + QVector 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 - int dummyErrorCode; + DWORD dummyErrorCode; enumerateLeDevices(&dummyErrorCode); QMutexLocker locker(cachedLeDeviceEntriesGuard()); @@ -296,24 +325,18 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( , adapterAddress(deviceAdapter) , pendingCancel(false) , pendingStart(false) - , scanWatcher(Q_NULLPTR) , active(false) - , systemErrorCode(NO_ERROR) - , hSearch(0) + , classicScanWatcher(nullptr) + , lowenergyScanWatcher(nullptr) , lowEnergySearchTimeout(-1) // remains -1 -> timeout not supported , q_ptr(parent) { - scanWatcher = new QFutureWatcher(this); - connect(scanWatcher, &QFutureWatcher::finished, - this, &QBluetoothDeviceDiscoveryAgentPrivate::taskFinished); } QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() { if (active) stop(); - - scanWatcher->waitForFinished(); } bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const @@ -327,13 +350,12 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() { - // FIXME: Add required discovery method! - return QBluetoothDeviceDiscoveryAgent::NoMethod; + return (LowEnergyMethod | ClassicMethod); } void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods) { - Q_UNUSED(methods); + requestedMethods = methods; if (pendingCancel == true) { pendingStart = true; @@ -371,10 +393,11 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent discoveredDevices.clear(); active = true; - // Start scan for first classic device. - const QFuture future = QtConcurrent::run( - findFirstClassicDevice, &systemErrorCode, &hSearch); - scanWatcher->setFuture(future); + // 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() @@ -386,54 +409,112 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop() pendingStart = false; } -void QBluetoothDeviceDiscoveryAgentPrivate::taskFinished() +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(this); + connect(lowenergyScanWatcher, &QFutureWatcher::finished, + this, &QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery); + } + const QFuture 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(); + 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(this); + connect(classicScanWatcher, &QFutureWatcher::finished, + this, &QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery); + } + const QFuture future = QtConcurrent::run(discoverClassicDevicesStatic, hSearch); + classicScanWatcher->setFuture(future); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery() +{ + const DiscoveryResult result = classicScanWatcher->result().value(); if (pendingCancel && !pendingStart) { - closeClassicSearch(&hSearch); - active = false; - pendingCancel = false; - emit q->canceled(); + closeClassicSearch(result.hSearch); + cancelDiscovery(); } else if (pendingStart) { - closeClassicSearch(&hSearch); - pendingStart = pendingCancel = false; - // FIXME: Add required discovery method! - start(QBluetoothDeviceDiscoveryAgent::NoMethod); + closeClassicSearch(result.hSearch); + restartDiscovery(); } else { - if (systemErrorCode == ERROR_NO_MORE_ITEMS) { - closeClassicSearch(&hSearch); - // Enumerate LE devices. - const QVector foundDevices = enumerateLeDevices(&systemErrorCode); - if (systemErrorCode == ERROR_NO_MORE_ITEMS) { - for (const QBluetoothDeviceInfo &foundDevice : foundDevices) - processDiscoveredDevice(foundDevice); - active = false; - emit q->finished(); - } else { - pendingStart = pendingCancel = false; - active = false; - lastError = (systemErrorCode == ERROR_INVALID_HANDLE) ? - QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError - : QBluetoothDeviceDiscoveryAgent::InputOutputError; - errorString = qt_error_string(systemErrorCode); - emit q->error(lastError); - } - } else if (systemErrorCode == NO_ERROR) { - processDiscoveredDevice(scanWatcher->result()); - // Start scan for next classic device. - const QFuture future = QtConcurrent::run( - findNextClassicDevice, &systemErrorCode, hSearch); - scanWatcher->setFuture(future); + 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(&hSearch); - pendingStart = pendingCancel = false; - active = false; - lastError = (systemErrorCode == ERROR_INVALID_HANDLE) ? - QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError - : QBluetoothDeviceDiscoveryAgent::InputOutputError; - errorString = qt_error_string(systemErrorCode); - emit q->error(lastError); + closeClassicSearch(result.hSearch); + const QBluetoothDeviceDiscoveryAgent::Error error = (result.systemErrorCode == ERROR_INVALID_HANDLE) + ? QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError + : QBluetoothDeviceDiscoveryAgent::InputOutputError; + finishDiscovery(error, qt_error_string(result.systemErrorCode)); } } } @@ -480,3 +561,5 @@ void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevice( } QT_END_NAMESPACE + +Q_DECLARE_METATYPE(DiscoveryResult) -- cgit v1.2.3 From e26edb020084554e8b567d3bc15ce638394cf7fc Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 14 Jun 2017 12:27:34 +0300 Subject: Improve processing of discovered device ... where now we use std::find_if algorithm instead of indexed container enumeration. Change-Id: I081dcc92e7b6c1d7a160e62d32ba2cc1182cf43a Reviewed-by: Alex Blasche --- .../qbluetoothdevicediscoveryagent_win.cpp | 60 ++++++++++------------ 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 0e6e1343..9f05ffa1 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -524,40 +524,34 @@ void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevice( { Q_Q(QBluetoothDeviceDiscoveryAgent); - for (int i = 0; i < discoveredDevices.size(); i++) { - QBluetoothDeviceInfo mergedDevice = discoveredDevices[i]; - - if (mergedDevice.address() == foundDevice.address()) { - if (mergedDevice == foundDevice - || mergedDevice.coreConfigurations() == foundDevice.coreConfigurations()) { - qCDebug(QT_BT_WINDOWS) << "Duplicate: " << foundDevice.address(); - return; - } - - // 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 (mergedDevice.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) - mergedDevice = 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. - mergedDevice.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration); - mergedDevice.setCached(foundDevice.isCached()); - - discoveredDevices.replace(i, mergedDevice); - Q_Q(QBluetoothDeviceDiscoveryAgent); - qCDebug(QT_BT_WINDOWS) << "Updated: " << mergedDevice.address(); - - emit q->deviceDiscovered(mergedDevice); - return; - } + 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); } - - qCDebug(QT_BT_WINDOWS) << "Emit: " << foundDevice.address(); - discoveredDevices.append(foundDevice); - emit q->deviceDiscovered(foundDevice); } QT_END_NAMESPACE -- cgit v1.2.3 From d51b2de5848e62ad1493d2cbbe0b2e8d86b42358 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Mon, 23 Oct 2017 12:03:43 +0200 Subject: Make QtBluetooth on Win32 compile (following merge from dev) Change-Id: Idfc9f44cd9ab6064448804ed3a5d9fb81e644281 Reviewed-by: Denis Shienkov --- src/bluetooth/qbluetoothsocket_win.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp index e18c985b..a6ce9684 100644 --- a/src/bluetooth/qbluetoothsocket_win.cpp +++ b/src/bluetooth/qbluetoothsocket_win.cpp @@ -145,4 +145,14 @@ qint64 QBluetoothSocketPrivate::bytesAvailable() const return 0; } +bool QBluetoothSocketPrivate::canReadLine() const +{ + return false; +} + +qint64 QBluetoothSocketPrivate::bytesToWrite() const +{ + return 0; +} + QT_END_NAMESPACE -- cgit v1.2.3 From a18338b552d45b6877e89221a5d6205081a54308 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Mon, 20 Nov 2017 16:27:59 +0100 Subject: Add warning to private header qwinlowenergybluetooth_p.h Without this warning the build would not succeed. Change-Id: Ie43c494d80db2dfffa1f45c8896d996928678df0 Reviewed-by: Denis Shienkov Reviewed-by: Alex Blasche --- src/bluetooth/windows/qwinlowenergybluetooth_p.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h index c12ea4ae..43fffbf8 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth_p.h +++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h @@ -35,6 +35,17 @@ #ifndef QWINLOWENERGYBLUETOOTH_P_H #define QWINLOWENERGYBLUETOOTH_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include #include -- cgit v1.2.3 From e78eb675e89fba8d6a518b7a9f02dd3086f6f52a Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov (VMware)" Date: Mon, 19 Feb 2018 20:17:14 +0200 Subject: Update license information for Windows files Change-Id: Iec42df246c5faec4260bbeb0dbf806477530d30f Reviewed-by: Alex Blasche --- .../qbluetoothdevicediscoveryagent_win.cpp | 34 +++++++++++++--------- src/bluetooth/qbluetoothlocaldevice_win.cpp | 34 +++++++++++++--------- src/bluetooth/qbluetoothserver_win.cpp | 34 +++++++++++++--------- .../qbluetoothservicediscoveryagent_win.cpp | 34 +++++++++++++--------- src/bluetooth/qbluetoothserviceinfo_win.cpp | 34 +++++++++++++--------- src/bluetooth/qbluetoothsocket_win.cpp | 34 +++++++++++++--------- src/bluetooth/qbluetoothutils_win.cpp | 2 +- src/bluetooth/qlowenergycontroller_win.cpp | 34 +++++++++++++--------- src/bluetooth/windows/qwinlowenergybluetooth_p.h | 34 +++++++++++++--------- 9 files changed, 161 insertions(+), 113 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 9f05ffa1..a7755696 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -1,32 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Copyright (C) 2014 Denis Shienkov -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** 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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp index 78b82a97..a3f3c339 100644 --- a/src/bluetooth/qbluetoothlocaldevice_win.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -1,32 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Copyright (C) 2014 Denis Shienkov -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** 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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/bluetooth/qbluetoothserver_win.cpp b/src/bluetooth/qbluetoothserver_win.cpp index 86b520d6..a9f8659e 100644 --- a/src/bluetooth/qbluetoothserver_win.cpp +++ b/src/bluetooth/qbluetoothserver_win.cpp @@ -1,31 +1,37 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** 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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index e8df3ebe..cae7968b 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -1,31 +1,37 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** 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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/bluetooth/qbluetoothserviceinfo_win.cpp b/src/bluetooth/qbluetoothserviceinfo_win.cpp index 8942149a..6629e610 100644 --- a/src/bluetooth/qbluetoothserviceinfo_win.cpp +++ b/src/bluetooth/qbluetoothserviceinfo_win.cpp @@ -1,31 +1,37 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** 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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp index a6ce9684..b2df61b3 100644 --- a/src/bluetooth/qbluetoothsocket_win.cpp +++ b/src/bluetooth/qbluetoothsocket_win.cpp @@ -1,31 +1,37 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** 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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/bluetooth/qbluetoothutils_win.cpp b/src/bluetooth/qbluetoothutils_win.cpp index fa3127cb..a5151d82 100644 --- a/src/bluetooth/qbluetoothutils_win.cpp +++ b/src/bluetooth/qbluetoothutils_win.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 9aeb32ba..fe4e56e9 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -1,32 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Copyright (C) 2014 Denis Shienkov -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** 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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h index 43fffbf8..ea7fc0c8 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth_p.h +++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h @@ -1,32 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Copyright (C) 2014 Denis Shienkov -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** 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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** -- cgit v1.2.3 From bb570997e645413efec1a4426baf997cf36767bd Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Fri, 26 Jan 2018 17:34:25 +0300 Subject: Use WIN32_FROM_HRESULT macro to decode BLE system errors The GetLastError() function always returns the NO_ERROR code when any of the BluetoothGATTXXX() function fails. The reason is that the BluetoothGATTXXX() function uses the COM API, but the GetLastError() function uses the WIN32 API. We need to convert the HRESULT error code to the WIN32 error code directly, using a macro such as WIN32_FROM_HRESULT. Note: At least, it is reproduced with BluetoothGATTSetCharacteristicValue() function on Windows 10. Change-Id: I548e90bb16647c885b06c32397c277255fd362f4 Reviewed-by: Oliver Wolff Reviewed-by: Lubomir I. Ivanov --- src/bluetooth/qlowenergycontroller_win.cpp | 112 +++++++++++++---------- src/bluetooth/windows/qwinlowenergybluetooth_p.h | 4 + 2 files changed, 69 insertions(+), 47 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index fe4e56e9..b05d20e6 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -261,14 +261,17 @@ static QVector enumeratePrimaryGattServices( &servicesCount, BLUETOOTH_GATT_FLAG_NONE); - if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { - foundServices.resize(servicesCount); - } else if (hr == S_OK) { + if (SUCCEEDED(hr)) { *systemErrorCode = NO_ERROR; return foundServices; } else { - *systemErrorCode = ::GetLastError(); - return QVector(); + const DWORD error = WIN32_FROM_HRESULT(hr); + if (error == ERROR_MORE_DATA) { + foundServices.resize(servicesCount); + } else { + *systemErrorCode = error; + return QVector(); + } } } } @@ -292,14 +295,17 @@ static QVector enumerateGattCharacteristics( &characteristicsCount, BLUETOOTH_GATT_FLAG_NONE); - if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { - foundCharacteristics.resize(characteristicsCount); - } else if (hr == S_OK) { + if (SUCCEEDED(hr)) { *systemErrorCode = NO_ERROR; return foundCharacteristics; } else { - *systemErrorCode = ::GetLastError(); - return QVector(); + const DWORD error = WIN32_FROM_HRESULT(hr); + if (error == ERROR_MORE_DATA) { + foundCharacteristics.resize(characteristicsCount); + } else { + *systemErrorCode = error; + return QVector(); + } } } } @@ -323,17 +329,19 @@ static QByteArray getGattCharacteristicValue( &valueBufferSize, BLUETOOTH_GATT_FLAG_NONE); - if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { - valueBuffer.resize(valueBufferSize); - } else if (hr == S_OK) { + if (SUCCEEDED(hr)) { *systemErrorCode = NO_ERROR; const PBTH_LE_GATT_CHARACTERISTIC_VALUE value = reinterpret_cast< PBTH_LE_GATT_CHARACTERISTIC_VALUE>(valueBuffer.data()); - return QByteArray(reinterpret_cast(&value->Data[0]), value->DataSize); } else { - *systemErrorCode = ::GetLastError(); - return QByteArray(); + const DWORD error = WIN32_FROM_HRESULT(hr); + if (error == ERROR_MORE_DATA) { + valueBuffer.resize(valueBufferSize); + } else { + *systemErrorCode = error; + return QByteArray(); + } } } } @@ -355,16 +363,17 @@ static void setGattCharacteristicValue( BTH_LE_GATT_RELIABLE_WRITE_CONTEXT reliableWriteContext = 0; - if (::BluetoothGATTSetCharacteristicValue( + const HRESULT hr = ::BluetoothGATTSetCharacteristicValue( hService, gattCharacteristic, reinterpret_cast(valueBuffer.data()), reliableWriteContext, - flags) != S_OK) { - *systemErrorCode = ::GetLastError(); - } else { + flags); + + if (SUCCEEDED(hr)) *systemErrorCode = NO_ERROR; - } + else + *systemErrorCode = WIN32_FROM_HRESULT(hr); } static QVector enumerateGattDescriptors( @@ -386,14 +395,17 @@ static QVector enumerateGattDescriptors( &descriptorsCount, BLUETOOTH_GATT_FLAG_NONE); - if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { - foundDescriptors.resize(descriptorsCount); - } else if (hr == S_OK) { + if (SUCCEEDED(hr)) { *systemErrorCode = NO_ERROR; return foundDescriptors; } else { - *systemErrorCode = ::GetLastError(); - return QVector(); + const DWORD error = WIN32_FROM_HRESULT(hr); + if (error == ERROR_MORE_DATA) { + foundDescriptors.resize(descriptorsCount); + } else { + *systemErrorCode = error; + return QVector(); + } } } } @@ -417,17 +429,20 @@ static QByteArray getGattDescriptorValue( &valueBufferSize, BLUETOOTH_GATT_FLAG_NONE); - if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { - valueBuffer.resize(valueBufferSize); - } else if (hr == S_OK) { + if (SUCCEEDED(hr)) { *systemErrorCode = NO_ERROR; const PBTH_LE_GATT_DESCRIPTOR_VALUE value = reinterpret_cast< PBTH_LE_GATT_DESCRIPTOR_VALUE>(valueBuffer.data()); return QByteArray(reinterpret_cast(&value->Data[0]), value->DataSize); } else { - *systemErrorCode = ::GetLastError(); - return QByteArray(); + const DWORD error = WIN32_FROM_HRESULT(hr); + if (error == ERROR_MORE_DATA) { + valueBuffer.resize(valueBufferSize); + } else { + *systemErrorCode = error; + return QByteArray(); + } } } } @@ -466,16 +481,16 @@ static void setGattDescriptorValue( gattValue->DataSize = ULONG(value.size()); ::memcpy(gattValue->Data, value.constData(), value.size()); - if (::BluetoothGATTSetDescriptorValue( + const HRESULT hr = ::BluetoothGATTSetDescriptorValue( hService, gattDescriptor, gattValue, - BLUETOOTH_GATT_FLAG_NONE) != S_OK) { - *systemErrorCode = ::GetLastError(); - return; - } + BLUETOOTH_GATT_FLAG_NONE); - *systemErrorCode = NO_ERROR; + if (SUCCEEDED(hr)) + *systemErrorCode = NO_ERROR; + else + *systemErrorCode = WIN32_FROM_HRESULT(hr); } static void WINAPI eventChangedCallbackEntry( @@ -510,18 +525,20 @@ static HANDLE registerEvent( ::ZeroMemory(®istration, sizeof(registration)); registration.NumCharacteristics = 1; registration.Characteristics[0] = gattCharacteristic; - if (::BluetoothGATTRegisterEvent( + + const HRESULT hr = ::BluetoothGATTRegisterEvent( hService, CharacteristicValueChangedEvent, ®istration, eventChangedCallbackEntry, context, &hEvent, - BLUETOOTH_GATT_FLAG_NONE) != S_OK) { - *systemErrorCode = ::GetLastError(); - } else { + BLUETOOTH_GATT_FLAG_NONE); + + if (SUCCEEDED(hr)) *systemErrorCode = NO_ERROR; - } + else + *systemErrorCode = WIN32_FROM_HRESULT(hr); return hEvent; } @@ -533,13 +550,14 @@ static void unregisterEvent(HANDLE hEvent, int *systemErrorCode) return; } - if (::BluetoothGATTUnregisterEvent( + const HRESULT hr = ::BluetoothGATTUnregisterEvent( hEvent, - BLUETOOTH_GATT_FLAG_NONE) != S_OK) { - *systemErrorCode = ::GetLastError(); - } else { + BLUETOOTH_GATT_FLAG_NONE); + + if (SUCCEEDED(hr)) *systemErrorCode = NO_ERROR; - } + else + *systemErrorCode = WIN32_FROM_HRESULT(hr); } static QBluetoothUuid qtBluetoothUuidFromNativeLeUuid(const BTH_LE_UUID &uuid) diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h index ea7fc0c8..39b88a5f 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth_p.h +++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h @@ -56,6 +56,10 @@ #include +#define WIN32_FROM_HRESULT(hr) \ + (SUCCEEDED(hr) ? ERROR_SUCCESS : \ + (HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : (hr))) + #define BLUETOOTH_GATT_FLAG_NONE 0x00000000 #define BLUETOOTH_GATT_FLAG_CONNECTION_ENCRYPTED 0x00000001 #define BLUETOOTH_GATT_FLAG_CONNECTION_AUTHENTICATED 0x00000002 -- cgit v1.2.3 From e63f53aefdecc7ae54986fedb459cb7b40859abd Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Mon, 29 Jan 2018 13:49:51 +0300 Subject: Reduce quantity of "reinterpret_cast" calls ... that simplified a code a bit. Change-Id: Ice7d082f005bfc36e86909784fb6fe38c6f6811f Reviewed-by: Lubomir I. Ivanov Reviewed-by: Oliver Wolff --- src/bluetooth/qlowenergycontroller_win.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index b05d20e6..9f10a167 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -321,19 +321,22 @@ static QByteArray getGattCharacteristicValue( QByteArray valueBuffer; USHORT valueBufferSize = 0; for (;;) { + const auto valuePtr = valueBuffer.isEmpty() + ? NULL + : reinterpret_cast(valueBuffer.data()); + const HRESULT hr = ::BluetoothGATTGetCharacteristicValue( hService, gattCharacteristic, valueBufferSize, - valueBuffer.isEmpty() ? NULL : reinterpret_cast(valueBuffer.data()), + valuePtr, &valueBufferSize, BLUETOOTH_GATT_FLAG_NONE); if (SUCCEEDED(hr)) { *systemErrorCode = NO_ERROR; - const PBTH_LE_GATT_CHARACTERISTIC_VALUE value = reinterpret_cast< - PBTH_LE_GATT_CHARACTERISTIC_VALUE>(valueBuffer.data()); - return QByteArray(reinterpret_cast(&value->Data[0]), value->DataSize); + return QByteArray(reinterpret_cast(&valuePtr->Data[0]), + valuePtr->DataSize); } else { const DWORD error = WIN32_FROM_HRESULT(hr); if (error == ERROR_MORE_DATA) { @@ -421,20 +424,22 @@ static QByteArray getGattDescriptorValue( QByteArray valueBuffer; USHORT valueBufferSize = 0; for (;;) { + const auto valuePtr = valueBuffer.isEmpty() + ? NULL + : reinterpret_cast(valueBuffer.data()); + const HRESULT hr = ::BluetoothGATTGetDescriptorValue( hService, gattDescriptor, valueBufferSize, - valueBuffer.isEmpty() ? NULL : reinterpret_cast(valueBuffer.data()), + valuePtr, &valueBufferSize, BLUETOOTH_GATT_FLAG_NONE); if (SUCCEEDED(hr)) { *systemErrorCode = NO_ERROR; - const PBTH_LE_GATT_DESCRIPTOR_VALUE value = reinterpret_cast< - PBTH_LE_GATT_DESCRIPTOR_VALUE>(valueBuffer.data()); - - return QByteArray(reinterpret_cast(&value->Data[0]), value->DataSize); + return QByteArray(reinterpret_cast(&valuePtr->Data[0]), + valuePtr->DataSize); } else { const DWORD error = WIN32_FROM_HRESULT(hr); if (error == ERROR_MORE_DATA) { -- cgit v1.2.3 From 6b3cfe2f836556c2d92b6c7915232fa00fd040f6 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov (VMware)" Date: Tue, 27 Feb 2018 21:24:33 +0200 Subject: qlecontroller_win: fix GetLastError() bug in getServiceSystemPath() Fix strange bug in getServiceSystemPath() where after the first call to SetupDiGetDeviceInterfaceDetail(), GetLastError() returns 0 instead of ERROR_INSUFFICIENT_BUFFER (122). A fix is to use a `const DWORD` for the error immediately after the SetupDiGetDeviceInterfaceDetail() call. Possibly a compiler issue. Target toolchain is: gcc version 5.3.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project) Change-Id: Id197201e9e161f7240c339231990196f58cb87eb Reviewed-by: Denis Shienkov --- src/bluetooth/qlowenergycontroller_win.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 9f10a167..56382e69 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -143,8 +143,9 @@ static QString getServiceSystemPath(const QBluetoothAddress &deviceAddress, deviceInterfaceDetailDataSize, &deviceInterfaceDetailDataSize, NULL)) { - if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - *systemErrorCode = ::GetLastError(); + const DWORD error = ::GetLastError(); + if (error != ERROR_INSUFFICIENT_BUFFER) { + *systemErrorCode = error; break; } } -- cgit v1.2.3 From ec54c731ad96df1b024fa4467a1f000672e5b7a6 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov (VMware)" Date: Tue, 27 Feb 2018 21:23:05 +0200 Subject: qlecontroller_win: use lastIndexOf() in getDeviceAddress() getDeviceAddress() does not work for all path cases, thus instead of using: firstbound = servicePath.indexOf(...) use: firstbound = servicePath.lastIndexOf(...) Example path that breaks the current code: "\\\\?\\bthledevice#{00001800-0000-1000-8000-00805f9b34fb}_dev_vid&01008f_pid&b00d_rev&0100_00802534ce0e#8&5481851&5&0001#{00001800-0000-1000-8000-00805f9b34fb}" The current code works with paths of this format: "\\\\?\\BTHLEDevice#{00001800-0000-1000-8000-00805f9b34fb}_7c669d8a6d7a#8&35290589&1&0001#{00001800-0000-1000-8000-00805f9b34fb}" Notice that there are more than one "_" in the path that breaks. Change-Id: I5f081b78d8f5bc5693ba05c8e73d4dcf0b42b6b2 Reviewed-by: Oliver Wolff --- src/bluetooth/qlowenergycontroller_win.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 56382e69..f7a1611f 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -96,7 +96,7 @@ static bool gattFunctionsResolved = false; static QBluetoothAddress getDeviceAddress(const QString &servicePath) { - const int firstbound = servicePath.indexOf(QStringLiteral("_")); + const int firstbound = servicePath.lastIndexOf(QStringLiteral("_")); const int lastbound = servicePath.indexOf(QLatin1Char('#'), firstbound); const QString hex = servicePath.mid(firstbound + 1, lastbound - firstbound - 1); bool ok = false; -- cgit v1.2.3 From 43eebc049aef3b858dd1d38ebe4bf9b395891722 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov (VMware)" Date: Tue, 28 Nov 2017 17:26:09 +0100 Subject: Add a thread for QLowEnergyControllerPrivateWin32 Create a re-usable thread ('thread') and worker ('treadWorker') in QLowEnergyControllerPrivateWin32 that have the same lifespan as the controller object. Add "job" support (ThreadWorkerJob) for the thread so that jobs are scheduled and executed sequentially. Each job should have a data struct. For writing that is WriteCharData. Handle writing of characteristics in: QLowEnergyControllerPrivateWin32::writeCharacteristic() QLowEnergyControllerPrivateWin32::jobFinished() ThreadWorker::runPendingJob() The above jobFinished() and runPendingJob() use a `switch` to determine the ThreadWorkerJob type. Change-Id: I3331e0d4adc29565a88fd792f9a54833881ea694 Reviewed-by: Denis Shienkov Reviewed-by: Oliver Wolff --- src/bluetooth/qlowenergycontroller_win.cpp | 126 ++++++++++++++++++++++------- src/bluetooth/qlowenergycontroller_win_p.h | 43 ++++++++++ 2 files changed, 140 insertions(+), 29 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index f7a1611f..27bfb7c7 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -45,11 +45,10 @@ #include // for open modes #include #include +#include #include // for std::max -#include - #include QT_BEGIN_NAMESPACE @@ -690,12 +689,22 @@ QLowEnergyControllerPrivateWin32::QLowEnergyControllerPrivateWin32() qCWarning(QT_BT_WINDOWS) << "LE is not supported on this OS"; return; } + + thread = new QThread; + threadWorker = new ThreadWorker; + threadWorker->moveToThread(thread); + connect(threadWorker, &ThreadWorker::jobFinished, this, &QLowEnergyControllerPrivateWin32::jobFinished); + connect(thread, &QThread::finished, threadWorker, &ThreadWorker::deleteLater); + connect(thread, &QThread::finished, thread, &QThread::deleteLater); + thread->start(); } QLowEnergyControllerPrivateWin32::~QLowEnergyControllerPrivateWin32() { QMutexLocker locker(&controllersGuard); qControllers()->removeAll(this); + if (thread) + thread->quit(); } void QLowEnergyControllerPrivateWin32::init() @@ -1020,17 +1029,20 @@ void QLowEnergyControllerPrivateWin32::writeCharacteristic( QLowEnergyService::WriteMode mode) { Q_ASSERT(!service.isNull()); - if (!service->characteristicList.contains(charHandle)) - return; - int systemErrorCode = NO_ERROR; + if (!service->characteristicList.contains(charHandle)) { + service->setError(QLowEnergyService::CharacteristicWriteError); + return; + } - const HANDLE hService = openSystemService( - remoteDevice, service->uuid, QIODevice::ReadWrite, &systemErrorCode); + WriteCharData data; + data.systemErrorCode = NO_ERROR; + data.hService = openSystemService( + remoteDevice, service->uuid, QIODevice::ReadWrite, &data.systemErrorCode); - if (systemErrorCode != NO_ERROR) { + if (data.systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() - << ":" << qt_error_string(systemErrorCode); + << ":" << qt_error_string(data.systemErrorCode); service->setError(QLowEnergyService::CharacteristicWriteError); return; } @@ -1038,35 +1050,58 @@ void QLowEnergyControllerPrivateWin32::writeCharacteristic( const QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle]; - BTH_LE_GATT_CHARACTERISTIC gattCharacteristic = recoverNativeLeGattCharacteristic( + data.gattCharacteristic = recoverNativeLeGattCharacteristic( service->startHandle, charHandle, charDetails); - const DWORD flags = (mode == QLowEnergyService::WriteWithResponse) + data.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); + ThreadWorkerJob job; + job.operation = ThreadWorkerJob::WriteChar; + data.newValue = newValue; + data.mode = mode; + job.data = QVariant::fromValue(data); - 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; - } + QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection, + Q_ARG(ThreadWorkerJob, job)); +} - updateValueOfCharacteristic(charHandle, newValue, false); +void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) +{ + switch (job.operation) { + case ThreadWorkerJob::WriteChar: + { + const WriteCharData data = job.data.value(); + closeSystemDevice(data.hService); + const QLowEnergyHandle charHandle = static_cast(data.gattCharacteristic.AttributeHandle); + const QSharedPointer service = serviceForHandle(charHandle); + + if (data.systemErrorCode != NO_ERROR) { + const QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle]; + qCWarning(QT_BT_WINDOWS) << "Unable to set value for characteristic" + << charDetails.uuid.toString() + << "of the service" << service->uuid.toString() + << ":" << qt_error_string(data.systemErrorCode); + service->setError(QLowEnergyService::CharacteristicWriteError); + return; + } + + updateValueOfCharacteristic(charHandle, data.newValue, false); - if (mode == QLowEnergyService::WriteWithResponse) { - const QLowEnergyCharacteristic ch(service, charHandle); - emit service->characteristicWritten(ch, newValue); + if (data.mode == QLowEnergyService::WriteWithResponse) { + const QLowEnergyCharacteristic ch = characteristicForHandle(charHandle); + emit service->characteristicWritten(ch, data.newValue); + } + } + break; + case ThreadWorkerJob::ReadChar: + case ThreadWorkerJob::WriteDescr: + case ThreadWorkerJob::ReadDescr: + break; } + + QMetaObject::invokeMethod(threadWorker, "runPendingJob", Qt::QueuedConnection); } void QLowEnergyControllerPrivateWin32::readDescriptor( @@ -1219,4 +1254,37 @@ void QLowEnergyControllerPrivateWin32::addToGenericAttributeList(const QLowEnerg Q_UNIMPLEMENTED(); } +void ThreadWorker::putJob(const ThreadWorkerJob &job) +{ + m_jobs.append(job); + if (m_jobs.count() == 1) + runPendingJob(); +} + +void ThreadWorker::runPendingJob() +{ + if (!m_jobs.count()) + return; + + ThreadWorkerJob job = m_jobs.first(); + + switch (job.operation) { + case ThreadWorkerJob::WriteChar: + { + WriteCharData data = job.data.value(); + setGattCharacteristicValue(data.hService, &data.gattCharacteristic, + data.newValue, data.flags, &data.systemErrorCode); + job.data = QVariant::fromValue(data); + } + break; + case ThreadWorkerJob::ReadChar: + case ThreadWorkerJob::WriteDescr: + case ThreadWorkerJob::ReadDescr: + break; + } + + m_jobs.removeFirst(); + emit jobFinished(job); +} + QT_END_NAMESPACE diff --git a/src/bluetooth/qlowenergycontroller_win_p.h b/src/bluetooth/qlowenergycontroller_win_p.h index 9c2d4f92..64f824e6 100644 --- a/src/bluetooth/qlowenergycontroller_win_p.h +++ b/src/bluetooth/qlowenergycontroller_win_p.h @@ -59,8 +59,47 @@ #include "qlowenergycontroller.h" #include "qlowenergycontrollerbase_p.h" +#include + QT_BEGIN_NAMESPACE +class QThread; +class QLowEnergyControllerPrivateWin32; + +class ThreadWorkerJob +{ +public: + enum Operation { WriteChar, ReadChar, WriteDescr, ReadDescr }; + Operation operation; + QVariant data; +}; + +Q_DECLARE_METATYPE(ThreadWorkerJob) + +struct WriteCharData +{ + QByteArray newValue; + QLowEnergyService::WriteMode mode; + HANDLE hService; + DWORD flags; + BTH_LE_GATT_CHARACTERISTIC gattCharacteristic; + int systemErrorCode; +}; + +Q_DECLARE_METATYPE(WriteCharData) + +class ThreadWorker : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE void putJob(const ThreadWorkerJob &job); + Q_INVOKABLE void runPendingJob(); +signals: + void jobFinished(const ThreadWorkerJob &job); +private: + QVector m_jobs; +}; + class QLowEnergyServiceData; extern void registerQLowEnergyControllerMetaType(); @@ -105,9 +144,13 @@ public: void addToGenericAttributeList(const QLowEnergyServiceData &service, QLowEnergyHandle startHandle) override; +public slots: + void jobFinished(const ThreadWorkerJob &job); protected: void customEvent(QEvent *e); private: + QThread *thread = nullptr; + ThreadWorker *threadWorker = nullptr; QString deviceSystemPath; }; -- cgit v1.2.3 From 785462c02a4c793be0801232f5bd0c97351d950e Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov (VMware)" Date: Tue, 6 Mar 2018 16:20:22 +0200 Subject: qlecontroller_win: read characteristics in a separate thread Add support for the ThreadWorkerJob type ReadChar in QLowEnergyControllerPrivateWin32. This type of job is responsible for reading GATT characteristics in a separate thread using the previously implemented ThreadWorkerJob scheme: - ThreadWorker::runPendingJob() - QLowEnergyControllerPrivateWin32::jobFinished() The blocking function in this case is getGattCharacteristicValue(). Change-Id: Idc87267a44aab51febae4addaca4b61d7e582a8a Reviewed-by: Denis Shienkov --- src/bluetooth/qlowenergycontroller_win.cpp | 62 +++++++++++++++++++----------- src/bluetooth/qlowenergycontroller_win_p.h | 10 +++++ 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 27bfb7c7..627ce545 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -988,38 +988,27 @@ void QLowEnergyControllerPrivateWin32::readCharacteristic( qCWarning(QT_BT_WINDOWS) << "Reading non-readable char" << charHandle; } - int systemErrorCode = NO_ERROR; - - const HANDLE hService = openSystemService( - remoteDevice, service->uuid, QIODevice::ReadOnly, &systemErrorCode); + ReadCharData data; + data.systemErrorCode = NO_ERROR; + data.hService = openSystemService( + remoteDevice, service->uuid, QIODevice::ReadOnly, &data.systemErrorCode); - if (systemErrorCode != NO_ERROR) { + if (data.systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() - << ":" << qt_error_string(systemErrorCode); + << ":" << qt_error_string(data.systemErrorCode); service->setError(QLowEnergyService::CharacteristicReadError); return; } - BTH_LE_GATT_CHARACTERISTIC gattCharacteristic = recoverNativeLeGattCharacteristic( + data.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); + ThreadWorkerJob job; + job.operation = ThreadWorkerJob::ReadChar; + job.data = QVariant::fromValue(data); - const QLowEnergyCharacteristic ch(service, charHandle); - emit service->characteristicRead(ch, characteristicValue); + QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection, + Q_ARG(ThreadWorkerJob, job)); } void QLowEnergyControllerPrivateWin32::writeCharacteristic( @@ -1096,6 +1085,27 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) } break; case ThreadWorkerJob::ReadChar: + { + const ReadCharData data = job.data.value(); + closeSystemDevice(data.hService); + const QLowEnergyHandle charHandle = static_cast(data.gattCharacteristic.AttributeHandle); + const QSharedPointer service = serviceForHandle(charHandle); + + if (data.systemErrorCode != NO_ERROR) { + const QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle]; + qCWarning(QT_BT_WINDOWS) << "Unable to get value for characteristic" + << charDetails.uuid.toString() + << "of the service" << service->uuid.toString() + << ":" << qt_error_string(data.systemErrorCode); + service->setError(QLowEnergyService::CharacteristicReadError); + return; + } + + updateValueOfCharacteristic(charHandle, data.value, false); + + const QLowEnergyCharacteristic ch(service, charHandle); + emit service->characteristicRead(ch, data.value); + } case ThreadWorkerJob::WriteDescr: case ThreadWorkerJob::ReadDescr: break; @@ -1278,6 +1288,12 @@ void ThreadWorker::runPendingJob() } break; case ThreadWorkerJob::ReadChar: + { + ReadCharData data = job.data.value(); + data.value = getGattCharacteristicValue( + data.hService, &data.gattCharacteristic, &data.systemErrorCode); + job.data = QVariant::fromValue(data); + } case ThreadWorkerJob::WriteDescr: case ThreadWorkerJob::ReadDescr: break; diff --git a/src/bluetooth/qlowenergycontroller_win_p.h b/src/bluetooth/qlowenergycontroller_win_p.h index 64f824e6..fc20a349 100644 --- a/src/bluetooth/qlowenergycontroller_win_p.h +++ b/src/bluetooth/qlowenergycontroller_win_p.h @@ -88,6 +88,16 @@ struct WriteCharData Q_DECLARE_METATYPE(WriteCharData) +struct ReadCharData +{ + QByteArray value; + HANDLE hService; + BTH_LE_GATT_CHARACTERISTIC gattCharacteristic; + int systemErrorCode; +}; + +Q_DECLARE_METATYPE(ReadCharData) + class ThreadWorker : public QObject { Q_OBJECT -- cgit v1.2.3 From d75664c75d82accc1cbca0032750dd7d28d1777c Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov (VMware)" Date: Mon, 12 Mar 2018 19:57:19 +0200 Subject: qlecontroller_win: write descriptors in a separate thread Add support for the ThreadWorkerJob type WriteDesc in QLowEnergyControllerPrivateWin32. This type of job is responsible for writing GATT descriptors in a separate thread using the previously implemented ThreadWorkerJob scheme: - ThreadWorker::runPendingJob() - QLowEnergyControllerPrivateWin32::jobFinished() The blocking function in this case is setGattDescriptorValue(). Change-Id: Ib221862d50cdbe5af951d4ad82850bea4f9a6645 Reviewed-by: Denis Shienkov --- src/bluetooth/qlowenergycontroller_win.cpp | 145 +++++++++++++++++------------ src/bluetooth/qlowenergycontroller_win_p.h | 10 ++ 2 files changed, 93 insertions(+), 62 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 627ce545..bb968194 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -1107,6 +1107,69 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) emit service->characteristicRead(ch, data.value); } case ThreadWorkerJob::WriteDescr: + { + WriteDescData data = job.data.value(); + const QLowEnergyHandle descriptorHandle = static_cast(data.gattDescriptor.AttributeHandle); + const QLowEnergyHandle charHandle = static_cast(data.gattDescriptor.CharacteristicHandle); + const QSharedPointer service = serviceForHandle(charHandle); + QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle]; + const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle]; + + if (data.systemErrorCode != NO_ERROR) { + closeSystemDevice(data.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(data.systemErrorCode); + service->setError(QLowEnergyService::DescriptorWriteError); + return; + } + + if (data.gattDescriptor.DescriptorType == ClientCharacteristicConfiguration) { + + QDataStream in(data.newValue); + quint8 u; + in >> u; + + if (u & ClientCharacteristicConfigurationValue::UseNotifications + || u & ClientCharacteristicConfigurationValue::UseIndications) { + if (!charDetails.hValueChangeEvent) { + BTH_LE_GATT_CHARACTERISTIC gattCharacteristic = recoverNativeLeGattCharacteristic( + service->startHandle, charHandle, charDetails); + + charDetails.hValueChangeEvent = registerEvent( + data.hService, gattCharacteristic, this, &data.systemErrorCode); + } + } else { + if (charDetails.hValueChangeEvent) { + unregisterEvent(charDetails.hValueChangeEvent, &data.systemErrorCode); + charDetails.hValueChangeEvent = NULL; + } + } + + closeSystemDevice(data.hService); + + if (data.systemErrorCode != NO_ERROR) { + qCWarning(QT_BT_WINDOWS) << "Unable to subscribe events for descriptor" + << dscrDetails.uuid.toString() + << "for characteristic" + << charDetails.uuid.toString() + << "of the service" << service->uuid.toString() + << ":" << qt_error_string(data.systemErrorCode); + service->setError(QLowEnergyService::DescriptorWriteError); + return; + } + } else { + closeSystemDevice(data.hService); + } + + updateValueOfDescriptor(charHandle, descriptorHandle, data.newValue, false); + + const QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle); + emit service->descriptorWritten(dscr, data.newValue); + } case ThreadWorkerJob::ReadDescr: break; } @@ -1183,14 +1246,15 @@ void QLowEnergyControllerPrivateWin32::writeDescriptor( if (!charDetails.descriptorList.contains(descriptorHandle)) return; - int systemErrorCode = NO_ERROR; - - const HANDLE hService = openSystemService( - remoteDevice, service->uuid, QIODevice::ReadWrite, &systemErrorCode); + WriteDescData data; + data.systemErrorCode = NO_ERROR; + data.newValue = newValue; + data.hService = openSystemService( + remoteDevice, service->uuid, QIODevice::ReadWrite, &data.systemErrorCode); - if (systemErrorCode != NO_ERROR) { + if (data.systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() - << ":" << qt_error_string(systemErrorCode); + << ":" << qt_error_string(data.systemErrorCode); service->setError(QLowEnergyService::DescriptorWriteError); return; } @@ -1198,65 +1262,15 @@ void QLowEnergyControllerPrivateWin32::writeDescriptor( const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle]; - BTH_LE_GATT_DESCRIPTOR gattDescriptor = recoverNativeLeGattDescriptor( + data.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); + ThreadWorkerJob job; + job.operation = ThreadWorkerJob::WriteDescr; + job.data = QVariant::fromValue(data); - const QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle); - emit service->descriptorWritten(dscr, newValue); + QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection, + Q_ARG(ThreadWorkerJob, job)); } void QLowEnergyControllerPrivateWin32::addToGenericAttributeList(const QLowEnergyServiceData &, QLowEnergyHandle) @@ -1294,7 +1308,14 @@ void ThreadWorker::runPendingJob() data.hService, &data.gattCharacteristic, &data.systemErrorCode); job.data = QVariant::fromValue(data); } + break; case ThreadWorkerJob::WriteDescr: + { + WriteDescData data = job.data.value(); + setGattDescriptorValue(data.hService, &data.gattDescriptor, + data.newValue, &data.systemErrorCode); + job.data = QVariant::fromValue(data); + } case ThreadWorkerJob::ReadDescr: break; } diff --git a/src/bluetooth/qlowenergycontroller_win_p.h b/src/bluetooth/qlowenergycontroller_win_p.h index fc20a349..8f2f78e5 100644 --- a/src/bluetooth/qlowenergycontroller_win_p.h +++ b/src/bluetooth/qlowenergycontroller_win_p.h @@ -98,6 +98,16 @@ struct ReadCharData Q_DECLARE_METATYPE(ReadCharData) +struct WriteDescData +{ + QByteArray newValue; + HANDLE hService; + BTH_LE_GATT_DESCRIPTOR gattDescriptor; + int systemErrorCode; +}; + +Q_DECLARE_METATYPE(WriteDescData) + class ThreadWorker : public QObject { Q_OBJECT -- cgit v1.2.3 From 2648bb9395e9bf5df0f35d9ecac54797d84ef71a Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov (VMware)" Date: Mon, 19 Mar 2018 19:05:16 +0200 Subject: qlecontroller_win: read descriptors in a separate thread Add support for the ThreadWorkerJob type ReadDesc in QLowEnergyControllerPrivateWin32. This type of job is responsible for reading GATT descriptors in a separate thread using the previously implemented ThreadWorkerJob scheme: - ThreadWorker::runPendingJob() - QLowEnergyControllerPrivateWin32::jobFinished() The blocking function in this case is getGattDescriptorValue(). Change-Id: I19298323f8ebc630b4dc34ae2e71c7e3c603beae Reviewed-by: Denis Shienkov --- src/bluetooth/qlowenergycontroller_win.cpp | 74 +++++++++++++++++++----------- src/bluetooth/qlowenergycontroller_win_p.h | 10 ++++ 2 files changed, 58 insertions(+), 26 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index bb968194..9b2ab6a2 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -1106,6 +1106,7 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) const QLowEnergyCharacteristic ch(service, charHandle); emit service->characteristicRead(ch, data.value); } + break; case ThreadWorkerJob::WriteDescr: { WriteDescData data = job.data.value(); @@ -1170,7 +1171,33 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) const QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle); emit service->descriptorWritten(dscr, data.newValue); } + break; case ThreadWorkerJob::ReadDescr: + { + ReadDescData data = job.data.value(); + const QLowEnergyHandle descriptorHandle = static_cast(data.gattDescriptor.AttributeHandle); + const QLowEnergyHandle charHandle = static_cast(data.gattDescriptor.CharacteristicHandle); + const QSharedPointer service = serviceForHandle(charHandle); + QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle]; + const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle]; + closeSystemDevice(data.hService); + + if (data.systemErrorCode != NO_ERROR) { + qCWarning(QT_BT_WINDOWS) << "Unable to get value for descriptor" + << dscrDetails.uuid.toString() + << "for characteristic" + << charDetails.uuid.toString() + << "of the service" << service->uuid.toString() + << ":" << qt_error_string(data.systemErrorCode); + service->setError(QLowEnergyService::DescriptorReadError); + return; + } + + updateValueOfDescriptor(charHandle, descriptorHandle, data.value, false); + + QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle); + emit service->descriptorRead(dscr, data.value); + } break; } @@ -1191,14 +1218,14 @@ void QLowEnergyControllerPrivateWin32::readDescriptor( if (!charDetails.descriptorList.contains(descriptorHandle)) return; - int systemErrorCode = NO_ERROR; - - const HANDLE hService = openSystemService( - remoteDevice, service->uuid, QIODevice::ReadOnly, &systemErrorCode); + ReadDescData data; + data.systemErrorCode = NO_ERROR; + data.hService = openSystemService( + remoteDevice, service->uuid, QIODevice::ReadOnly, &data.systemErrorCode); - if (systemErrorCode != NO_ERROR) { + if (data.systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() - << ":" << qt_error_string(systemErrorCode); + << ":" << qt_error_string(data.systemErrorCode); service->setError(QLowEnergyService::DescriptorReadError); return; } @@ -1206,29 +1233,15 @@ void QLowEnergyControllerPrivateWin32::readDescriptor( const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle]; - BTH_LE_GATT_DESCRIPTOR gattDescriptor = recoverNativeLeGattDescriptor( + data.gattDescriptor = recoverNativeLeGattDescriptor( service->startHandle, charHandle, descriptorHandle, dscrDetails); - const QByteArray value = getGattDescriptorValue( - hService, const_cast( - &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); + ThreadWorkerJob job; + job.operation = ThreadWorkerJob::ReadDescr; + job.data = QVariant::fromValue(data); - QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle); - emit service->descriptorRead(dscr, value); + QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection, + Q_ARG(ThreadWorkerJob, job)); } void QLowEnergyControllerPrivateWin32::writeDescriptor( @@ -1316,7 +1329,16 @@ void ThreadWorker::runPendingJob() data.newValue, &data.systemErrorCode); job.data = QVariant::fromValue(data); } + break; case ThreadWorkerJob::ReadDescr: + { + ReadDescData data = job.data.value(); + data.value = getGattDescriptorValue( + data.hService, + const_cast(&data.gattDescriptor), + &data.systemErrorCode); + job.data = QVariant::fromValue(data); + } break; } diff --git a/src/bluetooth/qlowenergycontroller_win_p.h b/src/bluetooth/qlowenergycontroller_win_p.h index 8f2f78e5..829e190c 100644 --- a/src/bluetooth/qlowenergycontroller_win_p.h +++ b/src/bluetooth/qlowenergycontroller_win_p.h @@ -108,6 +108,16 @@ struct WriteDescData Q_DECLARE_METATYPE(WriteDescData) +struct ReadDescData +{ + QByteArray value; + HANDLE hService; + BTH_LE_GATT_DESCRIPTOR gattDescriptor; + int systemErrorCode; +}; + +Q_DECLARE_METATYPE(ReadDescData) + class ThreadWorker : public QObject { Q_OBJECT -- cgit v1.2.3 From 4ca1d6a02cbcc5347205b231108b6bc2b351a4c7 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Wed, 4 Apr 2018 10:18:15 +0200 Subject: Fix namespaced build Change-Id: If20574e7bc182e5ee3d5adf6cc6d63cf9ee9d0ef Reviewed-by: Lubomir I. Ivanov Reviewed-by: Denis Shienkov Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_win_p.h | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win_p.h b/src/bluetooth/qlowenergycontroller_win_p.h index 829e190c..f8e3b10a 100644 --- a/src/bluetooth/qlowenergycontroller_win_p.h +++ b/src/bluetooth/qlowenergycontroller_win_p.h @@ -74,8 +74,6 @@ public: QVariant data; }; -Q_DECLARE_METATYPE(ThreadWorkerJob) - struct WriteCharData { QByteArray newValue; @@ -86,8 +84,6 @@ struct WriteCharData int systemErrorCode; }; -Q_DECLARE_METATYPE(WriteCharData) - struct ReadCharData { QByteArray value; @@ -96,8 +92,6 @@ struct ReadCharData int systemErrorCode; }; -Q_DECLARE_METATYPE(ReadCharData) - struct WriteDescData { QByteArray newValue; @@ -106,8 +100,6 @@ struct WriteDescData int systemErrorCode; }; -Q_DECLARE_METATYPE(WriteDescData) - struct ReadDescData { QByteArray value; @@ -116,8 +108,6 @@ struct ReadDescData int systemErrorCode; }; -Q_DECLARE_METATYPE(ReadDescData) - class ThreadWorker : public QObject { Q_OBJECT @@ -186,4 +176,10 @@ private: QT_END_NAMESPACE +Q_DECLARE_METATYPE(ThreadWorkerJob) +Q_DECLARE_METATYPE(WriteCharData) +Q_DECLARE_METATYPE(ReadCharData) +Q_DECLARE_METATYPE(WriteDescData) +Q_DECLARE_METATYPE(ReadDescData) + #endif // QLOWENERGYCONTROLLERPRIVATE_WIN32__P_H -- cgit v1.2.3 From 515c20f5f20dfb4e665e635a30485d7931ff12df Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov (VMware)" Date: Mon, 21 May 2018 17:16:18 +0200 Subject: win32: remove usage of QFuture in qbluetoothdevicediscoveryagent Introduce a couple of QThread instances and workers for the Bluetooth Classic and BLE device discovery. Replaces the usage of QFuture for this file. Remove includes of QtConcurrent. This introduced some errors which are solved by including: - QLoggingCategory in qbluetoothdevicediscoveryagent_win.cpp - QDataStream and QCoreApplication in qlowenergycontroller_win.cpp Change-Id: Iba2cbc147c714ae87515294d50cb4e502edd00a7 Reviewed-by: Denis Shienkov Reviewed-by: Oliver Wolff --- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 33 ++++++++++-- .../qbluetoothdevicediscoveryagent_win.cpp | 63 ++++++++++++++-------- src/bluetooth/qlowenergycontroller_win.cpp | 2 + 3 files changed, 72 insertions(+), 26 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index ee3531cb..489ed807 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -80,7 +80,28 @@ QT_END_NAMESPACE #endif #ifdef QT_WIN_BLUETOOTH -#include +QT_BEGIN_NAMESPACE +class QThread; + +class ThreadWorkerLE : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE void discover(); +signals: + void discoveryCompleted(const QVariant res); +}; + +class ThreadWorkerClassic : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE void discover(QVariant hSearch); +signals: + void discoveryCompleted(const QVariant res); +}; +QT_END_NAMESPACE + #elif defined(QT_WINRT_BLUETOOTH) #include #include @@ -177,9 +198,9 @@ private: void finishDiscovery(QBluetoothDeviceDiscoveryAgent::Error errorCode, const QString &errorText); void startLeDevicesDiscovery(); - void completeLeDevicesDiscovery(); + void completeLeDevicesDiscovery(const QVariant res); void startClassicDevicesDiscovery(Qt::HANDLE hSearch = nullptr); - void completeClassicDevicesDiscovery(); + void completeClassicDevicesDiscovery(const QVariant res); void processDiscoveredDevice(const QBluetoothDeviceInfo &foundDevice); @@ -188,8 +209,10 @@ private: bool pendingStart; bool active; - QFutureWatcher *classicScanWatcher; - QFutureWatcher *lowenergyScanWatcher; + QThread *threadLE = nullptr; + QThread *threadClassic = nullptr; + ThreadWorkerLE *threadWorkerLE = nullptr; + ThreadWorkerClassic *threadWorkerClassic = nullptr; #endif #ifdef QT_WINRT_BLUETOOTH diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index a7755696..3d1b0c4b 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -44,7 +44,8 @@ #include "qbluetoothlocaldevice_p.h" #include -#include +#include +#include #include #include @@ -332,17 +333,34 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( , pendingCancel(false) , pendingStart(false) , active(false) - , classicScanWatcher(nullptr) - , lowenergyScanWatcher(nullptr) , lowEnergySearchTimeout(-1) // remains -1 -> timeout not supported , q_ptr(parent) { + threadLE = new QThread; + threadWorkerLE = new ThreadWorkerLE; + threadWorkerLE->moveToThread(threadLE); + connect(threadWorkerLE, &ThreadWorkerLE::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery); + connect(threadLE, &QThread::finished, threadWorkerLE, &ThreadWorkerLE::deleteLater); + connect(threadLE, &QThread::finished, threadLE, &QThread::deleteLater); + threadLE->start(); + + threadClassic = new QThread; + threadWorkerClassic = new ThreadWorkerClassic; + threadWorkerClassic->moveToThread(threadClassic); + connect(threadWorkerClassic, &ThreadWorkerClassic::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery); + connect(threadClassic, &QThread::finished, threadWorkerClassic, &ThreadWorkerClassic::deleteLater); + connect(threadClassic, &QThread::finished, threadClassic, &QThread::deleteLater); + threadClassic->start(); } QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() { if (active) stop(); + if (threadLE) + threadLE->quit(); + if (threadClassic) + threadClassic->quit(); } bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const @@ -447,23 +465,17 @@ void QBluetoothDeviceDiscoveryAgentPrivate::finishDiscovery(QBluetoothDeviceDisc void QBluetoothDeviceDiscoveryAgentPrivate::startLeDevicesDiscovery() { - if (!lowenergyScanWatcher) { - lowenergyScanWatcher = new QFutureWatcher(this); - connect(lowenergyScanWatcher, &QFutureWatcher::finished, - this, &QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery); - } - const QFuture future = QtConcurrent::run(discoverLeDevicesStatic); - lowenergyScanWatcher->setFuture(future); + QMetaObject::invokeMethod(threadWorkerLE, "discover", Qt::QueuedConnection); } -void QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery() +void QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery(const QVariant res) { if (pendingCancel && !pendingStart) { cancelDiscovery(); } else if (pendingStart) { restartDiscovery(); } else { - const DiscoveryResult result = lowenergyScanWatcher->result().value(); + const DiscoveryResult result = res.value(); if (result.systemErrorCode == NO_ERROR || result.systemErrorCode == ERROR_NO_MORE_ITEMS) { for (const QBluetoothDeviceInfo &device : result.devices) processDiscoveredDevice(device); @@ -484,18 +496,13 @@ void QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery() void QBluetoothDeviceDiscoveryAgentPrivate::startClassicDevicesDiscovery(Qt::HANDLE hSearch) { - if (!classicScanWatcher) { - classicScanWatcher = new QFutureWatcher(this); - connect(classicScanWatcher, &QFutureWatcher::finished, - this, &QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery); - } - const QFuture future = QtConcurrent::run(discoverClassicDevicesStatic, hSearch); - classicScanWatcher->setFuture(future); + QMetaObject::invokeMethod(threadWorkerClassic, "discover", Qt::QueuedConnection, + Q_ARG(QVariant, QVariant::fromValue(hSearch))); } -void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery() +void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery(const QVariant res) { - const DiscoveryResult result = classicScanWatcher->result().value(); + const DiscoveryResult result = res.value(); if (pendingCancel && !pendingStart) { closeClassicSearch(result.hSearch); cancelDiscovery(); @@ -560,6 +567,20 @@ void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevice( } } + +void ThreadWorkerLE::discover() +{ + const QVariant res = discoverLeDevicesStatic(); + emit discoveryCompleted(res); +} + +void ThreadWorkerClassic::discover(QVariant search) +{ + Qt::HANDLE hSearch = search.value(); + const QVariant res = discoverClassicDevicesStatic(hSearch); + emit discoveryCompleted(res); +} + QT_END_NAMESPACE Q_DECLARE_METATYPE(DiscoveryResult) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index 9b2ab6a2..c6ea5c78 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -46,6 +46,8 @@ #include #include #include +#include +#include #include // for std::max -- cgit v1.2.3 From 3a90cf6d46dc6f34ccedb7f02515573d1db4704a Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov (VMware)" Date: Wed, 30 May 2018 00:49:58 +0200 Subject: win32: remove usage of QFuture in qbluetoothservicediscoveryagent Introduce usage of QThread for service discovery instead of QFuture. Details: - Make _q_nextSdpScan() accept arguments. - Make QBluetoothServiceDiscoveryAgentPrivate inherit QObject for QT_WIN_BLUETOOTH. - Remove usage of the member variables 'systemError', 'hSearch'. Pass values around, instead. - Add the helper structs 'FindServiceArguments' and 'FindServiceResult'. Change-Id: I4e2178b2a7b333c30a235a02807dd64526db4685 Reviewed-by: Oliver Wolff Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothservicediscoveryagent.h | 3 +- src/bluetooth/qbluetoothservicediscoveryagent_p.h | 27 ++-- .../qbluetoothservicediscoveryagent_win.cpp | 137 +++++++++++++-------- 3 files changed, 111 insertions(+), 56 deletions(-) diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.h b/src/bluetooth/qbluetoothservicediscoveryagent.h index 4b1a72c1..6a3f8f03 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent.h @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -130,7 +131,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state)) #endif #ifdef QT_WIN_BLUETOOTH - Q_PRIVATE_SLOT(d_func(), void _q_nextSdpScan()) + Q_PRIVATE_SLOT(d_func(), void _q_nextSdpScan(QVariant input)) #endif }; diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h index 3805ea44..cbaab458 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h @@ -73,7 +73,22 @@ QT_END_NAMESPACE #endif #ifdef QT_WIN_BLUETOOTH -#include +#include + +QT_BEGIN_NAMESPACE +class QThread; + +class ThreadWorkerFind : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE void findFirst(const QVariant data); + Q_INVOKABLE void findNext(const QVariant data); +signals: + void findFinished(QVariant result); +}; +QT_END_NAMESPACE + #elif defined(QT_WINRT_BLUETOOTH) #include #endif @@ -93,7 +108,7 @@ class QWinRTBluetoothServiceDiscoveryWorker; #endif class QBluetoothServiceDiscoveryAgentPrivate -#if defined QT_WINRT_BLUETOOTH +#if defined QT_WINRT_BLUETOOTH || defined QT_WIN_BLUETOOTH : public QObject { Q_OBJECT @@ -152,7 +167,7 @@ public: void _q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state); #endif #ifdef QT_WIN_BLUETOOTH - void _q_nextSdpScan(); + void _q_nextSdpScan(QVariant input); #endif private: @@ -207,13 +222,11 @@ private: #ifdef QT_WIN_BLUETOOTH private: - int systemError; bool pendingStop; bool pendingFinish; - QFutureWatcher *searchWatcher; - - Qt::HANDLE hSearch; + QThread *threadFind = nullptr; + ThreadWorkerFind *threadWorkerFind = nullptr; #endif #ifdef QT_WINRT_BLUETOOTH diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index cae7968b..8ee0f663 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -41,7 +41,10 @@ #include "qbluetoothservicediscoveryagent_p.h" #include -#include +#include +#include +#include +#include #include #include @@ -64,6 +67,18 @@ DEFINEFUNC(DWORD, BluetoothSdpGetElementData, LPBYTE, ULONG, PSDP_ELEMENT_DATA) QT_BEGIN_NAMESPACE +struct FindServiceArguments { + QBluetoothAddress address; + Qt::HANDLE hSearch; + int systemError; +}; + +struct FindServiceResult { + QBluetoothServiceInfo info; + Qt::HANDLE hSearch; + int systemError; +}; + Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) static inline QFunctionPointer resolveFunction(QLibrary *library, const char *func) @@ -276,9 +291,11 @@ enum { | LUP_RETURN_COMMENT }; -static QBluetoothServiceInfo findNextService(HANDLE hSearch, int *systemError) +static FindServiceResult findNextService(HANDLE hSearch, int systemError) { - QBluetoothServiceInfo result; + FindServiceResult result; + result.systemError = systemError; + result.hSearch = INVALID_HANDLE_VALUE; QByteArray resultBuffer(2048, 0); WSAQUERYSET *resultQuery = reinterpret_cast(resultBuffer.data()); @@ -289,34 +306,36 @@ static QBluetoothServiceInfo findNextService(HANDLE hSearch, int *systemError) resultQuery); if (resultCode == SOCKET_ERROR) { - *systemError = ::WSAGetLastError(); - if (*systemError == WSA_E_NO_MORE) + result.systemError = ::WSAGetLastError(); + if (result.systemError == WSA_E_NO_MORE) cleanupServiceDiscovery(hSearch); - return QBluetoothServiceInfo(); + return result; } if (resultQuery->lpBlob && BluetoothSdpEnumAttributes(resultQuery->lpBlob->pBlobData, resultQuery->lpBlob->cbSize, bluetoothSdpCallback, - &result)) { + &result.info)) { return result; } else { - *systemError = GetLastError(); + result.systemError = GetLastError(); } - return result; } -static QBluetoothServiceInfo findFirstService(LPHANDLE hSearch, const QBluetoothAddress &address, int *systemError) +static FindServiceResult findFirstService(HANDLE hSearch, const QBluetoothAddress &address) { //### should we try for 2.2 on all platforms ?? WSAData wsadata; + FindServiceResult result; + result.systemError = NO_ERROR; + result.hSearch = INVALID_HANDLE_VALUE; // IPv6 requires Winsock v2.0 or better. if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) { - *systemError = ::WSAGetLastError(); - return QBluetoothServiceInfo(); + result.systemError = ::WSAGetLastError(); + return result; } const QString addressAsString = QStringLiteral("(%1)").arg(address.toString()); @@ -336,14 +355,14 @@ static QBluetoothServiceInfo findFirstService(LPHANDLE hSearch, const QBluetooth const int resultCode = WSALookupServiceBegin(&serviceQuery, WSAControlFlags, - hSearch); + &hSearch); if (resultCode == SOCKET_ERROR) { - *systemError = ::WSAGetLastError(); - cleanupServiceDiscovery(hSearch); - return QBluetoothServiceInfo(); + result.systemError = ::WSAGetLastError(); + cleanupServiceDiscovery(&hSearch); + return result; } - *systemError = NO_ERROR; - return findNextService(*hSearch, systemError); + result.systemError = NO_ERROR; + return findNextService(hSearch, result.systemError); } QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( @@ -353,41 +372,43 @@ QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( 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(); + + threadFind = new QThread; + threadWorkerFind = new ThreadWorkerFind; + threadWorkerFind->moveToThread(threadFind); + connect(threadWorkerFind, &ThreadWorkerFind::findFinished, this, &QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan); + connect(threadFind, &QThread::finished, threadWorkerFind, &ThreadWorkerFind::deleteLater); + connect(threadFind, &QThread::finished, threadFind, &QThread::deleteLater); + threadFind->start(); } QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate() { if (pendingFinish) { stop(); - searchWatcher->waitForFinished(); } - delete searchWatcher; + if (threadFind) + threadFind->quit(); } 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 future = - QtConcurrent::run(&findFirstService, - &hSearch, - address, - &systemError); - searchWatcher->setFuture(future); + FindServiceArguments data; + data.address = address; + data.hSearch = INVALID_HANDLE_VALUE; + QMetaObject::invokeMethod(threadWorkerFind, "findFirst", Qt::QueuedConnection, + Q_ARG(QVariant, QVariant::fromValue(data))); } } @@ -396,42 +417,62 @@ void QBluetoothServiceDiscoveryAgentPrivate::stop() pendingStop = true; } -void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan() +void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(QVariant input) { Q_Q(QBluetoothServiceDiscoveryAgent); + auto result = input.value(); 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) ? + if (result.systemError == WSA_E_NO_MORE) { + result.systemError = NO_ERROR; + } else if (result.systemError != NO_ERROR) { + error = (result.systemError == ERROR_INVALID_HANDLE) ? QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError : QBluetoothServiceDiscoveryAgent::InputOutputError; - errorString = qt_error_string(systemError); + errorString = qt_error_string(result.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); + result.info.setDevice(discoveredDevices.at(0)); + if (result.info.isValid()) { + if (!isDuplicatedService(result.info)) { + discoveredServices.append(result.info); + emit q->serviceDiscovered(result.info); } } - const QFuture future = - QtConcurrent::run(&findNextService, hSearch, &systemError); - searchWatcher->setFuture(future); + FindServiceArguments data; + data.hSearch = result.hSearch; + data.systemError = result.systemError; + QMetaObject::invokeMethod(threadWorkerFind, "findNext", Qt::QueuedConnection, + Q_ARG(QVariant, QVariant::fromValue(data))); return; } - hSearch = INVALID_HANDLE_VALUE; pendingFinish = false; _q_serviceDiscoveryFinished(); } } +void ThreadWorkerFind::findFirst(const QVariant data) +{ + auto args = data.value(); + FindServiceResult result = findFirstService(args.hSearch, args.address); + result.hSearch = args.hSearch; + emit findFinished(QVariant::fromValue(result)); +} + + void ThreadWorkerFind::findNext(const QVariant data) +{ + auto args = data.value(); + FindServiceResult result = findNextService(args.hSearch, args.systemError); + result.hSearch = args.hSearch; + emit findFinished(QVariant::fromValue(result)); +} + QT_END_NAMESPACE + +Q_DECLARE_METATYPE(FindServiceArguments) +Q_DECLARE_METATYPE(FindServiceResult) -- cgit v1.2.3 From 62efb446b56bc489c998f90e307aa12e72cb6b8e Mon Sep 17 00:00:00 2001 From: Eric Lemanissier Date: Tue, 19 Jun 2018 13:02:39 +0200 Subject: win32: modernize qbluetooth*discoveryagent arguments pass by ref NULL and 0 replaced with nullptr ::ZeroMemory replaced by default initialization range based for loop Change-Id: I393806f19155ee31e4ebe7f33ce22e9d14eafe40 Reviewed-by: Lubomir I. Ivanov Reviewed-by: Oliver Wolff --- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 6 ++-- .../qbluetoothdevicediscoveryagent_win.cpp | 41 ++++++++++------------ src/bluetooth/qbluetoothservicediscoveryagent_p.h | 6 ++-- .../qbluetoothservicediscoveryagent_win.cpp | 9 +++-- 4 files changed, 28 insertions(+), 34 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index 489ed807..d60e89fa 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -96,7 +96,7 @@ class ThreadWorkerClassic : public QObject { Q_OBJECT public: - Q_INVOKABLE void discover(QVariant hSearch); + Q_INVOKABLE void discover(const QVariant &hSearch); signals: void discoveryCompleted(const QVariant res); }; @@ -198,9 +198,9 @@ private: void finishDiscovery(QBluetoothDeviceDiscoveryAgent::Error errorCode, const QString &errorText); void startLeDevicesDiscovery(); - void completeLeDevicesDiscovery(const QVariant res); + void completeLeDevicesDiscovery(const QVariant &res); void startClassicDevicesDiscovery(Qt::HANDLE hSearch = nullptr); - void completeClassicDevicesDiscovery(const QVariant res); + void completeClassicDevicesDiscovery(const QVariant &res); void processDiscoveredDevice(const QBluetoothDeviceInfo &foundDevice); diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 3d1b0c4b..1192677e 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -77,7 +77,7 @@ static QString devicePropertyString( deviceInfoData, registryProperty, &propertyRegDataType, - propertyBuffer.isEmpty() ? NULL : reinterpret_cast(propertyBuffer.data()), + propertyBuffer.isEmpty() ? nullptr : reinterpret_cast(propertyBuffer.data()), propertyBuffer.size(), &propertyBufferSize)) { @@ -132,8 +132,7 @@ static QBluetoothDeviceInfo createClassicDeviceInfo(const BLUETOOTH_DEVICE_INFO static QBluetoothDeviceInfo findFirstClassicDevice( DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND *hSearch) { - BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams; - ::ZeroMemory(&searchParams, sizeof(searchParams)); + BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = {0}; searchParams.dwSize = sizeof(searchParams); searchParams.cTimeoutMultiplier = 10; // 12.8 sec searchParams.fIssueInquiry = TRUE; @@ -141,10 +140,9 @@ static QBluetoothDeviceInfo findFirstClassicDevice( searchParams.fReturnConnected = TRUE; searchParams.fReturnRemembered = TRUE; searchParams.fReturnUnknown = TRUE; - searchParams.hRadio = NULL; + searchParams.hRadio = nullptr; - BLUETOOTH_DEVICE_INFO deviceInfo; - ::ZeroMemory(&deviceInfo, sizeof(deviceInfo)); + BLUETOOTH_DEVICE_INFO deviceInfo = {0}; deviceInfo.dwSize = sizeof(deviceInfo); const HBLUETOOTH_DEVICE_FIND hFind = ::BluetoothFindFirstDevice( @@ -165,8 +163,7 @@ static QBluetoothDeviceInfo findFirstClassicDevice( static QBluetoothDeviceInfo findNextClassicDevice( DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND hSearch) { - BLUETOOTH_DEVICE_INFO deviceInfo; - ::ZeroMemory(&deviceInfo, sizeof(deviceInfo)); + BLUETOOTH_DEVICE_INFO deviceInfo = {0}; deviceInfo.dwSize = sizeof(deviceInfo); QBluetoothDeviceInfo foundDevice; @@ -192,8 +189,8 @@ static QVector enumerateLeDevices( const QUuid deviceInterfaceGuid("781aee18-7733-4ce4-add0-91f41c67b592"); const HDEVINFO hDeviceInfo = ::SetupDiGetClassDevs( reinterpret_cast(&deviceInterfaceGuid), - NULL, - 0, + nullptr, + nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (hDeviceInfo == INVALID_HANDLE_VALUE) { @@ -207,13 +204,12 @@ static QVector enumerateLeDevices( QVector cachedEntries; for (;;) { - SP_DEVICE_INTERFACE_DATA deviceInterfaceData; - ::ZeroMemory(&deviceInterfaceData, sizeof(deviceInterfaceData)); + SP_DEVICE_INTERFACE_DATA deviceInterfaceData = {0}; deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); if (!::SetupDiEnumDeviceInterfaces( hDeviceInfo, - NULL, + nullptr, reinterpret_cast(&deviceInterfaceGuid), index++, &deviceInterfaceData)) { @@ -225,18 +221,17 @@ static QVector enumerateLeDevices( if (!::SetupDiGetDeviceInterfaceDetail( hDeviceInfo, &deviceInterfaceData, - NULL, + nullptr, deviceInterfaceDetailDataSize, &deviceInterfaceDetailDataSize, - NULL)) { + nullptr)) { if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) { *systemErrorCode = ::GetLastError(); break; } } - SP_DEVINFO_DATA deviceInfoData; - ::ZeroMemory(&deviceInfoData, sizeof(deviceInfoData)); + SP_DEVINFO_DATA deviceInfoData = {0}; deviceInfoData.cbSize = sizeof(deviceInfoData); QByteArray deviceInterfaceDetailDataBuffer( @@ -317,9 +312,9 @@ QString QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath( enumerateLeDevices(&dummyErrorCode); QMutexLocker locker(cachedLeDeviceEntriesGuard()); - for (int i = 0; i < cachedLeDeviceEntries()->count(); ++i) { - if (cachedLeDeviceEntries()->at(i).deviceAddress == deviceAddress) - return cachedLeDeviceEntries()->at(i).devicePath; + for (const LeDeviceEntry &e: *cachedLeDeviceEntries) { + if (e.deviceAddress == deviceAddress) + return e.devicePath; } return QString(); } @@ -468,7 +463,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLeDevicesDiscovery() QMetaObject::invokeMethod(threadWorkerLE, "discover", Qt::QueuedConnection); } -void QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery(const QVariant res) +void QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery(const QVariant &res) { if (pendingCancel && !pendingStart) { cancelDiscovery(); @@ -500,7 +495,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startClassicDevicesDiscovery(Qt::HAN Q_ARG(QVariant, QVariant::fromValue(hSearch))); } -void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery(const QVariant res) +void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery(const QVariant &res) { const DiscoveryResult result = res.value(); if (pendingCancel && !pendingStart) { @@ -574,7 +569,7 @@ void ThreadWorkerLE::discover() emit discoveryCompleted(res); } -void ThreadWorkerClassic::discover(QVariant search) +void ThreadWorkerClassic::discover(const QVariant &search) { Qt::HANDLE hSearch = search.value(); const QVariant res = discoverClassicDevicesStatic(hSearch); diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h index cbaab458..c845b528 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h @@ -82,8 +82,8 @@ class ThreadWorkerFind : public QObject { Q_OBJECT public: - Q_INVOKABLE void findFirst(const QVariant data); - Q_INVOKABLE void findNext(const QVariant data); + Q_INVOKABLE void findFirst(const QVariant &data); + Q_INVOKABLE void findNext(const QVariant &data); signals: void findFinished(QVariant result); }; @@ -167,7 +167,7 @@ public: void _q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state); #endif #ifdef QT_WIN_BLUETOOTH - void _q_nextSdpScan(QVariant input); + void _q_nextSdpScan(const QVariant &input); #endif private: diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index 8ee0f663..35ab558e 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -345,8 +345,7 @@ static FindServiceResult findFirstService(HANDLE hSearch, const QBluetoothAddres GUID protocol = L2CAP_PROTOCOL_UUID; //Search for L2CAP and RFCOMM services - WSAQUERYSET serviceQuery; - ::ZeroMemory(&serviceQuery, sizeof(serviceQuery)); + WSAQUERYSET serviceQuery = {0}; 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 @@ -417,7 +416,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::stop() pendingStop = true; } -void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(QVariant input) +void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &input) { Q_Q(QBluetoothServiceDiscoveryAgent); auto result = input.value(); @@ -456,7 +455,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(QVariant input) } } -void ThreadWorkerFind::findFirst(const QVariant data) +void ThreadWorkerFind::findFirst(const QVariant &data) { auto args = data.value(); FindServiceResult result = findFirstService(args.hSearch, args.address); @@ -464,7 +463,7 @@ void ThreadWorkerFind::findFirst(const QVariant data) emit findFinished(QVariant::fromValue(result)); } - void ThreadWorkerFind::findNext(const QVariant data) + void ThreadWorkerFind::findNext(const QVariant &data) { auto args = data.value(); FindServiceResult result = findNextService(args.hSearch, args.systemError); -- cgit v1.2.3 From 11968d018fededb40271978324cda16de4d36941 Mon Sep 17 00:00:00 2001 From: Eric Lemanissier Date: Tue, 19 Jun 2018 15:22:03 +0200 Subject: win32: make bluetooth tst_startStopDeviceDiscoveries pass QVERIFY(discoveryAgent.errorString().isEmpty()) was failing Change-Id: I7b45db5b1af1ee3da75569d8855487c771cb29ed Reviewed-by: Lubomir I. Ivanov Reviewed-by: Oliver Wolff --- src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 1192677e..e1c6bea4 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -507,7 +507,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery(cons } else { if (result.systemErrorCode == ERROR_NO_MORE_ITEMS) { closeClassicSearch(result.hSearch); - finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, qt_error_string(NO_ERROR)); + finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, QString()); } else if (result.systemErrorCode == NO_ERROR) { if (result.hSearch) { for (const QBluetoothDeviceInfo &device : result.devices) @@ -515,7 +515,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery(cons startClassicDevicesDiscovery(result.hSearch); } else { - finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, qt_error_string(NO_ERROR)); + finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, QString()); } } else { closeClassicSearch(result.hSearch); -- cgit v1.2.3 From d6fb710c0a21fb58a73ff580b08d00e783337bfa Mon Sep 17 00:00:00 2001 From: Eric Lemanissier Date: Tue, 19 Jun 2018 13:00:24 +0200 Subject: win32: fix QBluetoothServiceDiscoveryAgent cleanup remove calls to WSACleanup, because it terminates use of the Winsock 2 DLL, including sockets operations. WSALookupServiceEnd only is needed. add missing WSALookupServiceEnd if pendingStop or error remove useless parameters of findFirst/NextService Change-Id: I587987b77c1be8f05a840e4333c31fd79af06ed3 Reviewed-by: Lubomir I. Ivanov Reviewed-by: Oliver Wolff --- .../qbluetoothservicediscoveryagent_win.cpp | 55 +++++++--------------- 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index 35ab558e..d4e00c0a 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -67,12 +67,6 @@ DEFINEFUNC(DWORD, BluetoothSdpGetElementData, LPBYTE, ULONG, PSDP_ELEMENT_DATA) QT_BEGIN_NAMESPACE -struct FindServiceArguments { - QBluetoothAddress address; - Qt::HANDLE hSearch; - int systemError; -}; - struct FindServiceResult { QBluetoothServiceInfo info; Qt::HANDLE hSearch; @@ -275,13 +269,6 @@ static BOOL SDP_CALLBACK bluetoothSdpCallback(ULONG attributeId, LPBYTE valueStr return true; } -static void cleanupServiceDiscovery(HANDLE hSearch) -{ - if (hSearch != INVALID_HANDLE_VALUE) - WSALookupServiceEnd(hSearch); - WSACleanup(); -} - enum { WSAControlFlags = LUP_FLUSHCACHE | LUP_RETURN_NAME @@ -291,11 +278,11 @@ enum { | LUP_RETURN_COMMENT }; -static FindServiceResult findNextService(HANDLE hSearch, int systemError) +static FindServiceResult findNextService(HANDLE hSearch) { FindServiceResult result; - result.systemError = systemError; - result.hSearch = INVALID_HANDLE_VALUE; + result.systemError = NO_ERROR; + result.hSearch = hSearch; QByteArray resultBuffer(2048, 0); WSAQUERYSET *resultQuery = reinterpret_cast(resultBuffer.data()); @@ -308,7 +295,7 @@ static FindServiceResult findNextService(HANDLE hSearch, int systemError) if (resultCode == SOCKET_ERROR) { result.systemError = ::WSAGetLastError(); if (result.systemError == WSA_E_NO_MORE) - cleanupServiceDiscovery(hSearch); + WSALookupServiceEnd(hSearch); return result; } @@ -324,12 +311,11 @@ static FindServiceResult findNextService(HANDLE hSearch, int systemError) return result; } -static FindServiceResult findFirstService(HANDLE hSearch, const QBluetoothAddress &address) +static FindServiceResult findFirstService(const QBluetoothAddress &address) { //### should we try for 2.2 on all platforms ?? WSAData wsadata; FindServiceResult result; - result.systemError = NO_ERROR; result.hSearch = INVALID_HANDLE_VALUE; // IPv6 requires Winsock v2.0 or better. @@ -352,16 +338,16 @@ static FindServiceResult findFirstService(HANDLE hSearch, const QBluetoothAddres serviceQuery.dwNumberOfCsAddrs = 0; //As specified by the windows documentation serviceQuery.lpszContext = addressAsWChar.data(); //The remote address that query will run on + HANDLE hSearch; const int resultCode = WSALookupServiceBegin(&serviceQuery, WSAControlFlags, &hSearch); if (resultCode == SOCKET_ERROR) { result.systemError = ::WSAGetLastError(); - cleanupServiceDiscovery(&hSearch); + WSALookupServiceEnd(hSearch); return result; } - result.systemError = NO_ERROR; - return findNextService(hSearch, result.systemError); + return findNextService(hSearch); } QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( @@ -403,11 +389,8 @@ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &addr pendingFinish = true; pendingStop = false; - FindServiceArguments data; - data.address = address; - data.hSearch = INVALID_HANDLE_VALUE; QMetaObject::invokeMethod(threadWorkerFind, "findFirst", Qt::QueuedConnection, - Q_ARG(QVariant, QVariant::fromValue(data))); + Q_ARG(QVariant, QVariant::fromValue(address))); } } @@ -422,6 +405,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &inpu auto result = input.value(); if (pendingStop) { + WSALookupServiceEnd(result.hSearch); pendingStop = false; pendingFinish = false; emit q->canceled(); @@ -429,6 +413,8 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &inpu if (result.systemError == WSA_E_NO_MORE) { result.systemError = NO_ERROR; } else if (result.systemError != NO_ERROR) { + if (result.hSearch != INVALID_HANDLE_VALUE) + WSALookupServiceEnd(result.hSearch); error = (result.systemError == ERROR_INVALID_HANDLE) ? QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError : QBluetoothServiceDiscoveryAgent::InputOutputError; @@ -443,11 +429,8 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &inpu emit q->serviceDiscovered(result.info); } } - FindServiceArguments data; - data.hSearch = result.hSearch; - data.systemError = result.systemError; QMetaObject::invokeMethod(threadWorkerFind, "findNext", Qt::QueuedConnection, - Q_ARG(QVariant, QVariant::fromValue(data))); + Q_ARG(QVariant, QVariant::fromValue(result.hSearch))); return; } pendingFinish = false; @@ -457,21 +440,19 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &inpu void ThreadWorkerFind::findFirst(const QVariant &data) { - auto args = data.value(); - FindServiceResult result = findFirstService(args.hSearch, args.address); - result.hSearch = args.hSearch; + auto address = data.value(); + FindServiceResult result = findFirstService(address); emit findFinished(QVariant::fromValue(result)); } void ThreadWorkerFind::findNext(const QVariant &data) { - auto args = data.value(); - FindServiceResult result = findNextService(args.hSearch, args.systemError); - result.hSearch = args.hSearch; + auto hSearch = data.value(); + FindServiceResult result = findNextService(hSearch); emit findFinished(QVariant::fromValue(result)); } QT_END_NAMESPACE -Q_DECLARE_METATYPE(FindServiceArguments) +Q_DECLARE_METATYPE(Qt::HANDLE) Q_DECLARE_METATYPE(FindServiceResult) -- cgit v1.2.3 From f1e305a362c759b0781b6dde3dce5f649b47017c Mon Sep 17 00:00:00 2001 From: Eric Lemanissier Date: Tue, 19 Jun 2018 13:24:41 +0200 Subject: win32: inline unnecessary functions in qbluetooth*discoveryagent by migrating to templated QMetaObject::invokeMethod Change-Id: I79e2dec7aa81987894a26859648d31da60cf47ee Reviewed-by: Lubomir I. Ivanov Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 16 +---- .../qbluetoothdevicediscoveryagent_win.cpp | 73 +++++++++------------- src/bluetooth/qbluetoothservicediscoveryagent_p.h | 3 - .../qbluetoothservicediscoveryagent_win.cpp | 32 ++++------ 4 files changed, 44 insertions(+), 80 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index d60e89fa..cb12352f 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -83,23 +83,13 @@ QT_END_NAMESPACE QT_BEGIN_NAMESPACE class QThread; -class ThreadWorkerLE : public QObject +class ThreadWorkerDeviceDiscovery : public QObject { Q_OBJECT -public: - Q_INVOKABLE void discover(); signals: void discoveryCompleted(const QVariant res); }; -class ThreadWorkerClassic : public QObject -{ - Q_OBJECT -public: - Q_INVOKABLE void discover(const QVariant &hSearch); -signals: - void discoveryCompleted(const QVariant res); -}; QT_END_NAMESPACE #elif defined(QT_WINRT_BLUETOOTH) @@ -211,8 +201,8 @@ private: QThread *threadLE = nullptr; QThread *threadClassic = nullptr; - ThreadWorkerLE *threadWorkerLE = nullptr; - ThreadWorkerClassic *threadWorkerClassic = nullptr; + ThreadWorkerDeviceDiscovery *threadWorkerLE = nullptr; + ThreadWorkerDeviceDiscovery *threadWorkerClassic = nullptr; #endif #ifdef QT_WINRT_BLUETOOTH diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index e1c6bea4..7509c5f6 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -282,28 +282,6 @@ struct DiscoveryResult { 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) { @@ -332,18 +310,18 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( , q_ptr(parent) { threadLE = new QThread; - threadWorkerLE = new ThreadWorkerLE; + threadWorkerLE = new ThreadWorkerDeviceDiscovery; threadWorkerLE->moveToThread(threadLE); - connect(threadWorkerLE, &ThreadWorkerLE::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery); - connect(threadLE, &QThread::finished, threadWorkerLE, &ThreadWorkerLE::deleteLater); + connect(threadWorkerLE, &ThreadWorkerDeviceDiscovery::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery); + connect(threadLE, &QThread::finished, threadWorkerLE, &ThreadWorkerDeviceDiscovery::deleteLater); connect(threadLE, &QThread::finished, threadLE, &QThread::deleteLater); threadLE->start(); threadClassic = new QThread; - threadWorkerClassic = new ThreadWorkerClassic; + threadWorkerClassic = new ThreadWorkerDeviceDiscovery; threadWorkerClassic->moveToThread(threadClassic); - connect(threadWorkerClassic, &ThreadWorkerClassic::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery); - connect(threadClassic, &QThread::finished, threadWorkerClassic, &ThreadWorkerClassic::deleteLater); + connect(threadWorkerClassic, &ThreadWorkerDeviceDiscovery::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery); + connect(threadClassic, &QThread::finished, threadWorkerClassic, &ThreadWorkerDeviceDiscovery::deleteLater); connect(threadClassic, &QThread::finished, threadClassic, &QThread::deleteLater); threadClassic->start(); } @@ -460,7 +438,14 @@ void QBluetoothDeviceDiscoveryAgentPrivate::finishDiscovery(QBluetoothDeviceDisc void QBluetoothDeviceDiscoveryAgentPrivate::startLeDevicesDiscovery() { - QMetaObject::invokeMethod(threadWorkerLE, "discover", Qt::QueuedConnection); + const auto threadWorker = threadWorkerLE; + QMetaObject::invokeMethod(threadWorkerLE, [threadWorker]() + { + DiscoveryResult result; // Do not use hSearch here! + result.systemErrorCode = NO_ERROR; + result.devices = enumerateLeDevices(&result.systemErrorCode); + emit threadWorker->discoveryCompleted(QVariant::fromValue(result)); + }, Qt::QueuedConnection); } void QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery(const QVariant &res) @@ -491,8 +476,20 @@ void QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery(const QVa void QBluetoothDeviceDiscoveryAgentPrivate::startClassicDevicesDiscovery(Qt::HANDLE hSearch) { - QMetaObject::invokeMethod(threadWorkerClassic, "discover", Qt::QueuedConnection, - Q_ARG(QVariant, QVariant::fromValue(hSearch))); + const auto threadWorker = threadWorkerClassic; + QMetaObject::invokeMethod(threadWorker, [threadWorker, hSearch]() + { + DiscoveryResult result; + result.hSearch = hSearch; + result.systemErrorCode = NO_ERROR; + + const QBluetoothDeviceInfo device = hSearch + ? findNextClassicDevice(&result.systemErrorCode, result.hSearch) + : findFirstClassicDevice(&result.systemErrorCode, &result.hSearch); + + result.devices.append(device); + emit threadWorker->discoveryCompleted(QVariant::fromValue(result)); + }, Qt::QueuedConnection); } void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery(const QVariant &res) @@ -562,20 +559,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevice( } } - -void ThreadWorkerLE::discover() -{ - const QVariant res = discoverLeDevicesStatic(); - emit discoveryCompleted(res); -} - -void ThreadWorkerClassic::discover(const QVariant &search) -{ - Qt::HANDLE hSearch = search.value(); - const QVariant res = discoverClassicDevicesStatic(hSearch); - emit discoveryCompleted(res); -} - QT_END_NAMESPACE Q_DECLARE_METATYPE(DiscoveryResult) diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h index c845b528..37943c15 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h @@ -81,9 +81,6 @@ class QThread; class ThreadWorkerFind : public QObject { Q_OBJECT -public: - Q_INVOKABLE void findFirst(const QVariant &data); - Q_INVOKABLE void findNext(const QVariant &data); signals: void findFinished(QVariant result); }; diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index d4e00c0a..2c3c6437 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -389,8 +389,12 @@ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &addr pendingFinish = true; pendingStop = false; - QMetaObject::invokeMethod(threadWorkerFind, "findFirst", Qt::QueuedConnection, - Q_ARG(QVariant, QVariant::fromValue(address))); + const auto threadWorker = threadWorkerFind; + QMetaObject::invokeMethod(threadWorkerFind, [threadWorker, address]() + { + FindServiceResult result = findFirstService(address); + emit threadWorker->findFinished(QVariant::fromValue(result)); + }, Qt::QueuedConnection); } } @@ -429,8 +433,13 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &inpu emit q->serviceDiscovered(result.info); } } - QMetaObject::invokeMethod(threadWorkerFind, "findNext", Qt::QueuedConnection, - Q_ARG(QVariant, QVariant::fromValue(result.hSearch))); + const auto threadWorker = threadWorkerFind; + const auto hSearch = result.hSearch; + QMetaObject::invokeMethod(threadWorkerFind, [threadWorker, hSearch]() + { + FindServiceResult result = findNextService(hSearch); + emit threadWorker->findFinished(QVariant::fromValue(result)); + }, Qt::QueuedConnection); return; } pendingFinish = false; @@ -438,21 +447,6 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &inpu } } -void ThreadWorkerFind::findFirst(const QVariant &data) -{ - auto address = data.value(); - FindServiceResult result = findFirstService(address); - emit findFinished(QVariant::fromValue(result)); -} - - void ThreadWorkerFind::findNext(const QVariant &data) -{ - auto hSearch = data.value(); - FindServiceResult result = findNextService(hSearch); - emit findFinished(QVariant::fromValue(result)); -} - QT_END_NAMESPACE -Q_DECLARE_METATYPE(Qt::HANDLE) Q_DECLARE_METATYPE(FindServiceResult) -- cgit v1.2.3 From 6979c889ebd370b673d94907be766c6ef76ca7f4 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Wed, 25 Jul 2018 15:03:53 +0200 Subject: Port win32 port to new QBluetoothSocketBasePrivate pattern Change-Id: If3fc4e1078c63d7ca2af0e79353a62b9a9bb0c6f Reviewed-by: Oliver Wolff --- src/bluetooth/bluetooth.pro | 4 +- src/bluetooth/qbluetoothsocket.cpp | 6 ++ src/bluetooth/qbluetoothsocket.h | 1 + src/bluetooth/qbluetoothsocket_win.cpp | 155 ++++++++++++++++++++++++++++----- src/bluetooth/qbluetoothsocket_win_p.h | 107 +++++++++++++++++++++++ 5 files changed, 248 insertions(+), 25 deletions(-) create mode 100644 src/bluetooth/qbluetoothsocket_win_p.h diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index c82b052b..c90364c6 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -257,8 +257,8 @@ qtConfig(bluez) { qbluetoothserver_win.cpp \ qlowenergycontroller_win.cpp - PRIVATE_HEADERS += qlowenergycontroller_win_p.h - + PRIVATE_HEADERS += qlowenergycontroller_win_p.h \ + qbluetoothsocket_win_p.h } else { message("Unsupported Bluetooth platform, will not build a working QtBluetooth library.") message("Either no Qt D-Bus found or no BlueZ headers available.") diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp index dae844d7..1373615f 100644 --- a/src/bluetooth/qbluetoothsocket.cpp +++ b/src/bluetooth/qbluetoothsocket.cpp @@ -45,6 +45,8 @@ #include "qbluetoothsocket_android_p.h" #elif defined(QT_WINRT_BLUETOOTH) #include "qbluetoothsocket_winrt_p.h" +#elif defined(QT_WIN_BLUETOOTH) +#include "qbluetoothsocket_win_p.h" #else #include "qbluetoothsocket_dummy_p.h" #endif @@ -263,6 +265,8 @@ QBluetoothSocket::QBluetoothSocket(QBluetoothServiceInfo::Protocol socketType, Q d_ptr = new QBluetoothSocketPrivateAndroid(); #elif defined(QT_WINRT_BLUETOOTH) d_ptr = new QBluetoothSocketPrivateWinRT(); +#elif defined(QT_WIN_BLUETOOTH) + d_ptr = new QBluetoothSocketPrivateWin(); #else d_ptr = new QBluetoothSocketPrivateDummy(); #endif @@ -286,6 +290,8 @@ QBluetoothSocket::QBluetoothSocket(QObject *parent) d_ptr = new QBluetoothSocketPrivateAndroid(); #elif defined(QT_WINRT_BLUETOOTH) d_ptr = new QBluetoothSocketPrivateWinRT(); +#elif defined(QT_WIN_BLUETOOTH) + d_ptr = new QBluetoothSocketPrivateWin(); #else d_ptr = new QBluetoothSocketPrivateDummy(); #endif diff --git a/src/bluetooth/qbluetoothsocket.h b/src/bluetooth/qbluetoothsocket.h index cda64dff..e341d599 100644 --- a/src/bluetooth/qbluetoothsocket.h +++ b/src/bluetooth/qbluetoothsocket.h @@ -75,6 +75,7 @@ class Q_BLUETOOTH_EXPORT QBluetoothSocket : public QIODevice friend class QBluetoothSocketPrivateBluez; friend class QBluetoothSocketPrivateBluezDBus; friend class QBluetoothSocketPrivateDummy; + friend class QBluetoothSocketPrivateWin; friend class QBluetoothSocketPrivateWinRT; public: diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp index b2df61b3..f7e0e2d0 100644 --- a/src/bluetooth/qbluetoothsocket_win.cpp +++ b/src/bluetooth/qbluetoothsocket_win.cpp @@ -38,70 +38,179 @@ ****************************************************************************/ #include "qbluetoothsocket.h" -#include "qbluetoothsocket_p.h" +#include "qbluetoothsocket_win_p.h" + +#include + +#include QT_BEGIN_NAMESPACE -QBluetoothSocketPrivate::QBluetoothSocketPrivate() - : socket(-1), - socketType(QBluetoothServiceInfo::UnknownProtocol), - state(QBluetoothSocket::UnconnectedState), - socketError(QBluetoothSocket::NoSocketError) +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) + +QBluetoothSocketPrivateWin::QBluetoothSocketPrivateWin() + : QBluetoothSocketBasePrivate() { } -QBluetoothSocketPrivate::~QBluetoothSocketPrivate() +QBluetoothSocketPrivateWin::~QBluetoothSocketPrivateWin() { } -bool QBluetoothSocketPrivate::ensureNativeSocket(QBluetoothServiceInfo::Protocol type) +bool QBluetoothSocketPrivateWin::ensureNativeSocket(QBluetoothServiceInfo::Protocol type) { socketType = type; return false; } -void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) +void QBluetoothSocketPrivateWin::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) { Q_UNUSED(openMode); Q_UNUSED(address); Q_UNUSED(port); } -void QBluetoothSocketPrivate::abort() +void QBluetoothSocketPrivateWin::connectToService( + const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode) +{ + Q_Q(QBluetoothSocket); + + if (q->state() != QBluetoothSocket::UnconnectedState + && q->state() != QBluetoothSocket::ServiceLookupState) { + //qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWIN::connectToService called on busy socket"; + errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress"); + q->setSocketError(QBluetoothSocket::OperationError); + return; + } + + // we are checking the service protocol and not socketType() + // socketType will change in ensureNativeSocket() + if (service.socketProtocol() == QBluetoothServiceInfo::UnknownProtocol) { + qCWarning(QT_BT_WINDOWS) << "QBluetoothSocket::connectToService cannot " + "connect with 'UnknownProtocol' (type provided by given service)"; + errorString = QBluetoothSocket::tr("Socket type not supported"); + q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); + return; + } + + if (service.protocolServiceMultiplexer() > 0) { + Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::L2capProtocol); + + if (!ensureNativeSocket(QBluetoothServiceInfo::L2capProtocol)) { + errorString = QBluetoothSocket::tr("Unknown socket error"); + q->setSocketError(QBluetoothSocket::UnknownSocketError); + return; + } + connectToServiceHelper(service.device().address(), service.protocolServiceMultiplexer(), + openMode); + } else if (service.serverChannel() > 0) { + Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol); + + if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) { + errorString = QBluetoothSocket::tr("Unknown socket error"); + q->setSocketError(QBluetoothSocket::UnknownSocketError); + return; + } + connectToServiceHelper(service.device().address(), service.serverChannel(), openMode); + } else { + // try doing service discovery to see if we can find the socket + if (service.serviceUuid().isNull() + && !service.serviceClassUuids().contains(QBluetoothUuid::SerialPort)) { + qCWarning(QT_BT_WINDOWS) << "No port, no PSM, and no UUID provided. Unable to connect"; + return; + } + qCDebug(QT_BT_WINDOWS) << "Need a port/psm, doing discovery"; + q->doDeviceDiscovery(service, openMode); + } +} + +void QBluetoothSocketPrivateWin::connectToService( + const QBluetoothAddress &address, const QBluetoothUuid &uuid, + QIODevice::OpenMode openMode) +{ + Q_Q(QBluetoothSocket); + + if (q->state() != QBluetoothSocket::UnconnectedState) { + qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService called on busy socket"; + errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress"); + q->setSocketError(QBluetoothSocket::OperationError); + return; + } + + if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) { + qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService cannot " + "connect with 'UnknownProtocol' (type provided by given service)"; + errorString = QBluetoothSocket::tr("Socket type not supported"); + q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); + return; + } + + QBluetoothServiceInfo service; + QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice); + service.setDevice(device); + service.setServiceUuid(uuid); + q->doDeviceDiscovery(service, openMode); +} + +void QBluetoothSocketPrivateWin::connectToService( + const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) +{ + Q_Q(QBluetoothSocket); + + if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) { + qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService cannot " + "connect with 'UnknownProtocol' (type provided by given service)"; + errorString = QBluetoothSocket::tr("Socket type not supported"); + q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); + return; + } + + if (q->state() != QBluetoothSocket::UnconnectedState) { + qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService called on busy socket"; + errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress"); + q->setSocketError(QBluetoothSocket::OperationError); + return; + } + + q->setOpenMode(openMode); + connectToServiceHelper(address, port, openMode); +} + +void QBluetoothSocketPrivateWin::abort() { } -QString QBluetoothSocketPrivate::localName() const +QString QBluetoothSocketPrivateWin::localName() const { return QString(); } -QBluetoothAddress QBluetoothSocketPrivate::localAddress() const +QBluetoothAddress QBluetoothSocketPrivateWin::localAddress() const { return QBluetoothAddress(); } -quint16 QBluetoothSocketPrivate::localPort() const +quint16 QBluetoothSocketPrivateWin::localPort() const { return 0; } -QString QBluetoothSocketPrivate::peerName() const +QString QBluetoothSocketPrivateWin::peerName() const { return QString(); } -QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const +QBluetoothAddress QBluetoothSocketPrivateWin::peerAddress() const { return QBluetoothAddress(); } -quint16 QBluetoothSocketPrivate::peerPort() const +quint16 QBluetoothSocketPrivateWin::peerPort() const { return 0; } -qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize) +qint64 QBluetoothSocketPrivateWin::writeData(const char *data, qint64 maxSize) { Q_UNUSED(data); Q_UNUSED(maxSize); @@ -116,7 +225,7 @@ qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize) return -1; } -qint64 QBluetoothSocketPrivate::readData(char *data, qint64 maxSize) +qint64 QBluetoothSocketPrivateWin::readData(char *data, qint64 maxSize) { Q_UNUSED(data); Q_UNUSED(maxSize); @@ -132,11 +241,11 @@ qint64 QBluetoothSocketPrivate::readData(char *data, qint64 maxSize) return -1; } -void QBluetoothSocketPrivate::close() +void QBluetoothSocketPrivateWin::close() { } -bool QBluetoothSocketPrivate::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, +bool QBluetoothSocketPrivateWin::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode) { Q_UNUSED(socketDescriptor); @@ -146,17 +255,17 @@ bool QBluetoothSocketPrivate::setSocketDescriptor(int socketDescriptor, QBluetoo return false; } -qint64 QBluetoothSocketPrivate::bytesAvailable() const +qint64 QBluetoothSocketPrivateWin::bytesAvailable() const { return 0; } -bool QBluetoothSocketPrivate::canReadLine() const +bool QBluetoothSocketPrivateWin::canReadLine() const { return false; } -qint64 QBluetoothSocketPrivate::bytesToWrite() const +qint64 QBluetoothSocketPrivateWin::bytesToWrite() const { return 0; } diff --git a/src/bluetooth/qbluetoothsocket_win_p.h b/src/bluetooth/qbluetoothsocket_win_p.h new file mode 100644 index 00000000..3255428a --- /dev/null +++ b/src/bluetooth/qbluetoothsocket_win_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBLUETOOTHSOCKET_WIN_H +#define QBLUETOOTHSOCKET_WIN_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qbluetoothsocket.h" +#include "qbluetoothsocketbase_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QBluetoothSocketPrivateWin final : public QBluetoothSocketBasePrivate +{ + Q_OBJECT + friend class QBluetoothServerPrivate; + +public: + QBluetoothSocketPrivateWin(); + ~QBluetoothSocketPrivateWin() override; + + void connectToServiceHelper(const QBluetoothAddress &address, + quint16 port, + QIODevice::OpenMode openMode) override; + + void connectToService(const QBluetoothServiceInfo &service, + QIODevice::OpenMode openMode) override; + void connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, + QIODevice::OpenMode openMode) override; + void connectToService(const QBluetoothAddress &address, quint16 port, + QIODevice::OpenMode openMode) override; + + bool ensureNativeSocket(QBluetoothServiceInfo::Protocol type) override; + + QString localName() const override; + QBluetoothAddress localAddress() const override; + quint16 localPort() const override; + + QString peerName() const override; + QBluetoothAddress peerAddress() const override; + quint16 peerPort() const override; + + void abort() override; + void close() override; + + qint64 writeData(const char *data, qint64 maxSize) override; + qint64 readData(char *data, qint64 maxSize) override; + + bool setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, + QBluetoothSocket::SocketState socketState = QBluetoothSocket::ConnectedState, + QBluetoothSocket::OpenMode openMode = QBluetoothSocket::ReadWrite) override; + + qint64 bytesAvailable() const override; + bool canReadLine() const override; + qint64 bytesToWrite() const override; +}; + +QT_END_NAMESPACE + +#endif // QBLUETOOTHSOCKET_WIN_H -- cgit v1.2.3 From 2a382272a4b6891d4603e1cb9f2d357b374d7a74 Mon Sep 17 00:00:00 2001 From: Eric Lemanissier Date: Mon, 2 Jul 2018 17:09:26 +0200 Subject: win32: implement QBluetoothSocket Change-Id: Ib2b33490694c8608edda3eb0cbe7b8d0dd233254 Reviewed-by: Lubomir I. Ivanov Reviewed-by: Denis Shienkov --- src/bluetooth/qbluetoothsocket_win.cpp | 370 ++++++++++++++++++++++++++++++--- src/bluetooth/qbluetoothsocket_win_p.h | 16 ++ 2 files changed, 358 insertions(+), 28 deletions(-) diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp index f7e0e2d0..14c780d1 100644 --- a/src/bluetooth/qbluetoothsocket_win.cpp +++ b/src/bluetooth/qbluetoothsocket_win.cpp @@ -43,6 +43,12 @@ #include #include +#include +#include + +#include +#include +#include QT_BEGIN_NAMESPACE @@ -55,19 +61,85 @@ QBluetoothSocketPrivateWin::QBluetoothSocketPrivateWin() QBluetoothSocketPrivateWin::~QBluetoothSocketPrivateWin() { + abort(); } bool QBluetoothSocketPrivateWin::ensureNativeSocket(QBluetoothServiceInfo::Protocol type) { + Q_Q(QBluetoothSocket); + + if (socket != INVALID_SOCKET) { + if (socketType == type) + return true; + abort(); + } + socketType = type; - return false; + + switch (type) { + case QBluetoothServiceInfo::RfcommProtocol: + socket = ::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); + break; + default: + socket = INVALID_SOCKET; + errorString = QBluetoothSocket::tr("Unsupported protocol. Win32 only supports RFCOMM sockets"); + q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); + return false; + } + + if (socket == INVALID_SOCKET) { + const int error = ::WSAGetLastError(); + qCWarning(QT_BT_WINDOWS) << "Failed to create socket:" << error << qt_error_string(error); + errorString = QBluetoothSocket::tr("Failed to create socket"); + q->setSocketError(QBluetoothSocket::UnknownSocketError); + return false; + } + + if (!createNotifiers()) + return false; + + return true; } void QBluetoothSocketPrivateWin::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) { - Q_UNUSED(openMode); - Q_UNUSED(address); - Q_UNUSED(port); + Q_Q(QBluetoothSocket); + + if (socket == INVALID_SOCKET && !ensureNativeSocket(socketType)) + return; + + if (!configureSecurity()) + return; + + SOCKADDR_BTH addr = {0}; + addr.addressFamily = AF_BTH; + addr.port = port; + addr.btAddr = address.toUInt64(); + + switch (socketType) { + case QBluetoothServiceInfo::RfcommProtocol: + addr.serviceClassId = RFCOMM_PROTOCOL_UUID; + break; + default: + errorString = QBluetoothSocket::tr("Socket type not handled: %1").arg(socketType); + q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); + return; + } + + connectWriteNotifier->setEnabled(true); + readNotifier->setEnabled(true); + exceptNotifier->setEnabled(true); + + const int result = ::connect(socket, reinterpret_cast(&addr), sizeof(addr)); + + const int error = ::WSAGetLastError(); + if (result != SOCKET_ERROR || error == WSAEWOULDBLOCK) { + q->setSocketState(QBluetoothSocket::ConnectingState); + q->setOpenMode(openMode); + } else { + errorString = qt_error_string(error); + q->setSocketError(QBluetoothSocket::UnknownSocketError); + } } void QBluetoothSocketPrivateWin::connectToService( @@ -123,6 +195,97 @@ void QBluetoothSocketPrivateWin::connectToService( q->doDeviceDiscovery(service, openMode); } } +void QBluetoothSocketPrivateWin::_q_writeNotify() +{ + Q_Q(QBluetoothSocket); + + if (state == QBluetoothSocket::ConnectingState) { + updateAddressesAndPorts(); + q->setSocketState(QBluetoothSocket::ConnectedState); + emit q->connected(); + connectWriteNotifier->setEnabled(false); + } else { + if (txBuffer.isEmpty()) { + connectWriteNotifier->setEnabled(false); + return; + } + + char buf[1024]; + const int size = txBuffer.read(&buf[0], sizeof(buf)); + const int writtenBytes = ::send(socket, &buf[0], size, 0); + if (writtenBytes == SOCKET_ERROR) { + // every other case returns error + const int error = ::WSAGetLastError(); + errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error)); + q->setSocketError(QBluetoothSocket::NetworkError); + } else if (writtenBytes <= size) { + // add remainder back to buffer + char *remainder = &buf[writtenBytes]; + txBuffer.ungetBlock(remainder, size - writtenBytes); + if (writtenBytes > 0) + emit q->bytesWritten(writtenBytes); + } else { + errorString = QBluetoothSocket::tr("Logic error: more bytes sent than passed to ::send"); + q->setSocketError(QBluetoothSocket::NetworkError); + } + + if (!txBuffer.isEmpty()) { + connectWriteNotifier->setEnabled(true); + } else if (state == QBluetoothSocket::ClosingState) { + connectWriteNotifier->setEnabled(false); + this->close(); + } + } +} + +void QBluetoothSocketPrivateWin::_q_readNotify() +{ + Q_Q(QBluetoothSocket); + + char *writePointer = buffer.reserve(QPRIVATELINEARBUFFER_BUFFERSIZE); + const int bytesRead = ::recv(socket, writePointer, QPRIVATELINEARBUFFER_BUFFERSIZE, 0); + if (bytesRead == SOCKET_ERROR) { + const int error = ::WSAGetLastError(); + buffer.chop(QPRIVATELINEARBUFFER_BUFFERSIZE); + readNotifier->setEnabled(false); + connectWriteNotifier->setEnabled(false); + errorString = qt_error_string(error); + qCWarning(QT_BT_WINDOWS) << Q_FUNC_INFO << socket << "error:" << error << errorString; + switch (error) { + case WSAEHOSTDOWN: + q->setSocketError(QBluetoothSocket::HostNotFoundError); + break; + case WSAECONNRESET: + q->setSocketError(QBluetoothSocket::RemoteHostClosedError); + break; + default: + q->setSocketError(QBluetoothSocket::UnknownSocketError); + break; + } + + q->disconnectFromService(); + } else { + const int unusedBytes = QPRIVATELINEARBUFFER_BUFFERSIZE - bytesRead; + buffer.chop(unusedBytes); + if (bytesRead > 0) + emit q->readyRead(); + } +} + +void QBluetoothSocketPrivateWin::_q_exceptNotify() +{ + Q_Q(QBluetoothSocket); + + const int error = ::WSAGetLastError(); + errorString = qt_error_string(error); + q->setSocketError(QBluetoothSocket::UnknownSocketError); + + if (state == QBluetoothSocket::ConnectingState) { + abort(); + q->setSocketState(QBluetoothSocket::UnconnectedState); + emit q->disconnected(); + } +} void QBluetoothSocketPrivateWin::connectToService( const QBluetoothAddress &address, const QBluetoothUuid &uuid, @@ -178,43 +341,66 @@ void QBluetoothSocketPrivateWin::connectToService( void QBluetoothSocketPrivateWin::abort() { + delete readNotifier; + readNotifier = nullptr; + delete connectWriteNotifier; + connectWriteNotifier = nullptr; + delete exceptNotifier; + exceptNotifier = nullptr; + + m_localAddress.clear(); + m_localPort = 0; + m_peerAddress.clear(); + m_peerPort = 0; + + // We don't transition through Closing for abort, so + // we don't call disconnectFromService or QBluetoothSocket::close + ::closesocket(socket); + socket = INVALID_SOCKET; } QString QBluetoothSocketPrivateWin::localName() const { - return QString(); + const QBluetoothLocalDevice device(m_localAddress); + return device.name(); } QBluetoothAddress QBluetoothSocketPrivateWin::localAddress() const { - return QBluetoothAddress(); + return m_localAddress; } quint16 QBluetoothSocketPrivateWin::localPort() const { - return 0; + return m_localPort; } QString QBluetoothSocketPrivateWin::peerName() const { - return QString(); + if (socket == INVALID_SOCKET) + return {}; + BLUETOOTH_DEVICE_INFO bdi = {0}; + bdi.dwSize = sizeof(bdi); + bdi.Address.ullLong = m_peerAddress.toUInt64(); + const DWORD res = ::BluetoothGetDeviceInfo(nullptr, &bdi); + if (res == ERROR_SUCCESS) + return QString::fromWCharArray(&bdi.szName[0]); + qCWarning(QT_BT_WINDOWS) << "Error calling BluetoothGetDeviceInfo" << res << qt_error_string(res); + return {}; } QBluetoothAddress QBluetoothSocketPrivateWin::peerAddress() const { - return QBluetoothAddress(); + return m_peerAddress; } quint16 QBluetoothSocketPrivateWin::peerPort() const { - return 0; + return m_peerPort; } qint64 QBluetoothSocketPrivateWin::writeData(const char *data, qint64 maxSize) { - Q_UNUSED(data); - Q_UNUSED(maxSize); - Q_Q(QBluetoothSocket); if (state != QBluetoothSocket::ConnectedState) { @@ -222,52 +408,180 @@ qint64 QBluetoothSocketPrivateWin::writeData(const char *data, qint64 maxSize) q->setSocketError(QBluetoothSocket::OperationError); return -1; } - return -1; + + if (q->openMode() & QIODevice::Unbuffered) { + const int bytesWritten = ::send(socket, data, maxSize, 0); + + if (bytesWritten == SOCKET_ERROR) { + const int error = ::WSAGetLastError(); + errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error)); + q->setSocketError(QBluetoothSocket::NetworkError); + } + + if (bytesWritten > 0) + emit q->bytesWritten(bytesWritten); + + return bytesWritten; + } else { + + if (!connectWriteNotifier) + return -1; + + if (txBuffer.isEmpty()) + connectWriteNotifier->setEnabled(true); + + char *txbuf = txBuffer.reserve(maxSize); + ::memcpy(txbuf, data, maxSize); + + return maxSize; + } } qint64 QBluetoothSocketPrivateWin::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"); + errorString = QBluetoothSocket::tr("Cannot read while not connected"); q->setSocketError(QBluetoothSocket::OperationError); return -1; } - return -1; + const int bytesRead = buffer.read(data, maxSize); + return bytesRead; } void QBluetoothSocketPrivateWin::close() { + if (txBuffer.isEmpty()) + abort(); + else + connectWriteNotifier->setEnabled(true); } -bool QBluetoothSocketPrivateWin::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, +bool QBluetoothSocketPrivateWin::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; + Q_Q(QBluetoothSocket); + + abort(); + + socketType = socketType_; + socket = socketDescriptor; + + if (!createNotifiers()) + return false; + updateAddressesAndPorts(); + q->setSocketState(socketState); + q->setOpenMode(openMode); + if (socketState == QBluetoothSocket::ConnectedState) { + connectWriteNotifier->setEnabled(true); + readNotifier->setEnabled(true); + exceptNotifier->setEnabled(true); + } + + return true; } qint64 QBluetoothSocketPrivateWin::bytesAvailable() const { - return 0; + return buffer.size(); } bool QBluetoothSocketPrivateWin::canReadLine() const { - return false; + return buffer.canReadLine(); } qint64 QBluetoothSocketPrivateWin::bytesToWrite() const { - return 0; + return txBuffer.size(); } +bool QBluetoothSocketPrivateWin::createNotifiers() +{ + Q_Q(QBluetoothSocket); + + ULONG mode = 1; // 1 to enable non-blocking socket + const int result = ::ioctlsocket(socket, FIONBIO, &mode); + + if (result == SOCKET_ERROR) { + const int error = ::WSAGetLastError(); + errorString = qt_error_string(error); + q->setSocketError(QBluetoothSocket::UnknownSocketError); + qCWarning(QT_BT_WINDOWS) << "Error setting socket to non-blocking" << error << errorString; + abort(); + return false; + } + readNotifier = new QSocketNotifier(socket, QSocketNotifier::Read); + QObject::connect(readNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_readNotify); + connectWriteNotifier = new QSocketNotifier(socket, QSocketNotifier::Write, q); + QObject::connect(connectWriteNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_writeNotify); + exceptNotifier = new QSocketNotifier(socket, QSocketNotifier::Exception, q); + QObject::connect(exceptNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_exceptNotify); + + connectWriteNotifier->setEnabled(false); + readNotifier->setEnabled(false); + exceptNotifier->setEnabled(false); + return true; +} + +void QBluetoothSocketPrivateWin::updateAddressesAndPorts() +{ + SOCKADDR_BTH localAddr = {0}; + int localAddrLength = sizeof(localAddr); + const int localResult = ::getsockname(socket, reinterpret_cast(&localAddr), &localAddrLength); + if (localResult != SOCKET_ERROR) { + m_localAddress = QBluetoothAddress(localAddr.btAddr); + m_localPort = localAddr.port; + } else { + const int error = ::WSAGetLastError(); + qCWarning(QT_BT_WINDOWS) << "Error getting local address and port" << error << qt_error_string(error); + errorString = QBluetoothSocket::tr("Cannot get socket's local address and port"); + } + + SOCKADDR_BTH peerAddr = {0}; + int peerAddrLength = sizeof(peerAddr); + const int peerResult = ::getpeername(socket, reinterpret_cast(&peerAddr), &peerAddrLength); + if (peerResult != SOCKET_ERROR) { + m_peerAddress = QBluetoothAddress(peerAddr.btAddr); + m_peerPort = peerAddr.port; + } else { + const int error = ::WSAGetLastError(); + qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error); + errorString = QBluetoothSocket::tr("Cannot get socket's peer address and port"); + } +} + +bool QBluetoothSocketPrivateWin::configureSecurity() +{ + Q_Q(QBluetoothSocket); + + if (secFlags & QBluetooth::Authorization) { + ULONG authenticate = TRUE; + const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_AUTHENTICATE, reinterpret_cast(&authenticate), sizeof(authenticate)); + if (result == SOCKET_ERROR) { + const int error = ::WSAGetLastError(); + qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error; + qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error); + errorString = QBluetoothSocket::tr("Cannot set connection security level"); + q->setSocketError(QBluetoothSocket::UnknownSocketError); + return false; + } + } + + if (secFlags & QBluetooth::Encryption) { + ULONG encrypt = TRUE; + const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_ENCRYPT, reinterpret_cast(&encrypt), sizeof(encrypt)); + if (result == SOCKET_ERROR) { + const int error = ::WSAGetLastError(); + qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error; + qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error); + errorString = QBluetoothSocket::tr("Cannot set connection security level"); + q->setSocketError(QBluetoothSocket::UnknownSocketError); + return false; + } + } + return true; +} QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothsocket_win_p.h b/src/bluetooth/qbluetoothsocket_win_p.h index 3255428a..fe0fc99f 100644 --- a/src/bluetooth/qbluetoothsocket_win_p.h +++ b/src/bluetooth/qbluetoothsocket_win_p.h @@ -100,6 +100,22 @@ public: qint64 bytesAvailable() const override; bool canReadLine() const override; qint64 bytesToWrite() const override; + +private slots: + void _q_readNotify(); + void _q_writeNotify(); + void _q_exceptNotify(); + +private: + bool createNotifiers(); + void updateAddressesAndPorts(); + bool configureSecurity(); + + QSocketNotifier *exceptNotifier = nullptr; + QBluetoothAddress m_localAddress; + quint16 m_localPort = 0; + QBluetoothAddress m_peerAddress; + quint16 m_peerPort = 0; }; QT_END_NAMESPACE -- cgit v1.2.3 From 922e37e25b8d361597f1764a71baf75b340d8cf4 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov (VMware)" Date: Sun, 15 Jul 2018 23:55:29 +0300 Subject: win32-ble: store service handles until a service is destroyed Recently, a problem was discovered that adding event registration on a descriptor does not work because the associated service handle is closed right after. For events to work a service should remain open if an event is registered for it. Store service handles (hService) is QLowEnergyServicePrivate. A handle is opened by QLowEnergyControllerPrivateWin32::discoverServiceDetails() and remains open until the device is disconnected. This removes the need to open/close services each time a Write/Read operation is performed on GATT characteristics / descriptors. Move the creation of the worker thread to connectToDevice(). Quit the thread in disconnectFromDevice() and also close any open service handles. Change-Id: Ia634af2e4225f5c1be93b0ddd17639c2dbd70c21 Reviewed-by: Denis Shienkov Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_win.cpp | 69 ++++++++++++++---------------- src/bluetooth/qlowenergyserviceprivate_p.h | 6 +++ 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index c6ea5c78..cc848d1e 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -691,22 +691,12 @@ QLowEnergyControllerPrivateWin32::QLowEnergyControllerPrivateWin32() qCWarning(QT_BT_WINDOWS) << "LE is not supported on this OS"; return; } - - thread = new QThread; - threadWorker = new ThreadWorker; - threadWorker->moveToThread(thread); - connect(threadWorker, &ThreadWorker::jobFinished, this, &QLowEnergyControllerPrivateWin32::jobFinished); - connect(thread, &QThread::finished, threadWorker, &ThreadWorker::deleteLater); - connect(thread, &QThread::finished, thread, &QThread::deleteLater); - thread->start(); } QLowEnergyControllerPrivateWin32::~QLowEnergyControllerPrivateWin32() { QMutexLocker locker(&controllersGuard); qControllers()->removeAll(this); - if (thread) - thread->quit(); } void QLowEnergyControllerPrivateWin32::init() @@ -743,6 +733,14 @@ void QLowEnergyControllerPrivateWin32::connectToDevice() setState(QLowEnergyController::ConnectedState); + thread = new QThread; + threadWorker = new ThreadWorker; + threadWorker->moveToThread(thread); + connect(threadWorker, &ThreadWorker::jobFinished, this, &QLowEnergyControllerPrivateWin32::jobFinished); + connect(thread, &QThread::finished, threadWorker, &ThreadWorker::deleteLater); + connect(thread, &QThread::finished, thread, &QThread::deleteLater); + thread->start(); + Q_Q(QLowEnergyController); emit q->connected(); } @@ -758,6 +756,15 @@ void QLowEnergyControllerPrivateWin32::disconnectFromDevice() deviceSystemPath.clear(); setState(QLowEnergyController::UnconnectedState); + if (thread) { + disconnect(threadWorker, &ThreadWorker::jobFinished, this, &QLowEnergyControllerPrivateWin32::jobFinished); + thread->quit(); + thread = nullptr; + } + + for (const QSharedPointer servicePrivate: serviceList) + closeSystemDevice(servicePrivate->hService); + Q_Q(QLowEnergyController); emit q->disconnected(); } @@ -827,8 +834,12 @@ void QLowEnergyControllerPrivateWin32::discoverServiceDetails( int systemErrorCode = NO_ERROR; - const HANDLE hService = openSystemService( - remoteDevice, service, QIODevice::ReadOnly, &systemErrorCode); + // Only open a service once and close it in the QLowEnergyServicePrivate destructor + if (!servicePrivate->hService || servicePrivate->hService == INVALID_HANDLE_VALUE) { + servicePrivate->hService = openSystemService(remoteDevice, service, + QIODevice::ReadOnly | QIODevice::WriteOnly, + &systemErrorCode); + } if (systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service.toString() @@ -842,10 +853,9 @@ void QLowEnergyControllerPrivateWin32::discoverServiceDetails( servicePrivate->endHandle = servicePrivate->startHandle; const QVector foundCharacteristics = - enumerateGattCharacteristics(hService, NULL, &systemErrorCode); + enumerateGattCharacteristics(servicePrivate->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); @@ -884,7 +894,7 @@ void QLowEnergyControllerPrivateWin32::discoverServiceDetails( detailsData.properties = properties; detailsData.value = getGattCharacteristicValue( - hService, const_cast( + servicePrivate->hService, const_cast( &gattCharacteristic), &systemErrorCode); if (systemErrorCode != NO_ERROR) { @@ -903,12 +913,11 @@ void QLowEnergyControllerPrivateWin32::discoverServiceDetails( QLowEnergyHandle(gattCharacteristic.AttributeHandle + 1)); const QVector foundDescriptors = enumerateGattDescriptors( - hService, const_cast( + servicePrivate->hService, const_cast( &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() @@ -926,11 +935,10 @@ void QLowEnergyControllerPrivateWin32::discoverServiceDetails( data.uuid = qtBluetoothUuidFromNativeLeUuid( gattDescriptor.DescriptorUuid); - data.value = getGattDescriptorValue(hService, const_cast( + data.value = getGattDescriptorValue(servicePrivate->hService, const_cast( &gattDescriptor), &systemErrorCode); if (systemErrorCode != NO_ERROR) { - closeSystemDevice(hService); qCWarning(QT_BT_WINDOWS) << "Unable to get value for descriptor" << data.uuid.toString() << "for characteristic" @@ -953,8 +961,6 @@ void QLowEnergyControllerPrivateWin32::discoverServiceDetails( servicePrivate->characteristicList.insert(characteristicHandle, detailsData); } - closeSystemDevice(hService); - servicePrivate->setState(QLowEnergyService::ServiceDiscovered); } @@ -992,8 +998,7 @@ void QLowEnergyControllerPrivateWin32::readCharacteristic( ReadCharData data; data.systemErrorCode = NO_ERROR; - data.hService = openSystemService( - remoteDevice, service->uuid, QIODevice::ReadOnly, &data.systemErrorCode); + data.hService = service->hService; if (data.systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() @@ -1028,8 +1033,7 @@ void QLowEnergyControllerPrivateWin32::writeCharacteristic( WriteCharData data; data.systemErrorCode = NO_ERROR; - data.hService = openSystemService( - remoteDevice, service->uuid, QIODevice::ReadWrite, &data.systemErrorCode); + data.hService = service->hService; if (data.systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() @@ -1064,7 +1068,6 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) case ThreadWorkerJob::WriteChar: { const WriteCharData data = job.data.value(); - closeSystemDevice(data.hService); const QLowEnergyHandle charHandle = static_cast(data.gattCharacteristic.AttributeHandle); const QSharedPointer service = serviceForHandle(charHandle); @@ -1089,7 +1092,6 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) case ThreadWorkerJob::ReadChar: { const ReadCharData data = job.data.value(); - closeSystemDevice(data.hService); const QLowEnergyHandle charHandle = static_cast(data.gattCharacteristic.AttributeHandle); const QSharedPointer service = serviceForHandle(charHandle); @@ -1119,7 +1121,6 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle]; if (data.systemErrorCode != NO_ERROR) { - closeSystemDevice(data.hService); qCWarning(QT_BT_WINDOWS) << "Unable to set value for descriptor" << dscrDetails.uuid.toString() << "for characteristic" @@ -1142,6 +1143,7 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) BTH_LE_GATT_CHARACTERISTIC gattCharacteristic = recoverNativeLeGattCharacteristic( service->startHandle, charHandle, charDetails); + // note: if the service handle is closed the event registration is no longer valid. charDetails.hValueChangeEvent = registerEvent( data.hService, gattCharacteristic, this, &data.systemErrorCode); } @@ -1152,8 +1154,6 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) } } - closeSystemDevice(data.hService); - if (data.systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to subscribe events for descriptor" << dscrDetails.uuid.toString() @@ -1164,8 +1164,6 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) service->setError(QLowEnergyService::DescriptorWriteError); return; } - } else { - closeSystemDevice(data.hService); } updateValueOfDescriptor(charHandle, descriptorHandle, data.newValue, false); @@ -1182,7 +1180,6 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) const QSharedPointer service = serviceForHandle(charHandle); QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle]; const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle]; - closeSystemDevice(data.hService); if (data.systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to get value for descriptor" @@ -1222,8 +1219,7 @@ void QLowEnergyControllerPrivateWin32::readDescriptor( ReadDescData data; data.systemErrorCode = NO_ERROR; - data.hService = openSystemService( - remoteDevice, service->uuid, QIODevice::ReadOnly, &data.systemErrorCode); + data.hService = service->hService; if (data.systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() @@ -1264,8 +1260,7 @@ void QLowEnergyControllerPrivateWin32::writeDescriptor( WriteDescData data; data.systemErrorCode = NO_ERROR; data.newValue = newValue; - data.hService = openSystemService( - remoteDevice, service->uuid, QIODevice::ReadWrite, &data.systemErrorCode); + data.hService = service->hService; if (data.systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h index 26ca56f6..35c8de7e 100644 --- a/src/bluetooth/qlowenergyserviceprivate_p.h +++ b/src/bluetooth/qlowenergyserviceprivate_p.h @@ -60,6 +60,9 @@ #if defined(QT_ANDROID_BLUETOOTH) #include #endif +#if defined(QT_WIN_BLUETOOTH) +#include +#endif QT_BEGIN_NAMESPACE @@ -131,6 +134,9 @@ public: // reference to the BluetoothGattService object QAndroidJniObject androidService; #endif +#if defined(QT_WIN_BLUETOOTH) + Qt::HANDLE hService = nullptr; +#endif }; -- cgit v1.2.3 From 6cdb8dc9cd8a518d050d08be79938feee7a9bde2 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Sat, 29 Sep 2018 04:10:59 +0300 Subject: qbluetoothservicediscoveryagent_win: use lowercase import The import of "WinSock2.h" fails on MXE environments because cross building from Linux to Windows makes it so that the filesystem is case sensitive. Change-Id: I6d89530f430eb45bf07154cffb5938eb3761ee1e Reviewed-by: Denis Shienkov --- src/bluetooth/qbluetoothservicediscoveryagent_win.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index 2c3c6437..e76dd7b6 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -47,7 +47,7 @@ #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 5a2c8a39d76d634161e87b1c55d36cbbcc778289 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Mon, 11 Feb 2019 02:08:53 +0200 Subject: win32-bt: fix warnings related to comparisons and initializers Silence a couple of type of warnings: 1) warning: missing initializer for member '_BLUETOOTH_DEVICE_INFO::Address' [-Wmissing-field-initializers] BLUETOOTH_DEVICE_INFO deviceInfo = {0}; Unlike C, C++ is not happy about this type of initializer Use '{}' instead to init all members to zero or 'NULL' properly. 2) warning: comparison between signed and unsigned integer expressions [-Wsign-compare] if (socket != INVALID_SOCKET) { 'socket' is defined as 'int', while 'INVALID_SOCKET' is derived from 'SOCKET' which is of type UINT. Cast 'socket' to 'SOCKET' before performing the comparison. Change-Id: I9fcbff1c60ae36b8a8fed63382a8ffc0157f4923 Reviewed-by: Oliver Wolff --- src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp | 10 +++++----- src/bluetooth/qbluetoothservicediscoveryagent_win.cpp | 2 +- src/bluetooth/qbluetoothsocket_win.cpp | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 7509c5f6..3645bd21 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -132,7 +132,7 @@ static QBluetoothDeviceInfo createClassicDeviceInfo(const BLUETOOTH_DEVICE_INFO static QBluetoothDeviceInfo findFirstClassicDevice( DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND *hSearch) { - BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = {0}; + BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = {}; searchParams.dwSize = sizeof(searchParams); searchParams.cTimeoutMultiplier = 10; // 12.8 sec searchParams.fIssueInquiry = TRUE; @@ -142,7 +142,7 @@ static QBluetoothDeviceInfo findFirstClassicDevice( searchParams.fReturnUnknown = TRUE; searchParams.hRadio = nullptr; - BLUETOOTH_DEVICE_INFO deviceInfo = {0}; + BLUETOOTH_DEVICE_INFO deviceInfo = {}; deviceInfo.dwSize = sizeof(deviceInfo); const HBLUETOOTH_DEVICE_FIND hFind = ::BluetoothFindFirstDevice( @@ -163,7 +163,7 @@ static QBluetoothDeviceInfo findFirstClassicDevice( static QBluetoothDeviceInfo findNextClassicDevice( DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND hSearch) { - BLUETOOTH_DEVICE_INFO deviceInfo = {0}; + BLUETOOTH_DEVICE_INFO deviceInfo = {}; deviceInfo.dwSize = sizeof(deviceInfo); QBluetoothDeviceInfo foundDevice; @@ -204,7 +204,7 @@ static QVector enumerateLeDevices( QVector cachedEntries; for (;;) { - SP_DEVICE_INTERFACE_DATA deviceInterfaceData = {0}; + SP_DEVICE_INTERFACE_DATA deviceInterfaceData = {}; deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); if (!::SetupDiEnumDeviceInterfaces( @@ -231,7 +231,7 @@ static QVector enumerateLeDevices( } } - SP_DEVINFO_DATA deviceInfoData = {0}; + SP_DEVINFO_DATA deviceInfoData = {}; deviceInfoData.cbSize = sizeof(deviceInfoData); QByteArray deviceInterfaceDetailDataBuffer( diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index e76dd7b6..98ec1952 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -331,7 +331,7 @@ static FindServiceResult findFirstService(const QBluetoothAddress &address) GUID protocol = L2CAP_PROTOCOL_UUID; //Search for L2CAP and RFCOMM services - WSAQUERYSET serviceQuery = {0}; + WSAQUERYSET 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 diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp index 14c780d1..781decad 100644 --- a/src/bluetooth/qbluetoothsocket_win.cpp +++ b/src/bluetooth/qbluetoothsocket_win.cpp @@ -68,7 +68,7 @@ bool QBluetoothSocketPrivateWin::ensureNativeSocket(QBluetoothServiceInfo::Proto { Q_Q(QBluetoothSocket); - if (socket != INVALID_SOCKET) { + if (static_cast(socket) != INVALID_SOCKET) { if (socketType == type) return true; abort(); @@ -87,7 +87,7 @@ bool QBluetoothSocketPrivateWin::ensureNativeSocket(QBluetoothServiceInfo::Proto return false; } - if (socket == INVALID_SOCKET) { + if (static_cast(socket) == INVALID_SOCKET) { const int error = ::WSAGetLastError(); qCWarning(QT_BT_WINDOWS) << "Failed to create socket:" << error << qt_error_string(error); errorString = QBluetoothSocket::tr("Failed to create socket"); @@ -105,13 +105,13 @@ void QBluetoothSocketPrivateWin::connectToServiceHelper(const QBluetoothAddress { Q_Q(QBluetoothSocket); - if (socket == INVALID_SOCKET && !ensureNativeSocket(socketType)) + if (static_cast(socket) == INVALID_SOCKET && !ensureNativeSocket(socketType)) return; if (!configureSecurity()) return; - SOCKADDR_BTH addr = {0}; + SOCKADDR_BTH addr = {}; addr.addressFamily = AF_BTH; addr.port = port; addr.btAddr = address.toUInt64(); @@ -377,9 +377,9 @@ quint16 QBluetoothSocketPrivateWin::localPort() const QString QBluetoothSocketPrivateWin::peerName() const { - if (socket == INVALID_SOCKET) + if (static_cast(socket) == INVALID_SOCKET) return {}; - BLUETOOTH_DEVICE_INFO bdi = {0}; + BLUETOOTH_DEVICE_INFO bdi = {}; bdi.dwSize = sizeof(bdi); bdi.Address.ullLong = m_peerAddress.toUInt64(); const DWORD res = ::BluetoothGetDeviceInfo(nullptr, &bdi); @@ -528,7 +528,7 @@ bool QBluetoothSocketPrivateWin::createNotifiers() void QBluetoothSocketPrivateWin::updateAddressesAndPorts() { - SOCKADDR_BTH localAddr = {0}; + SOCKADDR_BTH localAddr = {}; int localAddrLength = sizeof(localAddr); const int localResult = ::getsockname(socket, reinterpret_cast(&localAddr), &localAddrLength); if (localResult != SOCKET_ERROR) { @@ -540,7 +540,7 @@ void QBluetoothSocketPrivateWin::updateAddressesAndPorts() errorString = QBluetoothSocket::tr("Cannot get socket's local address and port"); } - SOCKADDR_BTH peerAddr = {0}; + SOCKADDR_BTH peerAddr = {}; int peerAddrLength = sizeof(peerAddr); const int peerResult = ::getpeername(socket, reinterpret_cast(&peerAddr), &peerAddrLength); if (peerResult != SOCKET_ERROR) { -- cgit v1.2.3 From 8b7b52d66f2616040ca4aaae3f2732be96e19ab8 Mon Sep 17 00:00:00 2001 From: Andre de la Rocha Date: Fri, 23 Nov 2018 18:04:40 +0100 Subject: Enable the use of the Win32 Bluetooth backend This change enables the optional use of the Win32-based Bluetooth backend on Windows. By default, the WinRT backend is used, if supported by the platform. The use of the Win32 backend must be selected by the -native-win32-bluetooth configuration option. Task-number: QTBUG-40698 Change-Id: I6904bf077467d826e3ff5ad026ebae5f955f2e37 Reviewed-by: Oliver Wolff Reviewed-by: Alex Blasche --- src/bluetooth/bluetooth.pro | 3 - src/bluetooth/configure.json | 16 +- .../qbluetoothdevicediscoveryagent_win.cpp | 43 +++--- src/bluetooth/qbluetoothlocaldevice_win.cpp | 32 ++-- src/bluetooth/qbluetoothserver.cpp | 3 +- src/bluetooth/qbluetoothserver_android.cpp | 5 +- src/bluetooth/qbluetoothserver_bluez.cpp | 5 +- src/bluetooth/qbluetoothserver_p.cpp | 6 +- src/bluetooth/qbluetoothserver_p.h | 10 +- src/bluetooth/qbluetoothserver_win.cpp | 162 ++++++++++++++++++-- src/bluetooth/qbluetoothserver_winrt.cpp | 6 +- src/bluetooth/qbluetoothservicediscoveryagent.h | 4 - src/bluetooth/qbluetoothservicediscoveryagent_p.h | 1 + .../qbluetoothservicediscoveryagent_win.cpp | 157 +++++++++---------- src/bluetooth/qbluetoothserviceinfo_p.h | 13 ++ src/bluetooth/qbluetoothserviceinfo_win.cpp | 59 +++++++- src/bluetooth/qbluetoothsocket_win.cpp | 168 +++++++++++---------- src/bluetooth/qbluetoothsocket_win_p.h | 5 - src/bluetooth/qbluetoothtransfermanager.cpp | 4 +- src/bluetooth/qlowenergycontroller.cpp | 1 + src/bluetooth/qlowenergycontroller_p.h | 1 - src/bluetooth/qlowenergycontroller_win.cpp | 90 ++++++----- .../tst_qbluetoothlocaldevice.cpp | 13 ++ .../tst_qbluetoothserviceinfo.cpp | 2 +- .../tst_qlowenergydescriptor.cpp | 79 ++++++---- 25 files changed, 558 insertions(+), 330 deletions(-) diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index eedd4864..a2fd617d 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -252,9 +252,6 @@ qtConfig(bluez) { include(windows/windows.pri) - # remove dummy warning once platform port is complete - include(dummy/dummy.pri) - SOURCES += \ qbluetoothdevicediscoveryagent_win.cpp \ qbluetoothlocaldevice_win.cpp \ diff --git a/src/bluetooth/configure.json b/src/bluetooth/configure.json index 53923e12..3bd95903 100644 --- a/src/bluetooth/configure.json +++ b/src/bluetooth/configure.json @@ -2,6 +2,12 @@ "module": "bluetooth", "testDir": "../../config.tests", + "commandline": { + "options": { + "native-win32-bluetooth": "boolean" + } + }, + "libraries": { "bluez": { "label": "BlueZ", @@ -52,9 +58,16 @@ "condition": "features.bluez_le && tests.linux_crypto_api", "output": [ "privateFeature" ] }, + "native-win32-bluetooth": { + "label": "Native Win32 Bluetooth", + "purpose": "Uses the native Win32 Bluetooth backend.", + "section": "Qt Bluetooth", + "autoDetect": false, + "output": [ "publicFeature", "feature" ] + }, "winrt_bt": { "label": "WinRT Bluetooth API (desktop & UWP)", - "condition": "config.win32 && tests.winrt_bt", + "condition": "config.win32 && !features.native-win32-bluetooth && tests.winrt_bt", "output": [ "privateFeature" ] }, "winrt_btle_no_pairing": { @@ -85,6 +98,7 @@ Only classic Bluetooth will be available." "bluez", "bluez_le", "linux_crypto_api", + "native-win32-bluetooth", "winrt_bt", "winrt_btle_no_pairing" ] diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp index 3645bd21..159428d4 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp @@ -154,7 +154,7 @@ static QBluetoothDeviceInfo findFirstClassicDevice( *systemErrorCode = NO_ERROR; foundDevice = createClassicDeviceInfo(deviceInfo); } else { - *systemErrorCode = ::GetLastError(); + *systemErrorCode = int(::GetLastError()); } return foundDevice; @@ -168,7 +168,7 @@ static QBluetoothDeviceInfo findNextClassicDevice( QBluetoothDeviceInfo foundDevice; if (!::BluetoothFindNextDevice(hSearch, &deviceInfo)) { - *systemErrorCode = ::GetLastError(); + *systemErrorCode = int(::GetLastError()); } else { *systemErrorCode = NO_ERROR; foundDevice = createClassicDeviceInfo(deviceInfo); @@ -186,6 +186,7 @@ static void closeClassicSearch(HBLUETOOTH_DEVICE_FIND hSearch) static QVector enumerateLeDevices( DWORD *systemErrorCode) { + // GUID_BLUETOOTHLE_DEVICE_INTERFACE const QUuid deviceInterfaceGuid("781aee18-7733-4ce4-add0-91f41c67b592"); const HDEVINFO hDeviceInfo = ::SetupDiGetClassDevs( reinterpret_cast(&deviceInterfaceGuid), @@ -194,7 +195,7 @@ static QVector enumerateLeDevices( DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (hDeviceInfo == INVALID_HANDLE_VALUE) { - *systemErrorCode = ::GetLastError(); + *systemErrorCode = int(::GetLastError()); return QVector(); } @@ -213,7 +214,7 @@ static QVector enumerateLeDevices( reinterpret_cast(&deviceInterfaceGuid), index++, &deviceInterfaceData)) { - *systemErrorCode = ::GetLastError(); + *systemErrorCode = int(::GetLastError()); break; } @@ -226,7 +227,7 @@ static QVector enumerateLeDevices( &deviceInterfaceDetailDataSize, nullptr)) { if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - *systemErrorCode = ::GetLastError(); + *systemErrorCode = int(::GetLastError()); break; } } @@ -251,7 +252,7 @@ static QVector enumerateLeDevices( deviceInterfaceDetailDataBuffer.size(), &deviceInterfaceDetailDataSize, &deviceInfoData)) { - *systemErrorCode = ::GetLastError(); + *systemErrorCode = int(::GetLastError()); break; } @@ -534,28 +535,20 @@ void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevice( auto end = discoveredDevices.end(); auto deviceIt = std::find_if(discoveredDevices.begin(), end, equalAddress); if (deviceIt == end) { - qCDebug(QT_BT_WINDOWS) << "Emit: " << foundDevice.address(); + qCDebug(QT_BT_WINDOWS) << "Found device: " << foundDevice.name() << 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); + qCDebug(QT_BT_WINDOWS) << "Updating device:" << deviceIt->name() << deviceIt->address(); + // merge service uuids + QList uuids = deviceIt->serviceUuids(); + uuids.append(foundDevice.serviceUuids()); + const QSet uuidSet = uuids.toSet(); + if (deviceIt->serviceUuids().count() != uuidSet.count()) + deviceIt->setServiceUuids(uuidSet.toList().toVector()); + if (deviceIt->coreConfigurations() != foundDevice.coreConfigurations()) + deviceIt->setCoreConfigurations( + QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration); } } diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp index a3f3c339..cf17cad1 100644 --- a/src/bluetooth/qbluetoothlocaldevice_win.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp @@ -100,39 +100,34 @@ void QBluetoothLocalDevice::setHostMode( return; if (requestedMode == QBluetoothLocalDevice::HostPoweredOff) { - if (::BluetoothIsDiscoverable(NULL) - && !::BluetoothEnableDiscovery(NULL, FALSE)) { + if (::BluetoothIsDiscoverable(nullptr) + && !::BluetoothEnableDiscovery(nullptr, FALSE)) { qCWarning(QT_BT_WINDOWS) << "Unable to disable the discoverable mode"; emit error(QBluetoothLocalDevice::UnknownError); return; } - if (::BluetoothIsConnectable(NULL) - && !::BluetoothEnableIncomingConnections(NULL, FALSE)) { + if (::BluetoothIsConnectable(nullptr) + && !::BluetoothEnableIncomingConnections(nullptr, FALSE)) { qCWarning(QT_BT_WINDOWS) << "Unable to disable the connectable mode"; emit error(QBluetoothLocalDevice::UnknownError); return; } } else if (requestedMode == QBluetoothLocalDevice::HostConnectable) { - if (::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)) { + if (!::BluetoothIsConnectable(nullptr) + && !::BluetoothEnableIncomingConnections(nullptr, TRUE)) { qCWarning(QT_BT_WINDOWS) << "Unable to enable the connectable mode"; emit error(QBluetoothLocalDevice::UnknownError); return; } } else if (requestedMode == QBluetoothLocalDevice::HostDiscoverable || requestedMode == QBluetoothLocalDevice::HostDiscoverableLimitedInquiry) { - if (!::BluetoothIsConnectable(NULL) - && !::BluetoothEnableIncomingConnections(NULL, TRUE)) { + if (!::BluetoothIsConnectable(nullptr) + && !::BluetoothEnableIncomingConnections(nullptr, TRUE)) { qCWarning(QT_BT_WINDOWS) << "Unable to enable the connectable mode"; emit error(QBluetoothLocalDevice::UnknownError); return; } - if (!::BluetoothEnableDiscovery(NULL, TRUE)) { + if (!::BluetoothEnableDiscovery(nullptr, TRUE)) { qCWarning(QT_BT_WINDOWS) << "Unable to enable the discoverable mode"; emit error(QBluetoothLocalDevice::UnknownError); return; @@ -149,9 +144,9 @@ QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const return HostPoweredOff; } - if (::BluetoothIsDiscoverable(NULL)) + if (::BluetoothIsDiscoverable(nullptr)) return HostDiscoverable; - if (::BluetoothIsConnectable(NULL)) + if (::BluetoothIsConnectable(nullptr)) return HostConnectable; return HostPoweredOff; } @@ -170,18 +165,21 @@ void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pai { Q_UNUSED(address); Q_UNUSED(pairing); + Q_UNIMPLEMENTED(); } QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus( const QBluetoothAddress &address) const { Q_UNUSED(address); + Q_UNIMPLEMENTED(); return Unpaired; } void QBluetoothLocalDevice::pairingConfirmation(bool confirmation) { Q_UNUSED(confirmation); + Q_UNIMPLEMENTED(); } QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate( @@ -230,7 +228,7 @@ QList QBluetoothLocalDevicePrivate::localAdapters() QList foundAdapters; - HANDLE hRadio = 0; + HANDLE hRadio = nullptr; if (const HBLUETOOTH_RADIO_FIND hSearch = ::BluetoothFindFirstRadio(¶ms, &hRadio)) { for (;;) { BLUETOOTH_RADIO_INFO radio; diff --git a/src/bluetooth/qbluetoothserver.cpp b/src/bluetooth/qbluetoothserver.cpp index 6991518f..75ac9979 100644 --- a/src/bluetooth/qbluetoothserver.cpp +++ b/src/bluetooth/qbluetoothserver.cpp @@ -167,9 +167,8 @@ QT_BEGIN_NAMESPACE Constructs a bluetooth server with \a parent and \a serverType. */ QBluetoothServer::QBluetoothServer(QBluetoothServiceInfo::Protocol serverType, QObject *parent) - : QObject(parent), d_ptr(new QBluetoothServerPrivate(serverType)) + : QObject(parent), d_ptr(new QBluetoothServerPrivate(serverType, this)) { - d_ptr->q_ptr = this; } /*! diff --git a/src/bluetooth/qbluetoothserver_android.cpp b/src/bluetooth/qbluetoothserver_android.cpp index 4c469c76..eed3a1ea 100644 --- a/src/bluetooth/qbluetoothserver_android.cpp +++ b/src/bluetooth/qbluetoothserver_android.cpp @@ -53,9 +53,10 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) QHash __fakeServerPorts; -QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType) +QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType, + QBluetoothServer *parent) : socket(0),maxPendingConnections(1), securityFlags(QBluetooth::NoSecurity), serverType(sType), - m_lastError(QBluetoothServer::NoError) + m_lastError(QBluetoothServer::NoError), q_ptr(parent) { thread = new ServerAcceptanceThread(); thread->setMaxPendingConnections(maxPendingConnections); diff --git a/src/bluetooth/qbluetoothserver_bluez.cpp b/src/bluetooth/qbluetoothserver_bluez.cpp index 54bc85a0..a98f5398 100644 --- a/src/bluetooth/qbluetoothserver_bluez.cpp +++ b/src/bluetooth/qbluetoothserver_bluez.cpp @@ -66,9 +66,10 @@ QBluetoothSocket *QBluetoothServerPrivate::createSocketForServer( return socket; } -QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType) +QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType, + QBluetoothServer *parent) : maxPendingConnections(1), securityFlags(QBluetooth::Authorization), serverType(sType), - m_lastError(QBluetoothServer::NoError) + m_lastError(QBluetoothServer::NoError), q_ptr(parent) { if (sType == QBluetoothServiceInfo::RfcommProtocol) socket = createSocketForServer(QBluetoothServiceInfo::RfcommProtocol); diff --git a/src/bluetooth/qbluetoothserver_p.cpp b/src/bluetooth/qbluetoothserver_p.cpp index 4f28c9b1..6657e151 100644 --- a/src/bluetooth/qbluetoothserver_p.cpp +++ b/src/bluetooth/qbluetoothserver_p.cpp @@ -46,8 +46,10 @@ QT_BEGIN_NAMESPACE -QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType) - : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError) +QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType, + QBluetoothServer *parent) + : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError), + q_ptr(parent) { #ifndef QT_IOS_BLUETOOTH printDummyWarning(); diff --git a/src/bluetooth/qbluetoothserver_p.h b/src/bluetooth/qbluetoothserver_p.h index d78eee5f..5ace7f75 100644 --- a/src/bluetooth/qbluetoothserver_p.h +++ b/src/bluetooth/qbluetoothserver_p.h @@ -57,7 +57,7 @@ #include "qbluetoothserver.h" #include "qbluetooth.h" -#if QT_CONFIG(bluez) +#if QT_CONFIG(bluez) || defined(QT_WIN_BLUETOOTH) QT_FORWARD_DECLARE_CLASS(QSocketNotifier) #endif @@ -81,7 +81,6 @@ QT_BEGIN_NAMESPACE class QBluetoothAddress; class QBluetoothSocket; - class QBluetoothServer; #ifndef QT_OSX_BLUETOOTH @@ -91,7 +90,7 @@ class QBluetoothServerPrivate Q_DECLARE_PUBLIC(QBluetoothServer) public: - QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol serverType); + QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol serverType, QBluetoothServer *parent); ~QBluetoothServerPrivate(); #if QT_CONFIG(bluez) @@ -101,6 +100,9 @@ public: static QBluetoothSocket *createSocketForServer( QBluetoothServiceInfo::Protocol socketType = QBluetoothServiceInfo::RfcommProtocol); #endif +#if defined(QT_WIN_BLUETOOTH) + void _q_newConnection(); +#endif public: QBluetoothSocket *socket; @@ -114,7 +116,7 @@ protected: private: QBluetoothServer::Error m_lastError; -#if QT_CONFIG(bluez) +#if QT_CONFIG(bluez) || defined(QT_WIN_BLUETOOTH) QSocketNotifier *socketNotifier = nullptr; #elif defined(QT_ANDROID_BLUETOOTH) ServerAcceptanceThread *thread; diff --git a/src/bluetooth/qbluetoothserver_win.cpp b/src/bluetooth/qbluetoothserver_win.cpp index a9f8659e..a57d39a5 100644 --- a/src/bluetooth/qbluetoothserver_win.cpp +++ b/src/bluetooth/qbluetoothserver_win.cpp @@ -40,35 +40,134 @@ #include "qbluetoothserver.h" #include "qbluetoothserver_p.h" #include "qbluetoothsocket.h" +#include "qbluetoothlocaldevice.h" + +#include +#include + +#include +#include +#include QT_BEGIN_NAMESPACE -QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType) - : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError) +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) + +QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType, + QBluetoothServer *parent) + : maxPendingConnections(1), serverType(sType), q_ptr(parent), + m_lastError(QBluetoothServer::NoError) { - if (sType == QBluetoothServiceInfo::RfcommProtocol) - socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol); - else - socket = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol); + Q_Q(QBluetoothServer); + Q_ASSERT(sType == QBluetoothServiceInfo::RfcommProtocol); + socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, q); } QBluetoothServerPrivate::~QBluetoothServerPrivate() { - delete socket; +} + +void QBluetoothServerPrivate::_q_newConnection() +{ + // disable socket notifier until application calls nextPendingConnection(). + socketNotifier->setEnabled(false); + + emit q_ptr->newConnection(); } void QBluetoothServer::close() { + Q_D(QBluetoothServer); + + delete d->socketNotifier; + d->socketNotifier = nullptr; + + d->socket->close(); } bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port) { - Q_UNUSED(address); - Q_UNUSED(port); Q_D(QBluetoothServer); - d->m_lastError = UnsupportedProtocolError; - emit error(d->m_lastError); - return false; + + if (d->serverType != QBluetoothServiceInfo::RfcommProtocol) { + qCWarning(QT_BT_WINDOWS) << "Protocol is not supported."; + d->m_lastError = QBluetoothServer::UnsupportedProtocolError; + emit error(d->m_lastError); + return false; + } + + if (d->socket->state() == QBluetoothSocket::ListeningState) { + qCWarning(QT_BT_WINDOWS) << "Socket already in listen mode, close server first"; + return false; + } + + const QBluetoothLocalDevice device(address); + if (!device.isValid()) { + qCWarning(QT_BT_WINDOWS) << "Device does not support Bluetooth or" + << address.toString() << "is not a valid local adapter"; + d->m_lastError = QBluetoothServer::UnknownError; + emit error(d->m_lastError); + return false; + } + + const QBluetoothLocalDevice::HostMode hostMode = device.hostMode(); + if (hostMode == QBluetoothLocalDevice::HostPoweredOff) { + d->m_lastError = QBluetoothServer::PoweredOffError; + emit error(d->m_lastError); + qCWarning(QT_BT_WINDOWS) << "Bluetooth device is powered off"; + return false; + } + + int sock = d->socket->socketDescriptor(); + if (sock < 0) { + /* Negative socket descriptor is not always an error case. + * Another cause could be a call to close()/abort(). + * Check whether we can recover by re-creating the socket. + */ + delete d->socket; + d->socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this); + sock = d->socket->socketDescriptor(); + if (sock < 0) { + d->m_lastError = InputOutputError; + emit error(d->m_lastError); + return false; + } + } + + if (sock < 0) + return false; + + SOCKADDR_BTH addr = {}; + addr.addressFamily = AF_BTH; + addr.port = (port == 0) ? BT_PORT_ANY : port; + addr.btAddr = address.toUInt64(); + + if (::bind(sock, reinterpret_cast(&addr), sizeof(SOCKADDR_BTH)) < 0) { + if (errno == EADDRINUSE) + d->m_lastError = ServiceAlreadyRegisteredError; + else + d->m_lastError = InputOutputError; + emit error(d->m_lastError); + return false; + } + + if (::listen(sock, d->maxPendingConnections) < 0) { + d->m_lastError = InputOutputError; + emit error(d->m_lastError); + return false; + } + + d->socket->setSocketState(QBluetoothSocket::ListeningState); + + if (!d->socketNotifier) { + d->socketNotifier = new QSocketNotifier(d->socket->socketDescriptor(), + QSocketNotifier::Read, this); + connect(d->socketNotifier, &QSocketNotifier::activated, this, [d](){ + d->_q_newConnection(); + }); + } + + return true; } void QBluetoothServer::setMaxPendingConnections(int numConnections) @@ -78,22 +177,53 @@ void QBluetoothServer::setMaxPendingConnections(int numConnections) bool QBluetoothServer::hasPendingConnections() const { - return false; + Q_D(const QBluetoothServer); + + if (!d || !d->socketNotifier) + return false; + + // if the socket notifier is disabled there is a pending connection waiting for us to accept. + return !d->socketNotifier->isEnabled(); } QBluetoothSocket *QBluetoothServer::nextPendingConnection() { - return 0; + Q_D(QBluetoothServer); + + if (!hasPendingConnections()) + return nullptr; + + if (d->serverType != QBluetoothServiceInfo::RfcommProtocol) + return nullptr; + + SOCKADDR_BTH addr = {}; + int length = sizeof(SOCKADDR_BTH); + int pending = ::accept(d->socket->socketDescriptor(), + reinterpret_cast(&addr), &length); + + QBluetoothSocket *newSocket = nullptr; + + if (pending >= 0) { + newSocket = new QBluetoothSocket(); + newSocket->setSocketDescriptor(pending, QBluetoothServiceInfo::RfcommProtocol); + } + + d->socketNotifier->setEnabled(true); + return newSocket; } QBluetoothAddress QBluetoothServer::serverAddress() const { - return QBluetoothAddress(); + Q_D(const QBluetoothServer); + + return d->socket->localAddress(); } quint16 QBluetoothServer::serverPort() const { - return 0; + Q_D(const QBluetoothServer); + + return d->socket->localPort(); } void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security) diff --git a/src/bluetooth/qbluetoothserver_winrt.cpp b/src/bluetooth/qbluetoothserver_winrt.cpp index 3bcd6b33..b9d98eb7 100644 --- a/src/bluetooth/qbluetoothserver_winrt.cpp +++ b/src/bluetooth/qbluetoothserver_winrt.cpp @@ -70,8 +70,10 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) QHash __fakeServerPorts; -QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType) - : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError), socket(0) +QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType, + QBluetoothServer *parent) + : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError), + socket(0), q_ptr(parent) { #ifdef CLASSIC_APP_BUILD CoInitialize(NULL); diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.h b/src/bluetooth/qbluetoothservicediscoveryagent.h index 8533ab72..f1fa4640 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent.h @@ -111,10 +111,6 @@ Q_SIGNALS: private: QBluetoothServiceDiscoveryAgentPrivate *d_ptr; - -#ifdef QT_WIN_BLUETOOTH - Q_PRIVATE_SLOT(d_func(), void _q_nextSdpScan(QVariant input)) -#endif }; QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h index 4a522826..cb588f70 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h @@ -165,6 +165,7 @@ public: #endif #ifdef QT_WIN_BLUETOOTH void _q_nextSdpScan(const QVariant &input); + bool serviceMatches(const QBluetoothServiceInfo &info); #endif private: diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp index 98ec1952..c34443aa 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp @@ -49,55 +49,30 @@ #include #include #include -#include -#include - -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; +#if defined(Q_CC_MINGW) +// Workaround for MinGW headers declaring BluetoothSdpGetElementData incorrectly. +# define BluetoothSdpGetElementData _BluetoothSdpGetElementData_notok +# include +# undef BluetoothSdpGetElementData + extern "C" DWORD WINAPI BluetoothSdpGetElementData(LPBYTE, ULONG, PSDP_ELEMENT_DATA); +#else +# include +#endif -DEFINEFUNC(DWORD, BluetoothSdpGetElementData, LPBYTE, ULONG, PSDP_ELEMENT_DATA) +#include +#include QT_BEGIN_NAMESPACE struct FindServiceResult { QBluetoothServiceInfo info; - Qt::HANDLE hSearch; - int systemError; + Qt::HANDLE hSearch = INVALID_HANDLE_VALUE; + int systemError = NO_ERROR; }; 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 spdContainerToVariantList(LPBYTE containerStream, ULONG containerLength); static QVariant spdElementToVariant(const SDP_ELEMENT_DATA &element) @@ -152,19 +127,15 @@ static QVariant spdElementToVariant(const SDP_ELEMENT_DATA &element) } 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_UUID128: + variant = QVariant::fromValue(QBluetoothUuid(element.data.uuid128)); + break; case SDP_ST_UUID32: variant = QVariant::fromValue(QBluetoothUuid(quint32(element.data.uuid32))); + break; case SDP_ST_UUID16: variant = QVariant::fromValue(QBluetoothUuid(quint16(element.data.uuid16))); + break; default: break; } @@ -177,7 +148,7 @@ static QVariant spdElementToVariant(const SDP_ELEMENT_DATA &element) } case SDP_TYPE_URL: { const QString urlString = QString::fromLocal8Bit(reinterpret_cast(element.data.url.value), - (int)element.data.url.length); + int(element.data.url.length)); const QUrl url(urlString); if (url.isValid()) variant = QVariant::fromValue(url); @@ -198,7 +169,7 @@ static QVariant spdElementToVariant(const SDP_ELEMENT_DATA &element) break; } case SDP_TYPE_BOOLEAN: - variant = QVariant::fromValue((bool)element.data.booleanVal); + variant = QVariant::fromValue(bool(element.data.booleanVal)); break; case SDP_TYPE_NIL: break; @@ -211,13 +182,13 @@ static QVariant spdElementToVariant(const SDP_ELEMENT_DATA &element) static QList spdContainerToVariantList(LPBYTE containerStream, ULONG containerLength) { - HBLUETOOTH_CONTAINER_ELEMENT iter = NULL; - SDP_ELEMENT_DATA element; + HBLUETOOTH_CONTAINER_ELEMENT iter = nullptr; + SDP_ELEMENT_DATA element = {}; QList sequence; for (;;) { - const DWORD result = BluetoothSdpGetContainerElementData(containerStream, + const DWORD result = ::BluetoothSdpGetContainerElementData(containerStream, containerLength, &iter, &element); @@ -244,9 +215,9 @@ static BOOL SDP_CALLBACK bluetoothSdpCallback(ULONG attributeId, LPBYTE valueStr { QBluetoothServiceInfo *result = static_cast(param); - SDP_ELEMENT_DATA element; + SDP_ELEMENT_DATA element = {}; - if (pBluetoothSdpGetElementData(valueStream, streamSize, &element) == ERROR_SUCCESS) + if (::BluetoothSdpGetElementData(valueStream, streamSize, &element) == ERROR_SUCCESS) { switch (element.type) { case SDP_TYPE_UINT: case SDP_TYPE_INT: @@ -257,6 +228,7 @@ static BOOL SDP_CALLBACK bluetoothSdpCallback(ULONG attributeId, LPBYTE valueStr case SDP_TYPE_SEQUENCE: case SDP_TYPE_ALTERNATIVE: { const QVariant variant = spdElementToVariant(element); + result->setAttribute(attributeId, variant); break; } @@ -265,7 +237,7 @@ static BOOL SDP_CALLBACK bluetoothSdpCallback(ULONG attributeId, LPBYTE valueStr default: break; } - + } return true; } @@ -281,13 +253,12 @@ enum { static FindServiceResult findNextService(HANDLE hSearch) { FindServiceResult result; - result.systemError = NO_ERROR; result.hSearch = hSearch; QByteArray resultBuffer(2048, 0); WSAQUERYSET *resultQuery = reinterpret_cast(resultBuffer.data()); DWORD resultBufferSize = DWORD(resultBuffer.size()); - const int resultCode = WSALookupServiceNext(hSearch, + const int resultCode = ::WSALookupServiceNext(hSearch, WSAControlFlags, &resultBufferSize, resultQuery); @@ -295,12 +266,12 @@ static FindServiceResult findNextService(HANDLE hSearch) if (resultCode == SOCKET_ERROR) { result.systemError = ::WSAGetLastError(); if (result.systemError == WSA_E_NO_MORE) - WSALookupServiceEnd(hSearch); + ::WSALookupServiceEnd(hSearch); return result; } if (resultQuery->lpBlob - && BluetoothSdpEnumAttributes(resultQuery->lpBlob->pBlobData, + && ::BluetoothSdpEnumAttributes(resultQuery->lpBlob->pBlobData, resultQuery->lpBlob->cbSize, bluetoothSdpCallback, &result.info)) { @@ -313,38 +284,35 @@ static FindServiceResult findNextService(HANDLE hSearch) static FindServiceResult findFirstService(const QBluetoothAddress &address) { - //### should we try for 2.2 on all platforms ?? - WSAData wsadata; + WSAData wsadata = {}; FindServiceResult result; - result.hSearch = INVALID_HANDLE_VALUE; // IPv6 requires Winsock v2.0 or better. - if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) { + if (::WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) { result.systemError = ::WSAGetLastError(); return result; } const QString addressAsString = QStringLiteral("(%1)").arg(address.toString()); - - QVector addressAsWChar(addressAsString.size()); + QVector addressAsWChar(addressAsString.size() + 1); addressAsString.toWCharArray(addressAsWChar.data()); GUID protocol = L2CAP_PROTOCOL_UUID; //Search for L2CAP and RFCOMM services WSAQUERYSET serviceQuery = {}; - serviceQuery.dwSize = sizeof(WSAQUERYSET); //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 - - HANDLE hSearch; - const int resultCode = WSALookupServiceBegin(&serviceQuery, + serviceQuery.dwSize = sizeof(WSAQUERYSET); + serviceQuery.lpServiceClassId = &protocol; + serviceQuery.dwNameSpace = NS_BTH; + serviceQuery.dwNumberOfCsAddrs = 0; + serviceQuery.lpszContext = addressAsWChar.data(); + + HANDLE hSearch = nullptr; + const int resultCode = ::WSALookupServiceBegin(&serviceQuery, WSAControlFlags, &hSearch); if (resultCode == SOCKET_ERROR) { result.systemError = ::WSAGetLastError(); - WSALookupServiceEnd(hSearch); + ::WSALookupServiceEnd(hSearch); return result; } return findNextService(hSearch); @@ -363,8 +331,6 @@ QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( { Q_UNUSED(deviceAdapter); - resolveFunctions(bluetoothapis()); - threadFind = new QThread; threadWorkerFind = new ThreadWorkerFind; threadWorkerFind->moveToThread(threadFind); @@ -376,9 +342,8 @@ QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate() { - if (pendingFinish) { + if (pendingFinish) stop(); - } if (threadFind) threadFind->quit(); } @@ -392,7 +357,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &addr const auto threadWorker = threadWorkerFind; QMetaObject::invokeMethod(threadWorkerFind, [threadWorker, address]() { - FindServiceResult result = findFirstService(address); + const FindServiceResult result = findFirstService(address); emit threadWorker->findFinished(QVariant::fromValue(result)); }, Qt::QueuedConnection); } @@ -403,13 +368,29 @@ void QBluetoothServiceDiscoveryAgentPrivate::stop() pendingStop = true; } +bool QBluetoothServiceDiscoveryAgentPrivate::serviceMatches(const QBluetoothServiceInfo &info) +{ + if (uuidFilter.isEmpty()) + return true; + + if (uuidFilter.contains(info.serviceUuid())) + return true; + + const QList serviceClassUuids = info.serviceClassUuids(); + for (const QBluetoothUuid &uuid : serviceClassUuids) + if (uuidFilter.contains(uuid)) + return true; + + return false; +} + void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &input) { Q_Q(QBluetoothServiceDiscoveryAgent); auto result = input.value(); if (pendingStop) { - WSALookupServiceEnd(result.hSearch); + ::WSALookupServiceEnd(result.hSearch); pendingStop = false; pendingFinish = false; emit q->canceled(); @@ -418,7 +399,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &inpu result.systemError = NO_ERROR; } else if (result.systemError != NO_ERROR) { if (result.hSearch != INVALID_HANDLE_VALUE) - WSALookupServiceEnd(result.hSearch); + ::WSALookupServiceEnd(result.hSearch); error = (result.systemError == ERROR_INVALID_HANDLE) ? QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError : QBluetoothServiceDiscoveryAgent::InputOutputError; @@ -426,13 +407,17 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &inpu qCWarning(QT_BT_WINDOWS) << errorString; emit q->error(this->error); } else { - result.info.setDevice(discoveredDevices.at(0)); - if (result.info.isValid()) { - if (!isDuplicatedService(result.info)) { - discoveredServices.append(result.info); - emit q->serviceDiscovered(result.info); + + if (serviceMatches(result.info)) { + result.info.setDevice(discoveredDevices.at(0)); + if (result.info.isValid()) { + if (!isDuplicatedService(result.info)) { + discoveredServices.append(result.info); + emit q->serviceDiscovered(result.info); + } } } + const auto threadWorker = threadWorkerFind; const auto hSearch = result.hSearch; QMetaObject::invokeMethod(threadWorkerFind, [threadWorker, hSearch]() diff --git a/src/bluetooth/qbluetoothserviceinfo_p.h b/src/bluetooth/qbluetoothserviceinfo_p.h index e4e835e4..0638867d 100644 --- a/src/bluetooth/qbluetoothserviceinfo_p.h +++ b/src/bluetooth/qbluetoothserviceinfo_p.h @@ -78,6 +78,11 @@ namespace ABI { } #endif +#ifdef QT_WIN_BLUETOOTH +#include +#include +#endif + QT_BEGIN_NAMESPACE class QBluetoothServiceInfo; @@ -120,6 +125,14 @@ private: bool writeSdpAttributes(); #endif +#ifdef QT_WIN_BLUETOOTH + SOCKADDR_BTH sockaddr = {}; + CSADDR_INFO addrinfo = {}; + WSAQUERYSET regInfo = {}; + QVector serviceName; + QVector serviceDescription; +#endif + mutable bool registered; }; diff --git a/src/bluetooth/qbluetoothserviceinfo_win.cpp b/src/bluetooth/qbluetoothserviceinfo_win.cpp index 6629e610..1f1293ad 100644 --- a/src/bluetooth/qbluetoothserviceinfo_win.cpp +++ b/src/bluetooth/qbluetoothserviceinfo_win.cpp @@ -39,10 +39,15 @@ #include "qbluetoothserviceinfo.h" #include "qbluetoothserviceinfo_p.h" +#include "qbluetoothserver_p.h" +#include "qbluetoothserver.h" + +#include QT_BEGIN_NAMESPACE QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate() + : registered(false) { } @@ -52,18 +57,64 @@ QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate() bool QBluetoothServiceInfoPrivate::isRegistered() const { - return false; + return registered; } bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAdapter) { - Q_UNUSED(localAdapter); - return false; + if (registered) + return false; + + GUID serviceUuid = attributes.value(QBluetoothServiceInfo::ServiceId).value(); + const QString name = attributes.value(QBluetoothServiceInfo::ServiceName).toString(); + const QString description = attributes.value(QBluetoothServiceInfo::ServiceDescription).toString(); + + ::memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.addressFamily = AF_BTH; + sockaddr.port = serverChannel(); + sockaddr.btAddr = localAdapter.toUInt64(); + + ::memset(&addrinfo, 0, sizeof(addrinfo)); + addrinfo.LocalAddr.iSockaddrLength = sizeof(SOCKADDR_BTH); + addrinfo.LocalAddr.lpSockaddr = (LPSOCKADDR)&sockaddr; + addrinfo.RemoteAddr.iSockaddrLength = sizeof(SOCKADDR_BTH); + addrinfo.RemoteAddr.lpSockaddr = (LPSOCKADDR)&sockaddr; + addrinfo.iSocketType = SOCK_STREAM; + addrinfo.iProtocol = BTHPROTO_RFCOMM; + + serviceName.resize(name.size() + 1); + name.toWCharArray(serviceName.data()); + serviceName[name.size()] = WCHAR(0); + serviceDescription.resize(description.size() + 1); + description.toWCharArray(serviceDescription.data()); + serviceDescription[description.size()] = WCHAR(0); + + ::memset(®Info, 0, sizeof(regInfo)); + regInfo.dwSize = sizeof(WSAQUERYSET); + regInfo.dwNameSpace = NS_BTH; + regInfo.dwNumberOfCsAddrs = 1; + regInfo.lpcsaBuffer = &addrinfo; + regInfo.lpszServiceInstanceName = serviceName.data(); + regInfo.lpszComment = serviceDescription.data(); + regInfo.lpServiceClassId = &serviceUuid; + + if (::WSASetService(®Info, RNRSERVICE_REGISTER, 0) == SOCKET_ERROR) + return false; + + registered = true; + return true; } bool QBluetoothServiceInfoPrivate::unregisterService() { - return false; + if (!registered) + return false; + + if (::WSASetService(®Info, RNRSERVICE_DELETE, 0) == SOCKET_ERROR) + return false; + + registered = false; + return true; } QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp index 781decad..83855323 100644 --- a/src/bluetooth/qbluetoothsocket_win.cpp +++ b/src/bluetooth/qbluetoothsocket_win.cpp @@ -57,6 +57,8 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) QBluetoothSocketPrivateWin::QBluetoothSocketPrivateWin() : QBluetoothSocketBasePrivate() { + WSAData wsadata = {}; + ::WSAStartup(MAKEWORD(2, 0), &wsadata); } QBluetoothSocketPrivateWin::~QBluetoothSocketPrivateWin() @@ -73,20 +75,17 @@ bool QBluetoothSocketPrivateWin::ensureNativeSocket(QBluetoothServiceInfo::Proto return true; abort(); } - socketType = type; - switch (type) { - case QBluetoothServiceInfo::RfcommProtocol: - socket = ::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); - break; - default: - socket = INVALID_SOCKET; + if (type != QBluetoothServiceInfo::RfcommProtocol) { + socket = int(INVALID_SOCKET); errorString = QBluetoothSocket::tr("Unsupported protocol. Win32 only supports RFCOMM sockets"); q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); return false; } + socket = ::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); + if (static_cast(socket) == INVALID_SOCKET) { const int error = ::WSAGetLastError(); qCWarning(QT_BT_WINDOWS) << "Failed to create socket:" << error << qt_error_string(error); @@ -157,27 +156,14 @@ void QBluetoothSocketPrivateWin::connectToService( // we are checking the service protocol and not socketType() // socketType will change in ensureNativeSocket() - if (service.socketProtocol() == QBluetoothServiceInfo::UnknownProtocol) { - qCWarning(QT_BT_WINDOWS) << "QBluetoothSocket::connectToService cannot " - "connect with 'UnknownProtocol' (type provided by given service)"; + if (service.socketProtocol() != QBluetoothServiceInfo::RfcommProtocol) { + qCWarning(QT_BT_WINDOWS) << "QBluetoothSocket::connectToService called with unsupported protocol"; errorString = QBluetoothSocket::tr("Socket type not supported"); q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); return; } - if (service.protocolServiceMultiplexer() > 0) { - Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::L2capProtocol); - - if (!ensureNativeSocket(QBluetoothServiceInfo::L2capProtocol)) { - errorString = QBluetoothSocket::tr("Unknown socket error"); - q->setSocketError(QBluetoothSocket::UnknownSocketError); - return; - } - connectToServiceHelper(service.device().address(), service.protocolServiceMultiplexer(), - openMode); - } else if (service.serverChannel() > 0) { - Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol); - + if (service.serverChannel() > 0) { if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) { errorString = QBluetoothSocket::tr("Unknown socket error"); q->setSocketError(QBluetoothSocket::UnknownSocketError); @@ -195,14 +181,13 @@ void QBluetoothSocketPrivateWin::connectToService( q->doDeviceDiscovery(service, openMode); } } + void QBluetoothSocketPrivateWin::_q_writeNotify() { Q_Q(QBluetoothSocket); if (state == QBluetoothSocket::ConnectingState) { - updateAddressesAndPorts(); q->setSocketState(QBluetoothSocket::ConnectedState); - emit q->connected(); connectWriteNotifier->setEnabled(false); } else { if (txBuffer.isEmpty()) { @@ -220,7 +205,7 @@ void QBluetoothSocketPrivateWin::_q_writeNotify() q->setSocketError(QBluetoothSocket::NetworkError); } else if (writtenBytes <= size) { // add remainder back to buffer - char *remainder = &buf[writtenBytes]; + const char *remainder = &buf[writtenBytes]; txBuffer.ungetBlock(remainder, size - writtenBytes); if (writtenBytes > 0) emit q->bytesWritten(writtenBytes); @@ -264,6 +249,9 @@ void QBluetoothSocketPrivateWin::_q_readNotify() } q->disconnectFromService(); + } else if (bytesRead == 0) { + q->setSocketError(QBluetoothSocket::RemoteHostClosedError); + q->setSocketState(QBluetoothSocket::UnconnectedState); } else { const int unusedBytes = QPRIVATELINEARBUFFER_BUFFERSIZE - bytesRead; buffer.chop(unusedBytes); @@ -280,11 +268,8 @@ void QBluetoothSocketPrivateWin::_q_exceptNotify() errorString = qt_error_string(error); q->setSocketError(QBluetoothSocket::UnknownSocketError); - if (state == QBluetoothSocket::ConnectingState) { + if (state == QBluetoothSocket::ConnectingState) abort(); - q->setSocketState(QBluetoothSocket::UnconnectedState); - emit q->disconnected(); - } } void QBluetoothSocketPrivateWin::connectToService( @@ -348,55 +333,104 @@ void QBluetoothSocketPrivateWin::abort() delete exceptNotifier; exceptNotifier = nullptr; - m_localAddress.clear(); - m_localPort = 0; - m_peerAddress.clear(); - m_peerPort = 0; - // We don't transition through Closing for abort, so // we don't call disconnectFromService or QBluetoothSocket::close ::closesocket(socket); - socket = INVALID_SOCKET; + socket = int(INVALID_SOCKET); + + Q_Q(QBluetoothSocket); + + const bool wasConnected = q->state() == QBluetoothSocket::ConnectedState; + q->setSocketState(QBluetoothSocket::UnconnectedState); + if (wasConnected) { + q->setOpenMode(QIODevice::NotOpen); + emit q->readChannelFinished(); + } } QString QBluetoothSocketPrivateWin::localName() const { - const QBluetoothLocalDevice device(m_localAddress); + const QBluetoothAddress localAddr = localAddress(); + if (localAddr == QBluetoothAddress()) + return {}; + const QBluetoothLocalDevice device(localAddr); return device.name(); } QBluetoothAddress QBluetoothSocketPrivateWin::localAddress() const { - return m_localAddress; + if (static_cast(socket) == INVALID_SOCKET) + return {}; + SOCKADDR_BTH localAddr = {}; + int localAddrLength = sizeof(localAddr); + const int localResult = ::getsockname(socket, reinterpret_cast(&localAddr), &localAddrLength); + if (localResult == SOCKET_ERROR) { + const int error = ::WSAGetLastError(); + qCWarning(QT_BT_WINDOWS) << "Error getting local address" << error << qt_error_string(error); + return {}; + } + return QBluetoothAddress(localAddr.btAddr); } quint16 QBluetoothSocketPrivateWin::localPort() const { - return m_localPort; + if (static_cast(socket) == INVALID_SOCKET) + return {}; + SOCKADDR_BTH localAddr = {}; + int localAddrLength = sizeof(localAddr); + const int localResult = ::getsockname(socket, reinterpret_cast(&localAddr), &localAddrLength); + if (localResult == SOCKET_ERROR) { + const int error = ::WSAGetLastError(); + qCWarning(QT_BT_WINDOWS) << "Error getting local port" << error << qt_error_string(error); + return {}; + } + return localAddr.port; } QString QBluetoothSocketPrivateWin::peerName() const { - if (static_cast(socket) == INVALID_SOCKET) + const QBluetoothAddress peerAddr = peerAddress(); + if (peerAddr == QBluetoothAddress()) return {}; BLUETOOTH_DEVICE_INFO bdi = {}; bdi.dwSize = sizeof(bdi); - bdi.Address.ullLong = m_peerAddress.toUInt64(); + bdi.Address.ullLong = peerAddr.toUInt64(); const DWORD res = ::BluetoothGetDeviceInfo(nullptr, &bdi); - if (res == ERROR_SUCCESS) - return QString::fromWCharArray(&bdi.szName[0]); - qCWarning(QT_BT_WINDOWS) << "Error calling BluetoothGetDeviceInfo" << res << qt_error_string(res); - return {}; + if (res != ERROR_SUCCESS) { + qCWarning(QT_BT_WINDOWS) << "Error calling BluetoothGetDeviceInfo" << res << qt_error_string(res); + return {}; + } + return QString::fromWCharArray(&bdi.szName[0]); } QBluetoothAddress QBluetoothSocketPrivateWin::peerAddress() const { - return m_peerAddress; + if (static_cast(socket) == INVALID_SOCKET) + return {}; + SOCKADDR_BTH peerAddr = {}; + int peerAddrLength = sizeof(peerAddr); + const int peerResult = ::getpeername(socket, reinterpret_cast(&peerAddr), &peerAddrLength); + if (peerResult == SOCKET_ERROR) { + const int error = ::WSAGetLastError(); + qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error); + return {}; + } + return QBluetoothAddress(peerAddr.btAddr); } quint16 QBluetoothSocketPrivateWin::peerPort() const { - return m_peerPort; + if (static_cast(socket) == INVALID_SOCKET) + return {}; + SOCKADDR_BTH peerAddr = {}; + int peerAddrLength = sizeof(peerAddr); + const int peerResult = ::getpeername(socket, reinterpret_cast(&peerAddr), &peerAddrLength); + if (peerResult == SOCKET_ERROR) { + const int error = ::WSAGetLastError(); + qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error); + return {}; + } + return peerAddr.port; } qint64 QBluetoothSocketPrivateWin::writeData(const char *data, qint64 maxSize) @@ -427,8 +461,10 @@ qint64 QBluetoothSocketPrivateWin::writeData(const char *data, qint64 maxSize) if (!connectWriteNotifier) return -1; - if (txBuffer.isEmpty()) + if (txBuffer.isEmpty()) { connectWriteNotifier->setEnabled(true); + QMetaObject::invokeMethod(this, "_q_writeNotify", Qt::QueuedConnection); + } char *txbuf = txBuffer.reserve(maxSize); ::memcpy(txbuf, data, maxSize); @@ -459,19 +495,20 @@ void QBluetoothSocketPrivateWin::close() connectWriteNotifier->setEnabled(true); } -bool QBluetoothSocketPrivateWin::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType_, - QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode) +bool QBluetoothSocketPrivateWin::setSocketDescriptor(int socketDescriptor, + QBluetoothServiceInfo::Protocol protocol, + QBluetoothSocket::SocketState socketState, + QBluetoothSocket::OpenMode openMode) { Q_Q(QBluetoothSocket); abort(); - socketType = socketType_; + socketType = protocol; socket = socketDescriptor; if (!createNotifiers()) return false; - updateAddressesAndPorts(); q->setSocketState(socketState); q->setOpenMode(openMode); if (socketState == QBluetoothSocket::ConnectedState) { @@ -526,33 +563,6 @@ bool QBluetoothSocketPrivateWin::createNotifiers() return true; } -void QBluetoothSocketPrivateWin::updateAddressesAndPorts() -{ - SOCKADDR_BTH localAddr = {}; - int localAddrLength = sizeof(localAddr); - const int localResult = ::getsockname(socket, reinterpret_cast(&localAddr), &localAddrLength); - if (localResult != SOCKET_ERROR) { - m_localAddress = QBluetoothAddress(localAddr.btAddr); - m_localPort = localAddr.port; - } else { - const int error = ::WSAGetLastError(); - qCWarning(QT_BT_WINDOWS) << "Error getting local address and port" << error << qt_error_string(error); - errorString = QBluetoothSocket::tr("Cannot get socket's local address and port"); - } - - SOCKADDR_BTH peerAddr = {}; - int peerAddrLength = sizeof(peerAddr); - const int peerResult = ::getpeername(socket, reinterpret_cast(&peerAddr), &peerAddrLength); - if (peerResult != SOCKET_ERROR) { - m_peerAddress = QBluetoothAddress(peerAddr.btAddr); - m_peerPort = peerAddr.port; - } else { - const int error = ::WSAGetLastError(); - qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error); - errorString = QBluetoothSocket::tr("Cannot get socket's peer address and port"); - } -} - bool QBluetoothSocketPrivateWin::configureSecurity() { Q_Q(QBluetoothSocket); diff --git a/src/bluetooth/qbluetoothsocket_win_p.h b/src/bluetooth/qbluetoothsocket_win_p.h index fe0fc99f..77f4842e 100644 --- a/src/bluetooth/qbluetoothsocket_win_p.h +++ b/src/bluetooth/qbluetoothsocket_win_p.h @@ -108,14 +108,9 @@ private slots: private: bool createNotifiers(); - void updateAddressesAndPorts(); bool configureSecurity(); QSocketNotifier *exceptNotifier = nullptr; - QBluetoothAddress m_localAddress; - quint16 m_localPort = 0; - QBluetoothAddress m_peerAddress; - quint16 m_peerPort = 0; }; QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothtransfermanager.cpp b/src/bluetooth/qbluetoothtransfermanager.cpp index 37a3191a..d84f726c 100644 --- a/src/bluetooth/qbluetoothtransfermanager.cpp +++ b/src/bluetooth/qbluetoothtransfermanager.cpp @@ -121,8 +121,8 @@ QBluetoothTransferReply *QBluetoothTransferManager::put(const QBluetoothTransfer connect(reply, SIGNAL(finished(QBluetoothTransferReply*)), this, SIGNAL(finished(QBluetoothTransferReply*))); return reply; #else - // Android, iOS, and WinRT have no implementation -#if !defined(QT_ANDROID_BLUETOOTH) && !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH) + // Android, iOS, and Win/WinRT have no implementation +#if !defined(QT_ANDROID_BLUETOOTH) && !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH) && !defined(QT_WIN_BLUETOOTH) printDummyWarning(); #endif Q_UNUSED(request); diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp index c3742b76..444bfb38 100644 --- a/src/bluetooth/qlowenergycontroller.cpp +++ b/src/bluetooth/qlowenergycontroller.cpp @@ -324,6 +324,7 @@ static QLowEnergyControllerPrivate *privateController(QLowEnergyController::Role return new QLowEnergyControllerPrivateWinRT(); #endif #elif defined(QT_WIN_BLUETOOTH) + Q_UNUSED(role); return new QLowEnergyControllerPrivateWin32(); #else Q_UNUSED(role); diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h index fe1da852..54a49b53 100644 --- a/src/bluetooth/qlowenergycontroller_p.h +++ b/src/bluetooth/qlowenergycontroller_p.h @@ -104,7 +104,6 @@ public: const QByteArray &newValue) override; void addToGenericAttributeList(const QLowEnergyServiceData &service, - QLowEnergyHandle startHandle) override; }; diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index cc848d1e..ced69685 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -51,7 +51,7 @@ #include // for std::max -#include +#include QT_BEGIN_NAMESPACE @@ -81,7 +81,7 @@ public: return; m_value = QByteArray(reinterpret_cast(&gattValue->Data[0]), - gattValue->DataSize); + int(gattValue->DataSize)); } QByteArray m_value; @@ -109,12 +109,12 @@ static QString getServiceSystemPath(const QBluetoothAddress &deviceAddress, { const HDEVINFO deviceInfoSet = ::SetupDiGetClassDevs( reinterpret_cast(&serviceUuid), - NULL, - 0, + nullptr, + nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (deviceInfoSet == INVALID_HANDLE_VALUE) { - *systemErrorCode = ::GetLastError(); + *systemErrorCode = int(::GetLastError()); return QString(); } @@ -128,11 +128,11 @@ static QString getServiceSystemPath(const QBluetoothAddress &deviceAddress, if (!::SetupDiEnumDeviceInterfaces( deviceInfoSet, - NULL, + nullptr, reinterpret_cast(&serviceUuid), index++, &deviceInterfaceData)) { - *systemErrorCode = ::GetLastError(); + *systemErrorCode = int(::GetLastError()); break; } @@ -140,11 +140,11 @@ static QString getServiceSystemPath(const QBluetoothAddress &deviceAddress, if (!::SetupDiGetDeviceInterfaceDetail( deviceInfoSet, &deviceInterfaceData, - NULL, + nullptr, deviceInterfaceDetailDataSize, &deviceInterfaceDetailDataSize, - NULL)) { - const DWORD error = ::GetLastError(); + nullptr)) { + const int error = int(::GetLastError()); if (error != ERROR_INSUFFICIENT_BUFFER) { *systemErrorCode = error; break; @@ -156,7 +156,7 @@ static QString getServiceSystemPath(const QBluetoothAddress &deviceAddress, deviceInfoData.cbSize = sizeof(deviceInfoData); QByteArray deviceInterfaceDetailDataBuffer( - deviceInterfaceDetailDataSize, 0); + int(deviceInterfaceDetailDataSize), 0); PSP_INTERFACE_DEVICE_DETAIL_DATA deviceInterfaceDetailData = reinterpret_cast @@ -169,10 +169,10 @@ static QString getServiceSystemPath(const QBluetoothAddress &deviceAddress, deviceInfoSet, &deviceInterfaceData, deviceInterfaceDetailData, - deviceInterfaceDetailDataBuffer.size(), + DWORD(deviceInterfaceDetailDataBuffer.size()), &deviceInterfaceDetailDataSize, &deviceInfoData)) { - *systemErrorCode = ::GetLastError(); + *systemErrorCode = int(::GetLastError()); break; } @@ -195,29 +195,28 @@ static HANDLE openSystemDevice( const QString &systemPath, QIODevice::OpenMode openMode, int *systemErrorCode) { DWORD desiredAccess = 0; - DWORD shareMode = 0; + DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; if (openMode & QIODevice::ReadOnly) { desiredAccess |= GENERIC_READ; - shareMode |= FILE_SHARE_READ; } if (openMode & QIODevice::WriteOnly) { desiredAccess |= GENERIC_WRITE; - shareMode |= FILE_SHARE_WRITE; + shareMode &= ~DWORD(FILE_SHARE_WRITE); } const HANDLE hDevice = ::CreateFile( reinterpret_cast(systemPath.utf16()), desiredAccess, shareMode, - NULL, + nullptr, OPEN_EXISTING, 0, - NULL); + nullptr); *systemErrorCode = (INVALID_HANDLE_VALUE == hDevice) - ? ::GetLastError() : NO_ERROR; + ? int(::GetLastError()) : NO_ERROR; return hDevice; } @@ -259,7 +258,7 @@ static QVector enumeratePrimaryGattServices( const HRESULT hr = ::BluetoothGATTGetServices( hDevice, servicesCount, - foundServices.isEmpty() ? NULL : &foundServices[0], + foundServices.isEmpty() ? nullptr : &foundServices[0], &servicesCount, BLUETOOTH_GATT_FLAG_NONE); @@ -267,7 +266,7 @@ static QVector enumeratePrimaryGattServices( *systemErrorCode = NO_ERROR; return foundServices; } else { - const DWORD error = WIN32_FROM_HRESULT(hr); + const int error = WIN32_FROM_HRESULT(hr); if (error == ERROR_MORE_DATA) { foundServices.resize(servicesCount); } else { @@ -293,7 +292,7 @@ static QVector enumerateGattCharacteristics( hService, gattService, characteristicsCount, - foundCharacteristics.isEmpty() ? NULL : &foundCharacteristics[0], + foundCharacteristics.isEmpty() ? nullptr : &foundCharacteristics[0], &characteristicsCount, BLUETOOTH_GATT_FLAG_NONE); @@ -301,7 +300,7 @@ static QVector enumerateGattCharacteristics( *systemErrorCode = NO_ERROR; return foundCharacteristics; } else { - const DWORD error = WIN32_FROM_HRESULT(hr); + const int error = WIN32_FROM_HRESULT(hr); if (error == ERROR_MORE_DATA) { foundCharacteristics.resize(characteristicsCount); } else { @@ -324,7 +323,7 @@ static QByteArray getGattCharacteristicValue( USHORT valueBufferSize = 0; for (;;) { const auto valuePtr = valueBuffer.isEmpty() - ? NULL + ? nullptr : reinterpret_cast(valueBuffer.data()); const HRESULT hr = ::BluetoothGATTGetCharacteristicValue( @@ -338,11 +337,12 @@ static QByteArray getGattCharacteristicValue( if (SUCCEEDED(hr)) { *systemErrorCode = NO_ERROR; return QByteArray(reinterpret_cast(&valuePtr->Data[0]), - valuePtr->DataSize); + int(valuePtr->DataSize)); } else { - const DWORD error = WIN32_FROM_HRESULT(hr); + const int error = WIN32_FROM_HRESULT(hr); if (error == ERROR_MORE_DATA) { valueBuffer.resize(valueBufferSize); + valueBuffer.fill(0); } else { *systemErrorCode = error; return QByteArray(); @@ -362,7 +362,7 @@ static void setGattCharacteristicValue( QByteArray valueBuffer; QDataStream out(&valueBuffer, QIODevice::WriteOnly); - ULONG dataSize = value.size(); + ULONG dataSize = ULONG(value.size()); out.writeRawData(reinterpret_cast(&dataSize), sizeof(dataSize)); out.writeRawData(value.constData(), value.size()); @@ -396,7 +396,7 @@ static QVector enumerateGattDescriptors( hService, gattCharacteristic, descriptorsCount, - foundDescriptors.isEmpty() ? NULL : &foundDescriptors[0], + foundDescriptors.isEmpty() ? nullptr : &foundDescriptors[0], &descriptorsCount, BLUETOOTH_GATT_FLAG_NONE); @@ -404,7 +404,7 @@ static QVector enumerateGattDescriptors( *systemErrorCode = NO_ERROR; return foundDescriptors; } else { - const DWORD error = WIN32_FROM_HRESULT(hr); + const int error = WIN32_FROM_HRESULT(hr); if (error == ERROR_MORE_DATA) { foundDescriptors.resize(descriptorsCount); } else { @@ -427,7 +427,7 @@ static QByteArray getGattDescriptorValue( USHORT valueBufferSize = 0; for (;;) { const auto valuePtr = valueBuffer.isEmpty() - ? NULL + ? nullptr : reinterpret_cast(valueBuffer.data()); const HRESULT hr = ::BluetoothGATTGetDescriptorValue( @@ -440,12 +440,18 @@ static QByteArray getGattDescriptorValue( if (SUCCEEDED(hr)) { *systemErrorCode = NO_ERROR; + if (gattDescriptor->DescriptorType == CharacteristicUserDescription) { + QString valueString = QString::fromUtf16(reinterpret_cast(&valuePtr->Data[0]), + valuePtr->DataSize/2); + return valueString.toUtf8(); + } return QByteArray(reinterpret_cast(&valuePtr->Data[0]), - valuePtr->DataSize); + int(valuePtr->DataSize)); } else { - const DWORD error = WIN32_FROM_HRESULT(hr); + const int error = WIN32_FROM_HRESULT(hr); if (error == ERROR_MORE_DATA) { valueBuffer.resize(valueBufferSize); + valueBuffer.fill(0); } else { *systemErrorCode = error; return QByteArray(); @@ -463,7 +469,7 @@ static void setGattDescriptorValue( return; } - const int requiredValueBufferSize = sizeof(BTH_LE_GATT_DESCRIPTOR_VALUE) + const int requiredValueBufferSize = int(sizeof(BTH_LE_GATT_DESCRIPTOR_VALUE)) + value.size(); QByteArray valueBuffer(requiredValueBufferSize, 0); @@ -486,7 +492,7 @@ static void setGattDescriptorValue( } gattValue->DataSize = ULONG(value.size()); - ::memcpy(gattValue->Data, value.constData(), value.size()); + ::memcpy(gattValue->Data, value.constData(), size_t(value.size())); const HRESULT hr = ::BluetoothGATTSetDescriptorValue( hService, @@ -579,7 +585,7 @@ static BTH_LE_UUID nativeLeUuidFromQtBluetoothUuid(const QBluetoothUuid &uuid) ::ZeroMemory(&gattUuid, sizeof(gattUuid)); if (uuid.minimumSize() == 2) { gattUuid.IsShortUuid = TRUE; - gattUuid.Value.ShortUuid = uuid.data1; // other fields should be empty! + gattUuid.Value.ShortUuid = USHORT(uuid.data1); // other fields should be empty! } else { gattUuid.Value.LongUuid = uuid; } @@ -701,7 +707,6 @@ QLowEnergyControllerPrivateWin32::~QLowEnergyControllerPrivateWin32() void QLowEnergyControllerPrivateWin32::init() { - Q_UNIMPLEMENTED(); } void QLowEnergyControllerPrivateWin32::connectToDevice() @@ -762,7 +767,7 @@ void QLowEnergyControllerPrivateWin32::disconnectFromDevice() thread = nullptr; } - for (const QSharedPointer servicePrivate: serviceList) + for (const auto &servicePrivate: serviceList) closeSystemDevice(servicePrivate->hService); Q_Q(QLowEnergyController); @@ -839,6 +844,11 @@ void QLowEnergyControllerPrivateWin32::discoverServiceDetails( servicePrivate->hService = openSystemService(remoteDevice, service, QIODevice::ReadOnly | QIODevice::WriteOnly, &systemErrorCode); + if (systemErrorCode != NO_ERROR) { + servicePrivate->hService = openSystemService(remoteDevice, service, + QIODevice::ReadOnly, + &systemErrorCode); + } } if (systemErrorCode != NO_ERROR) { @@ -853,7 +863,7 @@ void QLowEnergyControllerPrivateWin32::discoverServiceDetails( servicePrivate->endHandle = servicePrivate->startHandle; const QVector foundCharacteristics = - enumerateGattCharacteristics(servicePrivate->hService, NULL, &systemErrorCode); + enumerateGattCharacteristics(servicePrivate->hService, nullptr, &systemErrorCode); if (systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to get characteristics for service" << service.toString() @@ -868,7 +878,7 @@ void QLowEnergyControllerPrivateWin32::discoverServiceDetails( QLowEnergyServicePrivate::CharData detailsData; - detailsData.hValueChangeEvent = NULL; + detailsData.hValueChangeEvent = nullptr; detailsData.uuid = qtBluetoothUuidFromNativeLeUuid( gattCharacteristic.CharacteristicUuid); @@ -1150,7 +1160,7 @@ void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) } else { if (charDetails.hValueChangeEvent) { unregisterEvent(charDetails.hValueChangeEvent, &data.systemErrorCode); - charDetails.hValueChangeEvent = NULL; + charDetails.hValueChangeEvent = nullptr; } } diff --git a/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp b/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp index ea1abef2..af7f0354 100644 --- a/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp +++ b/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp @@ -110,6 +110,9 @@ void tst_QBluetoothLocalDevice::tst_powerOn() #ifdef Q_OS_OSX QSKIP("Not possible on OS X"); #endif +#ifdef Q_OS_WIN + QSKIP("Not possible on Windows"); +#endif QBluetoothLocalDevice localDevice; @@ -135,6 +138,9 @@ void tst_QBluetoothLocalDevice::tst_powerOff() #ifdef Q_OS_OSX QSKIP("Not possible on OS X"); #endif +#ifdef Q_OS_WIN + QSKIP("Not possible on Windows"); +#endif if (!QBluetoothLocalDevice::allDevices().count()) QSKIP("Skipping test due to missing Bluetooth device"); @@ -183,6 +189,9 @@ void tst_QBluetoothLocalDevice::tst_hostModes() #ifdef Q_OS_OSX QSKIP("Not possible on OS X"); #endif +#ifdef Q_OS_WIN + QSKIP("Not possible on Windows"); +#endif QFETCH(QBluetoothLocalDevice::HostMode, hostModeExpected); QFETCH(bool, expectSignal); @@ -340,6 +349,10 @@ void tst_QBluetoothLocalDevice::tst_pairDevice_data() void tst_QBluetoothLocalDevice::tst_pairDevice() { +#ifdef Q_OS_WIN + QSKIP("Programmatic pairing not supported on Windows"); +#endif + QFETCH(QBluetoothAddress, deviceAddress); QFETCH(QBluetoothLocalDevice::Pairing, pairingExpected); QFETCH(int, pairingWaitTime); diff --git a/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp b/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp index ae8cf5d0..7833495d 100644 --- a/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp +++ b/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp @@ -182,7 +182,7 @@ void tst_QBluetoothServiceInfo::tst_assignment_data() bool l2cpSupported = true; //some platforms don't support L2CP -#ifdef QT_ANDROID_BLUETOOTH +#if defined(QT_ANDROID_BLUETOOTH) || defined(Q_OS_WIN) l2cpSupported = false; #endif QTest::newRow("assignment_data_l2cp") diff --git a/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp b/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp index 4e82aacd..5ebb0b1d 100644 --- a/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp +++ b/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp @@ -140,13 +140,18 @@ void tst_QLowEnergyDescriptor::initTestCase() const QList chars = leService->characteristics(); for (const QLowEnergyCharacteristic &ch : chars) { - if (!ch.descriptors().isEmpty()) { - globalService = leService; - globalControl = controller; - qWarning() << "Found service with descriptor" << remoteDeviceInfo.address() - << globalService->serviceName() << globalService->serviceUuid(); - break; + const QList descriptors = ch.descriptors(); + for (const QLowEnergyDescriptor &d : descriptors) { + if (!d.value().isEmpty()) { + globalService = leService; + globalControl = controller; + qWarning() << "Found service with descriptor" << remoteDeviceInfo.address() + << globalService->serviceName() << globalService->serviceUuid(); + break; + } } + if (globalControl) + break; } if (globalControl) @@ -238,11 +243,19 @@ void tst_QLowEnergyDescriptor::tst_assignCompare() QCOMPARE(target.uuid(), QBluetoothUuid()); QCOMPARE(target.value(), QByteArray()); + int index = -1; QList targets; const QList chars = globalService->characteristics(); for (const QLowEnergyCharacteristic &ch : chars) { if (!ch.descriptors().isEmpty()) { targets = ch.descriptors(); + for (int i = 0; i < targets.size(); ++i) { + // try to get a descriptor we can read + if (targets[i].type() == QBluetoothUuid::CharacteristicUserDescription) { + index = i; + break; + } + } break; } } @@ -250,8 +263,10 @@ void tst_QLowEnergyDescriptor::tst_assignCompare() if (targets.isEmpty()) QSKIP("No descriptor found despite prior indication."); + QVERIFY(index != -1); + // test assignment operator - target = targets.first(); + target = targets[index]; QVERIFY(target.isValid()); QVERIFY(target.type() != QBluetoothUuid::UnknownDescriptorType); QVERIFY(!target.name().isEmpty()); @@ -259,26 +274,26 @@ void tst_QLowEnergyDescriptor::tst_assignCompare() QVERIFY(!target.uuid().isNull()); QVERIFY(!target.value().isEmpty()); - QVERIFY(target == targets.first()); - QVERIFY(targets.first() == target); - QVERIFY(!(target != targets.first())); - QVERIFY(!(targets.first() != target)); + QVERIFY(target == targets[index]); + QVERIFY(targets[index] == target); + QVERIFY(!(target != targets[index])); + QVERIFY(!(targets[index] != target)); - QCOMPARE(target.isValid(), targets.first().isValid()); - QCOMPARE(target.type(), targets.first().type()); - QCOMPARE(target.name(), targets.first().name()); - QCOMPARE(target.handle(), targets.first().handle()); - QCOMPARE(target.uuid(), targets.first().uuid()); - QCOMPARE(target.value(), targets.first().value()); + QCOMPARE(target.isValid(), targets[index].isValid()); + QCOMPARE(target.type(), targets[index].type()); + QCOMPARE(target.name(), targets[index].name()); + QCOMPARE(target.handle(), targets[index].handle()); + QCOMPARE(target.uuid(), targets[index].uuid()); + QCOMPARE(target.value(), targets[index].value()); // test copy constructor QLowEnergyDescriptor copyConstructed(target); - QCOMPARE(copyConstructed.isValid(), targets.first().isValid()); - QCOMPARE(copyConstructed.type(), targets.first().type()); - QCOMPARE(copyConstructed.name(), targets.first().name()); - QCOMPARE(copyConstructed.handle(), targets.first().handle()); - QCOMPARE(copyConstructed.uuid(), targets.first().uuid()); - QCOMPARE(copyConstructed.value(), targets.first().value()); + QCOMPARE(copyConstructed.isValid(), targets[index].isValid()); + QCOMPARE(copyConstructed.type(), targets[index].type()); + QCOMPARE(copyConstructed.name(), targets[index].name()); + QCOMPARE(copyConstructed.handle(), targets[index].handle()); + QCOMPARE(copyConstructed.uuid(), targets[index].uuid()); + QCOMPARE(copyConstructed.value(), targets[index].value()); QVERIFY(copyConstructed == target); QVERIFY(target == copyConstructed); @@ -300,18 +315,18 @@ void tst_QLowEnergyDescriptor::tst_assignCompare() QVERIFY(!(invalid != target)); QVERIFY(!(target != invalid)); - QVERIFY(!(targets.first() == target)); - QVERIFY(!(target == targets.first())); - QVERIFY(targets.first() != target); - QVERIFY(target != targets.first()); + QVERIFY(!(targets[index] == target)); + QVERIFY(!(target == targets[index])); + QVERIFY(targets[index] != target); + QVERIFY(target != targets[index]); if (targets.count() >= 2) { - QLowEnergyDescriptor second = targets[1]; + QLowEnergyDescriptor second = targets[(index+1)%2]; // at least two descriptors - QVERIFY(!(targets.first() == second)); - QVERIFY(!(second == targets.first())); - QVERIFY(targets.first() != second); - QVERIFY(second != targets.first()); + QVERIFY(!(targets[index] == second)); + QVERIFY(!(second == targets[index])); + QVERIFY(targets[index] != second); + QVERIFY(second != targets[index]); } } -- cgit v1.2.3 From 2b9b462cb81fabcbfc7a94774dae5182101f7c5e Mon Sep 17 00:00:00 2001 From: Andre de la Rocha Date: Tue, 13 Aug 2019 18:22:26 +0200 Subject: Update Bluetooth docs to mention Win32 backend This change updates the Bluetooth documentation to reflect the addition of the native Win32 backend. Task-number: QTBUG-40698 Change-Id: I8d2bc2146527a17f1f47fff541730b94f58286fb Reviewed-by: Alex Blasche --- src/bluetooth/doc/src/bluetooth-index.qdoc | 19 ++++++++++++------- src/bluetooth/doc/src/bluetooth-overview.qdoc | 5 +++++ src/bluetooth/qbluetoothdevicediscoveryagent.cpp | 4 ++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/bluetooth/doc/src/bluetooth-index.qdoc b/src/bluetooth/doc/src/bluetooth-index.qdoc index a0e2a048..7b54df85 100644 --- a/src/bluetooth/doc/src/bluetooth-index.qdoc +++ b/src/bluetooth/doc/src/bluetooth-index.qdoc @@ -51,7 +51,7 @@ Currently, the API is supported on the following platforms: \li x \li x \li x - \li + \li x \row \li Bluetooth LE Central \li x @@ -59,7 +59,7 @@ Currently, the API is supported on the following platforms: \li x \li x \li x - \li + \li x \row \li Bluetooth LE Peripheral \li x @@ -78,10 +78,12 @@ Currently, the API is supported on the following platforms: \li \endtable -Despite there not being a Win32 port yet, the WinRT backend is automatically used -if the win32 target platform supports the required WinRT APIs. Minimal requirement is Windows 10 version 1507 -with slightly improved service discovery since Windows 10 version 1607. Therefore Windows 7 and 8.x -targets are excluded. +Qt 5.14 adds a native Win32 port supporting Classic Bluetooth on Windows 7 or newer, +and Bluetooth LE on Windows 8 or newer. It must be enabled at build time by configuration +option -native-win32-bluetooth. The WinRT backend is used by default if this option is not +set and the Win32 target platform supports the required WinRT APIs (minimal requirement is +Windows 10 version 1507, with slightly improved service discovery since Windows 10 version +1607). \section1 Overview @@ -115,7 +117,7 @@ import statement in your \c .qml file: \section2 Building Qt Bluetooth Despite the fact that the module can be built for all Qt platforms, -the module is not ported to all of them. Not supported platforms such as Windows desktop +the module is not ported to all of them. Not supported platforms employ a fake or dummy backend which is automatically selected when the platform is not supported. The dummy backend reports appropriate error messages and values which allow the Qt Bluetooth developer to detect at runtime that the @@ -167,6 +169,9 @@ The \l QtBluetooth module exports the following \row \li qt.bluetooth.winrt \li Enables logging of the \l {Qt for WinRT} {WinRT} implementation +\row + \li qt.bluetooth.windows + \li Enables logging of the \l {Qt for Windows} {Win32} implementation \endtable Logging categories can be used to enable additional warning and debug output diff --git a/src/bluetooth/doc/src/bluetooth-overview.qdoc b/src/bluetooth/doc/src/bluetooth-overview.qdoc index 8138e937..902a13b7 100644 --- a/src/bluetooth/doc/src/bluetooth-overview.qdoc +++ b/src/bluetooth/doc/src/bluetooth-overview.qdoc @@ -49,6 +49,11 @@ Note that the Object Push Profile is not supported on Android. + Note that the Received Signal Strength Indicator (RSSI), as well as + the Manufacturer Specific Data advertised by Bluetooth LE devices are + not supported by the Win32 backend. Also, it is only possible to find + devices that have been previously paired through Windows Settings. + The following sections describe how to use the Qt Bluetooth C++ API classes for the above use cases. diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp index fb14850e..4bb003e2 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp @@ -78,6 +78,10 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT) \note Due to API limitations it is only possible to find devices that have been paired using Windows' settings on Windows. + + \note The Win32 backend currently does not support the Received Signal Strength + Indicator (RSSI), as well as the Manufacturer Specific Data, or other data + updates advertised by Bluetooth LE devices after discovery. */ /*! -- cgit v1.2.3