summaryrefslogtreecommitdiffstats
path: root/src/bluetooth
diff options
context:
space:
mode:
authorOliver Wolff <oliver.wolff@qt.io>2019-08-05 11:28:30 +0200
committerOliver Wolff <oliver.wolff@qt.io>2019-08-07 10:44:32 +0200
commit99db6526341e6f0f2a4798088c1f954cff013b7b (patch)
treeda49c1c9717a03cdc2e69970ad4848e989e6bb8c /src/bluetooth
parent8b7b52d66f2616040ca4aaae3f2732be96e19ab8 (diff)
parentf2f9da656fd77330cce44dfe0721b3f68f1d809d (diff)
Merge remote-tracking branch 'origin/dev' into wip/win
Diffstat (limited to 'src/bluetooth')
-rw-r--r--src/bluetooth/android/devicediscoverybroadcastreceiver.cpp4
-rw-r--r--src/bluetooth/android/servicediscoverybroadcastreceiver.cpp3
-rw-r--r--src/bluetooth/bluetooth.pro34
-rw-r--r--src/bluetooth/doc/qtbluetooth.qdocconf1
-rw-r--r--src/bluetooth/doc/src/bluetooth-index.qdoc2
-rw-r--r--src/bluetooth/osx/btdelegates.cpp (renamed from src/bluetooth/osx/osxbtchanneldelegate.mm)30
-rw-r--r--src/bluetooth/osx/btdelegates_p.h149
-rw-r--r--src/bluetooth/osx/btraii.mm (renamed from src/bluetooth/osx/osxbtchanneldelegate_p.h)85
-rw-r--r--src/bluetooth/osx/btraii_p.h (renamed from src/bluetooth/qbluetoothserver_osx_p.h)121
-rw-r--r--src/bluetooth/osx/osxbt.pri15
-rw-r--r--src/bluetooth/osx/osxbtcentralmanager.mm49
-rw-r--r--src/bluetooth/osx/osxbtdeviceinquiry.mm24
-rw-r--r--src/bluetooth/osx/osxbtdeviceinquiry_p.h24
-rw-r--r--src/bluetooth/osx/osxbtl2capchannel.mm8
-rw-r--r--src/bluetooth/osx/osxbtl2capchannel_p.h6
-rw-r--r--src/bluetooth/osx/osxbtledeviceinquiry.mm87
-rw-r--r--src/bluetooth/osx/osxbtnotifier_p.h2
-rw-r--r--src/bluetooth/osx/osxbtperipheralmanager.mm4
-rw-r--r--src/bluetooth/osx/osxbtrfcommchannel.mm8
-rw-r--r--src/bluetooth/osx/osxbtrfcommchannel_p.h6
-rw-r--r--src/bluetooth/osx/osxbtsdpinquiry.mm9
-rw-r--r--src/bluetooth/osx/osxbtsdpinquiry_p.h14
-rw-r--r--src/bluetooth/osx/osxbtsocketlistener.mm21
-rw-r--r--src/bluetooth/osx/osxbtsocketlistener_p.h17
-rw-r--r--src/bluetooth/qbluetooth.cpp1
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent.cpp2
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp3
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm (renamed from src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm)327
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm458
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_p.h69
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp177
-rw-r--r--src/bluetooth/qbluetoothdeviceinfo.cpp1
-rw-r--r--src/bluetooth/qbluetoothdeviceinfo.h3
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_android.cpp15
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_p.cpp10
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_p.h36
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_winrt.cpp202
-rw-r--r--src/bluetooth/qbluetoothserver.cpp2
-rw-r--r--src/bluetooth/qbluetoothserver_osx.mm224
-rw-r--r--src/bluetooth/qbluetoothserver_p.h62
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent.cpp19
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_android.cpp133
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_osx.mm416
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_p.h21
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp40
-rw-r--r--src/bluetooth/qbluetoothserviceinfo.cpp14
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_bluez.cpp37
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_osx.mm375
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_p.h22
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_winrt.cpp8
-rw-r--r--src/bluetooth/qbluetoothsocket.cpp29
-rw-r--r--src/bluetooth/qbluetoothsocket.h16
-rw-r--r--src/bluetooth/qbluetoothsocket_android.cpp78
-rw-r--r--src/bluetooth/qbluetoothsocket_android_p.h2
-rw-r--r--src/bluetooth/qbluetoothsocket_bluez.cpp3
-rw-r--r--src/bluetooth/qbluetoothsocket_bluezdbus.cpp2
-rw-r--r--src/bluetooth/qbluetoothsocket_osx.mm839
-rw-r--r--src/bluetooth/qbluetoothsocket_osx_p.h82
-rw-r--r--src/bluetooth/qbluetoothsocket_winrt.cpp126
-rw-r--r--src/bluetooth/qbluetoothsocket_winrt_p.h15
-rw-r--r--src/bluetooth/qbluetoothsocketbase_p.h21
-rw-r--r--src/bluetooth/qbluetoothutils_winrt.cpp25
-rw-r--r--src/bluetooth/qbluetoothutils_winrt_p.h16
-rw-r--r--src/bluetooth/qlowenergycharacteristic.h2
-rw-r--r--src/bluetooth/qlowenergycontroller.cpp41
-rw-r--r--src/bluetooth/qlowenergycontroller.h8
-rw-r--r--src/bluetooth/qlowenergycontroller_android_p.h16
-rw-r--r--src/bluetooth/qlowenergycontroller_bluez.cpp8
-rw-r--r--src/bluetooth/qlowenergycontroller_bluezdbus.cpp13
-rw-r--r--src/bluetooth/qlowenergycontroller_darwin.mm (renamed from src/bluetooth/qlowenergycontroller_osx.mm)1022
-rw-r--r--src/bluetooth/qlowenergycontroller_darwin_p.h (renamed from src/bluetooth/qlowenergycontroller_osx_p.h)112
-rw-r--r--src/bluetooth/qlowenergycontroller_winrt.cpp131
-rw-r--r--src/bluetooth/qlowenergycontroller_winrt_new.cpp1342
-rw-r--r--src/bluetooth/qlowenergycontroller_winrt_new_p.h36
-rw-r--r--src/bluetooth/qlowenergycontroller_winrt_p.h6
-rw-r--r--src/bluetooth/qlowenergycontrollerbase.cpp5
-rw-r--r--src/bluetooth/qlowenergycontrollerbase_p.h22
-rw-r--r--src/bluetooth/qlowenergydescriptor.h2
-rw-r--r--src/bluetooth/qlowenergyservice.cpp19
-rw-r--r--src/bluetooth/qlowenergyservice.h1
-rw-r--r--src/bluetooth/qlowenergyservice_osx.mm277
81 files changed, 3488 insertions, 4229 deletions
diff --git a/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp b/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp
index 99245af3..f1f50516 100644
--- a/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp
+++ b/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp
@@ -516,7 +516,7 @@ QBluetoothDeviceInfo DeviceDiscoveryBroadcastReceiver::retrieveDeviceInfo(JNIEnv
const char *scanRecordBuffer = reinterpret_cast<const char *>(elems);
const int scanRecordLength = env->GetArrayLength(scanRecord);
- QList<QBluetoothUuid> serviceUuids;
+ QVector<QBluetoothUuid> serviceUuids;
int i = 0;
// Spec 4.2, Vol 3, Part C, Chapter 11
@@ -567,7 +567,7 @@ QBluetoothDeviceInfo DeviceDiscoveryBroadcastReceiver::retrieveDeviceInfo(JNIEnv
serviceUuids.append(foundService);
}
- info.setServiceUuids(serviceUuids, QBluetoothDeviceInfo::DataIncomplete);
+ info.setServiceUuids(serviceUuids);
env->ReleaseByteArrayElements(scanRecord, elems, JNI_ABORT);
}
diff --git a/src/bluetooth/android/servicediscoverybroadcastreceiver.cpp b/src/bluetooth/android/servicediscoverybroadcastreceiver.cpp
index be1953d5..283db623 100644
--- a/src/bluetooth/android/servicediscoverybroadcastreceiver.cpp
+++ b/src/bluetooth/android/servicediscoverybroadcastreceiver.cpp
@@ -99,7 +99,6 @@ QList<QBluetoothUuid> ServiceDiscoveryBroadcastReceiver::convertParcelableArray(
{
QList<QBluetoothUuid> result;
QAndroidJniEnvironment env;
- QAndroidJniObject p;
jobjectArray parcels = parcelUuidArray.object<jobjectArray>();
if (!parcels)
@@ -107,7 +106,7 @@ QList<QBluetoothUuid> ServiceDiscoveryBroadcastReceiver::convertParcelableArray(
jint size = env->GetArrayLength(parcels);
for (int i = 0; i < size; i++) {
- p = env->GetObjectArrayElement(parcels, i);
+ auto p = QAndroidJniObject::fromLocalRef(env->GetObjectArrayElement(parcels, i));
QBluetoothUuid uuid(p.callObjectMethod<jstring>("toString").toString());
//qCDebug(QT_BT_ANDROID) << uuid.toString();
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index a2fd617d..e1e4d7a2 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -162,42 +162,27 @@ qtConfig(bluez) {
include(osx/osxbt.pri)
OBJECTIVE_SOURCES += \
qbluetoothlocaldevice_osx.mm \
- qbluetoothdevicediscoveryagent_osx.mm \
+ qbluetoothdevicediscoveryagent_darwin.mm \
qbluetoothserviceinfo_osx.mm \
qbluetoothservicediscoveryagent_osx.mm \
qbluetoothsocket_osx.mm \
qbluetoothserver_osx.mm \
qbluetoothtransferreply_osx.mm \
- qlowenergycontroller_osx.mm \
- qlowenergyservice_osx.mm
+ qlowenergycontroller_darwin.mm
PRIVATE_HEADERS += qbluetoothsocket_osx_p.h \
- qbluetoothserver_osx_p.h \
qbluetoothtransferreply_osx_p.h \
- qbluetoothtransferreply_osx_p.h \
- qlowenergycontroller_osx_p.h
-
- SOURCES -= qbluetoothdevicediscoveryagent.cpp
- SOURCES -= qbluetoothserviceinfo.cpp
- SOURCES -= qbluetoothservicediscoveryagent.cpp
- SOURCES -= qbluetoothsocket.cpp
- SOURCES -= qbluetoothsocketbase.cpp
- SOURCES -= qbluetoothserver.cpp
- SOURCES -= qlowenergyservice_p.cpp
- SOURCES -= qlowenergyservice.cpp
- SOURCES -= qlowenergycontroller.cpp
- SOURCES -= qlowenergycontrollerbase.cpp
+ qlowenergycontroller_darwin_p.h
} else:ios|tvos {
DEFINES += QT_IOS_BLUETOOTH
LIBS_PRIVATE += -framework Foundation -framework CoreBluetooth
OBJECTIVE_SOURCES += \
- qbluetoothdevicediscoveryagent_ios.mm \
- qlowenergycontroller_osx.mm \
- qlowenergyservice_osx.mm
+ qbluetoothdevicediscoveryagent_darwin.mm \
+ qlowenergycontroller_darwin.mm
PRIVATE_HEADERS += \
- qlowenergycontroller_osx_p.h \
+ qlowenergycontroller_darwin_p.h \
qbluetoothsocket_dummy_p.h
include(osx/osxbt.pri)
@@ -207,11 +192,6 @@ qtConfig(bluez) {
qbluetoothservicediscoveryagent_p.cpp \
qbluetoothsocket_dummy.cpp \
qbluetoothserver_p.cpp
-
- SOURCES -= qbluetoothdevicediscoveryagent.cpp
- SOURCES -= qlowenergyservice.cpp
- SOURCES -= qlowenergycontroller.cpp
- SOURCES -= qlowenergycontrollerbase.cpp
} else: qtConfig(winrt_bt) {
DEFINES += QT_WINRT_BLUETOOTH
!winrt {
@@ -224,7 +204,7 @@ qtConfig(bluez) {
SOURCES += \
qbluetoothdevicediscoveryagent_winrt.cpp \
- qbluetoothlocaldevice_p.cpp \
+ qbluetoothlocaldevice_winrt.cpp \
qbluetoothserver_winrt.cpp \
qbluetoothservicediscoveryagent_winrt.cpp \
qbluetoothserviceinfo_winrt.cpp \
diff --git a/src/bluetooth/doc/qtbluetooth.qdocconf b/src/bluetooth/doc/qtbluetooth.qdocconf
index a994652b..9ee5a567 100644
--- a/src/bluetooth/doc/qtbluetooth.qdocconf
+++ b/src/bluetooth/doc/qtbluetooth.qdocconf
@@ -1,4 +1,5 @@
include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+include($QT_INSTALL_DOCS/config/exampleurl-qtconnectivity.qdocconf)
project = QtBluetooth
description = Qt Bluetooth Reference Documentation
diff --git a/src/bluetooth/doc/src/bluetooth-index.qdoc b/src/bluetooth/doc/src/bluetooth-index.qdoc
index 2a4f72bc..a0e2a048 100644
--- a/src/bluetooth/doc/src/bluetooth-index.qdoc
+++ b/src/bluetooth/doc/src/bluetooth-index.qdoc
@@ -41,7 +41,7 @@ Currently, the API is supported on the following platforms:
\li \l {Qt for Android}{Android}
\li \l {Qt for iOS}{iOS}
\li \l {Qt for Linux/X11}{Linux (BlueZ 4.x/5.x)}
- \li \l {Qt for OS X}{macOS}
+ \li \l \macos
\li \l {Qt for WinRT}{WinRT}
\li \l {Qt for Windows}{Win32}
\row
diff --git a/src/bluetooth/osx/osxbtchanneldelegate.mm b/src/bluetooth/osx/btdelegates.cpp
index 822e9d4e..531ca1df 100644
--- a/src/bluetooth/osx/osxbtchanneldelegate.mm
+++ b/src/bluetooth/osx/btdelegates.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -37,16 +37,40 @@
**
****************************************************************************/
-#include "osxbtchanneldelegate_p.h"
+#include "btdelegates_p.h"
+
+#if defined(Q_OS_MACOS)
QT_BEGIN_NAMESPACE
-namespace OSXBluetooth {
+namespace DarwinBluetooth {
+
+DeviceInquiryDelegate::~DeviceInquiryDelegate()
+{
+}
+
+PairingDelegate::~PairingDelegate()
+{
+}
+
+SDPInquiryDelegate::~SDPInquiryDelegate()
+{
+}
ChannelDelegate::~ChannelDelegate()
{
}
+ConnectionMonitor::~ConnectionMonitor()
+{
+}
+
+SocketListener::~SocketListener()
+{
}
+} // namespace DarwinBluetooth
+
QT_END_NAMESPACE
+
+#endif
diff --git a/src/bluetooth/osx/btdelegates_p.h b/src/bluetooth/osx/btdelegates_p.h
new file mode 100644
index 00000000..11fbcc28
--- /dev/null
+++ b/src/bluetooth/osx/btdelegates_p.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 BTDELEGATES_P_H
+#define BTDELEGATES_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 "qbluetoothdevicediscoveryagent.h"
+#include "qlowenergycontroller.h"
+#include "qbluetooth.h"
+
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qglobal.h>
+
+#if defined(Q_OS_MACOS)
+
+#include <IOKit/IOReturn.h>
+
+#include <cstdint>
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyServicePrivate;
+class QBluetoothAddress;
+class QByteArray;
+
+namespace DarwinBluetooth {
+
+class DeviceInquiryDelegate
+{
+public:
+ virtual ~DeviceInquiryDelegate();
+
+ virtual void inquiryFinished() = 0;
+ virtual void error(IOReturn error) = 0;
+ virtual void classicDeviceFound(void *ioBluetoothDevice) = 0;
+};
+
+class PairingDelegate
+{
+public:
+ using BluetoothNumericValue = uint32_t;
+ using BluetoothPasskey = BluetoothNumericValue;
+
+ virtual ~PairingDelegate();
+
+ virtual void connecting(void *pair) = 0;
+ virtual void requestPIN(void *pair) = 0;
+ virtual void requestUserConfirmation(void *pair,
+ BluetoothNumericValue) = 0;
+ virtual void passkeyNotification(void *pair,
+ BluetoothPasskey passkey) = 0;
+ virtual void error(void *pair, IOReturn errorCode) = 0;
+ virtual void pairingFinished(void *pair) = 0;
+};
+
+class SDPInquiryDelegate {
+public:
+ virtual ~SDPInquiryDelegate();
+
+ virtual void SDPInquiryFinished(void *ioBluetoothDevice) = 0;
+ virtual void SDPInquiryError(void *ioBluetoothDevice, IOReturn errorCode) = 0;
+};
+
+// L2CAP and RFCOMM.
+class ChannelDelegate
+{
+public:
+ virtual ~ChannelDelegate();
+
+ virtual void setChannelError(IOReturn errorCode) = 0;
+ virtual void channelOpenComplete() = 0;
+ virtual void channelClosed() = 0;
+
+ virtual void readChannelData(void *data, std::size_t size) = 0;
+ virtual void writeComplete() = 0;
+};
+
+class ConnectionMonitor {
+public:
+ virtual ~ConnectionMonitor();
+
+ virtual void deviceConnected(const QBluetoothAddress &address) = 0;
+ virtual void deviceDisconnected(const QBluetoothAddress &address) = 0;
+};
+
+class SocketListener
+{
+public:
+ virtual ~SocketListener();
+
+ virtual void openNotifyRFCOMM(void *rfcommChannel) = 0;
+ virtual void openNotifyL2CAP(void *l2capChannel) = 0;
+};
+
+
+} // namespace DarwinBluetooth
+
+QT_END_NAMESPACE
+
+#endif // Q_OS_MACOS
+
+#endif // DARWINBTDELEGATES_P_H
diff --git a/src/bluetooth/osx/osxbtchanneldelegate_p.h b/src/bluetooth/osx/btraii.mm
index 1102e935..a1bf2a8d 100644
--- a/src/bluetooth/osx/osxbtchanneldelegate_p.h
+++ b/src/bluetooth/osx/btraii.mm
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -37,43 +37,74 @@
**
****************************************************************************/
-#ifndef OSXBTCHANNELDELEGATE_P_H
-#define OSXBTCHANNELDELEGATE_P_H
+#include "btraii_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 <qdebug.h>
-#include <QtCore/qglobal.h>
+#include <Foundation/Foundation.h>
-#include <IOKit/IOReturn.h>
+#include <utility>
QT_BEGIN_NAMESPACE
-namespace OSXBluetooth {
+namespace DarwinBluetooth {
-class ChannelDelegate
+StrongReference::StrongReference(void *object, RetainPolicy policy)
+ : objCInstance(object)
{
-public:
- virtual ~ChannelDelegate();
+ if (policy == RetainPolicy::doInitialRetain)
+ objCInstance = [getAs<NSObject>() retain];
+}
- virtual void setChannelError(IOReturn errorCode) = 0;
- virtual void channelOpenComplete() = 0;
- virtual void channelClosed() = 0;
+StrongReference::StrongReference(const StrongReference &other)
+{
+ objCInstance = [other.getAs<NSObject>() retain];
+}
- virtual void readChannelData(void *data, std::size_t size) = 0;
- virtual void writeComplete() = 0;
-};
+StrongReference::StrongReference(StrongReference &&other)
+{
+ std::swap(objCInstance, other.objCInstance);
+}
+StrongReference::~StrongReference()
+{
+ [getAs<NSObject>() release];
}
-QT_END_NAMESPACE
+StrongReference &StrongReference::operator = (const StrongReference &other) noexcept
+{
+ if (this != &other) {
+ [getAs<NSObject>() release];
+ objCInstance = [other.getAs<NSObject>() retain];
+ }
+
+ return *this;
+}
+
+StrongReference &StrongReference::operator = (StrongReference &&other) noexcept
+{
+ swap(other);
+ return *this;
+}
+
+void StrongReference::reset()
+{
+ [getAs<NSObject>() release];
+ objCInstance = nullptr;
+}
-#endif
+void StrongReference::reset(void *obj, RetainPolicy policy)
+{
+ [getAs<NSObject>() release];
+ objCInstance = obj;
+
+ if (policy == RetainPolicy::doInitialRetain) {
+ auto newInstance = static_cast<NSObject *>(obj);
+ Q_ASSERT(newInstance);
+ objCInstance = [newInstance retain];
+ }
+}
+
+} // namespace DarwinBluetooth
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserver_osx_p.h b/src/bluetooth/osx/btraii_p.h
index 3116ca02..6053d63b 100644
--- a/src/bluetooth/qbluetoothserver_osx_p.h
+++ b/src/bluetooth/osx/btraii_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QBLUETOOTHSERVER_OSX_P_H
-#define QBLUETOOTHSERVER_OSX_P_H
+#ifndef BTRAII_P_H
+#define BTRAII_P_H
//
// W A R N I N G
@@ -51,77 +51,86 @@
// We mean it.
//
-#ifdef QT_OSX_BLUETOOTH
-
-#include "osx/osxbtsocketlistener_p.h"
-#include "qbluetoothserviceinfo.h"
-#include "osx/osxbtutility_p.h"
-#include "qbluetoothserver.h"
-
#include <QtCore/qglobal.h>
-#include <QtCore/qlist.h>
+
+#include <utility>
QT_BEGIN_NAMESPACE
-class QMutex;
+namespace DarwinBluetooth {
-class QBluetoothServerPrivate : public OSXBluetooth::SocketListener
+enum class RetainPolicy
{
- friend class QBluetoothServer;
- friend class QBluetoothServiceInfoPrivate;
+ noInitialRetain,
+ doInitialRetain
+};
+// The class StrongReference and its descendant ScopedGuard
+// are RAII classes dealing with raw pointers to NSObject class
+// and its descendants (and thus hiding Objective-C's retain/
+// release semantics). The header itself is meant to be included
+// into *.cpp files so it's a pure C++ code without any Objective-C
+// syntax. Thus it's a bit clunky - the type information is 'erased'
+// and has to be enforced by the code using these smart pointers.
+// That's because these types are Objective-C classes - thus require
+// Objective-C compiler to work. Member-function template 'getAs' is
+// a convenience shortcut giving the desired pointer type in
+// Objective-C++ files (*.mm).
+
+// TODO: on top of these classes I can build ObjCStrongReference (it's
+// now inside osxbtutils_p.h, a template class that does have type
+// information needed but works only in Objective-C++ environment.
+class StrongReference
+{
public:
- QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol type, QBluetoothServer *q);
- ~QBluetoothServerPrivate();
-
- void _q_newConnection();
-private:
- bool startListener(quint16 realPort);
- void stopListener();
-
- // SocketListener (delegate):
- void openNotify(IOBluetoothRFCOMMChannel *channel) override;
- void openNotify(IOBluetoothL2CAPChannel *channel) override;
+ StrongReference() = default;
+ StrongReference(void *object, RetainPolicy policy);
+ StrongReference(const StrongReference &other);
+ StrongReference(StrongReference &&other);
- QBluetoothServiceInfo::Protocol serverType;
- QBluetoothServer *q_ptr;
- QBluetoothServer::Error lastError;
+ ~StrongReference();
- // Either a "temporary" channelID/PSM assigned by QBluetoothServer::listen,
- // or a real channelID/PSM returned by IOBluetooth after we've registered
- // a service.
- quint16 port;
+ StrongReference &operator = (const StrongReference &other) noexcept;
+ StrongReference &operator = (StrongReference &&other) noexcept;
- typedef OSXBluetooth::ObjCScopedPointer<ObjCListener> Listener;
- Listener listener;
+ void swap(StrongReference &other) noexcept
+ {
+ std::swap(objCInstance, other.objCInstance);
+ }
- int maxPendingConnections;
+ void reset();
+ void reset(void *newInstance, RetainPolicy policy);
- // These static functions below
- // deal with differences between bluetooth sockets
- // (bluez and QtBluetooth's API) and IOBluetooth, where it's not possible
- // to have a real PSM/channelID _before_ a service is registered,
- // the solution - "fake" ports.
- // These functions require external locking - using channelMapMutex.
- static QMutex &channelMapMutex();
+ template<class ObjCType>
+ ObjCType *getAs() const
+ {
+ return static_cast<ObjCType *>(objCInstance);
+ }
- static bool channelIsBusy(quint16 channelID);
- static quint16 findFreeChannel();
+ operator bool() const
+ {
+ return !!objCInstance;
+ }
- static bool psmIsBusy(quint16 psm);
- static quint16 findFreePSM();
-
- static void registerServer(QBluetoothServerPrivate *server, quint16 port);
- static QBluetoothServerPrivate *registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol);
- static void unregisterServer(QBluetoothServerPrivate *server);
+private:
+ void *objCInstance = nullptr;
+};
- typedef OSXBluetooth::ObjCStrongReference<NSObject> PendingConnection;
- QList<PendingConnection> pendingConnections;
+class ScopedPointer final : public StrongReference
+{
+public:
+ ScopedPointer() = default;
+ ScopedPointer(void *instance, RetainPolicy policy)
+ : StrongReference(instance, policy)
+ {
+ }
+private:
+ Q_DISABLE_COPY_MOVE(ScopedPointer)
};
-QT_END_NAMESPACE
+} // namespace DarwinBluetooth
-#endif //QT_OSX_BLUETOOTH
+QT_END_NAMESPACE
-#endif
+#endif // BTRAII_P_H
diff --git a/src/bluetooth/osx/osxbt.pri b/src/bluetooth/osx/osxbt.pri
index b7ac0535..8f6ea0d1 100644
--- a/src/bluetooth/osx/osxbt.pri
+++ b/src/bluetooth/osx/osxbt.pri
@@ -1,8 +1,15 @@
-SOURCES += osx/uistrings.cpp osx/osxbtnotifier.cpp
+SOURCES += osx/uistrings.cpp \
+ osx/osxbtnotifier.cpp \
+ osx/btdelegates.cpp
+
PRIVATE_HEADERS += osx/uistrings_p.h \
- osx/osxbtgcdtimer_p.h
+ osx/osxbtgcdtimer_p.h \
+ osx/btraii_p.h \
+ osx/btdelegates_p.h
+
-OBJECTIVE_SOURCES += osx/osxbtgcdtimer.mm
+OBJECTIVE_SOURCES += osx/osxbtgcdtimer.mm \
+ osx/btraii.mm
#QMAKE_CXXFLAGS_WARN_ON += -Wno-nullability-completeness
CONFIG(osx) {
@@ -13,7 +20,6 @@ CONFIG(osx) {
osx/osxbtsdpinquiry_p.h \
osx/osxbtrfcommchannel_p.h \
osx/osxbtl2capchannel_p.h \
- osx/osxbtchanneldelegate_p.h \
osx/osxbtservicerecord_p.h \
osx/osxbtsocketlistener_p.h \
osx/osxbtobexsession_p.h \
@@ -30,7 +36,6 @@ CONFIG(osx) {
osx/osxbtsdpinquiry.mm \
osx/osxbtrfcommchannel.mm \
osx/osxbtl2capchannel.mm \
- osx/osxbtchanneldelegate.mm \
osx/osxbtservicerecord.mm \
osx/osxbtsocketlistener.mm \
osx/osxbtobexsession.mm \
diff --git a/src/bluetooth/osx/osxbtcentralmanager.mm b/src/bluetooth/osx/osxbtcentralmanager.mm
index cadabbaf..b9a1ae0f 100644
--- a/src/bluetooth/osx/osxbtcentralmanager.mm
+++ b/src/bluetooth/osx/osxbtcentralmanager.mm
@@ -39,6 +39,7 @@
#include "qlowenergyserviceprivate_p.h"
#include "qlowenergycharacteristic.h"
+#include "qlowenergycontroller.h"
#include "osxbtcentralmanager_p.h"
#include "osxbtnotifier_p.h"
@@ -132,6 +133,7 @@ QT_USE_NAMESPACE
- (CBDescriptor *)descriptor:(const QBluetoothUuid &)dUuid
forCharacteristic:(CBCharacteristic *)ch;
- (bool)cacheWriteValue:(const QByteArray &)value for:(NSObject *)obj;
+- (void)handleReadWriteError:(NSError *)error;
- (void)reset;
@end
@@ -1202,6 +1204,21 @@ QT_USE_NAMESPACE
// TODO: also serviceToVisit/VisitNext and visitedServices ?
}
+- (void)handleReadWriteError:(NSError *)error
+{
+ Q_ASSERT(notifier);
+
+ switch (error.code) {
+ case 0x05: // GATT_INSUFFICIENT_AUTHORIZATION
+ case 0x0F: // GATT_INSUFFICIENT_ENCRYPTION
+ emit notifier->CBManagerError(QLowEnergyController::AuthorizationError);
+ [self detach];
+ break;
+ default:
+ break;
+ }
+}
+
// CBCentralManagerDelegate (the real one).
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
@@ -1245,6 +1262,7 @@ QT_USE_NAMESPACE
if (notifier)
emit notifier->CBManagerError(QLowEnergyController::InvalidBluetoothAdapterError);
}
+ [self stopWatchers];
return;
}
@@ -1266,6 +1284,7 @@ QT_USE_NAMESPACE
if (notifier)
emit notifier->CBManagerError(QLowEnergyController::InvalidBluetoothAdapterError);
}
+ [self stopWatchers];
return;
}
@@ -1280,7 +1299,7 @@ QT_USE_NAMESPACE
}
} else {
// We actually handled all known states, but .. Core Bluetooth can change?
- Q_ASSERT_X(0, Q_FUNC_INFO, "invalid centra's state");
+ Q_ASSERT_X(0, Q_FUNC_INFO, "invalid central's state");
}
#pragma clang diagnostic pop
@@ -1371,6 +1390,30 @@ QT_USE_NAMESPACE
[self discoverIncludedServices];
}
+- (void)peripheral:(CBPeripheral *)aPeripheral
+ didModifyServices:(NSArray<CBService *> *)invalidatedServices
+{
+ Q_UNUSED(aPeripheral)
+ Q_UNUSED(invalidatedServices)
+
+ qCWarning(QT_BT_OSX) << "The peripheral has modified its services.";
+ // "This method is invoked whenever one or more services of a peripheral have changed.
+ // A peripheral’s services have changed if:
+ // * A service is removed from the peripheral’s database
+ // * A new service is added to the peripheral’s database
+ // * A service that was previously removed from the peripheral’s
+ // database is readded to the database at a different location"
+
+ // In case new services were added - we have to discover them.
+ // In case some were removed - we can end up with dangling pointers
+ // (see our 'watchdogs', for example). To handle the situation
+ // we stop all current operations here, report to QLowEnergyController
+ // so that it can trigger re-discovery.
+ [self reset];
+ managerState = OSXBluetooth::CentralManagerIdle;
+ if (notifier)
+ emit notifier->servicesWereModified();
+}
- (void)peripheral:(CBPeripheral *)aPeripheral didDiscoverIncludedServicesForService:(CBService *)service
error:(NSError *)error
@@ -1514,6 +1557,7 @@ QT_USE_NAMESPACE
currentReadHandle = 0;
requestPending = false;
emit notifier->CBManagerError(qtUuid, QLowEnergyService::CharacteristicReadError);
+ [self handleReadWriteError:error];
[self performNextRequest];
}
return;
@@ -1632,6 +1676,7 @@ QT_USE_NAMESPACE
currentReadHandle = 0;
requestPending = false;
emit notifier->CBManagerError(qtUuid, QLowEnergyService::DescriptorReadError);
+ [self handleReadWriteError:error];
[self performNextRequest];
}
return;
@@ -1721,6 +1766,7 @@ QT_USE_NAMESPACE
NSLog(@"%s failed with error %@", Q_FUNC_INFO, error);
emit notifier->CBManagerError(qt_uuid(characteristic.service.UUID),
QLowEnergyService::CharacteristicWriteError);
+ [self handleReadWriteError:error];
} else {
const QLowEnergyHandle cHandle = charMap.key(characteristic);
emit notifier->characteristicWritten(cHandle, valueToReport);
@@ -1755,6 +1801,7 @@ QT_USE_NAMESPACE
NSLog(@"%s failed with error %@", Q_FUNC_INFO, error);
emit notifier->CBManagerError(qt_uuid(descriptor.characteristic.service.UUID),
QLowEnergyService::DescriptorWriteError);
+ [self handleReadWriteError:error];
} else {
const QLowEnergyHandle dHandle = descMap.key(descriptor);
Q_ASSERT_X(dHandle, Q_FUNC_INFO, "descriptor not found in the descriptors map");
diff --git a/src/bluetooth/osx/osxbtdeviceinquiry.mm b/src/bluetooth/osx/osxbtdeviceinquiry.mm
index 57cd73e1..3a77c1f7 100644
--- a/src/bluetooth/osx/osxbtdeviceinquiry.mm
+++ b/src/bluetooth/osx/osxbtdeviceinquiry.mm
@@ -43,30 +43,16 @@
#include <QtCore/qloggingcategory.h>
#include <QtCore/qdebug.h>
-QT_BEGIN_NAMESPACE
-
-namespace OSXBluetooth {
-
-DeviceInquiryDelegate::~DeviceInquiryDelegate()
-{
-}
-
-}
-
-
-QT_END_NAMESPACE
-
QT_USE_NAMESPACE
-
@implementation QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry)
{
IOBluetoothDeviceInquiry *m_inquiry;
bool m_active;
- QT_PREPEND_NAMESPACE(OSXBluetooth::DeviceInquiryDelegate) *m_delegate;//C++ "delegate"
+ DarwinBluetooth::DeviceInquiryDelegate *m_delegate;//C++ "delegate"
}
-- (id)initWithDelegate:(OSXBluetooth::DeviceInquiryDelegate *)delegate
+- (id)initWithDelegate:(DarwinBluetooth::DeviceInquiryDelegate *)delegate
{
if (self = [super init]) {
Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid device inquiry delegate (null)");
@@ -158,9 +144,9 @@ QT_USE_NAMESPACE
// QtBluetooth has not too many error codes, 'UnknownError' is not really
// useful, report the actual error code here:
qCWarning(QT_BT_OSX) << "IOKit error code: " << error;
- m_delegate->error(sender, error);
+ m_delegate->error(error);
} else {
- m_delegate->inquiryFinished(sender);
+ m_delegate->inquiryFinished();
}
}
@@ -171,7 +157,7 @@ QT_USE_NAMESPACE
return;
Q_ASSERT_X(m_delegate, Q_FUNC_INFO, "invalid device inquiry delegate (null)");
- m_delegate->deviceFound(sender, device);
+ m_delegate->classicDeviceFound(device);
}
- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry *)sender
diff --git a/src/bluetooth/osx/osxbtdeviceinquiry_p.h b/src/bluetooth/osx/osxbtdeviceinquiry_p.h
index 0fec2db2..86ed3fdf 100644
--- a/src/bluetooth/osx/osxbtdeviceinquiry_p.h
+++ b/src/bluetooth/osx/osxbtdeviceinquiry_p.h
@@ -52,36 +52,16 @@
//
#include "osxbluetooth_p.h"
+#include "btdelegates_p.h"
#include <QtCore/qglobal.h>
#include <Foundation/Foundation.h>
#include <IOKit/IOReturn.h>
-@class QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry);
-
-QT_BEGIN_NAMESPACE
-
-namespace OSXBluetooth {
-
-class DeviceInquiryDelegate {
-public:
- typedef QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry) DeviceInquiryObjC;
-
- virtual ~DeviceInquiryDelegate();
-
- virtual void inquiryFinished(IOBluetoothDeviceInquiry *inq) = 0;
- virtual void error(IOBluetoothDeviceInquiry *inq, IOReturn error) = 0;
- virtual void deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device) = 0;
-};
-
-}
-
-QT_END_NAMESPACE
-
@interface QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry) : NSObject<IOBluetoothDeviceInquiryDelegate>
-- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth::DeviceInquiryDelegate) *)delegate;
+- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth::DeviceInquiryDelegate) *)delegate;
- (void)dealloc;
- (bool)isActive;
diff --git a/src/bluetooth/osx/osxbtl2capchannel.mm b/src/bluetooth/osx/osxbtl2capchannel.mm
index dc8468a0..03e3a982 100644
--- a/src/bluetooth/osx/osxbtl2capchannel.mm
+++ b/src/bluetooth/osx/osxbtl2capchannel.mm
@@ -37,10 +37,10 @@
**
****************************************************************************/
-#include "osxbtchanneldelegate_p.h"
#include "osxbtl2capchannel_p.h"
#include "qbluetoothaddress.h"
#include "osxbtutility_p.h"
+#include "btdelegates_p.h"
#include <QtCore/qloggingcategory.h>
#include <QtCore/qdebug.h>
@@ -49,13 +49,13 @@ QT_USE_NAMESPACE
@implementation QT_MANGLE_NAMESPACE(OSXBTL2CAPChannel)
{
- QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *delegate;
+ QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *delegate;
IOBluetoothDevice *device;
IOBluetoothL2CAPChannel *channel;
bool connected;
}
-- (id)initWithDelegate:(OSXBluetooth::ChannelDelegate *)aDelegate
+- (id)initWithDelegate:(DarwinBluetooth::ChannelDelegate *)aDelegate
{
Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)");
@@ -69,7 +69,7 @@ QT_USE_NAMESPACE
return self;
}
-- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth::ChannelDelegate) *)aDelegate
+- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth::ChannelDelegate) *)aDelegate
channel:(IOBluetoothL2CAPChannel *)aChannel
{
// This type of channel does not require connect, it's created with
diff --git a/src/bluetooth/osx/osxbtl2capchannel_p.h b/src/bluetooth/osx/osxbtl2capchannel_p.h
index 512087b4..42eec8e7 100644
--- a/src/bluetooth/osx/osxbtl2capchannel_p.h
+++ b/src/bluetooth/osx/osxbtl2capchannel_p.h
@@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE
class QBluetoothAddress;
-namespace OSXBluetooth {
+namespace DarwinBluetooth {
class ChannelDelegate;
@@ -73,8 +73,8 @@ QT_END_NAMESPACE
@interface QT_MANGLE_NAMESPACE(OSXBTL2CAPChannel) : NSObject<IOBluetoothL2CAPChannelDelegate>
-- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *)aDelegate;
-- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *)aDelegate
+- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *)aDelegate;
+- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *)aDelegate
channel:(IOBluetoothL2CAPChannel *)aChannel;
- (void)dealloc;
diff --git a/src/bluetooth/osx/osxbtledeviceinquiry.mm b/src/bluetooth/osx/osxbtledeviceinquiry.mm
index c56b6da3..70b96ab7 100644
--- a/src/bluetooth/osx/osxbtledeviceinquiry.mm
+++ b/src/bluetooth/osx/osxbtledeviceinquiry.mm
@@ -121,6 +121,11 @@ QT_END_NAMESPACE
QT_USE_NAMESPACE
+@interface QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry)(PrivateAPI)
+- (void)stopScanSafe;
+- (void)stopNotifier;
+@end
+
@implementation QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry)
{
LECBManagerNotifier *notifier;
@@ -147,17 +152,10 @@ QT_USE_NAMESPACE
- (void)dealloc
{
- if (manager) {
- [manager setDelegate:nil];
- if (internalState == InquiryActive)
- [manager stopScan];
- }
-
- if (notifier) {
- notifier->disconnect();
- notifier->deleteLater();
- }
-
+ [self stopScanSafe];
+ [manager setDelegate:nil];
+ [elapsedTimer cancelTimer];
+ [self stopNotifier];
[super dealloc];
}
@@ -166,7 +164,7 @@ QT_USE_NAMESPACE
Q_UNUSED(sender)
if (internalState == InquiryActive) {
- [manager stopScan];
+ [self stopScanSafe];
[manager setDelegate:nil];
internalState = InquiryFinished;
Q_ASSERT(notifier);
@@ -228,7 +226,7 @@ QT_USE_NAMESPACE
} else if (state == CBCentralManagerStateUnsupported || state == CBCentralManagerStateUnauthorized) {
#endif
if (internalState == InquiryActive) {
- [manager stopScan];
+ [self stopScanSafe];
// Not sure how this is possible at all,
// probably, can never happen.
internalState = ErrorPoweredOff;
@@ -244,8 +242,9 @@ QT_USE_NAMESPACE
#else
} else if (state == CBCentralManagerStatePoweredOff) {
#endif
+
+#ifndef Q_OS_MACOS
if (internalState == InquiryStarting) {
-#ifndef Q_OS_OSX
// On iOS a user can see at this point an alert asking to
// enable Bluetooth in the "Settings" app. If a user does so,
// we'll receive 'PoweredOn' state update later.
@@ -254,17 +253,19 @@ QT_USE_NAMESPACE
elapsedTimer.resetWithoutRetain([[GCDTimerObjC alloc] initWithDelegate:self]);
[elapsedTimer startWithTimeout:powerOffTimeoutMS step:300];
return;
+ }
#else
Q_UNUSED(powerOffTimeoutMS)
-#endif
- internalState = ErrorPoweredOff;
- emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
- } else {
- [manager stopScan];
- emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
- }
-
+#endif // Q_OS_MACOS
+ [elapsedTimer cancelTimer];
+ [self stopScanSafe];
[manager setDelegate:nil];
+ internalState = ErrorPoweredOff;
+ // On macOS we report PoweredOffError and our C++ owner will delete us
+ // (here we're kwnon as 'self'). Connection is Qt::QueuedConnection so we
+ // are apparently safe to call -stopNotifier after the signal.
+ emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
+ [self stopNotifier];
} else {
// The following two states we ignore (from Apple's docs):
//"
@@ -281,19 +282,45 @@ QT_USE_NAMESPACE
#pragma clang diagnostic pop
}
-- (void)stop
+- (void)stopScanSafe
{
- if (internalState == InquiryActive)
- [manager stopScan];
+ // CoreBluetooth warns about API misused if we call stopScan in a state
+ // other than powered on. Hence this 'Safe' ...
+ if (!manager)
+ return;
- [elapsedTimer cancelTimer];
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+
+ if (internalState == InquiryActive) {
+ const auto state = manager.data().state;
+ #if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_10_0) || QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13)
+ if (state == CBManagerStatePoweredOn)
+ #else
+ if (state == CBCentralManagerStatePoweredOn)
+ #endif
+ [manager stopScan];
+ }
+
+#pragma clang diagnostic pop
+}
+
+- (void)stopNotifier
+{
+ if (notifier) {
+ notifier->disconnect();
+ notifier->deleteLater();
+ notifier = nullptr;
+ }
+}
+- (void)stop
+{
+ [self stopScanSafe];
[manager setDelegate:nil];
+ [elapsedTimer cancelTimer];
+ [self stopNotifier];
internalState = InquiryCancelled;
-
- notifier->disconnect();
- notifier->deleteLater();
- notifier = nullptr;
}
- (void)centralManager:(CBCentralManager *)central
diff --git a/src/bluetooth/osx/osxbtnotifier_p.h b/src/bluetooth/osx/osxbtnotifier_p.h
index 47ee6ba1..397214d0 100644
--- a/src/bluetooth/osx/osxbtnotifier_p.h
+++ b/src/bluetooth/osx/osxbtnotifier_p.h
@@ -89,13 +89,13 @@ Q_SIGNALS:
void descriptorRead(QLowEnergyHandle descHandle, const QByteArray &value);
void descriptorWritten(QLowEnergyHandle descHandle, const QByteArray &value);
void notificationEnabled(QLowEnergyHandle charHandle, bool enabled);
+ void servicesWereModified();
void LEnotSupported();
void CBManagerError(QBluetoothDeviceDiscoveryAgent::Error error);
void CBManagerError(QLowEnergyController::Error error);
void CBManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyController::Error error);
void CBManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyService::ServiceError error);
-
};
}
diff --git a/src/bluetooth/osx/osxbtperipheralmanager.mm b/src/bluetooth/osx/osxbtperipheralmanager.mm
index 1998340a..39f9808c 100644
--- a/src/bluetooth/osx/osxbtperipheralmanager.mm
+++ b/src/bluetooth/osx/osxbtperipheralmanager.mm
@@ -340,7 +340,7 @@ bool qt_validate_value_range(const QLowEnergyCharacteristicData &data)
- (void)startAdvertising
{
state = PeripheralState::waitingForPowerOn;
- if (manager)
+ if (manager.data())
[manager setDelegate:nil];
manager.reset([[CBPeripheralManager alloc] initWithDelegate:self
queue:OSXBluetooth::qt_LE_queue()]);
@@ -405,7 +405,7 @@ bool qt_validate_value_range(const QLowEnergyCharacteristicData &data)
- (void) addServicesToPeripheral
{
- Q_ASSERT(manager);
+ Q_ASSERT(manager.data());
if (nextServiceToAdd < services.size())
[manager addService:services[nextServiceToAdd++]];
diff --git a/src/bluetooth/osx/osxbtrfcommchannel.mm b/src/bluetooth/osx/osxbtrfcommchannel.mm
index 00b67ee0..d2d3e2f8 100644
--- a/src/bluetooth/osx/osxbtrfcommchannel.mm
+++ b/src/bluetooth/osx/osxbtrfcommchannel.mm
@@ -37,22 +37,22 @@
**
****************************************************************************/
-#include "osxbtchanneldelegate_p.h"
#include "osxbtrfcommchannel_p.h"
#include "qbluetoothaddress.h"
#include "osxbtutility_p.h"
+#include "btdelegates_p.h"
QT_USE_NAMESPACE
@implementation QT_MANGLE_NAMESPACE(OSXBTRFCOMMChannel)
{
- QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *delegate;
+ QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *delegate;
IOBluetoothDevice *device;
IOBluetoothRFCOMMChannel *channel;
bool connected;
}
-- (id)initWithDelegate:(OSXBluetooth::ChannelDelegate *)aDelegate
+- (id)initWithDelegate:(DarwinBluetooth::ChannelDelegate *)aDelegate
{
Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)");
@@ -66,7 +66,7 @@ QT_USE_NAMESPACE
return self;
}
-- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth::ChannelDelegate) *)aDelegate
+- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth::ChannelDelegate) *)aDelegate
channel:(IOBluetoothRFCOMMChannel *)aChannel
{
// This type of channel does not require connect, it's created with
diff --git a/src/bluetooth/osx/osxbtrfcommchannel_p.h b/src/bluetooth/osx/osxbtrfcommchannel_p.h
index 775999ed..44416cce 100644
--- a/src/bluetooth/osx/osxbtrfcommchannel_p.h
+++ b/src/bluetooth/osx/osxbtrfcommchannel_p.h
@@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE
class QBluetoothAddress;
-namespace OSXBluetooth {
+namespace DarwinBluetooth {
class ChannelDelegate;
@@ -73,8 +73,8 @@ QT_END_NAMESPACE
@interface QT_MANGLE_NAMESPACE(OSXBTRFCOMMChannel) : NSObject<IOBluetoothRFCOMMChannelDelegate>
-- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *)aDelegate;
-- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *)aDelegate
+- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *)aDelegate;
+- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *)aDelegate
channel:(IOBluetoothRFCOMMChannel *)aChannel;
- (void)dealloc;
diff --git a/src/bluetooth/osx/osxbtsdpinquiry.mm b/src/bluetooth/osx/osxbtsdpinquiry.mm
index a7bdc2c4..a2b02b1a 100644
--- a/src/bluetooth/osx/osxbtsdpinquiry.mm
+++ b/src/bluetooth/osx/osxbtsdpinquiry.mm
@@ -41,6 +41,7 @@
#include "osxbtsdpinquiry_p.h"
#include "qbluetoothuuid.h"
#include "osxbtutility_p.h"
+#include "btdelegates_p.h"
#include <QtCore/qvariant.h>
#include <QtCore/qstring.h>
@@ -49,10 +50,6 @@ QT_BEGIN_NAMESPACE
namespace OSXBluetooth {
-SDPInquiryDelegate::~SDPInquiryDelegate()
-{
-}
-
namespace {
QBluetoothUuid sdp_element_to_uuid(IOBluetoothSDPDataElement *element)
@@ -213,12 +210,12 @@ using namespace OSXBluetooth;
@implementation QT_MANGLE_NAMESPACE(OSXBTSDPInquiry)
{
- QT_PREPEND_NAMESPACE(OSXBluetooth::SDPInquiryDelegate) *delegate;
+ QT_PREPEND_NAMESPACE(DarwinBluetooth::SDPInquiryDelegate) *delegate;
IOBluetoothDevice *device;
bool isActive;
}
-- (id)initWithDelegate:(SDPInquiryDelegate *)aDelegate
+- (id)initWithDelegate:(DarwinBluetooth::SDPInquiryDelegate *)aDelegate
{
Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)");
diff --git a/src/bluetooth/osx/osxbtsdpinquiry_p.h b/src/bluetooth/osx/osxbtsdpinquiry_p.h
index dd38a28b..e2658670 100644
--- a/src/bluetooth/osx/osxbtsdpinquiry_p.h
+++ b/src/bluetooth/osx/osxbtsdpinquiry_p.h
@@ -68,17 +68,13 @@ QT_BEGIN_NAMESPACE
class QBluetoothServiceInfo;
class QVariant;
-namespace OSXBluetooth {
+namespace DarwinBluetooth {
-class SDPInquiryDelegate {
-public:
- typedef QT_MANGLE_NAMESPACE(OSXBTSDPInquiry) ObjCServiceInquiry;
+class SDPInquiryDelegate;
- virtual ~SDPInquiryDelegate();
+}
- virtual void SDPInquiryFinished(IOBluetoothDevice *device) = 0;
- virtual void SDPInquiryError(IOBluetoothDevice *device, IOReturn errorCode) = 0;
-};
+namespace OSXBluetooth {
void extract_service_record(IOBluetoothSDPServiceRecord *record, QBluetoothServiceInfo &serviceInfo);
QVariant extract_attribute_value(IOBluetoothSDPDataElement *dataElement);
@@ -90,7 +86,7 @@ QT_END_NAMESPACE
@interface QT_MANGLE_NAMESPACE(OSXBTSDPInquiry) : NSObject
-- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth::SDPInquiryDelegate) *)aDelegate;
+- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth::SDPInquiryDelegate) *)aDelegate;
- (void)dealloc;
- (IOReturn)performSDPQueryWithDevice:(const QBluetoothAddress &)address;
diff --git a/src/bluetooth/osx/osxbtsocketlistener.mm b/src/bluetooth/osx/osxbtsocketlistener.mm
index 517b7f2d..10526b0f 100644
--- a/src/bluetooth/osx/osxbtsocketlistener.mm
+++ b/src/bluetooth/osx/osxbtsocketlistener.mm
@@ -39,31 +39,20 @@
#include "osxbtsocketlistener_p.h"
#include "osxbtutility_p.h"
+#include "btdelegates_p.h"
#include <QtCore/qdebug.h>
-QT_BEGIN_NAMESPACE
-
-namespace OSXBluetooth {
-
-SocketListener::~SocketListener()
-{
-}
-
-}
-
-QT_END_NAMESPACE
-
QT_USE_NAMESPACE
@implementation QT_MANGLE_NAMESPACE(OSXBTSocketListener)
{
IOBluetoothUserNotification *connectionNotification;
- QT_PREPEND_NAMESPACE(OSXBluetooth::SocketListener) *delegate;
+ QT_PREPEND_NAMESPACE(DarwinBluetooth::SocketListener) *delegate;
quint16 port;
}
-- (id)initWithListener:(OSXBluetooth::SocketListener *)aDelegate
+- (id)initWithListener:(DarwinBluetooth::SocketListener *)aDelegate
{
Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)");
if (self = [super init]) {
@@ -119,7 +108,7 @@ QT_USE_NAMESPACE
Q_UNUSED(notification)
Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
- delegate->openNotify(newChannel);
+ delegate->openNotifyRFCOMM(newChannel);
}
- (void)l2capOpenNotification:(IOBluetoothUserNotification *)notification
@@ -128,7 +117,7 @@ QT_USE_NAMESPACE
Q_UNUSED(notification)
Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
- delegate->openNotify(newChannel);
+ delegate->openNotifyL2CAP(newChannel);
}
- (quint16)port
diff --git a/src/bluetooth/osx/osxbtsocketlistener_p.h b/src/bluetooth/osx/osxbtsocketlistener_p.h
index cac0b7c4..3bbce24e 100644
--- a/src/bluetooth/osx/osxbtsocketlistener_p.h
+++ b/src/bluetooth/osx/osxbtsocketlistener_p.h
@@ -57,22 +57,15 @@
#include <Foundation/Foundation.h>
+// TODO: use the special macros we have to create an
+// alias for a mangled name.
@class QT_MANGLE_NAMESPACE(OSXBTSocketListener);
QT_BEGIN_NAMESPACE
-namespace OSXBluetooth {
+namespace DarwinBluetooth {
-class SocketListener
-{
-public:
- typedef QT_MANGLE_NAMESPACE(OSXBTSocketListener) ObjCListener;
-
- virtual ~SocketListener();
-
- virtual void openNotify(IOBluetoothRFCOMMChannel *channel) = 0;
- virtual void openNotify(IOBluetoothL2CAPChannel *channel) = 0;
-};
+class SocketListener;
}
@@ -83,7 +76,7 @@ QT_END_NAMESPACE
@interface QT_MANGLE_NAMESPACE(OSXBTSocketListener) : NSObject
-- (id)initWithListener:(QT_PREPEND_NAMESPACE(OSXBluetooth::SocketListener) *)aDelegate;
+- (id)initWithListener:(QT_PREPEND_NAMESPACE(DarwinBluetooth::SocketListener) *)aDelegate;
- (void)dealloc;
- (bool)listenRFCOMMConnectionsWithChannelID:(BluetoothRFCOMMChannelID)channelID;
diff --git a/src/bluetooth/qbluetooth.cpp b/src/bluetooth/qbluetooth.cpp
index 7b5fd266..1e8ce0b8 100644
--- a/src/bluetooth/qbluetooth.cpp
+++ b/src/bluetooth/qbluetooth.cpp
@@ -104,5 +104,6 @@ Q_LOGGING_CATEGORY(QT_BT_ANDROID, "qt.bluetooth.android")
Q_LOGGING_CATEGORY(QT_BT_BLUEZ, "qt.bluetooth.bluez")
Q_LOGGING_CATEGORY(QT_BT_WINDOWS, "qt.bluetooth.windows")
Q_LOGGING_CATEGORY(QT_BT_WINRT, "qt.bluetooth.winrt")
+Q_LOGGING_CATEGORY(QT_BT_WINRT_SERVICE_THREAD, "qt.bluetooth.winrt.service.thread")
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp
index b132a3a6..fb14850e 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp
@@ -176,8 +176,6 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT)
\sa QBluetoothDeviceInfo::rssi(), lowEnergyDiscoveryTimeout()
*/
-// TODO deviceUpdated() signal not implemented on WinRT
-
/*!
\fn void QBluetoothDeviceDiscoveryAgent::finished()
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp
index 443be14d..2f0524d4 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp
@@ -363,7 +363,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevices(
discoveredDevices.append(info);
qCDebug(QT_BT_ANDROID) << "Device found: " << info.name() << info.address().toString()
- << "isLeScanResult:" << isLeResult;
+ << "isLeScanResult:" << isLeResult
+ << "Manufacturer data size:" << info.manufacturerData().size();
emit q->deviceDiscovered(info);
}
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm
index f62ca0dd..d9883d28 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm
@@ -37,11 +37,14 @@
**
****************************************************************************/
+#include "qbluetoothdevicediscoveryagent_p.h"
#include "qbluetoothdevicediscoveryagent.h"
+
#include "osx/osxbtledeviceinquiry_p.h"
+#ifdef Q_OS_MACOS
#include "osx/osxbtdeviceinquiry_p.h"
-#include "qbluetoothlocaldevice.h"
#include "osx/osxbtsdpinquiry_p.h"
+#endif // Q_OS_MACOS
#include "qbluetoothdeviceinfo.h"
#include "osx/osxbtnotifier_p.h"
#include "osx/osxbtutility_p.h"
@@ -51,13 +54,14 @@
#include "qbluetoothaddress.h"
#include "osx/uistrings_p.h"
#include "qbluetoothuuid.h"
+#include "osx/btraii_p.h"
#include <QtCore/qloggingcategory.h>
#include <QtCore/qscopedpointer.h>
+#include <QtCore/qvector.h>
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qdebug.h>
-#include <QtCore/qlist.h>
#include <Foundation/Foundation.h>
@@ -75,108 +79,42 @@ void registerQDeviceDiscoveryMetaType()
initDone = true;
}
}
+#ifdef Q_OS_MACOS
+using InquiryObjC = QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry);
+#endif // Q_OS_MACOS
-}//namespace
-
-class QBluetoothDeviceDiscoveryAgentPrivate : public QObject,
- public OSXBluetooth::DeviceInquiryDelegate
-{
- friend class QBluetoothDeviceDiscoveryAgent;
-public:
- template<class T>
- using ObjCScopedPointer = OSXBluetooth::ObjCScopedPointer<T>;
- using LEDeviceInquiryObjC = QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry);
-
- QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress & address,
- QBluetoothDeviceDiscoveryAgent *q);
-
- ~QBluetoothDeviceDiscoveryAgentPrivate() override;
-
- bool isValid() const;
- bool isActive() const;
-
- void start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods);
- void startClassic();
- void startLE();
- void stop();
-
-private:
- enum AgentState {
- NonActive,
- ClassicScan,
- LEScan
- };
-
- // DeviceInquiryDelegate:
- void inquiryFinished(IOBluetoothDeviceInquiry *inq) override;
- void error(IOBluetoothDeviceInquiry *inq, IOReturn error) override;
- void deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device) override;
-
- void LEinquiryFinished();
- void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error);
- void LEnotSupported();
-
- // Check if it's a really new device/updated info and emit
- // q_ptr->deviceDiscovered.
- void deviceFound(const QBluetoothDeviceInfo &newDeviceInfo);
-
- void setError(IOReturn error, const QString &text = QString());
- void setError(QBluetoothDeviceDiscoveryAgent::Error,
- const QString &text = QString());
-
- QBluetoothDeviceDiscoveryAgent *q_ptr;
- AgentState agentState;
-
- QBluetoothAddress adapterAddress;
+using LEInquiryObjC = QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry);
- bool startPending;
- bool stopPending;
-
- QBluetoothDeviceDiscoveryAgent::Error lastError;
- QString errorString;
-
- QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType;
-
- using DeviceInquiry = ObjCScopedPointer<DeviceInquiryObjC>;
- DeviceInquiry inquiry;
-
- using LEDeviceInquiry = ObjCScopedPointer<LEDeviceInquiryObjC>;
- LEDeviceInquiry inquiryLE;
-
- using HostController = ObjCScopedPointer<IOBluetoothHostController>;
- HostController hostController;
-
- using DevicesList = QList<QBluetoothDeviceInfo>;
- DevicesList discoveredDevices;
-
- int lowEnergySearchTimeout;
- QBluetoothDeviceDiscoveryAgent::DiscoveryMethods requestedMethods;
-};
+} //namespace
QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &adapter,
QBluetoothDeviceDiscoveryAgent *q) :
- q_ptr(q),
+ inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry),
+ lastError(QBluetoothDeviceDiscoveryAgent::NoError),
agentState(NonActive),
adapterAddress(adapter),
startPending(false),
stopPending(false),
- lastError(QBluetoothDeviceDiscoveryAgent::NoError),
- inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry),
lowEnergySearchTimeout(OSXBluetooth::defaultLEScanTimeoutMS),
- requestedMethods(QBluetoothDeviceDiscoveryAgent::ClassicMethod
- | QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)
+#ifdef Q_OS_MACOS
+ requestedMethods(QBluetoothDeviceDiscoveryAgent::ClassicMethod | QBluetoothDeviceDiscoveryAgent::LowEnergyMethod),
+#else
+ requestedMethods(QBluetoothDeviceDiscoveryAgent::ClassicMethod),
+#endif // Q_OS_MACOS
+ q_ptr(q)
{
registerQDeviceDiscoveryMetaType();
Q_ASSERT_X(q != nullptr, Q_FUNC_INFO, "invalid q_ptr (null)");
- HostController controller([[IOBluetoothHostController defaultController] retain]);
- if (!controller || [controller powerState] != kBluetoothHCIPowerStateON) {
+#ifdef Q_OS_MACOS
+ IOBluetoothHostController *hostController = [IOBluetoothHostController defaultController];
+ if (!hostController || [hostController powerState] != kBluetoothHCIPowerStateON) {
qCCritical(QT_BT_OSX) << "no default host controller or adapter is off";
return;
}
-
- hostController.reset(controller.take());
+ controller.reset(hostController, DarwinBluetooth::RetainPolicy::doInitialRetain);
+#endif
}
QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
@@ -185,7 +123,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
// We want the LE scan to stop as soon as possible.
if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) {
// Local variable to be retained ...
- LEDeviceInquiryObjC *inq = inquiryLE.data();
+ LEInquiryObjC *inq = inquiryLE.getAs<LEInquiryObjC>();
dispatch_sync(leQueue, ^{
[inq stop];
});
@@ -193,11 +131,6 @@ QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
}
}
-bool QBluetoothDeviceDiscoveryAgentPrivate::isValid() const
-{
- return hostController && [hostController powerState] == kBluetoothHCIPowerStateON;
-}
-
bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const
{
if (startPending)
@@ -211,12 +144,19 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const
void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods)
{
- Q_ASSERT(isValid());
Q_ASSERT(!isActive());
Q_ASSERT(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError);
Q_ASSERT(methods & (QBluetoothDeviceDiscoveryAgent::ClassicMethod
| QBluetoothDeviceDiscoveryAgent::LowEnergyMethod));
+#ifdef Q_OS_MACOS
+ if (!controller) {
+ setError(QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError);
+ emit q_ptr->error(lastError);
+ return;
+ }
+#endif // Q_OS_MACOS
+
requestedMethods = methods;
if (stopPending) {
@@ -230,16 +170,18 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent
agentState = NonActive;
discoveredDevices.clear();
setError(QBluetoothDeviceDiscoveryAgent::NoError);
-
+#ifdef Q_OS_MACOS
if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod)
return startClassic();
+#endif // Q_OS_MACOS
startLE();
}
+#ifdef Q_OS_MACOS
+
void QBluetoothDeviceDiscoveryAgentPrivate::startClassic()
{
- Q_ASSERT(isValid());
Q_ASSERT(!isActive());
Q_ASSERT(lastError == QBluetoothDeviceDiscoveryAgent::NoError);
Q_ASSERT(requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod);
@@ -249,7 +191,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startClassic()
if (!inquiry) {
// The first Classic scan for this DDA.
- inquiry.reset([[DeviceInquiryObjC alloc]initWithDelegate:this]);
+ inquiry.reset([[InquiryObjC alloc] initWithDelegate:this],
+ DarwinBluetooth::RetainPolicy::noInitialRetain);
+
if (!inquiry) {
qCCritical(QT_BT_OSX) << "failed to initialize an Classic device inquiry";
setError(QBluetoothDeviceDiscoveryAgent::UnknownError,
@@ -261,7 +205,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startClassic()
agentState = ClassicScan;
- const IOReturn res = [inquiry start];
+ const IOReturn res = [inquiry.getAs<InquiryObjC>() start];
if (res != kIOReturnSuccess) {
setError(res, QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED));
agentState = NonActive;
@@ -269,9 +213,10 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startClassic()
}
}
+#endif // Q_OS_MACOS
+
void QBluetoothDeviceDiscoveryAgentPrivate::startLE()
{
- Q_ASSERT(isValid());
Q_ASSERT(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError);
Q_ASSERT(requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
@@ -291,7 +236,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE()
this, DeviceMemFunPtr(&QBluetoothDeviceDiscoveryAgentPrivate::deviceFound));
// Check queue and create scanner:
- inquiryLE.reset([[LEDeviceInquiryObjC alloc] initWithNotifier:notifier.data()]);
+ inquiryLE.reset([[LEInquiryObjC alloc] initWithNotifier:notifier.data()],
+ DarwinBluetooth::RetainPolicy::noInitialRetain);
if (inquiryLE)
notifier.take(); // Whatever happens next, inquiryLE is already the owner ...
@@ -307,7 +253,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE()
// Now start in on LE queue:
agentState = LEScan;
// We need the local variable so that it's retained ...
- LEDeviceInquiryObjC *inq = inquiryLE.data();
+ LEInquiryObjC *inq = inquiryLE.getAs<LEInquiryObjC>();
dispatch_async(leQueue, ^{
[inq startWithTimeout:lowEnergySearchTimeout];
});
@@ -315,7 +261,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE()
void QBluetoothDeviceDiscoveryAgentPrivate::stop()
{
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "called on invalid device discovery agent");
Q_ASSERT_X(isActive(), Q_FUNC_INFO, "called whithout active inquiry");
Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError,
Q_FUNC_INFO, "called with invalid bluetooth adapter");
@@ -328,8 +273,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop()
setError(QBluetoothDeviceDiscoveryAgent::NoError);
+#ifdef Q_OS_MACOS
if (agentState == ClassicScan) {
- const IOReturn res = [inquiry stop];
+ const IOReturn res = [inquiry.getAs<InquiryObjC>() stop];
if (res != kIOReturnSuccess) {
qCWarning(QT_BT_OSX) << "failed to stop";
startPending = prevStart;
@@ -338,10 +284,14 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop()
emit q_ptr->error(lastError);
}
} else {
+#else
+ {
+ Q_UNUSED(prevStart)
+#endif // Q_OS_MACOS
dispatch_queue_t leQueue(qt_LE_queue());
Q_ASSERT(leQueue);
// We need the local variable so that it's retained ...
- LEDeviceInquiryObjC *inq = inquiryLE.data();
+ LEInquiryObjC *inq = inquiryLE.getAs<LEInquiryObjC>();
dispatch_sync(leQueue, ^{
[inq stop];
});
@@ -351,12 +301,10 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop()
}
}
-void QBluetoothDeviceDiscoveryAgentPrivate::inquiryFinished(IOBluetoothDeviceInquiry *inq)
-{
- Q_UNUSED(inq)
-
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid device discovery agent"); //We can never be here.
+#ifdef Q_OS_MACOS
+void QBluetoothDeviceDiscoveryAgentPrivate::inquiryFinished()
+{
// The subsequent start(LE) function (if any)
// will (re)set the correct state.
agentState = NonActive;
@@ -381,12 +329,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::inquiryFinished(IOBluetoothDeviceInq
}
}
-void QBluetoothDeviceDiscoveryAgentPrivate::error(IOBluetoothDeviceInquiry *inq, IOReturn error)
+void QBluetoothDeviceDiscoveryAgentPrivate::error(IOReturn error)
{
- Q_UNUSED(inq)
-
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid device discovery agent");
-
startPending = false;
stopPending = false;
@@ -395,12 +339,11 @@ void QBluetoothDeviceDiscoveryAgentPrivate::error(IOBluetoothDeviceInquiry *inq,
emit q_ptr->error(lastError);
}
-void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device)
+void QBluetoothDeviceDiscoveryAgentPrivate::classicDeviceFound(void *obj)
{
- Q_UNUSED(inq)
-
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid device discovery agent");
+ auto device = static_cast<IOBluetoothDevice *>(obj);
Q_ASSERT_X(device, Q_FUNC_INFO, "invalid IOBluetoothDevice (nil)");
+
Q_ASSERT_X(agentState == ClassicScan, Q_FUNC_INFO,
"invalid agent state (expected classic scan)");
@@ -417,7 +360,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(IOBluetoothDeviceInquiry
if (device.name)
deviceName = QString::fromNSString(device.name);
- const qint32 classOfDevice(device.classOfDevice);
+ const auto classOfDevice = qint32(device.classOfDevice);
QBluetoothDeviceInfo deviceInfo(deviceAddress, deviceName, classOfDevice);
deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration);
@@ -439,6 +382,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::setError(IOReturn error, const QStri
setError(QBluetoothDeviceDiscoveryAgent::UnknownError, text);
}
+#endif // Q_OS_MACOS
+
void QBluetoothDeviceDiscoveryAgentPrivate::setError(QBluetoothDeviceDiscoveryAgent::Error error, const QString &text)
{
lastError = error;
@@ -459,14 +404,14 @@ void QBluetoothDeviceDiscoveryAgentPrivate::setError(QBluetoothDeviceDiscoveryAg
case QBluetoothDeviceDiscoveryAgent::InputOutputError:
errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_IO);
break;
+ case QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError:
+ errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_NOTSUPPORTED);
+ break;
case QBluetoothDeviceDiscoveryAgent::UnknownError:
default:
errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_UNKNOWN_ERROR);
}
}
-
- if (lastError != QBluetoothDeviceDiscoveryAgent::NoError)
- qCDebug(QT_BT_OSX) << "error set:"<<errorString;
}
void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error)
@@ -487,6 +432,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported()
{
qCDebug(QT_BT_OSX) << "no Bluetooth LE support";
+#ifdef Q_OS_MACOS
if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod) {
// Having both Classic | LE means this is not an error.
LEinquiryFinished();
@@ -497,6 +443,13 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported()
// as UnsupportedDiscoveryMethod.
LEinquiryError(QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod);
}
+#else
+ inquiryLE.reset();
+ startPending = false;
+ stopPending = false;
+ setError(QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError);
+ emit q_ptr->error(lastError);
+#endif
}
void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished()
@@ -522,8 +475,12 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(const QBluetoothDeviceIn
// Core Bluetooth does not allow us to access addresses, we have to use uuid instead.
// This uuid has nothing to do with uuids in Bluetooth in general (it's generated by
// Apple's framework using some algorithm), but it's a 128-bit uuid after all.
- const bool isLE = newDeviceInfo.coreConfigurations() == QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
-
+ const bool isLE =
+#ifdef Q_OS_MACOS
+ newDeviceInfo.coreConfigurations() == QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
+#else
+ true;
+#endif // Q_OS_MACOS
for (int i = 0, e = discoveredDevices.size(); i < e; ++i) {
if (isLE) {
if (discoveredDevices[i].deviceUuid() == newDeviceInfo.deviceUuid()) {
@@ -564,6 +521,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(const QBluetoothDeviceIn
return;
}
} else {
+#ifdef Q_OS_MACOS
if (discoveredDevices[i].address() == newDeviceInfo.address()) {
if (discoveredDevices[i] == newDeviceInfo)
return;
@@ -572,6 +530,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(const QBluetoothDeviceIn
emit q_ptr->deviceDiscovered(newDeviceInfo);
return;
}
+#else
+ Q_UNREACHABLE();
+#endif // Q_OS_MACOS
}
}
@@ -579,125 +540,13 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(const QBluetoothDeviceIn
emit q_ptr->deviceDiscovered(newDeviceInfo);
}
-QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(QObject *parent) :
- QObject(parent),
- d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(QBluetoothAddress(), this))
-{
-}
-
-QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(
- const QBluetoothAddress &deviceAdapter, QObject *parent) :
- QObject(parent),
- d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(deviceAdapter, this))
-{
- if (!deviceAdapter.isNull()) {
- const QList<QBluetoothHostInfo> localDevices = QBluetoothLocalDevice::allDevices();
- for (const QBluetoothHostInfo &hostInfo : localDevices) {
- if (hostInfo.address() == deviceAdapter)
- return;
- }
- d_ptr->setError(InvalidBluetoothAdapterError);
- }
-}
-
-QBluetoothDeviceDiscoveryAgent::~QBluetoothDeviceDiscoveryAgent()
-{
- delete d_ptr;
-}
-
-QBluetoothDeviceDiscoveryAgent::InquiryType QBluetoothDeviceDiscoveryAgent::inquiryType() const
-{
- return d_ptr->inquiryType;
-}
-
-void QBluetoothDeviceDiscoveryAgent::setInquiryType(InquiryType type)
-{
- d_ptr->inquiryType = type;
-}
-
-QList<QBluetoothDeviceInfo> QBluetoothDeviceDiscoveryAgent::discoveredDevices() const
-{
- return d_ptr->discoveredDevices;
-}
-
QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods()
{
+#ifdef Q_OS_MACOS
return ClassicMethod | LowEnergyMethod;
-}
-
-void QBluetoothDeviceDiscoveryAgent::start()
-{
- start(supportedDiscoveryMethods());
-}
-
-void QBluetoothDeviceDiscoveryAgent::start(DiscoveryMethods methods)
-{
- if (methods == NoMethod)
- return;
-
- if ((supportedDiscoveryMethods() & methods) != methods) {
- d_ptr->lastError = UnsupportedDiscoveryMethod;
- d_ptr->errorString = tr("One or more device discovery methods "
- "are not supported on this platform");
- emit error(d_ptr->lastError);
- return;
- }
-
- if (d_ptr->lastError != InvalidBluetoothAdapterError) {
- if (d_ptr->isValid()) {
- if (!isActive())
- d_ptr->start(methods);
- } else {
- // We previously failed to initialize d_ptr correctly:
- // either some memory allocation problem or
- // no BT adapter found.
- d_ptr->setError(InvalidBluetoothAdapterError);
- emit error(InvalidBluetoothAdapterError);
- }
- }
-}
-
-void QBluetoothDeviceDiscoveryAgent::stop()
-{
- if (d_ptr->isValid()) {
- if (isActive() && d_ptr->lastError != InvalidBluetoothAdapterError)
- d_ptr->stop();
- }
-}
-
-bool QBluetoothDeviceDiscoveryAgent::isActive() const
-{
- if (d_ptr->isValid())
- return d_ptr->isActive();
-
- return false;
-}
-
-QBluetoothDeviceDiscoveryAgent::Error QBluetoothDeviceDiscoveryAgent::error() const
-{
- return d_ptr->lastError;
-}
-
-QString QBluetoothDeviceDiscoveryAgent::errorString() const
-{
- return d_ptr->errorString;
-}
-
-void QBluetoothDeviceDiscoveryAgent::setLowEnergyDiscoveryTimeout(int timeout)
-{
- // cannot deliberately turn it off
- if (timeout < 0) {
- qCDebug(QT_BT_OSX) << "The Bluetooth Low Energy device discovery timeout cannot be negative.";
- return;
- }
-
- d_ptr->lowEnergySearchTimeout = timeout;
- return;
-}
-
-int QBluetoothDeviceDiscoveryAgent::lowEnergyDiscoveryTimeout() const
-{
- return d_ptr->lowEnergySearchTimeout;
+#else
+ return LowEnergyMethod;
+#endif // Q_OS_MACOS
}
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm
deleted file mode 100644
index 059f244d..00000000
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm
+++ /dev/null
@@ -1,458 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtBluetooth module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qbluetoothdevicediscoveryagent.h"
-#include "osx/osxbtledeviceinquiry_p.h"
-#include "qbluetoothlocaldevice.h"
-#include "qbluetoothdeviceinfo.h"
-#include "osx/osxbtnotifier_p.h"
-#include "osx/osxbtutility_p.h"
-#include "osx/uistrings_p.h"
-#include "qbluetoothuuid.h"
-
-#include <QtCore/qloggingcategory.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qglobal.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qlist.h>
-
-#include <CoreBluetooth/CoreBluetooth.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace
-{
-
-void registerQDeviceDiscoveryMetaType()
-{
- static bool initDone = false;
- if (!initDone) {
- qRegisterMetaType<QBluetoothDeviceInfo>();
- qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::Error>();
- initDone = true;
- }
-}
-
-}//namespace
-
-class QBluetoothDeviceDiscoveryAgentPrivate : public QObject
-{
- friend class QBluetoothDeviceDiscoveryAgent;
-
-public:
- QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &address,
- QBluetoothDeviceDiscoveryAgent *q);
- virtual ~QBluetoothDeviceDiscoveryAgentPrivate();
-
- bool isActive() const;
-
- void start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods m);
- void stop();
-
-private:
- using LEDeviceInquiryObjC = QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry);
-
- void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error);
- void LEnotSupported();
- void LEdeviceFound(const QBluetoothDeviceInfo &info);
- void LEinquiryFinished();
-
- void setError(QBluetoothDeviceDiscoveryAgent::Error, const QString &text = QString());
-
- QBluetoothDeviceDiscoveryAgent *q_ptr;
-
- QBluetoothDeviceDiscoveryAgent::Error lastError;
- QString errorString;
-
- QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType;
-
- using LEDeviceInquiry = OSXBluetooth::ObjCScopedPointer<LEDeviceInquiryObjC>;
- LEDeviceInquiry inquiryLE;
-
- using DevicesList = QList<QBluetoothDeviceInfo>;
- DevicesList discoveredDevices;
-
- bool startPending;
- bool stopPending;
-
- int lowEnergySearchTimeout;
-};
-
-QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &adapter,
- QBluetoothDeviceDiscoveryAgent *q) :
- q_ptr(q),
- lastError(QBluetoothDeviceDiscoveryAgent::NoError),
- inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry),
- startPending(false),
- stopPending(false),
- lowEnergySearchTimeout(OSXBluetooth::defaultLEScanTimeoutMS)
-{
- Q_UNUSED(adapter);
-
- registerQDeviceDiscoveryMetaType();
- Q_ASSERT_X(q != nullptr, Q_FUNC_INFO, "invalid q_ptr (null)");
-}
-
-QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
-{
- if (inquiryLE) {
- // We want the LE scan to stop as soon as possible.
- if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) {
- // Local variable to be retained ...
- LEDeviceInquiryObjC *inq = inquiryLE.data();
- dispatch_sync(leQueue, ^{
- [inq stop];
- });
- }
- }
-}
-
-bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const
-{
- if (startPending)
- return true;
- if (stopPending)
- return false;
-
- return inquiryLE;
-}
-
-void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods /*methods*/)
-{
- Q_ASSERT_X(!isActive(), Q_FUNC_INFO, "called on active device discovery agent");
-
- if (stopPending) {
- startPending = true;
- return;
- }
-
- using namespace OSXBluetooth;
-
- QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier);
- // Connections:
- using ErrMemFunPtr = void (LECBManagerNotifier::*)(QBluetoothDeviceDiscoveryAgent::Error);
- notifier->connect(notifier.data(), ErrMemFunPtr(&LECBManagerNotifier::CBManagerError),
- this, &QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError);
- notifier->connect(notifier.data(), &LECBManagerNotifier::LEnotSupported,
- this, &QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported);
- notifier->connect(notifier.data(), &LECBManagerNotifier::discoveryFinished,
- this, &QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished);
- notifier->connect(notifier.data(), &LECBManagerNotifier::deviceDiscovered,
- this, &QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound);
-
- inquiryLE.reset([[LEDeviceInquiryObjC alloc] initWithNotifier:notifier.data()]);
- if (inquiryLE)
- notifier.take(); // Whatever happens next, inquiryLE is already the owner ...
-
- dispatch_queue_t leQueue(qt_LE_queue());
- if (!leQueue || !inquiryLE) {
- setError(QBluetoothDeviceDiscoveryAgent::UnknownError,
- QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED));
- emit q_ptr->error(lastError);
- return;
- }
-
- discoveredDevices.clear();
- setError(QBluetoothDeviceDiscoveryAgent::NoError);
-
- // Create a local variable - to have a strong referece in a block.
- LEDeviceInquiryObjC *inq = inquiryLE.data();
- dispatch_async(leQueue, ^{
- [inq startWithTimeout:lowEnergySearchTimeout];
- });
-}
-
-void QBluetoothDeviceDiscoveryAgentPrivate::stop()
-{
- Q_ASSERT_X(isActive(), Q_FUNC_INFO, "called whithout active inquiry");
-
- startPending = false;
- stopPending = true;
-
- dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
- Q_ASSERT(leQueue);
-
- setError(QBluetoothDeviceDiscoveryAgent::NoError);
-
- // Create a local variable - to have a strong referece in a block.
- LEDeviceInquiryObjC *inq = inquiryLE.data();
- dispatch_sync(leQueue, ^{
- [inq stop];
- });
- // We consider LE scan to be stopped immediately and
- // do not care about this LEDeviceInquiry object anymore.
- LEinquiryFinished();
-}
-
-void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error)
-{
- // At the moment the only error reported by osxbtledeviceinquiry
- // can be 'powered off' error, it happens
- // after the LE scan started (so we have LE support and this is
- // a real PoweredOffError).
- Q_ASSERT_X(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError,
- Q_FUNC_INFO, "unexpected error");
-
- inquiryLE.reset();
-
- startPending = false;
- stopPending = false;
- setError(error);
- emit q_ptr->error(lastError);
-}
-
-void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported()
-{
- inquiryLE.reset();
-
- startPending = false;
- stopPending = false;
- setError(QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError);
- emit q_ptr->error(lastError);
-}
-
-void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(const QBluetoothDeviceInfo &newDeviceInfo)
-{
- // Update, append or discard.
- for (int i = 0, e = discoveredDevices.size(); i < e; ++i) {
- if (discoveredDevices[i].deviceUuid() == newDeviceInfo.deviceUuid()) {
- QBluetoothDeviceInfo::Fields updatedFields = QBluetoothDeviceInfo::Field::None;
- if (discoveredDevices[i].rssi() != newDeviceInfo.rssi()) {
- qCDebug(QT_BT_OSX) << "Updating RSSI for" << newDeviceInfo.address()
- << newDeviceInfo.rssi();
- discoveredDevices[i].setRssi(newDeviceInfo.rssi());
- updatedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI);
- }
-
- if (discoveredDevices[i].manufacturerData() != newDeviceInfo.manufacturerData()) {
- qCDebug(QT_BT_OSX) << "Updating manufacturer data for" << newDeviceInfo.address();
- const QVector<quint16> keys = newDeviceInfo.manufacturerIds();
- for (auto key: keys)
- discoveredDevices[i].setManufacturerData(key, newDeviceInfo.manufacturerData(key));
- updatedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData);
- }
-
- if (lowEnergySearchTimeout > 0) {
- if (discoveredDevices[i] != newDeviceInfo) {
- discoveredDevices.replace(i, newDeviceInfo);
- emit q_ptr->deviceDiscovered(newDeviceInfo);
- } else {
- if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None))
- emit q_ptr->deviceUpdated(discoveredDevices[i], updatedFields);
- }
-
- return;
- }
-
- discoveredDevices.replace(i, newDeviceInfo);
- emit q_ptr->deviceDiscovered(newDeviceInfo);
-
- if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None))
- emit q_ptr->deviceUpdated(discoveredDevices[i], updatedFields);
-
- return;
- }
- }
-
- discoveredDevices.append(newDeviceInfo);
- emit q_ptr->deviceDiscovered(newDeviceInfo);
-}
-
-void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished()
-{
- inquiryLE.reset();
-
- if (stopPending && !startPending) {
- stopPending = false;
- emit q_ptr->canceled();
- } else if (startPending) {
- startPending = false;
- stopPending = false;
- // always the same method for start() on iOS
- // classic search not supported
- start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
- } else {
- emit q_ptr->finished();
- }
-}
-
-void QBluetoothDeviceDiscoveryAgentPrivate::setError(QBluetoothDeviceDiscoveryAgent::Error error,
- const QString &text)
-{
- lastError = error;
-
- if (text.length() > 0) {
- errorString = text;
- } else {
- switch (lastError) {
- case QBluetoothDeviceDiscoveryAgent::NoError:
- errorString = QString();
- break;
- case QBluetoothDeviceDiscoveryAgent::PoweredOffError:
- errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_POWERED_OFF);
- break;
- case QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError:
- errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_INVALID_ADAPTER);
- break;
- case QBluetoothDeviceDiscoveryAgent::InputOutputError:
- errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_IO);
- break;
- case QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError:
- errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_NOTSUPPORTED);
- break;
- case QBluetoothDeviceDiscoveryAgent::UnknownError:
- default:
- errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_UNKNOWN_ERROR);
- }
- }
-}
-
-QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(QObject *parent) :
- QObject(parent),
- d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(QBluetoothAddress(), this))
-{
-}
-
-QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(
- const QBluetoothAddress &deviceAdapter, QObject *parent) :
- QObject(parent),
- d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(deviceAdapter, this))
-{
- if (!deviceAdapter.isNull()) {
- qCWarning(QT_BT_OSX) << "local device address is "
- "not available, provided address is ignored";
- d_ptr->setError(InvalidBluetoothAdapterError);
- }
-}
-
-QBluetoothDeviceDiscoveryAgent::~QBluetoothDeviceDiscoveryAgent()
-{
- delete d_ptr;
-}
-
-QBluetoothDeviceDiscoveryAgent::InquiryType QBluetoothDeviceDiscoveryAgent::inquiryType() const
-{
- return d_ptr->inquiryType;
-}
-
-void QBluetoothDeviceDiscoveryAgent::setInquiryType(QBluetoothDeviceDiscoveryAgent::InquiryType type)
-{
- d_ptr->inquiryType = type;
-}
-
-QList<QBluetoothDeviceInfo> QBluetoothDeviceDiscoveryAgent::discoveredDevices() const
-{
- return d_ptr->discoveredDevices;
-}
-
-QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods()
-{
- return LowEnergyMethod;
-}
-
-void QBluetoothDeviceDiscoveryAgent::start()
-{
- if (d_ptr->lastError != InvalidBluetoothAdapterError) {
- if (!isActive())
- d_ptr->start(supportedDiscoveryMethods());
- else
- qCDebug(QT_BT_OSX) << "already started";
- }
-}
-
-void QBluetoothDeviceDiscoveryAgent::start(DiscoveryMethods methods)
-{
- if (methods == NoMethod)
- return;
-
- DiscoveryMethods supported =
- QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods();
-
- if (!((supported & methods) == methods)) {
- d_ptr->lastError = UnsupportedDiscoveryMethod;
- d_ptr->errorString = QBluetoothDeviceDiscoveryAgent::tr("One or more device discovery methods "
- "are not supported on this platform");
- emit error(d_ptr->lastError);
- return;
- }
-
- if (!isActive() && d_ptr->lastError != InvalidBluetoothAdapterError)
- d_ptr->start(methods);
-}
-
-void QBluetoothDeviceDiscoveryAgent::stop()
-{
- if (isActive() && d_ptr->lastError != InvalidBluetoothAdapterError)
- d_ptr->stop();
-}
-
-bool QBluetoothDeviceDiscoveryAgent::isActive() const
-{
- return d_ptr->isActive();
-}
-
-QBluetoothDeviceDiscoveryAgent::Error QBluetoothDeviceDiscoveryAgent::error() const
-{
- return d_ptr->lastError;
-}
-
-QString QBluetoothDeviceDiscoveryAgent::errorString() const
-{
- return d_ptr->errorString;
-}
-
-int QBluetoothDeviceDiscoveryAgent::lowEnergyDiscoveryTimeout() const
-{
- return d_ptr->lowEnergySearchTimeout;
-}
-
-void QBluetoothDeviceDiscoveryAgent::setLowEnergyDiscoveryTimeout(int timeout)
-{
- // cannot deliberately turn it off
- if (timeout < 0) {
- qCDebug(QT_BT_OSX) << "The Bluetooth Low Energy device discovery timeout cannot be negative.";
- return;
- }
-
- d_ptr->lowEnergySearchTimeout = timeout;
- return;
-}
-
-QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
index 97beced3..be3a8863 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
@@ -59,6 +59,11 @@
#include <QtCore/QTimer>
#endif
+#ifdef Q_OS_DARWIN
+#include "osx/btdelegates_p.h"
+#include "osx/btraii_p.h"
+#endif // Q_OS_DARWIN
+
#include <QtCore/QVariantMap>
#include <QtBluetooth/QBluetoothAddress>
@@ -95,6 +100,9 @@ QT_END_NAMESPACE
#elif defined(QT_WINRT_BLUETOOTH)
#include <QtCore/QPointer>
#include <QtCore/QTimer>
+
+using ManufacturerData = QHash<quint16, QByteArray>;
+Q_DECLARE_METATYPE(ManufacturerData)
#endif
QT_BEGIN_NAMESPACE
@@ -104,11 +112,15 @@ class QWinRTBluetoothDeviceDiscoveryWorker;
#endif
class QBluetoothDeviceDiscoveryAgentPrivate
-#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(QT_WIN_BLUETOOTH)
+#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(QT_WIN_BLUETOOTH) \
+ || defined(Q_OS_DARWIN)
: public QObject
+#if defined(Q_OS_MACOS)
+ , public DarwinBluetooth::DeviceInquiryDelegate
+#endif // Q_OS_MACOS
{
Q_OBJECT
-#else
+#else // BlueZ
{
#endif
Q_DECLARE_PUBLIC(QBluetoothDeviceDiscoveryAgent)
@@ -208,6 +220,8 @@ private:
#ifdef QT_WINRT_BLUETOOTH
private slots:
void registerDevice(const QBluetoothDeviceInfo &info);
+ void updateDeviceData(const QBluetoothAddress &address, QBluetoothDeviceInfo::Fields fields,
+ qint16 rssi, ManufacturerData manufacturerData);
void onScanFinished();
private:
@@ -216,6 +230,57 @@ private:
QTimer *leScanTimer;
#endif
+#ifdef Q_OS_DARWIN
+
+ void startLE();
+
+#ifdef Q_OS_MACOS
+
+ void startClassic();
+
+ // Classic (IOBluetooth) inquiry delegate's methods:
+ void inquiryFinished() override;
+ void error(IOReturn error) override;
+ void classicDeviceFound(void *device) override;
+ // Classic (IOBluetooth) errors:
+ void setError(IOReturn error, const QString &text = QString());
+
+#endif // Q_OS_MACOS
+
+ // LE scan delegates (CoreBluetooth, all Darwin OSes):
+ void LEinquiryFinished();
+ void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error);
+ void LEnotSupported();
+
+ // LE errors:
+ void setError(QBluetoothDeviceDiscoveryAgent::Error,
+ const QString &text = QString());
+
+ // Both LE and Classic devices go there:
+ void deviceFound(const QBluetoothDeviceInfo &newDeviceInfo);
+
+ enum AgentState {
+ NonActive,
+ ClassicScan, // macOS (IOBluetooth) only
+ LEScan
+ } agentState;
+
+ QBluetoothAddress adapterAddress;
+
+ bool startPending;
+ bool stopPending;
+
+#ifdef Q_OS_MACOS
+
+ DarwinBluetooth::ScopedPointer controller;
+ DarwinBluetooth::ScopedPointer inquiry;
+
+#endif // Q_OS_MACOS
+
+ DarwinBluetooth::ScopedPointer inquiryLE;
+
+#endif // Q_OS_DARWIN
+
int lowEnergySearchTimeout;
QBluetoothDeviceDiscoveryAgent::DiscoveryMethods requestedMethods;
QBluetoothDeviceDiscoveryAgent *q_ptr;
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
index a353f5e3..9d306053 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
@@ -52,6 +52,7 @@
#include <QtCore/QLoggingCategory>
#include <QtCore/private/qeventdispatcher_winrt_p.h>
+#include <robuffer.h>
#include <wrl.h>
#include <windows.devices.enumeration.h>
#include <windows.devices.bluetooth.h>
@@ -68,6 +69,7 @@ using namespace ABI::Windows::Devices;
using namespace ABI::Windows::Devices::Bluetooth;
using namespace ABI::Windows::Devices::Bluetooth::Advertisement;
using namespace ABI::Windows::Devices::Enumeration;
+using namespace ABI::Windows::Storage::Streams;
QT_BEGIN_NAMESPACE
@@ -79,6 +81,39 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
ret; \
}
+#define WARN_AND_CONTINUE_IF_FAILED(msg) \
+ if (FAILED(hr)) { \
+ qCWarning(QT_BT_WINRT) << msg; \
+ continue; \
+ }
+
+static ManufacturerData extractManufacturerData(ComPtr<IBluetoothLEAdvertisement> ad)
+{
+ ManufacturerData ret;
+ ComPtr<IVector<BluetoothLEManufacturerData*>> data;
+ HRESULT hr = ad->get_ManufacturerData(&data);
+ WARN_AND_RETURN_IF_FAILED("Could not obtain list of manufacturer data.", return ret);
+ quint32 size;
+ hr = data->get_Size(&size);
+ WARN_AND_RETURN_IF_FAILED("Could not obtain manufacturer data's list size.", return ret);
+ for (quint32 i = 0; i < size; ++i) {
+ ComPtr<IBluetoothLEManufacturerData> d;
+ hr = data->GetAt(i, &d);
+ WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data.");
+ quint16 id;
+ hr = d->get_CompanyId(&id);
+ WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data company id.");
+ ComPtr<IBuffer> buffer;
+ hr = d->get_Data(&buffer);
+ WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data set.");
+ const QByteArray bufferData = byteArrayFromBuffer(buffer);
+ if (ret.contains(id))
+ qCWarning(QT_BT_WINRT) << "Company ID already present in manufacturer data.";
+ ret.insert(id, bufferData);
+ }
+ return ret;
+}
+
class QWinRTBluetoothDeviceDiscoveryWorker : public QObject
{
Q_OBJECT
@@ -86,7 +121,7 @@ public:
explicit QWinRTBluetoothDeviceDiscoveryWorker(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods);
~QWinRTBluetoothDeviceDiscoveryWorker();
void start();
- void stop();
+ void stopLEWatcher();
private:
void startDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode);
@@ -117,6 +152,8 @@ public slots:
Q_SIGNALS:
void deviceFound(const QBluetoothDeviceInfo &info);
+ void deviceDataChanged(const QBluetoothAddress &address, QBluetoothDeviceInfo::Fields,
+ qint16 rssi, ManufacturerData manufacturerData);
void scanFinished();
public:
@@ -127,9 +164,15 @@ private:
EventRegistrationToken m_leDeviceAddedToken;
#if QT_CONFIG(winrt_btle_no_pairing)
QMutex m_foundDevicesMutex;
- QMap<quint64, QVector<QBluetoothUuid>> m_foundLEDevicesMap;
+ struct LEAdvertisingInfo {
+ QVector<QBluetoothUuid> services;
+ qint16 rssi = 0;
+ };
+
+ QMap<quint64, LEAdvertisingInfo> m_foundLEDevicesMap;
#endif
- QVector<quint64> m_foundLEDevices;
+ QMap<quint64, qint16> m_foundLEDevices;
+ QMap<quint64, ManufacturerData> m_foundLEManufacturerData;
int m_pendingPairedDevices;
ComPtr<IBluetoothDeviceStatics> m_deviceStatics;
@@ -141,6 +184,8 @@ QWinRTBluetoothDeviceDiscoveryWorker::QWinRTBluetoothDeviceDiscoveryWorker(QBlue
, m_pendingPairedDevices(0)
{
qRegisterMetaType<QBluetoothDeviceInfo>();
+ qRegisterMetaType<QBluetoothDeviceInfo::Fields>();
+ qRegisterMetaType<ManufacturerData>();
#ifdef CLASSIC_APP_BUILD
CoInitialize(NULL);
@@ -153,7 +198,7 @@ QWinRTBluetoothDeviceDiscoveryWorker::QWinRTBluetoothDeviceDiscoveryWorker(QBlue
QWinRTBluetoothDeviceDiscoveryWorker::~QWinRTBluetoothDeviceDiscoveryWorker()
{
- stop();
+ stopLEWatcher();
#ifdef CLASSIC_APP_BUILD
CoUninitialize();
#endif
@@ -175,7 +220,7 @@ void QWinRTBluetoothDeviceDiscoveryWorker::start()
qCDebug(QT_BT_WINRT) << "Worker started";
}
-void QWinRTBluetoothDeviceDiscoveryWorker::stop()
+void QWinRTBluetoothDeviceDiscoveryWorker::stopLEWatcher()
{
if (m_leWatcher) {
HRESULT hr = m_leWatcher->Stop();
@@ -250,7 +295,8 @@ void QWinRTBluetoothDeviceDiscoveryWorker::gatherMultipleDeviceInformation(quint
{
for (quint32 i = 0; i < deviceCount; ++i) {
ComPtr<IDeviceInformation> device;
- HRESULT hr = devices->GetAt(i, &device);
+ HRESULT hr;
+ hr = devices->GetAt(i, &device);
Q_ASSERT_SUCCEEDED(hr);
gatherDeviceInformation(device.Get(), mode);
}
@@ -271,11 +317,23 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher()
HRESULT hr;
hr = args->get_BluetoothAddress(&address);
Q_ASSERT_SUCCEEDED(hr);
+ qint16 rssi;
+ hr = args->get_RawSignalStrengthInDBm(&rssi);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IBluetoothLEAdvertisement> ad;
+ hr = args->get_Advertisement(&ad);
+ Q_ASSERT_SUCCEEDED(hr);
+ const ManufacturerData manufacturerData = extractManufacturerData(ad);
+ QBluetoothDeviceInfo::Fields changedFields = QBluetoothDeviceInfo::Field::None;
+ if (!m_foundLEManufacturerData.contains(address)) {
+ m_foundLEManufacturerData.insert(address, manufacturerData);
+ changedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData);
+ } else if (m_foundLEManufacturerData.value(address) != manufacturerData) {
+ m_foundLEManufacturerData[address] = manufacturerData;
+ changedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData);
+ }
#if QT_CONFIG(winrt_btle_no_pairing)
if (supportsNewLEApi()) {
- ComPtr<IBluetoothLEAdvertisement> ad;
- hr = args->get_Advertisement(&ad);
- Q_ASSERT_SUCCEEDED(hr);
ComPtr<IVector<GUID>> guids;
hr = ad->get_ServiceUuids(&guids);
Q_ASSERT_SUCCEEDED(hr);
@@ -295,7 +353,12 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher()
if (m_foundLEDevicesMap.contains(address)) {
if (size == 0)
return S_OK;
- QVector<QBluetoothUuid> foundServices = m_foundLEDevicesMap.value(address);
+ const LEAdvertisingInfo adInfo = m_foundLEDevicesMap.value(address);
+ QVector<QBluetoothUuid> foundServices = adInfo.services;
+ if (adInfo.rssi != rssi) {
+ m_foundLEDevicesMap[address].rssi = rssi;
+ changedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI);
+ }
bool newServiceAdded = false;
for (const QBluetoothUuid &uuid : qAsConst(serviceUuids)) {
if (!foundServices.contains(uuid)) {
@@ -303,20 +366,43 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher()
newServiceAdded = true;
}
}
- if (!newServiceAdded)
+ if (!newServiceAdded) {
+ if (!changedFields.testFlag(QBluetoothDeviceInfo::Field::None)) {
+ QMetaObject::invokeMethod(this, "deviceDataChanged", Qt::AutoConnection,
+ Q_ARG(QBluetoothAddress, QBluetoothAddress(address)),
+ Q_ARG(QBluetoothDeviceInfo::Fields, changedFields),
+ Q_ARG(qint16, rssi),
+ Q_ARG(ManufacturerData, manufacturerData));
+ }
return S_OK;
- m_foundLEDevicesMap[address] = foundServices;
+ }
+ m_foundLEDevicesMap[address].services = foundServices;
} else {
- m_foundLEDevicesMap.insert(address, serviceUuids);
+ LEAdvertisingInfo info;
+ info.services = std::move(serviceUuids);
+ info.rssi = rssi;
+ m_foundLEDevicesMap.insert(address, info);
}
locker.unlock();
} else
#endif
{
- if (m_foundLEDevices.contains(address))
+ if (m_foundLEDevices.contains(address)) {
+ if (m_foundLEDevices.value(address) != rssi) {
+ m_foundLEDevices[address] = rssi;
+ changedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI);
+ }
+ if (!changedFields.testFlag(QBluetoothDeviceInfo::Field::None)) {
+ QMetaObject::invokeMethod(this, "deviceDataChanged", Qt::AutoConnection,
+ Q_ARG(QBluetoothAddress, QBluetoothAddress(address)),
+ Q_ARG(QBluetoothDeviceInfo::Fields, changedFields),
+ Q_ARG(qint16, rssi),
+ Q_ARG(ManufacturerData, manufacturerData));
+ }
return S_OK;
- m_foundLEDevices.append(address);
+ }
+ m_foundLEDevices.insert(address, rssi);
}
leBluetoothInfoFromAddressAsync(address);
return S_OK;
@@ -329,6 +415,7 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher()
void QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery()
{
emit scanFinished();
+ stopLEWatcher();
deleteLater();
}
@@ -616,13 +703,19 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IB
Q_ASSERT_SUCCEEDED(hr);
uuids.append(QBluetoothUuid(uuid));
}
+ const qint16 rssi = m_foundLEDevices.value(address);
+ const ManufacturerData manufacturerData = m_foundLEManufacturerData.value(address);
qCDebug(QT_BT_WINRT) << "Discovered BTLE device: " << QString::number(address) << btName
- << "Num UUIDs" << uuids.count();
+ << "Num UUIDs" << uuids.count() << "RSSI:" << rssi
+ << "Num manufacturer data" << manufacturerData.count();
QBluetoothDeviceInfo info(QBluetoothAddress(address), btName, 0);
info.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
info.setServiceUuids(uuids);
+ info.setRssi(rssi);
+ for (const quint16 key : manufacturerData.keys())
+ info.setManufacturerData(key, manufacturerData.value(key));
info.setCached(true);
QMetaObject::invokeMethod(this, "deviceFound", Qt::AutoConnection,
@@ -665,11 +758,13 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IB
boolean isPaired;
hr = pairing->get_IsPaired(&isPaired);
Q_ASSERT_SUCCEEDED(hr);
- QList<QBluetoothUuid> uuids;
+ QVector<QBluetoothUuid> uuids;
+ const LEAdvertisingInfo adInfo = m_foundLEDevicesMap.value(address);
+ const qint16 rssi = adInfo.rssi;
// Use the services obtained from the advertisement data if the device is not paired
if (!isPaired) {
- uuids = m_foundLEDevicesMap.value(address).toList();
+ uuids = adInfo.services;
} else {
IVectorView <GenericAttributeProfile::GattDeviceService *> *deviceServices;
hr = device->get_GattServices(&deviceServices);
@@ -687,13 +782,18 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IB
uuids.append(QBluetoothUuid(uuid));
}
}
+ const ManufacturerData manufacturerData = m_foundLEManufacturerData.value(address);
qCDebug(QT_BT_WINRT) << "Discovered BTLE device: " << QString::number(address) << btName
- << "Num UUIDs" << uuids.count();
+ << "Num UUIDs" << uuids.count() << "RSSI:" << rssi
+ << "Num manufacturer data" << manufacturerData.count();
QBluetoothDeviceInfo info(QBluetoothAddress(address), btName, 0);
info.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
- info.setServiceUuids(uuids, QBluetoothDeviceInfo::DataIncomplete);
+ info.setServiceUuids(uuids);
+ info.setRssi(rssi);
+ for (quint16 key : manufacturerData.keys())
+ info.setManufacturerData(key, manufacturerData.value(key));
info.setCached(true);
QMetaObject::invokeMethod(this, "deviceFound", Qt::AutoConnection,
@@ -739,6 +839,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent
discoveredDevices.clear();
connect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceFound,
this, &QBluetoothDeviceDiscoveryAgentPrivate::registerDevice);
+ connect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceDataChanged,
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceData);
connect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::scanFinished,
this, &QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished);
worker->start();
@@ -759,7 +861,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop()
{
Q_Q(QBluetoothDeviceDiscoveryAgent);
if (worker) {
- worker->stop();
+ worker->stopLEWatcher();
disconnectAndClearWorker();
emit q->canceled();
}
@@ -793,6 +895,30 @@ void QBluetoothDeviceDiscoveryAgentPrivate::registerDevice(const QBluetoothDevic
emit q->deviceDiscovered(info);
}
+void QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceData(const QBluetoothAddress &address,
+ QBluetoothDeviceInfo::Fields fields,
+ qint16 rssi,
+ ManufacturerData manufacturerData)
+{
+ if (fields.testFlag(QBluetoothDeviceInfo::Field::None))
+ return;
+
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ for (QList<QBluetoothDeviceInfo>::iterator iter = discoveredDevices.begin();
+ iter != discoveredDevices.end(); ++iter) {
+ if (iter->address() == address) {
+ qCDebug(QT_BT_WINRT) << "Updating data for device" << iter->name() << iter->address();
+ if (fields.testFlag(QBluetoothDeviceInfo::Field::RSSI))
+ iter->setRssi(rssi);
+ if (fields.testFlag(QBluetoothDeviceInfo::Field::ManufacturerData))
+ for (quint16 key : manufacturerData.keys())
+ iter->setManufacturerData(key, manufacturerData.value(key));
+ emit q->deviceUpdated(*iter, fields);
+ return;
+ }
+ }
+}
+
void QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished()
{
Q_Q(QBluetoothDeviceDiscoveryAgent);
@@ -802,17 +928,18 @@ void QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished()
void QBluetoothDeviceDiscoveryAgentPrivate::disconnectAndClearWorker()
{
- Q_Q(QBluetoothDeviceDiscoveryAgent);
if (!worker)
return;
disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::scanFinished,
- this, &QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished);
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished);
disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceFound,
- q, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered);
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::registerDevice);
+ disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceDataChanged,
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceData);
if (leScanTimer) {
disconnect(leScanTimer, &QTimer::timeout,
- worker, &QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery);
+ worker, &QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery);
}
worker.clear();
}
diff --git a/src/bluetooth/qbluetoothdeviceinfo.cpp b/src/bluetooth/qbluetoothdeviceinfo.cpp
index cc0d98a4..46df5c7b 100644
--- a/src/bluetooth/qbluetoothdeviceinfo.cpp
+++ b/src/bluetooth/qbluetoothdeviceinfo.cpp
@@ -647,7 +647,6 @@ QVector<quint16> QBluetoothDeviceInfo::manufacturerIds() const
*/
QByteArray QBluetoothDeviceInfo::manufacturerData(quint16 manufacturerId) const
{
- // TODO Currently not implemented on WinRT
Q_D(const QBluetoothDeviceInfo);
return d->manufacturerData.value(manufacturerId);
}
diff --git a/src/bluetooth/qbluetoothdeviceinfo.h b/src/bluetooth/qbluetoothdeviceinfo.h
index db0de7cd..11cb2bea 100644
--- a/src/bluetooth/qbluetoothdeviceinfo.h
+++ b/src/bluetooth/qbluetoothdeviceinfo.h
@@ -284,5 +284,8 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QBluetoothDeviceInfo::ServiceClasses)
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QBluetoothDeviceInfo)
+#ifdef QT_WINRT_BLUETOOTH
+Q_DECLARE_METATYPE(QBluetoothDeviceInfo::Fields)
+#endif
#endif
diff --git a/src/bluetooth/qbluetoothlocaldevice_android.cpp b/src/bluetooth/qbluetoothlocaldevice_android.cpp
index b46923eb..40e4c2d4 100644
--- a/src/bluetooth/qbluetoothlocaldevice_android.cpp
+++ b/src/bluetooth/qbluetoothlocaldevice_android.cpp
@@ -90,23 +90,16 @@ static QAndroidJniObject getDefaultAdapter()
QAndroidJniObject adapter = QAndroidJniObject::callStaticObjectMethod(
"android/bluetooth/BluetoothAdapter", "getDefaultAdapter",
"()Landroid/bluetooth/BluetoothAdapter;");
+ QAndroidJniExceptionCleaner exCleaner{QAndroidJniExceptionCleaner::OutputMode::Verbose};
if (!adapter.isValid()) {
- QAndroidJniEnvironment env;
- if (env->ExceptionCheck()) {
- env->ExceptionDescribe();
- env->ExceptionClear();
- }
+ exCleaner.clean();
// workaround stupid bt implementations where first call of BluetoothAdapter.getDefaultAdapter() always fails
adapter = QAndroidJniObject::callStaticObjectMethod(
"android/bluetooth/BluetoothAdapter", "getDefaultAdapter",
"()Landroid/bluetooth/BluetoothAdapter;");
- if (!adapter.isValid()) {
- if (env->ExceptionCheck()) {
- env->ExceptionDescribe();
- env->ExceptionClear();
- }
- }
+ if (!adapter.isValid())
+ exCleaner.clean();
}
return adapter;
}
diff --git a/src/bluetooth/qbluetoothlocaldevice_p.cpp b/src/bluetooth/qbluetoothlocaldevice_p.cpp
index 793a8311..fa4a509e 100644
--- a/src/bluetooth/qbluetoothlocaldevice_p.cpp
+++ b/src/bluetooth/qbluetoothlocaldevice_p.cpp
@@ -51,7 +51,7 @@ QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) :
QObject(parent),
d_ptr(new QBluetoothLocalDevicePrivate(this, QBluetoothAddress()))
{
-#if !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH)
+#if !defined(QT_IOS_BLUETOOTH)
printDummyWarning();
#endif
registerQBluetoothLocalDeviceMetaType();
@@ -85,11 +85,7 @@ void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode)
QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const
{
-#ifdef QT_WINRT_BLUETOOTH
- return HostConnectable;
-#else
return HostPoweredOff;
-#endif
}
QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
@@ -116,11 +112,7 @@ QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(
const QBluetoothAddress &address) const
{
Q_UNUSED(address);
-#ifdef QT_WINRT_BLUETOOTH
- return Paired;
-#else
return Unpaired;
-#endif
}
void QBluetoothLocalDevice::pairingConfirmation(bool confirmation)
diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h
index e18169f9..28e7ed53 100644
--- a/src/bluetooth/qbluetoothlocaldevice_p.h
+++ b/src/bluetooth/qbluetoothlocaldevice_p.h
@@ -85,6 +85,21 @@ QT_END_NAMESPACE
#include <QtCore/QPair>
#endif
+#ifdef QT_WINRT_BLUETOOTH
+#include <wrl.h>
+
+namespace ABI {
+ namespace Windows {
+ namespace Devices {
+ namespace Bluetooth {
+ struct IBluetoothDeviceStatics;
+ struct IBluetoothLEDeviceStatics;
+ }
+ }
+ }
+}
+#endif
+
QT_BEGIN_NAMESPACE
extern void registerQBluetoothLocalDeviceMetaType();
@@ -232,7 +247,22 @@ public:
private:
QBluetoothLocalDevice *q_ptr;
};
-#elif !defined(QT_OSX_BLUETOOTH) // winrt and dummy backend
+#elif defined(QT_WINRT_BLUETOOTH)
+class QBluetoothLocalDevicePrivate : public QObject
+{
+ Q_DECLARE_PUBLIC(QBluetoothLocalDevice)
+public:
+ QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q,
+ QBluetoothAddress = QBluetoothAddress());
+
+ bool isValid() const;
+
+private:
+ QBluetoothLocalDevice *q_ptr;
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::IBluetoothDeviceStatics> mStatics;
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::IBluetoothLEDeviceStatics> mLEStatics;
+};
+#elif !defined(QT_OSX_BLUETOOTH) // dummy backend
class QBluetoothLocalDevicePrivate : public QObject
{
public:
@@ -243,11 +273,7 @@ public:
bool isValid() const
{
-#ifndef QT_WINRT_BLUETOOTH
return false;
-#else
- return true;
-#endif
}
};
#endif
diff --git a/src/bluetooth/qbluetoothlocaldevice_winrt.cpp b/src/bluetooth/qbluetoothlocaldevice_winrt.cpp
new file mode 100644
index 00000000..ae794db0
--- /dev/null
+++ b/src/bluetooth/qbluetoothlocaldevice_winrt.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothlocaldevice.h"
+#include "qbluetoothaddress.h"
+
+#include "qbluetoothlocaldevice_p.h"
+
+#ifdef CLASSIC_APP_BUILD
+#define Q_OS_WINRT
+#endif
+#include <QtCore/qfunctions_winrt.h>
+
+#include <robuffer.h>
+#include <windows.devices.bluetooth.h>
+#include <wrl.h>
+
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Devices::Bluetooth;
+using namespace ABI::Windows::Devices::Enumeration;
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+
+QT_BEGIN_NAMESPACE
+
+template <class DeviceStatics, class OpResult, class Device, class Device2>
+ComPtr<IDeviceInformationPairing> getPairingInfo(ComPtr<DeviceStatics> deviceStatics,
+ const QBluetoothAddress &address)
+{
+ ComPtr<IAsyncOperation<OpResult *>> op;
+ if (!deviceStatics)
+ return nullptr;
+ HRESULT hr = deviceStatics->FromBluetoothAddressAsync(address.toUInt64(), &op);
+ RETURN_IF_FAILED("Could not obtain device from address", return nullptr);
+ ComPtr<Device> device;
+ hr = QWinRTFunctions::await(op, device.GetAddressOf(),
+ QWinRTFunctions::ProcessMainThreadEvents, 5000);
+ if (FAILED(hr) || !device) {
+ qErrnoWarning("Could not obtain device from address");
+ return nullptr;
+ }
+ ComPtr<Device2> device2;
+ hr = device.As(&device2);
+ RETURN_IF_FAILED("Could not cast device", return nullptr);
+ ComPtr<IDeviceInformation> deviceInfo;
+ hr = device2->get_DeviceInformation(&deviceInfo);
+ if (FAILED(hr) || !deviceInfo) {
+ qErrnoWarning("Could not obtain device information");
+ return nullptr;
+ }
+ ComPtr<IDeviceInformation2> deviceInfo2;
+ hr = deviceInfo.As(&deviceInfo2);
+ RETURN_IF_FAILED("Could not cast device information", return nullptr);
+ ComPtr<IDeviceInformationPairing> pairingInfo;
+ hr = deviceInfo2->get_Pairing(&pairingInfo);
+ RETURN_IF_FAILED("Could not obtain pairing information", return nullptr);
+ return pairingInfo;
+}
+
+QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothLocalDevicePrivate(this, QBluetoothAddress()))
+{
+ registerQBluetoothLocalDeviceMetaType();
+}
+
+QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothLocalDevicePrivate(this, address))
+{
+ registerQBluetoothLocalDeviceMetaType();
+}
+
+QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, QBluetoothAddress)
+ : q_ptr(q)
+{
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(), &mLEStatics);
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothDevice).Get(), &mStatics);
+}
+
+bool QBluetoothLocalDevicePrivate::isValid() const
+{
+ return (mStatics != nullptr && mLEStatics != nullptr);
+}
+
+void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing)
+{
+ Q_UNUSED(address);
+ Q_UNUSED(pairing);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QBluetoothLocalDevice::Error,
+ QBluetoothLocalDevice::PairingError));
+}
+
+QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(
+ const QBluetoothAddress &address) const
+{
+ if (!isValid() || address.isNull())
+ return QBluetoothLocalDevice::Unpaired;
+
+ ComPtr<IDeviceInformationPairing> pairingInfo = getPairingInfo<IBluetoothLEDeviceStatics,
+ BluetoothLEDevice, IBluetoothLEDevice, IBluetoothLEDevice2>(d_ptr->mLEStatics, address);
+ if (!pairingInfo)
+ pairingInfo = getPairingInfo<IBluetoothDeviceStatics, BluetoothDevice,
+ IBluetoothDevice, IBluetoothDevice2>(d_ptr->mStatics, address);
+ if (!pairingInfo)
+ return QBluetoothLocalDevice::Unpaired;
+ boolean isPaired;
+ HRESULT hr = pairingInfo->get_IsPaired(&isPaired);
+ RETURN_IF_FAILED("Could not obtain device pairing", return QBluetoothLocalDevice::Unpaired);
+ if (!isPaired)
+ return QBluetoothLocalDevice::Unpaired;
+
+ ComPtr<IDeviceInformationPairing2> pairingInfo2;
+ hr = pairingInfo.As(&pairingInfo2);
+ RETURN_IF_FAILED("Could not cast pairing info", return QBluetoothLocalDevice::Paired);
+ DevicePairingProtectionLevel protection = DevicePairingProtectionLevel_None;
+ hr = pairingInfo2->get_ProtectionLevel(&protection);
+ RETURN_IF_FAILED("Could not obtain pairing protection level", return QBluetoothLocalDevice::Paired);
+ if (protection == DevicePairingProtectionLevel_Encryption
+ || protection == DevicePairingProtectionLevel_EncryptionAndAuthentication)
+ return QBluetoothLocalDevice::AuthorizedPaired;
+ return QBluetoothLocalDevice::Paired;
+}
+
+void QBluetoothLocalDevice::pairingConfirmation(bool confirmation)
+{
+ Q_UNUSED(confirmation);
+}
+
+void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode)
+{
+ Q_UNUSED(mode);
+}
+
+QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const
+{
+ return HostConnectable;
+}
+
+QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
+{
+ return QList<QBluetoothAddress>();
+}
+
+void QBluetoothLocalDevice::powerOn()
+{
+}
+
+QString QBluetoothLocalDevice::name() const
+{
+ return QString();
+}
+
+QBluetoothAddress QBluetoothLocalDevice::address() const
+{
+ return QBluetoothAddress();
+}
+
+QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
+{
+ QList<QBluetoothHostInfo> localDevices;
+ return localDevices;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserver.cpp b/src/bluetooth/qbluetoothserver.cpp
index 75ac9979..daed5dc2 100644
--- a/src/bluetooth/qbluetoothserver.cpp
+++ b/src/bluetooth/qbluetoothserver.cpp
@@ -265,7 +265,7 @@ bool QBluetoothServer::isListening() const
{
Q_D(const QBluetoothServer);
-#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH)
+#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(QT_OSX_BLUETOOTH)
return d->isListening();
#endif
diff --git a/src/bluetooth/qbluetoothserver_osx.mm b/src/bluetooth/qbluetoothserver_osx.mm
index eefaf4da..83d7e060 100644
--- a/src/bluetooth/qbluetoothserver_osx.mm
+++ b/src/bluetooth/qbluetoothserver_osx.mm
@@ -38,7 +38,7 @@
****************************************************************************/
#include "osx/osxbtsocketlistener_p.h"
-#include "qbluetoothserver_osx_p.h"
+#include "qbluetoothserver_p.h"
// The order is important: a workround for
// a private header included by private header
@@ -58,7 +58,6 @@
#include <QtCore/qglobal.h>
#include <QtCore/qmutex.h>
-// Import, since Obj-C headers do not have inclusion guards.
#include <Foundation/Foundation.h>
#include <limits>
@@ -67,7 +66,9 @@ QT_BEGIN_NAMESPACE
namespace {
-typedef QBluetoothServiceInfo QSInfo;
+using DarwinBluetooth::RetainPolicy;
+using ServiceInfo = QBluetoothServiceInfo;
+using ObjCListener = QT_MANGLE_NAMESPACE(OSXBTSocketListener);
QMap<quint16, QBluetoothServerPrivate *> &busyPSMs()
{
@@ -86,79 +87,89 @@ typedef QMap<quint16, QBluetoothServerPrivate *>::iterator ServerMapIterator;
}
-QBluetoothServerPrivate::QBluetoothServerPrivate(QSInfo::Protocol type, QBluetoothServer *q)
- : serverType(type),
- q_ptr(q),
- lastError(QBluetoothServer::NoError),
- port(0),
- maxPendingConnections(1)
+QBluetoothServerPrivate::QBluetoothServerPrivate(ServiceInfo::Protocol type,
+ QBluetoothServer *parent)
+ : socket(nullptr),
+ maxPendingConnections(1),
+ securityFlags(QBluetooth::NoSecurity),
+ serverType(type),
+ q_ptr(parent),
+ m_lastError(QBluetoothServer::NoError),
+ port(0)
{
- Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
- if (serverType == QSInfo::UnknownProtocol)
+ if (serverType == ServiceInfo::UnknownProtocol)
qCWarning(QT_BT_OSX) << "unknown protocol";
}
QBluetoothServerPrivate::~QBluetoothServerPrivate()
{
- // Actually, not good, but lock must be acquired.
- // TODO: test this.
const QMutexLocker lock(&channelMapMutex());
unregisterServer(this);
}
-void QBluetoothServerPrivate::_q_newConnection()
-{
- // Noop, we have openNotify for this.
-}
-
bool QBluetoothServerPrivate::startListener(quint16 realPort)
{
Q_ASSERT_X(realPort, Q_FUNC_INFO, "invalid port");
- if (serverType == QSInfo::UnknownProtocol) {
+ if (serverType == ServiceInfo::UnknownProtocol) {
qCWarning(QT_BT_OSX) << "invalid protocol";
return false;
}
- if (!listener)
- listener.reset([[ObjCListener alloc] initWithListener:this]);
+ if (!listener) {
+ listener.reset([[ObjCListener alloc] initWithListener:this],
+ RetainPolicy::noInitialRetain);
+ }
bool result = false;
- if (serverType == QSInfo::RfcommProtocol)
- result = [listener listenRFCOMMConnectionsWithChannelID:realPort];
+ if (serverType == ServiceInfo::RfcommProtocol)
+ result = [listener.getAs<ObjCListener>() listenRFCOMMConnectionsWithChannelID:realPort];
else
- result = [listener listenL2CAPConnectionsWithPSM:realPort];
+ result = [listener.getAs<ObjCListener>() listenL2CAPConnectionsWithPSM:realPort];
if (!result)
- listener.reset(nil);
+ listener.reset();
return result;
}
+bool QBluetoothServerPrivate::isListening() const
+{
+ if (serverType == ServiceInfo::UnknownProtocol)
+ return false;
+
+ const QMutexLocker lock(&QBluetoothServerPrivate::channelMapMutex());
+ return QBluetoothServerPrivate::registeredServer(q_ptr->serverPort(), serverType);
+}
+
void QBluetoothServerPrivate::stopListener()
{
- listener.reset(nil);
+ listener.reset();
}
-void QBluetoothServerPrivate::openNotify(IOBluetoothRFCOMMChannel *channel)
+void QBluetoothServerPrivate::openNotifyRFCOMM(void *generic)
{
+ auto channel = static_cast<IOBluetoothRFCOMMChannel *>(generic);
+
Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)");
Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)");
Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
- PendingConnection newConnection(channel, true);
+ PendingConnection newConnection(channel, RetainPolicy::doInitialRetain);
pendingConnections.append(newConnection);
emit q_ptr->newConnection();
}
-void QBluetoothServerPrivate::openNotify(IOBluetoothL2CAPChannel *channel)
+void QBluetoothServerPrivate::openNotifyL2CAP(void *generic)
{
+ auto channel = static_cast<IOBluetoothL2CAPChannel *>(generic);
+
Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)");
Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)");
Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
- PendingConnection newConnection(channel, true);
+ PendingConnection newConnection(channel, RetainPolicy::doInitialRetain);
pendingConnections.append(newConnection);
emit q_ptr->newConnection();
@@ -209,11 +220,11 @@ void QBluetoothServerPrivate::registerServer(QBluetoothServerPrivate *server, qu
// External lock is required + port must be free.
Q_ASSERT_X(server, Q_FUNC_INFO, "invalid server (null)");
- const QSInfo::Protocol type = server->serverType;
- if (type == QSInfo::RfcommProtocol) {
+ const ServiceInfo::Protocol type = server->serverType;
+ if (type == ServiceInfo::RfcommProtocol) {
Q_ASSERT_X(!channelIsBusy(port), Q_FUNC_INFO, "port is busy");
busyChannels()[port] = server;
- } else if (type == QSInfo::L2capProtocol) {
+ } else if (type == ServiceInfo::L2capProtocol) {
Q_ASSERT_X(!psmIsBusy(port), Q_FUNC_INFO, "port is busy");
busyPSMs()[port] = server;
} else {
@@ -225,11 +236,11 @@ void QBluetoothServerPrivate::registerServer(QBluetoothServerPrivate *server, qu
QBluetoothServerPrivate *QBluetoothServerPrivate::registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol)
{
// Eternal lock is required.
- if (protocol == QSInfo::RfcommProtocol) {
+ if (protocol == ServiceInfo::RfcommProtocol) {
ServerMapIterator it = busyChannels().find(port);
if (it != busyChannels().end())
return it.value();
- } else if (protocol == QSInfo::L2capProtocol) {
+ } else if (protocol == ServiceInfo::L2capProtocol) {
ServerMapIterator it = busyPSMs().find(port);
if (it != busyPSMs().end())
return it.value();
@@ -243,17 +254,17 @@ QBluetoothServerPrivate *QBluetoothServerPrivate::registeredServer(quint16 port,
void QBluetoothServerPrivate::unregisterServer(QBluetoothServerPrivate *server)
{
// External lock is required.
- const QSInfo::Protocol type = server->serverType;
+ const ServiceInfo::Protocol type = server->serverType;
const quint16 port = server->port;
- if (type == QSInfo::RfcommProtocol) {
+ if (type == ServiceInfo::RfcommProtocol) {
ServerMapIterator it = busyChannels().find(port);
if (it != busyChannels().end()) {
busyChannels().erase(it);
} else {
qCWarning(QT_BT_OSX) << "server is not registered";
}
- } else if (type == QSInfo::L2capProtocol) {
+ } else if (type == ServiceInfo::L2capProtocol) {
ServerMapIterator it = busyPSMs().find(port);
if (it != busyPSMs().end()) {
busyPSMs().erase(it);
@@ -265,21 +276,9 @@ void QBluetoothServerPrivate::unregisterServer(QBluetoothServerPrivate *server)
}
}
-
-QBluetoothServer::QBluetoothServer(QSInfo::Protocol serverType, QObject *parent)
- : QObject(parent),
- d_ptr(new QBluetoothServerPrivate(serverType, this))
-{
-}
-
-QBluetoothServer::~QBluetoothServer()
-{
- delete d_ptr;
-}
-
void QBluetoothServer::close()
{
- d_ptr->listener.reset(nil);
+ d_ptr->listener.reset();
// Needs a lock :(
const QMutexLocker lock(&d_ptr->channelMapMutex());
@@ -289,8 +288,6 @@ void QBluetoothServer::close()
bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
{
- typedef QBluetoothServerPrivate::ObjCListener ObjCListener;
-
OSXBluetooth::qt_test_iobluetooth_runloop();
if (d_ptr->listener) {
@@ -303,7 +300,7 @@ bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
qCWarning(QT_BT_OSX) << "device does not support Bluetooth or"
<< address.toString()
<< "is not a valid local adapter";
- d_ptr->lastError = UnknownError;
+ d_ptr->m_lastError = UnknownError;
emit error(UnknownError);
return false;
}
@@ -311,53 +308,53 @@ bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
const QBluetoothLocalDevice::HostMode hostMode = device.hostMode();
if (hostMode == QBluetoothLocalDevice::HostPoweredOff) {
qCWarning(QT_BT_OSX) << "Bluetooth device is powered off";
- d_ptr->lastError = PoweredOffError;
+ d_ptr->m_lastError = PoweredOffError;
emit error(PoweredOffError);
return false;
}
- const QSInfo::Protocol type = d_ptr->serverType;
+ const ServiceInfo::Protocol type = d_ptr->serverType;
- if (type == QSInfo::UnknownProtocol) {
+ if (type == ServiceInfo::UnknownProtocol) {
qCWarning(QT_BT_OSX) << "invalid protocol";
- d_ptr->lastError = UnsupportedProtocolError;
- emit error(d_ptr->lastError);
+ d_ptr->m_lastError = UnsupportedProtocolError;
+ emit error(d_ptr->m_lastError);
return false;
}
- d_ptr->lastError = QBluetoothServer::NoError;
+ d_ptr->m_lastError = QBluetoothServer::NoError;
// Now we have to register a (fake) port, doing a proper (?) lock.
const QMutexLocker lock(&d_ptr->channelMapMutex());
if (port) {
- if (type == QSInfo::RfcommProtocol) {
+ if (type == ServiceInfo::RfcommProtocol) {
if (d_ptr->channelIsBusy(port)) {
qCWarning(QT_BT_OSX) << "server port:" << port
<< "already registered";
- d_ptr->lastError = ServiceAlreadyRegisteredError;
+ d_ptr->m_lastError = ServiceAlreadyRegisteredError;
}
} else {
if (d_ptr->psmIsBusy(port)) {
qCWarning(QT_BT_OSX) << "server port:" << port
<< "already registered";
- d_ptr->lastError = ServiceAlreadyRegisteredError;
+ d_ptr->m_lastError = ServiceAlreadyRegisteredError;
}
}
} else {
- type == QSInfo::RfcommProtocol ? port = d_ptr->findFreeChannel()
+ type == ServiceInfo::RfcommProtocol ? port = d_ptr->findFreeChannel()
: port = d_ptr->findFreePSM();
}
- if (d_ptr->lastError != QBluetoothServer::NoError) {
- emit error(d_ptr->lastError);
+ if (d_ptr->m_lastError != QBluetoothServer::NoError) {
+ emit error(d_ptr->m_lastError);
return false;
}
if (!port) {
qCWarning(QT_BT_OSX) << "all ports are busy";
- d_ptr->lastError = ServiceAlreadyRegisteredError;
- emit error(d_ptr->lastError);
+ d_ptr->m_lastError = ServiceAlreadyRegisteredError;
+ emit error(d_ptr->m_lastError);
return false;
}
@@ -365,82 +362,17 @@ bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
// (provided after a service was registered).
d_ptr->port = port;
d_ptr->registerServer(d_ptr, port);
- d_ptr->listener.reset([[ObjCListener alloc] initWithListener:d_ptr]);
+ d_ptr->listener.reset([[ObjCListener alloc] initWithListener:d_ptr],
+ RetainPolicy::noInitialRetain);
return true;
}
-QBluetoothServiceInfo QBluetoothServer::listen(const QBluetoothUuid &uuid, const QString &serviceName)
-{
- if (!listen())
- return QBluetoothServiceInfo();
-
- QBluetoothServiceInfo serviceInfo;
- serviceInfo.setAttribute(QSInfo::ServiceName, serviceName);
- QBluetoothServiceInfo::Sequence publicBrowse;
- publicBrowse << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup));
- serviceInfo.setAttribute(QSInfo::BrowseGroupList, publicBrowse);
-
- QSInfo::Sequence profileSequence;
- QSInfo::Sequence classId;
- classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort));
- classId << QVariant::fromValue(quint16(0x100));
- profileSequence.append(QVariant::fromValue(classId));
- serviceInfo.setAttribute(QSInfo::BluetoothProfileDescriptorList, profileSequence);
-
- classId.clear();
- classId << QVariant::fromValue(uuid);
- classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort));
- serviceInfo.setAttribute(QSInfo::ServiceClassIds, classId);
- serviceInfo.setServiceUuid(uuid);
-
- QSInfo::Sequence protocolDescriptorList;
- QSInfo::Sequence protocol;
- protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap));
- if (d_ptr->serverType == QSInfo::L2capProtocol)
- protocol << QVariant::fromValue(serverPort());
- protocolDescriptorList.append(QVariant::fromValue(protocol));
- protocol.clear();
-
- if (d_ptr->serverType == QBluetoothServiceInfo::RfcommProtocol) {
- protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm))
- << QVariant::fromValue(quint8(serverPort()));
- protocolDescriptorList.append(QVariant::fromValue(protocol));
- }
-
- serviceInfo.setAttribute(QSInfo::ProtocolDescriptorList,
- protocolDescriptorList);
-
-
- // It's now up to a service info to acquire a real PSM/channel ID
- // (provided by IOBluetooth) and start a listener.
- if (!serviceInfo.registerService())
- return QBluetoothServiceInfo();
-
- return serviceInfo;
-}
-
-bool QBluetoothServer::isListening() const
-{
- if (d_ptr->serverType == QSInfo::UnknownProtocol)
- return false;
-
- const QMutexLocker lock(&QBluetoothServerPrivate::channelMapMutex());
- return QBluetoothServerPrivate::registeredServer(serverPort(), d_ptr->serverType);
-}
-
void QBluetoothServer::setMaxPendingConnections(int numConnections)
{
- // That's a 'fake' limit, it affects nothing.
d_ptr->maxPendingConnections = numConnections;
}
-int QBluetoothServer::maxPendingConnections() const
-{
- // That's a 'fake' limit, it affects nothing.
- return d_ptr->maxPendingConnections;
-}
-
bool QBluetoothServer::hasPendingConnections() const
{
return d_ptr->pendingConnections.size();
@@ -457,11 +389,11 @@ QBluetoothSocket *QBluetoothServer::nextPendingConnection()
// Remove it even if we have some errors below.
d_ptr->pendingConnections.pop_front();
- if (d_ptr->serverType == QSInfo::RfcommProtocol) {
- if (!newSocket->d_ptr->setChannel(static_cast<IOBluetoothRFCOMMChannel *>(channel)))
+ if (d_ptr->serverType == ServiceInfo::RfcommProtocol) {
+ if (!static_cast<QBluetoothSocketPrivate *>(newSocket->d_ptr)->setRFCOMChannel(channel.getAs<IOBluetoothRFCOMMChannel>()))
return nullptr;
} else {
- if (!newSocket->d_ptr->setChannel(static_cast<IOBluetoothL2CAPChannel *>(channel)))
+ if (!static_cast<QBluetoothSocketPrivate *>(newSocket->d_ptr)->setL2CAPChannel(channel.getAs<IOBluetoothL2CAPChannel>()))
return nullptr;
}
@@ -481,25 +413,13 @@ quint16 QBluetoothServer::serverPort() const
void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security)
{
Q_UNUSED(security)
- // Not implemented (yet?)
+ Q_UNIMPLEMENTED();
}
QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const
{
- // Not implemented (yet?)
+ Q_UNIMPLEMENTED();
return QBluetooth::NoSecurity;
}
-QSInfo::Protocol QBluetoothServer::serverType() const
-{
- return d_ptr->serverType;
-}
-
-QBluetoothServer::Error QBluetoothServer::error() const
-{
- return d_ptr->lastError;
-}
-
-#include "moc_qbluetoothserver.cpp"
-
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserver_p.h b/src/bluetooth/qbluetoothserver_p.h
index 5ace7f75..d14dc7b4 100644
--- a/src/bluetooth/qbluetoothserver_p.h
+++ b/src/bluetooth/qbluetoothserver_p.h
@@ -77,15 +77,25 @@ class ServerAcceptanceThread;
#include <windows.networking.sockets.h>
#endif
+#ifdef QT_OSX_BLUETOOTH
+
+#include "osx/btdelegates_p.h"
+#include "osx/btraii_p.h"
+
+#include <QtCore/qvector.h>
+
+#endif // QT_OSX_BLUETOOTH
+
QT_BEGIN_NAMESPACE
class QBluetoothAddress;
class QBluetoothSocket;
class QBluetoothServer;
-#ifndef QT_OSX_BLUETOOTH
-
class QBluetoothServerPrivate
+#ifdef QT_OSX_BLUETOOTH
+ : public DarwinBluetooth::SocketListener
+#endif
{
Q_DECLARE_PUBLIC(QBluetoothServer)
@@ -142,9 +152,53 @@ public:
bool initiateActiveListening(const QString &serviceName);
bool deactivateActiveListening();
#endif
-};
-#endif //QT_OSX_BLUETOOTH
+#ifdef QT_OSX_BLUETOOTH
+
+public:
+
+ friend class QBluetoothServer;
+ friend class QBluetoothServiceInfoPrivate;
+
+private:
+ bool startListener(quint16 realPort);
+ void stopListener();
+ bool isListening() const;
+
+ // SocketListener (delegate):
+ void openNotifyRFCOMM(void *channel) override;
+ void openNotifyL2CAP(void *channel) override;
+
+ // Either a "temporary" channelID/PSM assigned by QBluetoothServer::listen,
+ // or a real channelID/PSM returned by IOBluetooth after we've registered
+ // a service.
+ quint16 port;
+
+ DarwinBluetooth::StrongReference listener;
+
+ // These static functions below
+ // deal with differences between bluetooth sockets
+ // (bluez and QtBluetooth's API) and IOBluetooth, where it's not possible
+ // to have a real PSM/channelID _before_ a service is registered,
+ // the solution - "fake" ports.
+ // These functions require external locking - using channelMapMutex.
+ static QMutex &channelMapMutex();
+
+ static bool channelIsBusy(quint16 channelID);
+ static quint16 findFreeChannel();
+
+ static bool psmIsBusy(quint16 psm);
+ static quint16 findFreePSM();
+
+ static void registerServer(QBluetoothServerPrivate *server, quint16 port);
+ static QBluetoothServerPrivate *registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol);
+ static void unregisterServer(QBluetoothServerPrivate *server);
+
+ using PendingConnection = DarwinBluetooth::StrongReference;
+ QVector<PendingConnection> pendingConnections;
+
+#endif // QT_OSX_BLUETOOTH
+};
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.cpp b/src/bluetooth/qbluetoothservicediscoveryagent.cpp
index a5fc7654..e76c2311 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent.cpp
+++ b/src/bluetooth/qbluetoothservicediscoveryagent.cpp
@@ -169,6 +169,11 @@ QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(QObject *parent
\note On WinRT the passed adapter address will be ignored.
+ \note On Android passing any \a deviceAdapter address is meaningless as Android 6.0 or later does not publish
+ the local Bluetooth address anymore. Subsequently, the passed adapter address can never be matched
+ against the local adapter address. Therefore the subsequent call to \l start() will always trigger
+ \l InvalidBluetoothAdapterError.
+
\sa error()
*/
QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(const QBluetoothAddress &deviceAdapter, QObject *parent)
@@ -303,6 +308,13 @@ QBluetoothAddress QBluetoothServiceDiscoveryAgent::remoteAddress() const
return QBluetoothAddress();
}
+namespace OSXBluetooth {
+
+void qt_test_iobluetooth_runloop();
+
+}
+
+
/*!
Starts service discovery. \a mode specifies the type of service discovery to perform.
@@ -313,6 +325,10 @@ QBluetoothAddress QBluetoothServiceDiscoveryAgent::remoteAddress() const
void QBluetoothServiceDiscoveryAgent::start(DiscoveryMode mode)
{
Q_D(QBluetoothServiceDiscoveryAgent);
+#ifdef QT_OSX_BLUETOOTH
+ // Make sure we are on the right thread/have a run loop:
+ OSXBluetooth::qt_test_iobluetooth_runloop();
+#endif
if (d->discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::Inactive
&& d->error != InvalidBluetoothAdapterError) {
@@ -568,7 +584,8 @@ bool QBluetoothServiceDiscoveryAgentPrivate::isDuplicatedService(
const QBluetoothServiceInfo &info = discoveredServices.at(j);
if (info.device() == serviceInfo.device()
&& info.serviceClassUuids() == serviceInfo.serviceClassUuids()
- && info.serviceUuid() == serviceInfo.serviceUuid()) {
+ && info.serviceUuid() == serviceInfo.serviceUuid()
+ && info.serverChannel() == serviceInfo.serverChannel()) {
return true;
}
}
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp
index ce2911d3..3ab0d580 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp
@@ -47,6 +47,7 @@
#include <QtBluetooth/QBluetoothServiceDiscoveryAgent>
#include "qbluetoothservicediscoveryagent_p.h"
+#include "qbluetoothsocket_android_p.h"
#include "android/servicediscoverybroadcastreceiver_p.h"
#include "android/localdevicebroadcastreceiver_p.h"
@@ -55,21 +56,33 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
- QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &/*deviceAdapter*/)
+ QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter)
: error(QBluetoothServiceDiscoveryAgent::NoError),
+ m_deviceAdapterAddress(deviceAdapter),
state(Inactive),
mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery),
singleDevice(false),
q_ptr(qp)
{
- QList<QBluetoothHostInfo> devices = QBluetoothLocalDevice::allDevices();
- Q_ASSERT(devices.count() <= 1); //Android only supports one device at the moment
-
- if (devices.isEmpty()) {
- error = QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError;
- errorString = QBluetoothServiceDiscoveryAgent::tr("Invalid Bluetooth adapter address");
- return;
+ // If a specific adapter address is requested we need to check it matches
+ // the current local adapter. If it does not match we emit
+ // InvalidBluetoothAdapterError when calling start()
+
+ bool createAdapter = true;
+ if (!deviceAdapter.isNull()) {
+ const QList<QBluetoothHostInfo> devices = QBluetoothLocalDevice::allDevices();
+ if (devices.isEmpty()) {
+ createAdapter = false;
+ } else {
+ auto match = [deviceAdapter](const QBluetoothHostInfo& info) {
+ return info.address() == deviceAdapter;
+ };
+
+ auto result = std::find_if(devices.begin(), devices.end(), match);
+ if (result == devices.end())
+ createAdapter = false;
+ }
}
if (QtAndroidPrivate::androidSdkVersion() < 15)
@@ -84,7 +97,8 @@ QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
The logic below must change once there is more than one adapter.
*/
- btAdapter = QAndroidJniObject::callStaticObjectMethod("android/bluetooth/BluetoothAdapter",
+ if (createAdapter)
+ btAdapter = QAndroidJniObject::callStaticObjectMethod("android/bluetooth/BluetoothAdapter",
"getDefaultAdapter",
"()Landroid/bluetooth/BluetoothAdapter;");
if (!btAdapter.isValid())
@@ -110,8 +124,15 @@ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &addr
Q_Q(QBluetoothServiceDiscoveryAgent);
if (!btAdapter.isValid()) {
- error = QBluetoothServiceDiscoveryAgent::UnknownError;
- errorString = QBluetoothServiceDiscoveryAgent::tr("Platform does not support Bluetooth");
+ if (m_deviceAdapterAddress.isNull()) {
+ error = QBluetoothServiceDiscoveryAgent::UnknownError;
+ errorString = QBluetoothServiceDiscoveryAgent::tr("Platform does not support Bluetooth");
+ } else {
+ // specific adapter was requested which does not match the locally
+ // existing adapter
+ error = QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError;
+ errorString = QBluetoothServiceDiscoveryAgent::tr("Invalid Bluetooth adapter address");
+ }
//abort any outstanding discoveries
discoveredDevices.clear();
@@ -315,42 +336,62 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_processFetchedUuids(
void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QBluetoothDeviceInfo &remoteDevice, const QList<QBluetoothUuid> &uuids)
{
- /* Android doesn't provide decent SDP data. A list of uuids is close to meaning-less
+ /* Android doesn't provide decent SDP data. A flat list of uuids is all we get.
*
* The following approach is chosen:
* - If we see an SPP service class and we see
- * one or more custom uuids we match them up. Such services will always be SPP services.
+ * one or more custom uuids we match them up. Such services will always
+ * be SPP services. There is the chance that a custom uuid is eronously
+ * mapped as being an SPP service. In addition, the SPP uuid will be mapped as
+ * standalone SPP service.
* - If we see a custom uuid but no SPP uuid then we return
- * BluetoothServiceInfo instance with just a servuceUuid (no service class set)
+ * BluetoothServiceInfo instance with just a serviceUuid (no service class set)
+ * - If we don't find any custom uuid but the SPP uuid, we return a
+ * BluetoothServiceInfo instance where classId and serviceUuid() are set to SPP.
* - Any other service uuid will stand on its own.
* */
Q_Q(QBluetoothServiceDiscoveryAgent);
//find SPP and custom uuid
- QBluetoothUuid uuid;
- int sppIndex = -1;
+ bool haveSppClass = false;
QVector<int> customUuids;
for (int i = 0; i < uuids.count(); i++) {
- uuid = uuids.at(i);
+ const QBluetoothUuid uuid = uuids.at(i);
if (uuid.isNull())
continue;
//check for SPP protocol
bool ok = false;
- quint16 uuid16 = uuid.toUInt16(&ok);
- if (ok && uuid16 == QBluetoothUuid::SerialPort)
- sppIndex = i;
+ auto uuid16 = uuid.toUInt16(&ok);
+ haveSppClass |= ok && uuid16 == QBluetoothUuid::SerialPort;
//check for custom uuid
if (uuid.minimumSize() == 16)
customUuids.append(i);
}
+ auto rfcommProtocolDescriptorList = []() -> QBluetoothServiceInfo::Sequence {
+ QBluetoothServiceInfo::Sequence protocol;
+ protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm))
+ << QVariant::fromValue(0);
+ return protocol;
+ };
+
+ auto sppProfileDescriptorList = []() -> QBluetoothServiceInfo::Sequence {
+ QBluetoothServiceInfo::Sequence profileSequence;
+ QBluetoothServiceInfo::Sequence classId;
+ classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort));
+ classId << QVariant::fromValue(quint16(0x100));
+ profileSequence.append(QVariant::fromValue(classId));
+ return profileSequence;
+ };
+
for (int i = 0; i < uuids.count(); i++) {
- if (i == sppIndex && !customUuids.isEmpty())
+ const QBluetoothUuid &uuid = uuids.at(i);
+ if (uuid.isNull())
continue;
QBluetoothServiceInfo serviceInfo;
@@ -363,52 +404,38 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB
protocolDescriptorList.append(QVariant::fromValue(protocol));
}
- if (customUuids.contains(i) && sppIndex > -1) {
+ if (customUuids.contains(i) && haveSppClass) {
//we have a custom uuid of service class type SPP
//set rfcomm protocol
- QBluetoothServiceInfo::Sequence protocol;
- protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm))
- << QVariant::fromValue(0);
- protocolDescriptorList.append(QVariant::fromValue(protocol));
+ protocolDescriptorList.append(QVariant::fromValue(rfcommProtocolDescriptorList()));
- QBluetoothServiceInfo::Sequence profileSequence;
- QBluetoothServiceInfo::Sequence classId;
- classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort));
- classId << QVariant::fromValue(quint16(0x100));
- profileSequence.append(QVariant::fromValue(classId));
+ //set SPP profile descriptor list
serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList,
- profileSequence);
+ sppProfileDescriptorList());
- classId.clear();
+ QBluetoothServiceInfo::Sequence classId;
//set SPP service class uuid
- classId << QVariant::fromValue(uuids.at(i));
+ classId << QVariant::fromValue(uuid);
classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort));
serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId);
serviceInfo.setServiceName(QBluetoothServiceDiscoveryAgent::tr("Serial Port Profile"));
- serviceInfo.setServiceUuid(uuids.at(i));
- } else if (sppIndex == i && customUuids.isEmpty()) {
+ serviceInfo.setServiceUuid(uuid);
+ } else if (uuid == QBluetoothUuid{QBluetoothUuid::SerialPort}) {
//set rfcomm protocol
- QBluetoothServiceInfo::Sequence protocol;
- protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm))
- << QVariant::fromValue(0);
- protocolDescriptorList.append(QVariant::fromValue(protocol));
+ protocolDescriptorList.append(QVariant::fromValue(rfcommProtocolDescriptorList()));
- QBluetoothServiceInfo::Sequence profileSequence;
- QBluetoothServiceInfo::Sequence classId;
- classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort));
- classId << QVariant::fromValue(quint16(0x100));
- profileSequence.append(QVariant::fromValue(classId));
+ //set SPP profile descriptor list
serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList,
- profileSequence);
+ sppProfileDescriptorList());
//also we need to set the custom uuid to the SPP uuid
//otherwise QBluetoothSocket::connectToService() would fail due to a missing service uuid
- serviceInfo.setServiceUuid(uuids.at(i));
+ serviceInfo.setServiceUuid(uuid);
} else if (customUuids.contains(i)) {
//custom uuid but no serial port
- serviceInfo.setServiceUuid(uuids.at(i));
+ serviceInfo.setServiceUuid(uuid);
}
serviceInfo.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList);
@@ -419,18 +446,20 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB
if (!customUuids.contains(i)) {
//if we don't have custom uuid use it as class id as well
QBluetoothServiceInfo::Sequence classId;
- classId << QVariant::fromValue(uuids.at(i));
+ classId << QVariant::fromValue(uuid);
serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId);
- QBluetoothUuid::ServiceClassUuid clsId
- = static_cast<QBluetoothUuid::ServiceClassUuid>(uuids.at(i).toUInt16());
+ auto clsId = QBluetoothUuid::ServiceClassUuid(uuid.toUInt16());
serviceInfo.setServiceName(QBluetoothUuid::serviceClassToString(clsId));
}
//Check if the service is in the uuidFilter
if (!uuidFilter.isEmpty()) {
bool match = uuidFilter.contains(serviceInfo.serviceUuid());
- for (const auto &uuid : qAsConst(uuidFilter))
+ match |= uuidFilter.contains(QBluetoothSocketPrivateAndroid::reverseUuid(serviceInfo.serviceUuid()));
+ for (const auto &uuid : qAsConst(uuidFilter)) {
match |= serviceInfo.serviceClassUuids().contains(uuid);
+ match |= serviceInfo.serviceClassUuids().contains(QBluetoothSocketPrivateAndroid::reverseUuid(uuid));
+ }
if (!match)
continue;
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm
index bd9cc7f3..d8decae1 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm
@@ -37,6 +37,7 @@
**
****************************************************************************/
+#include "qbluetoothservicediscoveryagent_p.h"
#include "qbluetoothservicediscoveryagent.h"
#include "qbluetoothdevicediscoveryagent.h"
#include "qbluetoothlocaldevice.h"
@@ -57,132 +58,41 @@
QT_BEGIN_NAMESPACE
-class QBluetoothServiceDiscoveryAgentPrivate : public QObject, public OSXBluetooth::SDPInquiryDelegate
-{
- friend class QBluetoothServiceDiscoveryAgent;
-public:
- enum DiscoveryState {
- Inactive,
- DeviceDiscovery,
- ServiceDiscovery,
- };
-
- QBluetoothServiceDiscoveryAgentPrivate(QBluetoothServiceDiscoveryAgent *qp,
- const QBluetoothAddress &localAddress);
-
- void startDeviceDiscovery();
- void stopDeviceDiscovery();
-
- void startServiceDiscovery();
- void stopServiceDiscovery();
-
- DiscoveryState discoveryState();
- void setDiscoveryMode(QBluetoothServiceDiscoveryAgent::DiscoveryMode m);
- QBluetoothServiceDiscoveryAgent::DiscoveryMode DiscoveryMode();
-
- void _q_deviceDiscovered(const QBluetoothDeviceInfo &info);
- void _q_deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error);
- void _q_deviceDiscoveryFinished();
-
-private:
- // SDPInquiryDelegate:
- void SDPInquiryFinished(IOBluetoothDevice *device) override;
- void SDPInquiryError(IOBluetoothDevice *device, IOReturn errorCode) override;
-
- void performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress);
- void setupDeviceDiscoveryAgent();
- bool isDuplicatedService(const QBluetoothServiceInfo &serviceInfo) const;
- void serviceDiscoveryFinished();
-
- bool serviceHasMathingUuid(const QBluetoothServiceInfo &serviceInfo) const;
-
- QBluetoothServiceDiscoveryAgent *q_ptr;
+namespace {
- QBluetoothServiceDiscoveryAgent::Error error;
- QString errorString;
+using DarwinBluetooth::RetainPolicy;
+using ObjCServiceInquiry = QT_MANGLE_NAMESPACE(OSXBTSDPInquiry);
- QList<QBluetoothDeviceInfo> discoveredDevices;
- QList<QBluetoothServiceInfo> discoveredServices;
- QList<QBluetoothUuid> uuidFilter;
-
- bool singleDevice;
- QBluetoothAddress deviceAddress;
- QBluetoothAddress localAdapterAddress;
-
- DiscoveryState state;
- QBluetoothServiceDiscoveryAgent::DiscoveryMode discoveryMode;
-
- QScopedPointer<QBluetoothDeviceDiscoveryAgent> deviceDiscoveryAgent;
- OSXBluetooth::ObjCScopedPointer<ObjCServiceInquiry> serviceInquiry;
-};
+}
QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &localAddress) :
- q_ptr(qp),
+
error(QBluetoothServiceDiscoveryAgent::NoError),
- singleDevice(false),
- localAdapterAddress(localAddress),
+ m_deviceAdapterAddress(localAddress),
state(Inactive),
- discoveryMode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery)
-{
- serviceInquiry.reset([[ObjCServiceInquiry alloc] initWithDelegate:this]);
-}
+ mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery),
+ singleDevice(false),
+ q_ptr(qp)
-void QBluetoothServiceDiscoveryAgentPrivate::startDeviceDiscovery()
{
- Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
- Q_ASSERT_X(state == Inactive, Q_FUNC_INFO, "invalid state");
- Q_ASSERT_X(error != QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError,
- Q_FUNC_INFO, "invalid bluetooth adapter");
-
- Q_ASSERT_X(deviceDiscoveryAgent.isNull(), "startDeviceDiscovery()",
- "discovery agent already exists");
-
- state = DeviceDiscovery;
-
- setupDeviceDiscoveryAgent();
- deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::ClassicMethod);
+ Q_ASSERT(q_ptr);
+ serviceInquiry.reset([[ObjCServiceInquiry alloc] initWithDelegate:this], RetainPolicy::noInitialRetain);
}
-void QBluetoothServiceDiscoveryAgentPrivate::stopDeviceDiscovery()
+QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate()
{
- Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
- Q_ASSERT_X(!deviceDiscoveryAgent.isNull(), Q_FUNC_INFO,
- "invalid device discovery agent (null)");
- Q_ASSERT_X(state == DeviceDiscovery, Q_FUNC_INFO, "invalid state");
-
- deviceDiscoveryAgent->stop();
- deviceDiscoveryAgent.reset(nullptr);
- state = Inactive;
-
- emit q_ptr->canceled();
}
-void QBluetoothServiceDiscoveryAgentPrivate::startServiceDiscovery()
+void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &deviceAddress)
{
- // Any of 'Inactive'/'DeviceDiscovery'/'ServiceDiscovery' states
- // are possible.
-
- Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
- Q_ASSERT_X(error != QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError,
- Q_FUNC_INFO, "invalid bluetooth adapter");
-
- if (discoveredDevices.isEmpty()) {
- state = Inactive;
- emit q_ptr->finished();
- return;
- }
-
QT_BT_MAC_AUTORELEASEPOOL;
- state = ServiceDiscovery;
- const QBluetoothAddress &address(discoveredDevices.at(0).address());
-
- if (address.isNull()) {
+ if (deviceAddress.isNull()) {
// This can happen: LE scan works with CoreBluetooth, but CBPeripherals
// do not expose hardware addresses.
// Pop the current QBluetoothDeviceInfo and decide what to do next.
- return serviceDiscoveryFinished();
+ return _q_serviceDiscoveryFinished();
}
// Autoreleased object.
@@ -195,17 +105,18 @@ void QBluetoothServiceDiscoveryAgentPrivate::startServiceDiscovery()
emit q_ptr->error(error);
}
- return serviceDiscoveryFinished();
+ return _q_serviceDiscoveryFinished();
}
if (DiscoveryMode() == QBluetoothServiceDiscoveryAgent::MinimalDiscovery) {
- performMinimalServiceDiscovery(address);
+ performMinimalServiceDiscovery(deviceAddress);
} else {
IOReturn result = kIOReturnSuccess;
+ auto nativeInquiry = serviceInquiry.getAs<ObjCServiceInquiry>();
if (uuidFilter.size())
- result = [serviceInquiry performSDPQueryWithDevice:address filters:uuidFilter];
+ result = [nativeInquiry performSDPQueryWithDevice:deviceAddress filters:uuidFilter];
else
- result = [serviceInquiry performSDPQueryWithDevice:address];
+ result = [nativeInquiry performSDPQueryWithDevice:deviceAddress];
if (result != kIOReturnSuccess) {
// Failed immediately to perform an SDP inquiry on IOBluetoothDevice:
@@ -214,87 +125,21 @@ void QBluetoothServiceDiscoveryAgentPrivate::startServiceDiscovery()
}
}
-void QBluetoothServiceDiscoveryAgentPrivate::stopServiceDiscovery()
+void QBluetoothServiceDiscoveryAgentPrivate::stop()
{
- Q_ASSERT_X(state != Inactive, Q_FUNC_INFO, "invalid state");
Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
discoveredDevices.clear();
- state = Inactive;
// "Stops" immediately.
- [serviceInquiry stopSDPQuery];
+ [serviceInquiry.getAs<ObjCServiceInquiry>() stopSDPQuery];
emit q_ptr->canceled();
}
-QBluetoothServiceDiscoveryAgentPrivate::DiscoveryState
- QBluetoothServiceDiscoveryAgentPrivate::discoveryState()
-{
- return state;
-}
-
-void QBluetoothServiceDiscoveryAgentPrivate::setDiscoveryMode(
- QBluetoothServiceDiscoveryAgent::DiscoveryMode m)
-{
- discoveryMode = m;
-
-}
-
-QBluetoothServiceDiscoveryAgent::DiscoveryMode
- QBluetoothServiceDiscoveryAgentPrivate::DiscoveryMode()
-{
- return discoveryMode;
-}
-
-void QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscovered(const QBluetoothDeviceInfo &info)
-{
- // Look for duplicates, and cached entries
- for (int i = 0; i < discoveredDevices.count(); i++) {
- if (discoveredDevices.at(i).address() == info.address()) {
- discoveredDevices.removeAt(i);
- break;
- }
- }
-
- discoveredDevices.prepend(info);
-}
-
-void QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error)
-{
- Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
-
- error = QBluetoothServiceDiscoveryAgent::UnknownError;
- errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_UNKNOWN_ERROR);
-
- deviceDiscoveryAgent->stop();
- deviceDiscoveryAgent.reset(nullptr);
-
- state = QBluetoothServiceDiscoveryAgentPrivate::Inactive;
- emit q_ptr->error(error);
- emit q_ptr->finished();
-}
-
-void QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryFinished()
-{
- Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
-
- if (deviceDiscoveryAgent->error() != QBluetoothDeviceDiscoveryAgent::NoError) {
- //Forward the device discovery error
- error = static_cast<QBluetoothServiceDiscoveryAgent::Error>(deviceDiscoveryAgent->error());
- errorString = deviceDiscoveryAgent->errorString();
- deviceDiscoveryAgent.reset(nullptr);
- state = Inactive;
- emit q_ptr->error(error);
- emit q_ptr->finished();
- } else {
- deviceDiscoveryAgent.reset(nullptr);
- startServiceDiscovery();
- }
-}
-
-void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryFinished(IOBluetoothDevice *device)
+void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryFinished(void *generic)
{
+ auto device = static_cast<IOBluetoothDevice *>(generic);
Q_ASSERT_X(device, Q_FUNC_INFO, "invalid IOBluetoothDevice (nil)");
if (state == Inactive)
@@ -323,10 +168,10 @@ void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryFinished(IOBluetoothDevic
}
}
- serviceDiscoveryFinished();
+ _q_serviceDiscoveryFinished();
}
-void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryError(IOBluetoothDevice *device, IOReturn errorCode)
+void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryError(void *device, IOReturn errorCode)
{
Q_UNUSED(device)
@@ -340,7 +185,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryError(IOBluetoothDevice *
emit q_ptr->error(error);
}
- serviceDiscoveryFinished();
+ _q_serviceDiscoveryFinished();
}
void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress)
@@ -371,7 +216,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(cons
if (!serviceInfo.isValid())
continue;
- if (!uuidFilter.isEmpty() && !serviceHasMathingUuid(serviceInfo))
+ if (!uuidFilter.isEmpty() && !serviceHasMatchingUuid(serviceInfo))
continue;
if (!isDuplicatedService(serviceInfo)) {
@@ -381,52 +226,10 @@ void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(cons
}
}
- serviceDiscoveryFinished();
-}
-
-void QBluetoothServiceDiscoveryAgentPrivate::setupDeviceDiscoveryAgent()
-{
- Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
- Q_ASSERT_X(deviceDiscoveryAgent.isNull() || !deviceDiscoveryAgent->isActive(),
- Q_FUNC_INFO, "device discovery agent is active");
-
- deviceDiscoveryAgent.reset(new QBluetoothDeviceDiscoveryAgent(localAdapterAddress, q_ptr));
-
- QObject::connect(deviceDiscoveryAgent.data(), &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,
- this, &QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscovered);
- QObject::connect(deviceDiscoveryAgent.data(), &QBluetoothDeviceDiscoveryAgent::finished,
- this, &QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryFinished);
- QObject::connect(deviceDiscoveryAgent.data(),
- QOverload<QBluetoothDeviceDiscoveryAgent::Error>::of(&QBluetoothDeviceDiscoveryAgent::error),
- this,
- &QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryError);
-}
-
-bool QBluetoothServiceDiscoveryAgentPrivate::isDuplicatedService(const QBluetoothServiceInfo &serviceInfo) const
-{
- //check the service is not already part of our known list
- for (int j = 0; j < discoveredServices.count(); j++) {
- const QBluetoothServiceInfo &info = discoveredServices.at(j);
- if (info.device() == serviceInfo.device()
- && info.serviceClassUuids() == serviceInfo.serviceClassUuids()
- && info.serviceUuid() == serviceInfo.serviceUuid()) {
- return true;
- }
- }
-
- return false;
-}
-
-void QBluetoothServiceDiscoveryAgentPrivate::serviceDiscoveryFinished()
-{
- if (!discoveredDevices.isEmpty())
- discoveredDevices.removeFirst();
-
- if (state == ServiceDiscovery)
- startServiceDiscovery();
+ _q_serviceDiscoveryFinished();
}
-bool QBluetoothServiceDiscoveryAgentPrivate::serviceHasMathingUuid(const QBluetoothServiceInfo &serviceInfo) const
+bool QBluetoothServiceDiscoveryAgentPrivate::serviceHasMatchingUuid(const QBluetoothServiceInfo &serviceInfo) const
{
for (const auto &requestedUuid : uuidFilter) {
if (serviceInfo.serviceUuid() == requestedUuid)
@@ -437,161 +240,4 @@ bool QBluetoothServiceDiscoveryAgentPrivate::serviceHasMathingUuid(const QBlueto
return false;
}
-QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(QObject *parent)
-: QObject(parent),
- d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(this, QBluetoothAddress()))
-{
-}
-
-QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(const QBluetoothAddress &deviceAdapter, QObject *parent)
-: QObject(parent),
- d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(this, deviceAdapter))
-{
- if (!deviceAdapter.isNull()) {
- const QList<QBluetoothHostInfo> localDevices = QBluetoothLocalDevice::allDevices();
- for (const QBluetoothHostInfo &hostInfo : localDevices) {
- if (hostInfo.address() == deviceAdapter)
- return;
- }
- d_ptr->error = InvalidBluetoothAdapterError;
- d_ptr->errorString = QCoreApplication::translate(SERVICE_DISCOVERY, SD_INVALID_ADDRESS);
- }
-}
-
-QBluetoothServiceDiscoveryAgent::~QBluetoothServiceDiscoveryAgent()
-{
- delete d_ptr;
-}
-
-QList<QBluetoothServiceInfo> QBluetoothServiceDiscoveryAgent::discoveredServices() const
-{
- return d_ptr->discoveredServices;
-}
-
-/*
- Sets the UUID filter to \a uuids. Only services matching the UUIDs in \a uuids will be
- returned.
-
- An empty UUID list is equivalent to a list containing only QBluetoothUuid::PublicBrowseGroup.
-
- \sa uuidFilter()
-*/
-void QBluetoothServiceDiscoveryAgent::setUuidFilter(const QList<QBluetoothUuid> &uuids)
-{
- d_ptr->uuidFilter = uuids;
-}
-
-/*
- This is an overloaded member function, provided for convenience.
-
- Sets the UUID filter to a list containing the single element \a uuid.
-
- \sa uuidFilter()
-*/
-void QBluetoothServiceDiscoveryAgent::setUuidFilter(const QBluetoothUuid &uuid)
-{
- d_ptr->uuidFilter.clear();
- d_ptr->uuidFilter.append(uuid);
-}
-
-/*
- Returns the UUID filter.
-
- \sa setUuidFilter()
-*/
-QList<QBluetoothUuid> QBluetoothServiceDiscoveryAgent::uuidFilter() const
-{
- return d_ptr->uuidFilter;
-}
-
-/*
- Sets the remote device address to \a address. If \a address is default constructed,
- services will be discovered on all contactable Bluetooth devices. A new remote
- address can only be set while there is no service discovery in progress; otherwise
- this function returns false.
-
- \sa remoteAddress()
-*/
-bool QBluetoothServiceDiscoveryAgent::setRemoteAddress(const QBluetoothAddress &address)
-{
- if (isActive())
- return false;
-
- if (!address.isNull())
- d_ptr->singleDevice = true;
-
- d_ptr->deviceAddress = address;
- return true;
-}
-
-QBluetoothAddress QBluetoothServiceDiscoveryAgent::remoteAddress() const
-{
- if (d_ptr->singleDevice)
- return d_ptr->deviceAddress;
-
- return QBluetoothAddress();
-}
-
-void QBluetoothServiceDiscoveryAgent::start(DiscoveryMode mode)
-{
- OSXBluetooth::qt_test_iobluetooth_runloop();
-
- if (d_ptr->discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::Inactive
- && d_ptr->error != InvalidBluetoothAdapterError)
- {
- d_ptr->setDiscoveryMode(mode);
- if (d_ptr->deviceAddress.isNull()) {
- d_ptr->startDeviceDiscovery();
- } else {
- d_ptr->discoveredDevices.append(QBluetoothDeviceInfo(d_ptr->deviceAddress, QString(), 0));
- d_ptr->startServiceDiscovery();
- }
- }
-}
-
-void QBluetoothServiceDiscoveryAgent::stop()
-{
- if (d_ptr->error == InvalidBluetoothAdapterError || !isActive())
- return;
-
- switch (d_ptr->discoveryState()) {
- case QBluetoothServiceDiscoveryAgentPrivate::DeviceDiscovery:
- d_ptr->stopDeviceDiscovery();
- break;
- case QBluetoothServiceDiscoveryAgentPrivate::ServiceDiscovery:
- d_ptr->stopServiceDiscovery();
- default:;
- }
-
- d_ptr->discoveredDevices.clear();
-}
-
-void QBluetoothServiceDiscoveryAgent::clear()
-{
- // Don't clear the list while the search is ongoing
- if (isActive())
- return;
-
- d_ptr->discoveredDevices.clear();
- d_ptr->discoveredServices.clear();
- d_ptr->uuidFilter.clear();
-}
-
-bool QBluetoothServiceDiscoveryAgent::isActive() const
-{
- return d_ptr->state != QBluetoothServiceDiscoveryAgentPrivate::Inactive;
-}
-
-QBluetoothServiceDiscoveryAgent::Error QBluetoothServiceDiscoveryAgent::error() const
-{
- return d_ptr->error;
-}
-
-QString QBluetoothServiceDiscoveryAgent::errorString() const
-{
- return d_ptr->errorString;
-}
-
-#include "moc_qbluetoothservicediscoveryagent.cpp"
-
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
index cb588f70..41410b70 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
@@ -90,6 +90,11 @@ QT_END_NAMESPACE
#include <QtCore/QPointer>
#endif
+#ifdef QT_OSX_BLUETOOTH
+#include "osx/btdelegates_p.h"
+#include "osx/btraii_p.h"
+#endif
+
QT_BEGIN_NAMESPACE
class QBluetoothDeviceDiscoveryAgent;
@@ -109,6 +114,9 @@ class QBluetoothServiceDiscoveryAgentPrivate
: public QObject
{
Q_OBJECT
+#elif defined(QT_OSX_BLUETOOTH)
+ : public QObject, public DarwinBluetooth::SDPInquiryDelegate
+{
#else
{
#endif
@@ -238,6 +246,19 @@ private:
QPointer<QWinRTBluetoothServiceDiscoveryWorker> worker;
#endif
+#ifdef QT_OSX_BLUETOOTH
+ // SDPInquiryDelegate:
+ void SDPInquiryFinished(void *device) override;
+ void SDPInquiryError(void *device, IOReturn errorCode) override;
+
+ void performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress);
+ //void serviceDiscoveryFinished();
+
+ bool serviceHasMatchingUuid(const QBluetoothServiceInfo &serviceInfo) const;
+
+ DarwinBluetooth::ScopedPointer serviceInquiry;
+#endif // QT_OSX_BLUETOOTH
+
protected:
QBluetoothServiceDiscoveryAgent *q_ptr;
};
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp
index c6b00346..f1476758 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp
@@ -52,6 +52,7 @@
#include <windows.devices.enumeration.h>
#include <windows.devices.bluetooth.h>
#include <windows.foundation.collections.h>
+#include <windows.networking.h>
#include <windows.storage.streams.h>
#include <wrl.h>
@@ -81,24 +82,6 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
#define TYPE_STRING 37
#define TYPE_SEQUENCE 53
-static QByteArray byteArrayFromBuffer(const ComPtr<IBuffer> &buffer, bool isWCharString = false)
-{
- ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
- HRESULT hr = buffer.As(&byteAccess);
- Q_ASSERT_SUCCEEDED(hr);
- char *data;
- hr = byteAccess->Buffer(reinterpret_cast<byte **>(&data));
- Q_ASSERT_SUCCEEDED(hr);
- UINT32 size;
- hr = buffer->get_Length(&size);
- Q_ASSERT_SUCCEEDED(hr);
- if (isWCharString) {
- QString valueString = QString::fromUtf16(reinterpret_cast<ushort *>(data)).left(size / 2);
- return valueString.toUtf8();
- }
- return QByteArray(data, size);
-}
-
class QWinRTBluetoothServiceDiscoveryWorker : public QObject
{
Q_OBJECT
@@ -226,6 +209,14 @@ void QWinRTBluetoothServiceDiscoveryWorker::processServiceSearchResult(quint64 a
hr = service->get_ConnectionServiceName(name.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
const QString serviceName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr));
+ ComPtr<ABI::Windows::Networking::IHostName> host;
+ hr = service->get_ConnectionHostName(host.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ HString hostName;
+ hr = host->get_RawName(hostName.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ const QString qHostName = QString::fromWCharArray(WindowsGetStringRawBuffer(hostName.Get(),
+ nullptr));
ComPtr<IRfcommServiceId> id;
hr = service->get_ServiceId(&id);
Q_ASSERT_SUCCEEDED(hr);
@@ -235,6 +226,8 @@ void QWinRTBluetoothServiceDiscoveryWorker::processServiceSearchResult(quint64 a
Q_ASSERT_SUCCEEDED(hr);
QBluetoothServiceInfo info;
+ info.setAttribute(0xBEEF, QVariant(qHostName));
+ info.setAttribute(0xBEF0, QVariant(serviceName));
info.setServiceName(serviceName);
info.setServiceUuid(uuid);
ComPtr<IAsyncOperation<IMapView<UINT32, IBuffer *> *>> op;
@@ -343,6 +336,17 @@ void QWinRTBluetoothServiceDiscoveryWorker::processServiceSearchResult(quint64 a
}
hr = iterator->MoveNext(&current);
}
+ // Windows is only able to discover Rfcomm services but the according protocolDescriptor is
+ // not always set in the raw attribute map. If we encounter a service like that we should
+ // fill the protocol descriptor ourselves.
+ if (info.protocolDescriptor(QBluetoothUuid::Rfcomm).isEmpty()) {
+ QBluetoothServiceInfo::Sequence protocolDescriptorList;
+ QBluetoothServiceInfo::Sequence protocol;
+ protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm))
+ << QVariant::fromValue(0);
+ protocolDescriptorList.append(QVariant::fromValue(protocol));
+ info.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList);
+ }
emit serviceFound(address, info);
}
emit scanFinished(address);
diff --git a/src/bluetooth/qbluetoothserviceinfo.cpp b/src/bluetooth/qbluetoothserviceinfo.cpp
index 74b17ac4..23a78c81 100644
--- a/src/bluetooth/qbluetoothserviceinfo.cpp
+++ b/src/bluetooth/qbluetoothserviceinfo.cpp
@@ -180,7 +180,12 @@ bool QBluetoothServiceInfo::isRegistered() const
bool QBluetoothServiceInfo::registerService(const QBluetoothAddress &localAdapter)
{
+#ifdef QT_OSX_BLUETOOTH
+ Q_UNUSED(localAdapter)
+ return d_ptr->registerService(*this);
+#else
return d_ptr->registerService(localAdapter);
+#endif
}
/*!
@@ -413,6 +418,9 @@ void QBluetoothServiceInfo::setDevice(const QBluetoothDeviceInfo &device)
If the service information is already registered with the platform's SDP database,
the database entry will not be updated until \l registerService() was called again.
+ \note If an attribute expectes a byte-encoded value (e.g. Bluetooth HID services),
+ it should be set as QByteArray.
+
\sa isRegistered(), registerService()
*/
void QBluetoothServiceInfo::setAttribute(quint16 attributeId, const QVariant &value)
@@ -578,6 +586,10 @@ static void dumpAttributeVariant(QDebug dbg, const QVariant &var, const QString&
dbg << QString::asprintf("%sstring %s\n", indent.toUtf8().constData(),
var.toString().toUtf8().constData());
break;
+ case QMetaType::QByteArray:
+ dbg << QString::asprintf("%sbytearray %s\n", indent.toUtf8().constData(),
+ var.toByteArray().toHex().constData());
+ break;
case QMetaType::Bool:
dbg << QString::asprintf("%sbool %d\n", indent.toUtf8().constData(), var.toBool());
break;
@@ -631,7 +643,7 @@ QDebug operator<<(QDebug dbg, const QBluetoothServiceInfo &info)
{
QDebugStateSaver saver(dbg);
dbg.noquote() << "\n";
- QList<quint16> attributes = info.attributes();
+ const QList<quint16> attributes = info.attributes();
for (quint16 id : attributes) {
dumpAttributeVariant(dbg, info.attribute(id), QStringLiteral("(%1)\t").arg(id));
}
diff --git a/src/bluetooth/qbluetoothserviceinfo_bluez.cpp b/src/bluetooth/qbluetoothserviceinfo_bluez.cpp
index 09829b13..d91367c4 100644
--- a/src/bluetooth/qbluetoothserviceinfo_bluez.cpp
+++ b/src/bluetooth/qbluetoothserviceinfo_bluez.cpp
@@ -69,66 +69,57 @@ static void writeAttribute(QXmlStreamWriter *stream, const QVariant &attribute)
stream->writeAttribute(QStringLiteral("value"),
unsignedFormat.arg(attribute.value<quint8>(), 2, 16,
QLatin1Char('0')));
- //stream->writeAttribute(QStringLiteral("name"), foo);
break;
case QMetaType::UShort:
stream->writeEmptyElement(QStringLiteral("uint16"));
stream->writeAttribute(QStringLiteral("value"),
unsignedFormat.arg(attribute.value<quint16>(), 4, 16,
QLatin1Char('0')));
- //stream->writeAttribute(QStringLiteral("name"), foo);
break;
case QMetaType::UInt:
stream->writeEmptyElement(QStringLiteral("uint32"));
stream->writeAttribute(QStringLiteral("value"),
unsignedFormat.arg(attribute.value<quint32>(), 8, 16,
QLatin1Char('0')));
- //stream->writeAttribute(QStringLiteral("name"), foo);
break;
case QMetaType::Char:
stream->writeEmptyElement(QStringLiteral("int8"));
stream->writeAttribute(QStringLiteral("value"),
- QString::number(attribute.value<uchar>(), 16));
- //stream->writeAttribute(QStringLiteral("name"), foo);
+ QString::number(attribute.value<qint8>()));
break;
case QMetaType::Short:
stream->writeEmptyElement(QStringLiteral("int16"));
stream->writeAttribute(QStringLiteral("value"),
- QString::number(attribute.value<qint16>(), 16));
- //stream->writeAttribute(QStringLiteral("name"), foo);
+ QString::number(attribute.value<qint16>()));
break;
case QMetaType::Int:
stream->writeEmptyElement(QStringLiteral("int32"));
stream->writeAttribute(QStringLiteral("value"),
- QString::number(attribute.value<qint32>(), 16));
- //stream->writeAttribute(QStringLiteral("name"), foo);
+ QString::number(attribute.value<qint32>()));
+ break;
+ case QMetaType::QByteArray:
+ stream->writeEmptyElement(QStringLiteral("text"));
+ stream->writeAttribute(QStringLiteral("value"),
+ QString::fromLatin1(attribute.value<QByteArray>().toHex().constData()));
+ stream->writeAttribute(QStringLiteral("encoding"), QStringLiteral("hex"));
break;
case QMetaType::QString:
stream->writeEmptyElement(QStringLiteral("text"));
- if (/* require hex encoding */ false) {
- stream->writeAttribute(QStringLiteral("value"), QString::fromLatin1(
- attribute.value<QString>().toUtf8().toHex().constData()));
- stream->writeAttribute(QStringLiteral("encoding"), QStringLiteral("hex"));
- } else {
- stream->writeAttribute(QStringLiteral("value"), attribute.value<QString>());
- stream->writeAttribute(QStringLiteral("encoding"), QStringLiteral("normal"));
- }
- //stream->writeAttribute(QStringLiteral("name"), foo);
+ stream->writeAttribute(QStringLiteral("value"), attribute.value<QString>());
+ stream->writeAttribute(QStringLiteral("encoding"), QStringLiteral("normal"));
break;
- case QMetaType::Bool:
+ case QMetaType::Bool:
stream->writeEmptyElement(QStringLiteral("boolean"));
if (attribute.value<bool>())
stream->writeAttribute(QStringLiteral("value"), QStringLiteral("true"));
else
stream->writeAttribute(QStringLiteral("value"), QStringLiteral("false"));
- //stream->writeAttribute(QStringLiteral("name"), foo);
break;
- case QMetaType::QUrl:
+ case QMetaType::QUrl:
stream->writeEmptyElement(QStringLiteral("url"));
stream->writeAttribute(QStringLiteral("value"), attribute.value<QUrl>().toString());
- //stream->writeAttribute(QStringLiteral("name"), foo);
break;
- case QVariant::UserType:
+ case QVariant::UserType:
if (attribute.userType() == qMetaTypeId<QBluetoothUuid>()) {
stream->writeEmptyElement(QStringLiteral("uuid"));
diff --git a/src/bluetooth/qbluetoothserviceinfo_osx.mm b/src/bluetooth/qbluetoothserviceinfo_osx.mm
index 27da70fc..41e4e8b7 100644
--- a/src/bluetooth/qbluetoothserviceinfo_osx.mm
+++ b/src/bluetooth/qbluetoothserviceinfo_osx.mm
@@ -38,9 +38,10 @@
****************************************************************************/
#include "osx/osxbtservicerecord_p.h"
-#include "qbluetoothserver_osx_p.h"
+#include "qbluetoothserviceinfo_p.h"
#include "qbluetoothserviceinfo.h"
#include "qbluetoothdeviceinfo.h"
+#include "qbluetoothserver_p.h"
#include "osx/osxbtutility_p.h"
#include "osx/osxbluetooth_p.h"
@@ -55,85 +56,116 @@
QT_BEGIN_NAMESPACE
-class QBluetoothServiceInfoPrivate
+namespace {
+
+using DarwinBluetooth::RetainPolicy;
+using ServiceInfo = QBluetoothServiceInfo;
+
+// Alas, since there is no d_ptr<->q_ptr link (which is not that bad in itself),
+// I need these getters duplicated here:
+ServiceInfo::Protocol socket_protocol(const QBluetoothServiceInfoPrivate &privateInfo)
{
-public:
+ ServiceInfo::Sequence parameters = privateInfo.protocolDescriptor(QBluetoothUuid::Rfcomm);
+ if (!parameters.isEmpty())
+ return ServiceInfo::RfcommProtocol;
- typedef QBluetoothServiceInfo QSInfo;
+ parameters = privateInfo.protocolDescriptor(QBluetoothUuid::L2cap);
+ if (!parameters.isEmpty())
+ return ServiceInfo::L2capProtocol;
- bool registerService(const OSXBluetooth::ObjCStrongReference<NSMutableDictionary> &serviceDict);
- bool isRegistered() const;
- bool unregisterService();
+ return ServiceInfo::UnknownProtocol;
+}
- QBluetoothDeviceInfo deviceInfo;
- QMap<quint16, QVariant> attributes;
+int channel_or_psm(const QBluetoothServiceInfoPrivate &privateInfo, QBluetoothUuid::ProtocolUuid uuid)
+{
+ const auto parameters = privateInfo.protocolDescriptor(uuid);
+ if (parameters.isEmpty())
+ return -1;
+ else if (parameters.count() == 1)
+ return 0;
- QBluetoothServiceInfo::Sequence protocolDescriptor(QBluetoothUuid::ProtocolUuid protocol) const;
- QBluetoothServiceInfo::Protocol socketProtocol() const;
- int protocolServiceMultiplexer() const;
- int serverChannel() const;
+ return parameters.at(1).toInt();
+}
-private:
+} // unnamed namespace
- bool registered = false;
+QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate()
+{
+}
- typedef OSXBluetooth::ObjCScopedPointer<IOBluetoothSDPServiceRecord> SDPRecord;
- SDPRecord serviceRecord;
- BluetoothSDPServiceRecordHandle serviceRecordHandle = 0;
-};
+QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate()
+{
+}
-bool QBluetoothServiceInfoPrivate::registerService(const OSXBluetooth::ObjCStrongReference<NSMutableDictionary> &serviceDict)
+bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAddress)
+{
+ Q_UNUSED(localAddress);
+ return false;
+}
+
+bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothServiceInfo &info)
{
using namespace OSXBluetooth;
- Q_ASSERT(serviceDict);
+ if (isRegistered())
+ return false;
+
+ using namespace OSXBluetooth;
+
+ ObjCStrongReference<NSMutableDictionary> serviceDict(iobluetooth_service_dictionary(info));
+ if (!serviceDict) {
+ qCWarning(QT_BT_OSX) << "failed to create a service dictionary";
+ return false;
+ }
+
Q_ASSERT(!registered);
Q_ASSERT_X(!serviceRecord, Q_FUNC_INFO, "not registered, but serviceRecord is not nil");
SDPRecord newRecord;
- newRecord.reset([[IOBluetoothSDPServiceRecord
- publishedServiceRecordWithDictionary:serviceDict] retain]);
+ newRecord.reset([IOBluetoothSDPServiceRecord
+ publishedServiceRecordWithDictionary:serviceDict], RetainPolicy::doInitialRetain);
if (!newRecord) {
qCWarning(QT_BT_OSX) << "failed to register a service record";
return false;
}
BluetoothSDPServiceRecordHandle newRecordHandle = 0;
- if ([newRecord getServiceRecordHandle:&newRecordHandle] != kIOReturnSuccess) {
+ auto *ioSDPRecord = newRecord.getAs<IOBluetoothSDPServiceRecord>();
+ if ([ioSDPRecord getServiceRecordHandle:&newRecordHandle] != kIOReturnSuccess) {
qCWarning(QT_BT_OSX) << "failed to register a service record";
- [newRecord removeServiceRecord];
+ [ioSDPRecord removeServiceRecord];
return false;
}
- const QSInfo::Protocol type = socketProtocol();
+ const ServiceInfo::Protocol type = info.socketProtocol();
quint16 realPort = 0;
QBluetoothServerPrivate *server = nullptr;
bool configured = false;
if (type == QBluetoothServiceInfo::L2capProtocol) {
BluetoothL2CAPPSM psm = 0;
- server = QBluetoothServerPrivate::registeredServer(protocolServiceMultiplexer(), type);
- if ([newRecord getL2CAPPSM:&psm] == kIOReturnSuccess) {
+ server = QBluetoothServerPrivate::registeredServer(info.protocolServiceMultiplexer(), type);
+ if ([ioSDPRecord getL2CAPPSM:&psm] == kIOReturnSuccess) {
configured = true;
realPort = psm;
}
} else if (type == QBluetoothServiceInfo::RfcommProtocol) {
BluetoothRFCOMMChannelID channelID = 0;
- server = QBluetoothServerPrivate::registeredServer(serverChannel(), type);
- if ([newRecord getRFCOMMChannelID:&channelID] == kIOReturnSuccess) {
+ server = QBluetoothServerPrivate::registeredServer(info.serverChannel(), type);
+ if ([ioSDPRecord getRFCOMMChannelID:&channelID] == kIOReturnSuccess) {
configured = true;
realPort = channelID;
}
}
if (!configured) {
- [newRecord removeServiceRecord];
+ [ioSDPRecord removeServiceRecord];
qCWarning(QT_BT_OSX) << "failed to register a service record";
return false;
}
registered = true;
- serviceRecord.reset(newRecord.take());
+ serviceRecord.swap(newRecord);
serviceRecordHandle = newRecordHandle;
if (server)
@@ -154,17 +186,18 @@ bool QBluetoothServiceInfoPrivate::unregisterService()
Q_ASSERT_X(serviceRecord, Q_FUNC_INFO, "service registered, but serviceRecord is nil");
- [serviceRecord removeServiceRecord];
- serviceRecord.reset(nil);
+ auto *nativeRecord = serviceRecord.getAs<IOBluetoothSDPServiceRecord>();
+ [nativeRecord removeServiceRecord];
+ serviceRecord.reset();
- const QSInfo::Protocol type = socketProtocol();
+ const ServiceInfo::Protocol type = socket_protocol(*this);
QBluetoothServerPrivate *server = nullptr;
const QMutexLocker lock(&QBluetoothServerPrivate::channelMapMutex());
- if (type == QSInfo::RfcommProtocol)
- server = QBluetoothServerPrivate::registeredServer(serverChannel(), type);
- else if (type == QSInfo::L2capProtocol)
- server = QBluetoothServerPrivate::registeredServer(protocolServiceMultiplexer(), type);
+ if (type == ServiceInfo::RfcommProtocol)
+ server = QBluetoothServerPrivate::registeredServer(channel_or_psm(*this, QBluetoothUuid::Rfcomm), type);
+ else if (type == ServiceInfo::L2capProtocol)
+ server = QBluetoothServerPrivate::registeredServer(channel_or_psm(*this, QBluetoothUuid::L2cap), type);
if (server)
server->stopListener();
@@ -175,268 +208,4 @@ bool QBluetoothServiceInfoPrivate::unregisterService()
return true;
}
-bool QBluetoothServiceInfo::isRegistered() const
-{
- return d_ptr->isRegistered();
-}
-
-bool QBluetoothServiceInfo::registerService(const QBluetoothAddress &localAdapter)
-{
- Q_UNUSED(localAdapter);
- if (isRegistered())
- return false;
-
- using namespace OSXBluetooth;
-
- ObjCStrongReference<NSMutableDictionary> serviceDict(iobluetooth_service_dictionary(*this));
- if (!serviceDict) {
- qCWarning(QT_BT_OSX) << "failed to create a service dictionary";
- return false;
- }
-
- return d_ptr->registerService(serviceDict);
-}
-
-bool QBluetoothServiceInfo::unregisterService()
-{
- return d_ptr->unregisterService();
-}
-
-QBluetoothServiceInfo::QBluetoothServiceInfo()
- : d_ptr(new QBluetoothServiceInfoPrivate)
-{
-}
-
-QBluetoothServiceInfo::QBluetoothServiceInfo(const QBluetoothServiceInfo &other)
- : d_ptr(other.d_ptr)
-{
-}
-
-QBluetoothServiceInfo::~QBluetoothServiceInfo()
-{
-}
-
-bool QBluetoothServiceInfo::isValid() const
-{
- return !d_ptr->attributes.isEmpty();
-}
-
-bool QBluetoothServiceInfo::isComplete() const
-{
- return d_ptr->attributes.contains(ProtocolDescriptorList);
-}
-
-QBluetoothDeviceInfo QBluetoothServiceInfo::device() const
-{
- return d_ptr->deviceInfo;
-}
-
-void QBluetoothServiceInfo::setDevice(const QBluetoothDeviceInfo &device)
-{
- d_ptr->deviceInfo = device;
-}
-
-void QBluetoothServiceInfo::setAttribute(quint16 attributeId, const QVariant &value)
-{
- d_ptr->attributes[attributeId] = value;
-}
-
-QVariant QBluetoothServiceInfo::attribute(quint16 attributeId) const
-{
- return d_ptr->attributes.value(attributeId);
-}
-
-QList<quint16> QBluetoothServiceInfo::attributes() const
-{
- return d_ptr->attributes.keys();
-}
-
-bool QBluetoothServiceInfo::contains(quint16 attributeId) const
-{
- return d_ptr->attributes.contains(attributeId);
-}
-
-void QBluetoothServiceInfo::removeAttribute(quint16 attributeId)
-{
- d_ptr->attributes.remove(attributeId);
-}
-
-QBluetoothServiceInfo::Protocol QBluetoothServiceInfo::socketProtocol() const
-{
- return d_ptr->socketProtocol();
-}
-
-int QBluetoothServiceInfo::protocolServiceMultiplexer() const
-{
- return d_ptr->protocolServiceMultiplexer();
-}
-
-int QBluetoothServiceInfo::serverChannel() const
-{
- return d_ptr->serverChannel();
-}
-
-QBluetoothServiceInfo::Sequence QBluetoothServiceInfo::protocolDescriptor(QBluetoothUuid::ProtocolUuid protocol) const
-{
- return d_ptr->protocolDescriptor(protocol);
-}
-
-QList<QBluetoothUuid> QBluetoothServiceInfo::serviceClassUuids() const
-{
- QList<QBluetoothUuid> results;
-
- const QVariant var = attribute(QBluetoothServiceInfo::ServiceClassIds);
- if (!var.isValid())
- return results;
-
- const QBluetoothServiceInfo::Sequence seq = var.value<QBluetoothServiceInfo::Sequence>();
- for (int i = 0; i < seq.count(); i++)
- results.append(seq.at(i).value<QBluetoothUuid>());
-
- return results;
-}
-
-QBluetoothServiceInfo &QBluetoothServiceInfo::operator=(const QBluetoothServiceInfo &other)
-{
- if (this != &other)
- d_ptr = other.d_ptr;
-
- return *this;
-}
-
-static void dumpAttributeVariant(const QVariant &var, const QString indent)
-{
- switch (int(var.type())) {
- case QMetaType::Void:
- qDebug("%sEmpty", indent.toLocal8Bit().constData());
- break;
- case QMetaType::UChar:
- qDebug("%suchar %u", indent.toLocal8Bit().constData(), var.toUInt());
- break;
- case QMetaType::UShort:
- qDebug("%sushort %u", indent.toLocal8Bit().constData(), var.toUInt());
- case QMetaType::UInt:
- qDebug("%suint %u", indent.toLocal8Bit().constData(), var.toUInt());
- break;
- case QMetaType::Char:
- qDebug("%schar %d", indent.toLocal8Bit().constData(), var.toInt());
- break;
- case QMetaType::Short:
- qDebug("%sshort %d", indent.toLocal8Bit().constData(), var.toInt());
- break;
- case QMetaType::Int:
- qDebug("%sint %d", indent.toLocal8Bit().constData(), var.toInt());
- break;
- case QMetaType::QString:
- qDebug("%sstring %s", indent.toLocal8Bit().constData(), var.toString().toLocal8Bit().constData());
- break;
- case QMetaType::Bool:
- qDebug("%sbool %d", indent.toLocal8Bit().constData(), var.toBool());
- break;
- case QMetaType::QUrl:
- qDebug("%surl %s", indent.toLocal8Bit().constData(), var.toUrl().toString().toLocal8Bit().constData());
- break;
- case QVariant::UserType:
- if (var.userType() == qMetaTypeId<QBluetoothUuid>()) {
- QBluetoothUuid uuid = var.value<QBluetoothUuid>();
- switch (uuid.minimumSize()) {
- case 0:
- qDebug("%suuid NULL", indent.toLocal8Bit().constData());
- break;
- case 2:
- qDebug("%suuid %04x", indent.toLocal8Bit().constData(), uuid.toUInt16());
- break;
- case 4:
- qDebug("%suuid %08x", indent.toLocal8Bit().constData(), uuid.toUInt32());
- break;
- case 16:
- qDebug("%suuid %s", indent.toLocal8Bit().constData(), QByteArray(reinterpret_cast<const char *>(uuid.toUInt128().data), 16).toHex().constData());
- break;
- default:
- qDebug("%suuid ???", indent.toLocal8Bit().constData());
- ;
- }
- } else if (var.userType() == qMetaTypeId<QBluetoothServiceInfo::Sequence>()) {
- qDebug("%sSequence", indent.toLocal8Bit().constData());
- const QBluetoothServiceInfo::Sequence *sequence = static_cast<const QBluetoothServiceInfo::Sequence *>(var.data());
- for (const QVariant &v : *sequence)
- dumpAttributeVariant(v, indent + QLatin1Char('\t'));
- } else if (var.userType() == qMetaTypeId<QBluetoothServiceInfo::Alternative>()) {
- qDebug("%sAlternative", indent.toLocal8Bit().constData());
- const QBluetoothServiceInfo::Alternative *alternative = static_cast<const QBluetoothServiceInfo::Alternative *>(var.data());
- for (const QVariant &v : *alternative)
- dumpAttributeVariant(v, indent + QLatin1Char('\t'));
- }
- break;
- default:
- qDebug("%sunknown variant type %d", indent.toLocal8Bit().constData(), var.userType());
- }
-}
-
-QDebug operator << (QDebug dbg, const QBluetoothServiceInfo &info)
-{
- const QList<quint16> attributes = info.attributes();
- for (quint16 id : attributes) {
- dumpAttributeVariant(info.attribute(id), QString::fromLatin1("(%1)\t").arg(id));
- }
- return dbg;
-}
-
-QBluetoothServiceInfo::Sequence QBluetoothServiceInfoPrivate::protocolDescriptor(QBluetoothUuid::ProtocolUuid protocol) const
-{
- if (!attributes.contains(QBluetoothServiceInfo::ProtocolDescriptorList))
- return QBluetoothServiceInfo::Sequence();
-
- const QBluetoothServiceInfo::Sequence sequence
- = attributes.value(QBluetoothServiceInfo::ProtocolDescriptorList).value<QBluetoothServiceInfo::Sequence>();
- for (const QVariant &v : sequence) {
- QBluetoothServiceInfo::Sequence parameters = v.value<QBluetoothServiceInfo::Sequence>();
- if (parameters.empty())
- continue;
- if (parameters.at(0).userType() == qMetaTypeId<QBluetoothUuid>()) {
- if (parameters.at(0).value<QBluetoothUuid>() == protocol)
- return parameters;
- }
- }
-
- return QBluetoothServiceInfo::Sequence();
-}
-
-QBluetoothServiceInfo::Protocol QBluetoothServiceInfoPrivate::socketProtocol() const
-{
- QBluetoothServiceInfo::Sequence parameters = protocolDescriptor(QBluetoothUuid::Rfcomm);
- if (!parameters.isEmpty())
- return QBluetoothServiceInfo::RfcommProtocol;
-
- parameters = protocolDescriptor(QBluetoothUuid::L2cap);
- if (!parameters.isEmpty())
- return QBluetoothServiceInfo::L2capProtocol;
-
- return QBluetoothServiceInfo::UnknownProtocol;
-}
-
-
-int QBluetoothServiceInfoPrivate::protocolServiceMultiplexer() const
-{
- const QBluetoothServiceInfo::Sequence parameters = protocolDescriptor(QBluetoothUuid::L2cap);
- if (parameters.isEmpty())
- return -1;
- else if (parameters.count() == 1)
- return 0;
-
- return parameters.at(1).toUInt();
-}
-
-
-int QBluetoothServiceInfoPrivate::serverChannel() const
-{
- const QBluetoothServiceInfo::Sequence parameters = protocolDescriptor(QBluetoothUuid::Rfcomm);
- if (parameters.isEmpty())
- return -1;
- else if (parameters.count() == 1)
- return 0;
-
- return parameters.at(1).toUInt();
-}
-
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserviceinfo_p.h b/src/bluetooth/qbluetoothserviceinfo_p.h
index 0638867d..3ed005e1 100644
--- a/src/bluetooth/qbluetoothserviceinfo_p.h
+++ b/src/bluetooth/qbluetoothserviceinfo_p.h
@@ -59,6 +59,10 @@
#include <QMap>
#include <QVariant>
+#ifdef Q_OS_MACOS
+#include "osx/btraii_p.h"
+#endif
+
class OrgBluezServiceInterface;
class OrgBluezProfileManager1Interface;
@@ -87,7 +91,6 @@ QT_BEGIN_NAMESPACE
class QBluetoothServiceInfo;
-#ifndef QT_OSX_BLUETOOTH
class QBluetoothServiceInfoPrivate
: public QObject
@@ -133,11 +136,20 @@ private:
QVector<WCHAR> serviceDescription;
#endif
- mutable bool registered;
-};
+#if QT_OSX_BLUETOOTH
+public:
+ bool registerService(const QBluetoothServiceInfo &info);
-#endif
+private:
+
+ using SDPRecord = DarwinBluetooth::ScopedPointer;
+ SDPRecord serviceRecord;
+ quint32 serviceRecordHandle = 0;
+#endif // QT_OSX_BLUETOOTH
+
+ mutable bool registered = false;
+};
QT_END_NAMESPACE
-#endif
+#endif // QBLUETOOTHSERVICEINFO_P_H
diff --git a/src/bluetooth/qbluetoothserviceinfo_winrt.cpp b/src/bluetooth/qbluetoothserviceinfo_winrt.cpp
index 45262735..e806096f 100644
--- a/src/bluetooth/qbluetoothserviceinfo_winrt.cpp
+++ b/src/bluetooth/qbluetoothserviceinfo_winrt.cpp
@@ -297,6 +297,14 @@ static ComPtr<IBuffer> bufferFromAttribute(const QVariant &attribute)
hr = writer->WriteInt64(attribute.value<qint64>());
Q_ASSERT_SUCCEEDED(hr);
break;
+ case QMetaType::QByteArray: {
+ qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::QByteArray:" << attribute.value<QString>();
+ const QString stringValue = QString::fromLatin1(attribute.value<QByteArray>().toHex());
+ const bool writeSuccess = writeStringHelper(stringValue, writer);
+ if (!writeSuccess)
+ return nullptr;
+ break;
+ }
case QMetaType::QString: {
qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::QString:" << attribute.value<QString>();
const QString stringValue = attribute.value<QString>();
diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp
index 54eb6024..e4d85447 100644
--- a/src/bluetooth/qbluetoothsocket.cpp
+++ b/src/bluetooth/qbluetoothsocket.cpp
@@ -49,6 +49,8 @@
#include "qbluetoothsocket_winrt_p.h"
#elif defined(QT_WIN_BLUETOOTH)
#include "qbluetoothsocket_win_p.h"
+#elif defined(QT_OSX_BLUETOOTH)
+#include "qbluetoothsocket_osx_p.h"
#else
#include "qbluetoothsocket_dummy_p.h"
#endif
@@ -271,6 +273,8 @@ static QBluetoothSocketBasePrivate *createSocketPrivate()
return new QBluetoothSocketPrivateWinRT();
#elif defined(QT_WIN_BLUETOOTH)
return new QBluetoothSocketPrivateWin();
+#elif defined(QT_OSX_BLUETOOTH)
+ return new QBluetoothSocketPrivate();
#else
return new QBluetoothSocketPrivateDummy();
#endif
@@ -364,8 +368,8 @@ qint64 QBluetoothSocket::bytesToWrite() const
/*!
Attempts to connect to the service described by \a service.
- The socket is opened in the given \a openMode. The \l socketType() may change
- depending on the protocol required by \a service.
+ The socket is opened in the given \a openMode. The \l socketType() is ignored
+ if \a service specifies a differing \l QBluetoothServiceInfo::socketProtocol().
The socket first enters ConnectingState and attempts to connect to the device providing
\a service. If a connection is established, QBluetoothSocket enters ConnectedState and
@@ -376,6 +380,9 @@ qint64 QBluetoothSocket::bytesToWrite() const
Note that most platforms require a pairing prior to connecting to the remote device. Otherwise
the connection process may fail.
+ On Android, only RFCOMM connections are possible. This function ignores any socket protocol indicator
+ and assumes RFCOMM.
+
\sa state(), disconnectFromService()
*/
void QBluetoothSocket::connectToService(const QBluetoothServiceInfo &service, OpenMode openMode)
@@ -514,6 +521,9 @@ QString QBluetoothSocket::errorString() const
*/
void QBluetoothSocket::setPreferredSecurityFlags(QBluetooth::SecurityFlags flags)
{
+#ifdef QT_OSX_BLUETOOTH
+ return; // not supported on macOS.
+#endif
Q_D(QBluetoothSocketBase);
if (d->secFlags != flags)
d->secFlags = flags;
@@ -535,8 +545,13 @@ void QBluetoothSocket::setPreferredSecurityFlags(QBluetooth::SecurityFlags flags
*/
QBluetooth::SecurityFlags QBluetoothSocket::preferredSecurityFlags() const
{
+#if QT_OSX_BLUETOOTH
+ // not supported on macOS - platform always uses encryption
+ return QBluetooth::Secure;
+#else
Q_D(const QBluetoothSocketBase);
return d->secFlags;
+#endif // QT_OSX_BLUETOOTH
}
/*!
@@ -560,6 +575,9 @@ void QBluetoothSocket::setSocketState(QBluetoothSocket::SocketState state)
emit disconnected();
}
if(state == ListeningState){
+#ifdef QT_OSX_BLUETOOTH
+ qCWarning(QT_BT) << "listening socket is not supported by IOBluetooth";
+#endif
// TODO: look at this, is this really correct?
// if we're a listening socket we can't handle connects?
if (d->readNotifier) {
@@ -641,6 +659,13 @@ void QBluetoothSocket::serviceDiscovered(const QBluetoothServiceInfo &service)
connectToService(service, d->openMode);
d->discoveryAgent->deleteLater();
d->discoveryAgent = nullptr;
+#ifdef QT_WINRT_BLUETOOTH
+ } else if (!service.attribute(0xBEEF).isNull()
+ && !service.attribute(0xBEF0).isNull()) {
+ connectToService(service, d->openMode);
+ d->discoveryAgent->deleteLater();
+ d->discoveryAgent = nullptr;
+#endif
} else {
qCDebug(QT_BT) << "Could not find port/psm for potential remote service";
}
diff --git a/src/bluetooth/qbluetoothsocket.h b/src/bluetooth/qbluetoothsocket.h
index eefcd2ad..8d35f77e 100644
--- a/src/bluetooth/qbluetoothsocket.h
+++ b/src/bluetooth/qbluetoothsocket.h
@@ -52,21 +52,14 @@
QT_BEGIN_NAMESPACE
-#ifndef QT_OSX_BLUETOOTH
+
class QBluetoothSocketBasePrivate;
-#else
-class QBluetoothSocketPrivate;
-#endif
class Q_BLUETOOTH_EXPORT QBluetoothSocket : public QIODevice
{
Q_OBJECT
-#ifndef QT_OSX_BLUETOOTH
- Q_DECLARE_PRIVATE(QBluetoothSocketBase)
-#else
- Q_DECLARE_PRIVATE(QBluetoothSocket)
-#endif
+ Q_DECLARE_PRIVATE(QBluetoothSocketBase)
friend class QBluetoothServer;
friend class QBluetoothServerPrivate;
@@ -188,11 +181,8 @@ protected:
QBluetoothServiceInfo::Protocol socketType,
QObject *parent = nullptr);
#endif
-#ifndef QT_OSX_BLUETOOTH
+
QBluetoothSocketBasePrivate *d_ptr;
-#else
- QBluetoothSocketPrivate *d_ptr;
-#endif
private:
friend class QLowEnergyControllerPrivateBluez;
diff --git a/src/bluetooth/qbluetoothsocket_android.cpp b/src/bluetooth/qbluetoothsocket_android.cpp
index d7f17d17..85da325b 100644
--- a/src/bluetooth/qbluetoothsocket_android.cpp
+++ b/src/bluetooth/qbluetoothsocket_android.cpp
@@ -182,30 +182,6 @@ private:
QPointer<SocketConnectWorker> workerPointer;
};
-/*
- * This function is part of a workaround for QTBUG-61392
- *
- * Returns null uuid if the given \a serviceUuid is not a uuid
- * derived from the Bluetooth base uuid.
- */
-static QBluetoothUuid reverseUuid(const QBluetoothUuid &serviceUuid)
-{
- if (serviceUuid.isNull())
- return QBluetoothUuid();
-
- bool isBaseUuid = false;
- serviceUuid.toUInt32(&isBaseUuid);
- if (isBaseUuid)
- return serviceUuid;
-
- const quint128 original = serviceUuid.toUInt128();
- quint128 reversed;
- for (int i = 0; i < 16; i++)
- reversed.data[15-i] = original.data[i];
-
- return QBluetoothUuid(reversed);
-}
-
QBluetoothSocketPrivateAndroid::QBluetoothSocketPrivateAndroid()
:
inputThread(0)
@@ -518,7 +494,33 @@ void QBluetoothSocketPrivateAndroid::connectToService(
return;
}
- if (!ensureNativeSocket(service.socketProtocol())) {
+ // Workaround for QTBUG-75035
+ /* Not all Android devices publish or discover the SPP uuid for serial services.
+ * Also, Android does not permit the detection of the protocol used by a serial
+ * Bluetooth connection.
+ *
+ * Therefore, QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices()
+ * may have to guess what protocol a potential custom uuid uses. The guessing works
+ * reasonably well as long as the SDP discovery finds the SPP uuid. Otherwise
+ * the SPP and rfcomm protocol info is missing in \a service.
+ *
+ * Android only supports RFCOMM (no L2CP). We assume (in favor of user experience)
+ * that a non-RFCOMM protocol implies a missing SPP uuid during discovery but the user
+ * still wanting to connect with the given \a service instance.
+ */
+
+ auto protocol = service.socketProtocol();
+ switch (protocol) {
+ case QBluetoothServiceInfo::L2capProtocol:
+ case QBluetoothServiceInfo::UnknownProtocol:
+ qCWarning(QT_BT_ANDROID) << "Changing socket protocol to RFCOMM";
+ protocol = QBluetoothServiceInfo::RfcommProtocol;
+ break;
+ case QBluetoothServiceInfo::RfcommProtocol:
+ break;
+ }
+
+ if (!ensureNativeSocket(protocol)) {
errorString = QBluetoothSocket::tr("Socket type not supported");
q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
return;
@@ -942,6 +944,32 @@ qint64 QBluetoothSocketPrivateAndroid::bytesToWrite() const
return 0; // nothing because always unbuffered
}
+/*
+ * This function is part of a workaround for QTBUG-61392
+ *
+ * Returns null uuid if the given \a serviceUuid is not a uuid
+ * derived from the Bluetooth base uuid.
+ */
+QBluetoothUuid QBluetoothSocketPrivateAndroid::reverseUuid(const QBluetoothUuid &serviceUuid)
+{
+ if (QtAndroid::androidSdkVersion() < 23)
+ return serviceUuid;
+
+ if (serviceUuid.isNull())
+ return QBluetoothUuid();
+
+ bool isBaseUuid = false;
+ serviceUuid.toUInt32(&isBaseUuid);
+ if (isBaseUuid)
+ return serviceUuid;
+
+ const quint128 original = serviceUuid.toUInt128();
+ quint128 reversed;
+ for (int i = 0; i < 16; i++)
+ reversed.data[15-i] = original.data[i];
+ return QBluetoothUuid{reversed};
+}
+
bool QBluetoothSocketPrivateAndroid::canReadLine() const
{
// We cannot access buffer directly as it is part of different thread
diff --git a/src/bluetooth/qbluetoothsocket_android_p.h b/src/bluetooth/qbluetoothsocket_android_p.h
index 7bf42e32..042e1bcd 100644
--- a/src/bluetooth/qbluetoothsocket_android_p.h
+++ b/src/bluetooth/qbluetoothsocket_android_p.h
@@ -112,6 +112,8 @@ public:
bool canReadLine() const override;
qint64 bytesToWrite() const override;
+ static QBluetoothUuid reverseUuid(const QBluetoothUuid &serviceUuid);
+
QAndroidJniObject adapter;
QAndroidJniObject socketObject;
QAndroidJniObject remoteDevice;
diff --git a/src/bluetooth/qbluetoothsocket_bluez.cpp b/src/bluetooth/qbluetoothsocket_bluez.cpp
index bbc32a90..25f07bb3 100644
--- a/src/bluetooth/qbluetoothsocket_bluez.cpp
+++ b/src/bluetooth/qbluetoothsocket_bluez.cpp
@@ -674,6 +674,9 @@ bool QBluetoothSocketPrivateBluez::setSocketDescriptor(int socketDescriptor, QBl
connectWriteNotifier = nullptr;
socketType = socketType_;
+ if (socket != -1)
+ QT_CLOSE(socket);
+
socket = socketDescriptor;
// ensure that O_NONBLOCK is set on new connections.
diff --git a/src/bluetooth/qbluetoothsocket_bluezdbus.cpp b/src/bluetooth/qbluetoothsocket_bluezdbus.cpp
index c98d0c26..d3fc13e4 100644
--- a/src/bluetooth/qbluetoothsocket_bluezdbus.cpp
+++ b/src/bluetooth/qbluetoothsocket_bluezdbus.cpp
@@ -538,6 +538,8 @@ void QBluetoothSocketPrivateBluezDBus::remoteConnected(const QDBusUnixFileDescri
q, &QBluetoothSocket::readyRead);
connect(localSocket, &QLocalSocket::stateChanged,
this, &QBluetoothSocketPrivateBluezDBus::socketStateChanged);
+ connect(localSocket, &QLocalSocket::bytesWritten,
+ q, &QBluetoothSocket::bytesWritten);
socket = descriptor;
q->setSocketState(QBluetoothSocket::ConnectedState);
diff --git a/src/bluetooth/qbluetoothsocket_osx.mm b/src/bluetooth/qbluetoothsocket_osx.mm
index 7f630146..f74c14f8 100644
--- a/src/bluetooth/qbluetoothsocket_osx.mm
+++ b/src/bluetooth/qbluetoothsocket_osx.mm
@@ -43,6 +43,9 @@
// dependencies problem.
#include "qbluetoothsocketbase_p.h"
#include "qbluetoothsocket_osx_p.h"
+
+#include "osx/osxbtrfcommchannel_p.h"
+#include "osx/osxbtl2capchannel_p.h"
#include "qbluetoothlocaldevice.h"
#include "qbluetoothdeviceinfo.h"
#include "osx/osxbtutility_p.h"
@@ -57,30 +60,294 @@
QT_BEGIN_NAMESPACE
+namespace {
+
+using DarwinBluetooth::RetainPolicy;
+using ObjCL2CAPChannel = QT_MANGLE_NAMESPACE(OSXBTL2CAPChannel);
+using ObjCRFCOMMChannel = QT_MANGLE_NAMESPACE(OSXBTRFCOMMChannel);
+
+} // unnamed namespace
+
QBluetoothSocketPrivate::QBluetoothSocketPrivate()
- : writeChunk(std::numeric_limits<UInt16>::max()),
- openMode(QIODevice::NotOpen), // That's what is set in public class' ctors.
- state(QBluetoothSocket::UnconnectedState),
- socketType(QBluetoothServiceInfo::UnknownProtocol),
- socketError(QBluetoothSocket::NoSocketError),
- isConnecting(false)
+ : writeChunk(std::numeric_limits<UInt16>::max())
{
q_ptr = nullptr;
}
QBluetoothSocketPrivate::~QBluetoothSocketPrivate()
{
- // "Empty" dtor to make a shared pointer happy (parametrized with
- // incomplete type in the header file).
+}
+
+bool QBluetoothSocketPrivate::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
+{
+ // For now - very simplistic, we don't call it in this file, public class
+ // only calls it in a ctor, setting the protocol RFCOMM (in case of Android)
+ // or, indeed, doing, socket-related initialization in BlueZ backend.
+ Q_ASSERT(socketType == QBluetoothServiceInfo::UnknownProtocol);
+ socketType = type;
+ return type != QBluetoothServiceInfo::UnknownProtocol;
+}
+
+QString QBluetoothSocketPrivate::localName() const
+{
+ const QBluetoothLocalDevice device;
+ return device.name();
+}
+
+QBluetoothAddress QBluetoothSocketPrivate::localAddress() const
+{
+ const QBluetoothLocalDevice device;
+ return device.address();
+}
+
+quint16 QBluetoothSocketPrivate::localPort() const
+{
+ return 0;
+}
+
+QString QBluetoothSocketPrivate::peerName() const
+{
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ NSString *nsName = nil;
+ if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
+ if (rfcommChannel)
+ nsName = [rfcommChannel.getAs<ObjCRFCOMMChannel>() peerName];
+ } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
+ if (l2capChannel)
+ nsName = [l2capChannel.getAs<ObjCL2CAPChannel>() peerName];
+ }
+
+ if (nsName)
+ return QString::fromNSString(nsName);
+
+ return QString();
+}
+
+QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const
+{
+ BluetoothDeviceAddress addr = {};
+ if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
+ if (rfcommChannel)
+ addr = [rfcommChannel.getAs<ObjCRFCOMMChannel>() peerAddress];
+ } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
+ if (l2capChannel)
+ addr = [l2capChannel.getAs<ObjCL2CAPChannel>() peerAddress];
+ }
+
+ return OSXBluetooth::qt_address(&addr);
+}
+
+quint16 QBluetoothSocketPrivate::peerPort() const
+{
+ if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
+ if (rfcommChannel)
+ return [rfcommChannel.getAs<ObjCRFCOMMChannel>() getChannelID];
+ } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
+ if (l2capChannel)
+ return [l2capChannel.getAs<ObjCL2CAPChannel>() getPSM];
+ }
+
+ return 0;
+}
+
+void QBluetoothSocketPrivate::abort()
+{
+ // Can never be called while we're in connectToService:
+ Q_ASSERT_X(!isConnecting, Q_FUNC_INFO, "internal inconsistency - "
+ "still in connectToService()");
+
+ if (socketType == QBluetoothServiceInfo::RfcommProtocol)
+ rfcommChannel.reset();
+ else if (socketType == QBluetoothServiceInfo::L2capProtocol)
+ l2capChannel.reset();
+
+ Q_ASSERT(q_ptr);
+
+ q_ptr->setSocketState(QBluetoothSocket::UnconnectedState);
+ emit q_ptr->readChannelFinished();
+ emit q_ptr->disconnected();
+
+}
+
+void QBluetoothSocketPrivate::close()
+{
+ // Can never be called while we're in connectToService:
+ Q_ASSERT_X(!isConnecting, Q_FUNC_INFO, "internal inconsistency - "
+ "still in connectToService()");
+
+ if (!txBuffer.size())
+ abort();
+}
+
+
+qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize)
+{
+ Q_ASSERT_X(data, Q_FUNC_INFO, "invalid data (null)");
+ Q_ASSERT_X(maxSize > 0, Q_FUNC_INFO, "invalid data size");
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QCoreApplication::translate(SOCKET, SOC_NOWRITE);
+ q_ptr->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ // We do not have a real socket API under the hood,
+ // IOBluetoothL2CAPChannel is buffered (writeAsync).
+
+ if (!txBuffer.size())
+ QMetaObject::invokeMethod(this, "_q_writeNotify", Qt::QueuedConnection);
+
+ char *dst = txBuffer.reserve(int(maxSize));
+ std::copy(data, data + maxSize, dst);
+
+ return maxSize;
+}
+
+qint64 QBluetoothSocketPrivate::readData(char *data, qint64 maxSize)
+{
+ if (!data)
+ return 0;
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QCoreApplication::translate(SOCKET, SOC_NOREAD);
+ q_ptr->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ if (!buffer.isEmpty())
+ return buffer.read(data, int(maxSize));
+
+ return 0;
+}
+
+qint64 QBluetoothSocketPrivate::bytesAvailable() const
+{
+ return buffer.size();
+}
+
+bool QBluetoothSocketPrivate::canReadLine() const
+{
+ return buffer.canReadLine();
+}
+
+qint64 QBluetoothSocketPrivate::bytesToWrite() const
+{
+ return txBuffer.size();
+}
+
+bool QBluetoothSocketPrivate::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
+ QBluetoothSocket::SocketState socketState, QIODevice::OpenMode openMode)
+{
+ Q_UNUSED(socketDescriptor)
+ Q_UNUSED(socketType)
+ Q_UNUSED(socketState)
+ Q_UNUSED(openMode)
+
+ qCWarning(QT_BT_OSX) << "setting a socket descriptor is not supported by IOBluetooth";
+ // Noop on macOS.
+ return true;
+}
+
+void QBluetoothSocketPrivate::connectToServiceHelper(const QBluetoothAddress &address, quint16 port,
+ QIODevice::OpenMode openMode)
+{
+ Q_UNUSED(address)
+ Q_UNUSED(port)
+ Q_UNUSED(openMode)
+}
+
+void QBluetoothSocketPrivate::connectToService(const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
+{
+ Q_ASSERT(q_ptr);
+
+ OSXBluetooth::qt_test_iobluetooth_runloop();
+
+ if (state!= QBluetoothSocket::UnconnectedState && state != QBluetoothSocket::ServiceLookupState) {
+ qCWarning(QT_BT_OSX) << "called on a busy socket";
+ errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS);
+ q_ptr->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ // Report this problem early, potentially avoid device discovery:
+ if (service.socketProtocol() == QBluetoothServiceInfo::UnknownProtocol) {
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type";
+ errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR);
+ q_ptr->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ socketType = service.socketProtocol();
+
+ if (service.protocolServiceMultiplexer() > 0) {
+ connectToService(service.device().address(),
+ quint16(service.protocolServiceMultiplexer()),
+ openMode);
+ } else if (service.serverChannel() > 0) {
+ connectToService(service.device().address(),
+ quint16(service.serverChannel()),
+ openMode);
+ } else {
+ // Try service discovery.
+ if (service.serviceUuid().isNull()) {
+ qCWarning(QT_BT_OSX) << "No port, no PSM, and no "
+ "UUID provided, unable to connect";
+ return;
+ }
+
+ q_ptr->doDeviceDiscovery(service, openMode);
+ }
+}
+
+void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid,
+ QIODevice::OpenMode openMode)
+{
+ Q_ASSERT(q_ptr);
+
+ OSXBluetooth::qt_test_iobluetooth_runloop();
+
+ // Report this problem early, avoid device discovery:
+ if (socketType == QBluetoothServiceInfo::UnknownProtocol) {
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type";
+ errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR);
+ q_ptr->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ if (state != QBluetoothSocket::UnconnectedState) {
+ qCWarning(QT_BT_OSX) << "called on a busy socket";
+ errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS);
+ q_ptr->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice);
+ QBluetoothServiceInfo service;
+ service.setDevice(device);
+ service.setServiceUuid(uuid);
+ q_ptr->doDeviceDiscovery(service, openMode);
}
void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, quint16 port,
QIODevice::OpenMode mode)
{
- Q_ASSERT_X(state == QBluetoothSocket::ServiceLookupState
- || state == QBluetoothSocket::UnconnectedState,
+ Q_ASSERT(q_ptr);
+
+ OSXBluetooth::qt_test_iobluetooth_runloop();
+
+ if (socketType == QBluetoothServiceInfo::UnknownProtocol) {
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type";
+ errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR);
+ q_ptr->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ Q_ASSERT_X(state == QBluetoothSocket::ServiceLookupState || state == QBluetoothSocket::UnconnectedState,
Q_FUNC_INFO, "invalid state");
+ q_ptr->setOpenMode(mode);
+
socketError = QBluetoothSocket::NoSocketError;
errorString.clear();
buffer.clear();
@@ -100,15 +367,15 @@ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address,
openMode = mode;
if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
- rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this]);
+ rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this], RetainPolicy::noInitialRetain);
if (rfcommChannel)
- status = [rfcommChannel connectAsyncToDevice:address withChannelID:port];
+ status = [rfcommChannel.getAs<ObjCRFCOMMChannel>() connectAsyncToDevice:address withChannelID:port];
else
status = kIOReturnNoMemory;
} else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
- l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this]);
+ l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this], RetainPolicy::noInitialRetain);
if (l2capChannel)
- status = [l2capChannel connectAsyncToDevice:address withPSM:port];
+ status = [l2capChannel.getAs<ObjCL2CAPChannel>() connectAsyncToDevice:address withPSM:port];
else
status = kIOReturnNoMemory;
}
@@ -148,84 +415,6 @@ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address,
}
}
-void QBluetoothSocketPrivate::close()
-{
- // Can never be called while we're in connectToService:
- Q_ASSERT_X(!isConnecting, Q_FUNC_INFO, "internal inconsistency - "
- "still in connectToService()");
-
- if (!txBuffer.size())
- abort();
-}
-
-void QBluetoothSocketPrivate::abort()
-{
- // Can never be called while we're in connectToService:
- Q_ASSERT_X(!isConnecting, Q_FUNC_INFO, "internal inconsistency - "
- "still in connectToService()");
-
- if (socketType == QBluetoothServiceInfo::RfcommProtocol)
- rfcommChannel.reset(nil);
- else if (socketType == QBluetoothServiceInfo::L2capProtocol)
- l2capChannel.reset(nil);
-}
-
-quint64 QBluetoothSocketPrivate::bytesAvailable() const
-{
- return buffer.size();
-}
-
-QString QBluetoothSocketPrivate::peerName() const
-{
- QT_BT_MAC_AUTORELEASEPOOL;
-
- NSString *nsName = nil;
- if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
- if (rfcommChannel)
- nsName = [rfcommChannel peerName];
- } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
- if (l2capChannel)
- nsName = [l2capChannel peerName];
- }
-
- if (nsName)
- return QString::fromNSString(nsName);
-
- return QString();
-}
-
-QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const
-{
- BluetoothDeviceAddress addr = {};
- if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
- if (rfcommChannel)
- addr = [rfcommChannel peerAddress];
- } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
- if (l2capChannel)
- addr = [l2capChannel peerAddress];
- }
-
- return OSXBluetooth::qt_address(&addr);
-}
-
-quint16 QBluetoothSocketPrivate::peerPort() const
-{
- if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
- if (rfcommChannel)
- return [rfcommChannel getChannelID];
- } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
- if (l2capChannel)
- return [l2capChannel getPSM];
- }
-
- return 0;
-}
-
-void QBluetoothSocketPrivate::_q_readNotify()
-{
- // Noop.
-}
-
void QBluetoothSocketPrivate::_q_writeNotify()
{
Q_ASSERT_X(socketType == QBluetoothServiceInfo::L2capProtocol
@@ -238,14 +427,14 @@ void QBluetoothSocketPrivate::_q_writeNotify()
if (txBuffer.size()) {
const bool isL2CAP = socketType == QBluetoothServiceInfo::L2capProtocol;
writeChunk.resize(isL2CAP ? std::numeric_limits<UInt16>::max() :
- [rfcommChannel getMTU]);
+ [rfcommChannel.getAs<ObjCRFCOMMChannel>() getMTU]);
const int size = txBuffer.read(writeChunk.data(), writeChunk.size());
IOReturn status = kIOReturnError;
if (!isL2CAP)
- status = [rfcommChannel writeAsync:writeChunk.data() length:UInt16(size)];
+ status = [rfcommChannel.getAs<ObjCRFCOMMChannel>() writeAsync:writeChunk.data() length:UInt16(size)];
else
- status = [l2capChannel writeAsync:writeChunk.data() length:UInt16(size)];
+ status = [l2capChannel.getAs<ObjCL2CAPChannel>() writeAsync:writeChunk.data() length:UInt16(size)];
if (status != kIOReturnSuccess) {
errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR);
@@ -260,21 +449,22 @@ void QBluetoothSocketPrivate::_q_writeNotify()
close();
}
-bool QBluetoothSocketPrivate::setChannel(IOBluetoothRFCOMMChannel *channel)
+bool QBluetoothSocketPrivate::setRFCOMChannel(void *generic)
{
// A special case "constructor": on OS X we do not have a real listening socket,
// instead a bluetooth server "listens" for channel open notifications and
// creates (if asked by a user later) a "socket" object
// for this connection. This function initializes
// a "socket" from such an external channel (reported by a notification).
-
+ auto channel = static_cast<IOBluetoothRFCOMMChannel *>(generic);
// It must be a newborn socket!
Q_ASSERT_X(socketError == QBluetoothSocket::NoSocketError
&& state == QBluetoothSocket::UnconnectedState && !rfcommChannel && !l2capChannel,
Q_FUNC_INFO, "unexpected socket state");
openMode = QIODevice::ReadWrite;
- rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this channel:channel]);
+ rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this channel:channel],
+ RetainPolicy::noInitialRetain);
if (rfcommChannel) {// We do not handle errors, up to an external user.
q_ptr->setOpenMode(QIODevice::ReadWrite);
state = QBluetoothSocket::ConnectedState;
@@ -284,13 +474,14 @@ bool QBluetoothSocketPrivate::setChannel(IOBluetoothRFCOMMChannel *channel)
return rfcommChannel;
}
-bool QBluetoothSocketPrivate::setChannel(IOBluetoothL2CAPChannel *channel)
+bool QBluetoothSocketPrivate::setL2CAPChannel(void *generic)
{
// A special case "constructor": on OS X we do not have a real listening socket,
// instead a bluetooth server "listens" for channel open notifications and
// creates (if asked by a user later) a "socket" object
// for this connection. This function initializes
// a "socket" from such an external channel (reported by a notification).
+ auto channel = static_cast<IOBluetoothL2CAPChannel *>(generic);
// It must be a newborn socket!
Q_ASSERT_X(socketError == QBluetoothSocket::NoSocketError
@@ -298,7 +489,7 @@ bool QBluetoothSocketPrivate::setChannel(IOBluetoothL2CAPChannel *channel)
Q_FUNC_INFO, "unexpected socket state");
openMode = QIODevice::ReadWrite;
- l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this channel:channel]);
+ l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this channel:channel], RetainPolicy::noInitialRetain);
if (l2capChannel) {// We do not handle errors, up to an external user.
q_ptr->setOpenMode(QIODevice::ReadWrite);
state = QBluetoothSocket::ConnectedState;
@@ -308,7 +499,6 @@ bool QBluetoothSocketPrivate::setChannel(IOBluetoothL2CAPChannel *channel)
return l2capChannel;
}
-
void QBluetoothSocketPrivate::setChannelError(IOReturn errorCode)
{
Q_UNUSED(errorCode)
@@ -365,7 +555,7 @@ void QBluetoothSocketPrivate::readChannelData(void *data, std::size_t size)
Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
const char *src = static_cast<char *>(data);
- char *dst = buffer.reserve(size);
+ char *dst = buffer.reserve(int(size));
std::copy(src, src + size, dst);
if (!isConnecting) {
@@ -379,449 +569,4 @@ void QBluetoothSocketPrivate::writeComplete()
_q_writeNotify();
}
-qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize)
-{
- Q_ASSERT_X(data, Q_FUNC_INFO, "invalid data (null)");
- Q_ASSERT_X(maxSize > 0, Q_FUNC_INFO, "invalid data size");
-
- if (state != QBluetoothSocket::ConnectedState) {
- errorString = QCoreApplication::translate(SOCKET, SOC_NOWRITE);
- q_ptr->setSocketError(QBluetoothSocket::OperationError);
- return -1;
- }
-
- // We do not have a real socket API under the hood,
- // IOBluetoothL2CAPChannel buffered (writeAsync).
-
- if (!txBuffer.size())
- QMetaObject::invokeMethod(this, "_q_writeNotify", Qt::QueuedConnection);
-
- char *dst = txBuffer.reserve(maxSize);
- std::copy(data, data + maxSize, dst);
-
- return maxSize;
-}
-
-QBluetoothSocket::QBluetoothSocket(QBluetoothServiceInfo::Protocol socketType, QObject *parent)
- : QIODevice(parent),
- d_ptr(new QBluetoothSocketPrivate)
-{
- d_ptr->q_ptr = this;
- d_ptr->socketType = socketType;
-
- setOpenMode(NotOpen);
-}
-
-QBluetoothSocket::QBluetoothSocket(QObject *parent)
- : QIODevice(parent),
- d_ptr(new QBluetoothSocketPrivate)
-{
- d_ptr->q_ptr = this;
- setOpenMode(NotOpen);
-}
-
-QBluetoothSocket::~QBluetoothSocket()
-{
- delete d_ptr;
-}
-
-bool QBluetoothSocket::isSequential() const
-{
- return true;
-}
-
-qint64 QBluetoothSocket::bytesAvailable() const
-{
- return QIODevice::bytesAvailable() + d_ptr->bytesAvailable();
-}
-
-qint64 QBluetoothSocket::bytesToWrite() const
-{
- return d_ptr->txBuffer.size();
-}
-
-void QBluetoothSocket::connectToService(const QBluetoothServiceInfo &service, OpenMode openMode)
-{
- OSXBluetooth::qt_test_iobluetooth_runloop();
-
- if (state() != UnconnectedState && state() != ServiceLookupState) {
- qCWarning(QT_BT_OSX) << "called on a busy socket";
- d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS);
- setSocketError(OperationError);
- return;
- }
-
- // Report this problem early, potentially avoid device discovery:
- if (service.socketProtocol() == QBluetoothServiceInfo::UnknownProtocol) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type";
- d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR);
- setSocketError(QBluetoothSocket::UnsupportedProtocolError);
- return;
- }
-
- d_ptr->socketType = service.socketProtocol();
-
- if (service.protocolServiceMultiplexer() > 0) {
- d_ptr->connectToService(service.device().address(),
- service.protocolServiceMultiplexer(),
- openMode);
- } else if (service.serverChannel() > 0) {
- d_ptr->connectToService(service.device().address(),
- service.serverChannel(), openMode);
- } else {
- // Try service discovery.
- if (service.serviceUuid().isNull()) {
- qCWarning(QT_BT_OSX) << "No port, no PSM, and no "
- "UUID provided, unable to connect";
- return;
- }
-
- doDeviceDiscovery(service, openMode);
- }
-}
-
-void QBluetoothSocket::connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid,
- OpenMode openMode)
-{
- OSXBluetooth::qt_test_iobluetooth_runloop();
-
- // Report this problem early, avoid device discovery:
- if (socketType() == QBluetoothServiceInfo::UnknownProtocol) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type";
- d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR);
- setSocketError(QBluetoothSocket::UnsupportedProtocolError);
- return;
- }
-
- if (state() != QBluetoothSocket::UnconnectedState) {
- qCWarning(QT_BT_OSX) << "called on a busy socket";
- d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS);
- setSocketError(QBluetoothSocket::OperationError);
- return;
- }
-
- QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice);
- QBluetoothServiceInfo service;
- service.setDevice(device);
- service.setServiceUuid(uuid);
- doDeviceDiscovery(service, openMode);
-}
-
-void QBluetoothSocket::connectToService(const QBluetoothAddress &address, quint16 port,
- OpenMode openMode)
-{
- OSXBluetooth::qt_test_iobluetooth_runloop();
-
- if (socketType() == QBluetoothServiceInfo::UnknownProtocol) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type";
- d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR);
- setSocketError(QBluetoothSocket::UnsupportedProtocolError);
- return;
- }
-
- if (state() != QBluetoothSocket::UnconnectedState) {
- qCWarning(QT_BT_OSX) << "called on a busy socket";
- d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS);
- setSocketError(OperationError);
- return;
- }
-
- setOpenMode(openMode);
- d_ptr->connectToService(address, port, openMode);
-}
-
-QBluetoothServiceInfo::Protocol QBluetoothSocket::socketType() const
-{
- return d_ptr->socketType;
-}
-
-QBluetoothSocket::SocketState QBluetoothSocket::state() const
-{
- return d_ptr->state;
-}
-
-QBluetoothSocket::SocketError QBluetoothSocket::error() const
-{
- return d_ptr->socketError;
-}
-
-QString QBluetoothSocket::errorString() const
-{
- return d_ptr->errorString;
-}
-
-void QBluetoothSocket::setSocketState(QBluetoothSocket::SocketState state)
-{
- const SocketState oldState = d_ptr->state;
- d_ptr->state = state;
- if (oldState != d_ptr->state)
- emit stateChanged(state);
-
- if (state == ListeningState) {
- // We can register for L2CAP/RFCOMM open notifications,
- // that's different from 'listen' and is implemented
- // in QBluetoothServer.
- qCWarning(QT_BT_OSX) << "listening sockets are not supported";
- }
-}
-
-bool QBluetoothSocket::canReadLine() const
-{
- return d_ptr->buffer.canReadLine() || QIODevice::canReadLine();
-}
-
-void QBluetoothSocket::setSocketError(QBluetoothSocket::SocketError socketError)
-{
- d_ptr->socketError = socketError;
- emit error(socketError);
-}
-
-void QBluetoothSocket::doDeviceDiscovery(const QBluetoothServiceInfo &service, OpenMode openMode)
-{
- OSXBluetooth::qt_test_iobluetooth_runloop();
-
- setSocketState(ServiceLookupState);
-
- if (d_ptr->discoveryAgent)
- d_ptr->discoveryAgent->stop();
-
- d_ptr->discoveryAgent.reset(new QBluetoothServiceDiscoveryAgent(this));
- d_ptr->discoveryAgent->setRemoteAddress(service.device().address());
-
- connect(d_ptr->discoveryAgent.data(), SIGNAL(serviceDiscovered(QBluetoothServiceInfo)),
- this, SLOT(serviceDiscovered(QBluetoothServiceInfo)));
- connect(d_ptr->discoveryAgent.data(), SIGNAL(finished()),
- this, SLOT(discoveryFinished()));
-
- d_ptr->openMode = openMode;
-
- if (!service.serviceUuid().isNull())
- d_ptr->discoveryAgent->setUuidFilter(service.serviceUuid());
-
- if (!service.serviceClassUuids().isEmpty())
- d_ptr->discoveryAgent->setUuidFilter(service.serviceClassUuids());
-
- Q_ASSERT_X(!d_ptr->discoveryAgent->uuidFilter().isEmpty(), Q_FUNC_INFO,
- "invalid service info");
-
- d_ptr->discoveryAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery);
-}
-
-void QBluetoothSocket::serviceDiscovered(const QBluetoothServiceInfo &service)
-{
- if (service.protocolServiceMultiplexer() != 0 || service.serverChannel() != 0) {
- d_ptr->discoveryAgent->stop();
- connectToService(service, d_ptr->openMode);
- }
-}
-
-void QBluetoothSocket::discoveryFinished()
-{
- d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_SERVICE_NOT_FOUND);
- setSocketState(UnconnectedState);
- setSocketError(ServiceNotFoundError);
-}
-
-void QBluetoothSocket::abort()
-{
- if (state() == UnconnectedState)
- return;
-
- setOpenMode(NotOpen);
-
- if (state() == ServiceLookupState && d_ptr->discoveryAgent) {
- d_ptr->discoveryAgent->disconnect();
- d_ptr->discoveryAgent->stop();
- d_ptr->discoveryAgent.reset();
- }
-
- setSocketState(QBluetoothSocket::ClosingState);
- d_ptr->abort();
-
- setSocketState(QBluetoothSocket::UnconnectedState);
- emit readChannelFinished();
- emit disconnected();
-}
-
-void QBluetoothSocket::disconnectFromService()
-{
- close();
-}
-
-QString QBluetoothSocket::localName() const
-{
- const QBluetoothLocalDevice device;
- return device.name();
-}
-
-QBluetoothAddress QBluetoothSocket::localAddress() const
-{
- const QBluetoothLocalDevice device;
- return device.address();
-}
-
-quint16 QBluetoothSocket::localPort() const
-{
- return 0;
-}
-
-QString QBluetoothSocket::peerName() const
-{
- return d_ptr->peerName();
-}
-
-QBluetoothAddress QBluetoothSocket::peerAddress() const
-{
- return d_ptr->peerAddress();
-}
-
-quint16 QBluetoothSocket::peerPort() const
-{
- return d_ptr->peerPort();
-}
-
-qint64 QBluetoothSocket::writeData(const char *data, qint64 maxSize)
-{
- if (!data || maxSize <= 0) {
- d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_INVAL_DATASIZE);
- setSocketError(QBluetoothSocket::OperationError);
- return -1;
- }
-
- return d_ptr->writeData(data, maxSize);
-}
-
-qint64 QBluetoothSocketPrivate::readData(char *data, qint64 maxSize)
-{
- if (state != QBluetoothSocket::ConnectedState) {
- errorString = QCoreApplication::translate(SOCKET, SOC_NOREAD);
- q_ptr->setSocketError(QBluetoothSocket::OperationError);
- return -1;
- }
-
- if (!buffer.isEmpty())
- return buffer.read(data, maxSize);
-
- return 0;
-}
-
-qint64 QBluetoothSocket::readData(char *data, qint64 maxSize)
-{
- return d_ptr->readData(data, maxSize);
-}
-
-void QBluetoothSocket::close()
-{
- if (state() == UnconnectedState)
- return;
-
- setOpenMode(NotOpen);
-
- if (state() == ServiceLookupState && d_ptr->discoveryAgent) {
- d_ptr->discoveryAgent->disconnect();
- d_ptr->discoveryAgent->stop();
- d_ptr->discoveryAgent.reset();
- }
-
- setSocketState(ClosingState);
-
- d_ptr->close();
-
- setSocketState(UnconnectedState);
- emit readChannelFinished();
- emit disconnected();
-}
-
-bool QBluetoothSocket::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
- SocketState socketState, OpenMode openMode)
-{
- Q_UNUSED(socketDescriptor)
- Q_UNUSED(socketType)
- Q_UNUSED(socketState)
- Q_UNUSED(openMode)
-
- // Noop on OS X.
- return true;
-}
-
-int QBluetoothSocket::socketDescriptor() const
-{
- return -1;
-}
-
-/* not supported on OS X */
-void QBluetoothSocket::setPreferredSecurityFlags(QBluetooth::SecurityFlags flags)
-{
- Q_UNUSED(flags)
-}
-
-/* not supported on OS X - platform always uses encryption */
-QBluetooth::SecurityFlags QBluetoothSocket::preferredSecurityFlags() const
-{
- return QBluetooth::Secure;
-}
-
-#ifndef QT_NO_DEBUG_STREAM
-
-QDebug operator<<(QDebug debug, QBluetoothSocket::SocketError error)
-{
- switch (error) {
- case QBluetoothSocket::UnknownSocketError:
- debug << "QBluetoothSocket::UnknownSocketError";
- break;
- case QBluetoothSocket::HostNotFoundError:
- debug << "QBluetoothSocket::HostNotFoundError";
- break;
- case QBluetoothSocket::RemoteHostClosedError:
- debug << "QBluetoothSocket::RemoteHostClosedError";
- break;
- case QBluetoothSocket::ServiceNotFoundError:
- debug << "QBluetoothSocket::ServiceNotFoundError";
- break;
- case QBluetoothSocket::NetworkError:
- debug << "QBluetoothSocket::NetworkError";
- break;
- case QBluetoothSocket::UnsupportedProtocolError:
- debug << "QBluetoothSocket::UnsupportedProtocolError";
- break;
- default:
- debug << "QBluetoothSocket::SocketError(" << (int)error << ")";
- }
- return debug;
-}
-
-QDebug operator<<(QDebug debug, QBluetoothSocket::SocketState state)
-{
- switch (state) {
- case QBluetoothSocket::UnconnectedState:
- debug << "QBluetoothSocket::UnconnectedState";
- break;
- case QBluetoothSocket::ConnectingState:
- debug << "QBluetoothSocket::ConnectingState";
- break;
- case QBluetoothSocket::ConnectedState:
- debug << "QBluetoothSocket::ConnectedState";
- break;
- case QBluetoothSocket::BoundState:
- debug << "QBluetoothSocket::BoundState";
- break;
- case QBluetoothSocket::ClosingState:
- debug << "QBluetoothSocket::ClosingState";
- break;
- case QBluetoothSocket::ListeningState:
- debug << "QBluetoothSocket::ListeningState";
- break;
- case QBluetoothSocket::ServiceLookupState:
- debug << "QBluetoothSocket::ServiceLookupState";
- break;
- default:
- debug << "QBluetoothSocket::SocketState(" << (int)state << ")";
- }
- return debug;
-}
-
-#endif // QT_NO_DEBUG_STREAM
-
-#include "moc_qbluetoothsocket.cpp"
-
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothsocket_osx_p.h b/src/bluetooth/qbluetoothsocket_osx_p.h
index dcc684b8..1291878c 100644
--- a/src/bluetooth/qbluetoothsocket_osx_p.h
+++ b/src/bluetooth/qbluetoothsocket_osx_p.h
@@ -53,12 +53,11 @@
#ifdef QT_OSX_BLUETOOTH
-#include "osx/osxbtchanneldelegate_p.h"
-#include "osx/osxbtrfcommchannel_p.h"
-#include "osx/osxbtl2capchannel_p.h"
+#include "qbluetoothsocketbase_p.h"
#include "qbluetoothserviceinfo.h"
-#include "osx/osxbtutility_p.h"
+#include "osx/btdelegates_p.h"
#include "qbluetoothsocket.h"
+#include "osx/btraii_p.h"
#ifndef QPRIVATELINEARBUFFER_BUFFERSIZE
#define QPRIVATELINEARBUFFER_BUFFERSIZE Q_INT64_C(16384)
@@ -74,14 +73,11 @@
#include <QtCore/qstring.h>
#include <QtCore/qvector.h>
-@class IOBluetoothRFCOMMChannel;
-@class IOBluetoothL2CAPChannel;
-
QT_BEGIN_NAMESPACE
class QBluetoothAddress;
-class QBluetoothSocketPrivate : public QBluetoothSocketBasePrivate, public OSXBluetooth::ChannelDelegate
+class QBluetoothSocketPrivate : public QBluetoothSocketBasePrivate, public DarwinBluetooth::ChannelDelegate
{
friend class QBluetoothSocket;
friend class QBluetoothServer;
@@ -90,25 +86,47 @@ public:
QBluetoothSocketPrivate();
~QBluetoothSocketPrivate();
- void connectToService(const QBluetoothAddress &address, quint16 port,
- QIODevice::OpenMode openMode);
+ //
+ 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 close();
- void abort();
+ void abort() override;
+ void close() override;
- quint64 bytesAvailable() const;
+ qint64 writeData(const char *data, qint64 maxSize) override;
+ qint64 readData(char *data, qint64 maxSize) override;
- QString peerName() const;
- QBluetoothAddress peerAddress() const;
- quint16 peerPort() const;
+ qint64 bytesAvailable() const override;
+ bool canReadLine() const override;
+ qint64 bytesToWrite() const override;
- void _q_readNotify();
- void _q_writeNotify() override;
+ bool setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
+ QBluetoothSocket::SocketState socketState,
+ QBluetoothSocket::OpenMode openMode) 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;
+
+ void _q_writeNotify();
private:
// Create a socket from an external source (without connectToService).
- bool setChannel(IOBluetoothRFCOMMChannel *channel);
- bool setChannel(IOBluetoothL2CAPChannel *channel);
+ bool setRFCOMChannel(void *channel);
+ bool setL2CAPChannel(void *channel);
// L2CAP and RFCOMM delegate
void setChannelError(IOReturn errorCode) override;
@@ -117,33 +135,15 @@ private:
void readChannelData(void *data, std::size_t size) override;
void writeComplete() override;
- qint64 writeData(const char *data, qint64 maxSize);
- qint64 readData(char *data, qint64 maxSize);
-
- QScopedPointer<QBluetoothServiceDiscoveryAgent> discoveryAgent;
-
- QPrivateLinearBuffer buffer;
- QPrivateLinearBuffer txBuffer;
QVector<char> writeChunk;
- // Probably, not needed.
- QIODevice::OpenMode openMode;
-
- QBluetoothSocket::SocketState state;
- QBluetoothServiceInfo::Protocol socketType;
-
- QBluetoothSocket::SocketError socketError;
- QString errorString;
-
- typedef QT_MANGLE_NAMESPACE(OSXBTL2CAPChannel) ObjCL2CAPChannel;
- typedef OSXBluetooth::ObjCScopedPointer<ObjCL2CAPChannel> L2CAPChannel;
+ using L2CAPChannel = DarwinBluetooth::ScopedPointer;
L2CAPChannel l2capChannel;
- typedef QT_MANGLE_NAMESPACE(OSXBTRFCOMMChannel) ObjCRFCOMMChannel;
- typedef OSXBluetooth::ObjCScopedPointer<ObjCRFCOMMChannel> RFCOMMChannel;
+ using RFCOMMChannel = L2CAPChannel;
RFCOMMChannel rfcommChannel;
// A trick to deal with channel open too fast (synchronously).
- bool isConnecting;
+ bool isConnecting = false;
};
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothsocket_winrt.cpp b/src/bluetooth/qbluetoothsocket_winrt.cpp
index 79dccdd6..48b14757 100644
--- a/src/bluetooth/qbluetoothsocket_winrt.cpp
+++ b/src/bluetooth/qbluetoothsocket_winrt.cpp
@@ -94,15 +94,22 @@ static inline QString qt_QStringFromHString(const HString &string)
{
UINT32 length;
PCWSTR rawString = string.GetRawBuffer(&length);
- return QString::fromWCharArray(rawString, length);
+ if (length > INT_MAX)
+ length = INT_MAX;
+ return QString::fromWCharArray(rawString, int(length));
}
static qint64 writeIOStream(ComPtr<IOutputStream> stream, const char *data, qint64 len)
{
ComPtr<IBuffer> buffer;
- HRESULT hr = g->bufferFactory->Create(len, &buffer);
+ if (len > UINT32_MAX) {
+ qCWarning(QT_BT_WINRT) << "writeIOStream can only write up to" << UINT32_MAX << "bytes.";
+ len = UINT32_MAX;
+ }
+ quint32 ulen = static_cast<quint32>(len);
+ HRESULT hr = g->bufferFactory->Create(ulen, &buffer);
Q_ASSERT_SUCCEEDED(hr);
- hr = buffer->put_Length(len);
+ hr = buffer->put_Length(ulen);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
hr = buffer.As(&byteArrayAccess);
@@ -110,7 +117,7 @@ static qint64 writeIOStream(ComPtr<IOutputStream> stream, const char *data, qint
byte *bytes;
hr = byteArrayAccess->Buffer(&bytes);
Q_ASSERT_SUCCEEDED(hr);
- memcpy(bytes, data, len);
+ memcpy(bytes, data, ulen);
ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> op;
hr = stream->WriteAsync(buffer.Get(), &op);
RETURN_IF_FAILED("Failed to write to stream", return -1);
@@ -257,7 +264,7 @@ public:
return S_OK;
}
- QByteArray newData(reinterpret_cast<const char*>(data), qint64(bufferLength));
+ QByteArray newData(reinterpret_cast<const char*>(data), int(bufferLength));
QMutexLocker readLocker(&m_mutex);
if (m_pendingData.isEmpty())
QMetaObject::invokeMethod(this, "notifyAboutNewData", Qt::QueuedConnection);
@@ -359,10 +366,11 @@ bool QBluetoothSocketPrivateWinRT::ensureNativeSocket(QBluetoothServiceInfo::Pro
return true;
}
-void QBluetoothSocketPrivateWinRT::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+void QBluetoothSocketPrivateWinRT::connectToService(Microsoft::WRL::ComPtr<IHostName> hostName,
+ const QString &serviceName,
+ QIODevice::OpenMode openMode)
{
Q_Q(QBluetoothSocket);
- Q_UNUSED(openMode);
if (socket == -1 && !ensureNativeSocket(socketType)) {
errorString = QBluetoothSocket::tr("Unknown socket error");
@@ -370,20 +378,9 @@ void QBluetoothSocketPrivateWinRT::connectToServiceHelper(const QBluetoothAddres
return;
}
- const QString addressString = address.toString();
- HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
- ComPtr<IHostNameFactory> hostNameFactory;
- HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
- &hostNameFactory);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IHostName> remoteHost;
- hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
- RETURN_VOID_IF_FAILED("QBluetoothSocketPrivateWinRT::connectToService: Could not create hostname.");
-
- const QString portString = QString::number(port);
- HStringReference portReference(reinterpret_cast<LPCWSTR>(portString.utf16()));
+ HStringReference serviceNameReference(reinterpret_cast<LPCWSTR>(serviceName.utf16()));
- hr = m_socketObject->ConnectAsync(remoteHost.Get(), portReference.Get(), &m_connectOp);
+ HRESULT hr = m_socketObject->ConnectAsync(hostName.Get(), serviceNameReference.Get(), &m_connectOp);
if (hr == E_ACCESSDENIED) {
qErrnoWarning(hr, "QBluetoothSocketPrivateWinRT::connectToService: Unable to connect to bluetooth socket."
"Please check your manifest capabilities.");
@@ -404,6 +401,29 @@ void QBluetoothSocketPrivateWinRT::connectToServiceHelper(const QBluetoothAddres
Q_ASSERT_SUCCEEDED(hr);
}
+void QBluetoothSocketPrivateWinRT::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (socket == -1 && !ensureNativeSocket(socketType)) {
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return;
+ }
+
+ const QString addressString = address.toString();
+ HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
+ ComPtr<IHostNameFactory> hostNameFactory;
+ HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
+ &hostNameFactory);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IHostName> remoteHost;
+ hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
+ RETURN_VOID_IF_FAILED("QBluetoothSocketPrivateWinRT::connectToService: Could not create hostname.");
+ const QString portString = QString::number(port);
+ connectToService(remoteHost, portString, openMode);
+}
+
void QBluetoothSocketPrivateWinRT::connectToService(
const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
{
@@ -425,6 +445,8 @@ void QBluetoothSocketPrivateWinRT::connectToService(
return;
}
+ const QString connectionHostName = service.attribute(0xBEEF).toString();
+ const QString connectionServiceName = service.attribute(0xBEF0).toString();
if (service.protocolServiceMultiplexer() > 0) {
Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::L2capProtocol);
@@ -433,8 +455,24 @@ void QBluetoothSocketPrivateWinRT::connectToService(
q->setSocketError(QBluetoothSocket::UnknownSocketError);
return;
}
- connectToServiceHelper(service.device().address(), service.protocolServiceMultiplexer(), openMode);
- } else if (service.serverChannel() > 0) {
+ connectToServiceHelper(service.device().address(),
+ quint16(service.protocolServiceMultiplexer()), openMode);
+ } else if (!connectionHostName.isEmpty() && !connectionServiceName.isEmpty()) {
+ Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol);
+ if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return;
+ }
+ HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(connectionHostName.utf16()));
+ ComPtr<IHostNameFactory> hostNameFactory;
+ HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
+ &hostNameFactory);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IHostName> remoteHost;
+ hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
+ connectToService(remoteHost, connectionServiceName, openMode);
+ } else if (service.serverChannel() > 0) {
Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol);
if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
@@ -442,8 +480,9 @@ void QBluetoothSocketPrivateWinRT::connectToService(
q->setSocketError(QBluetoothSocket::UnknownSocketError);
return;
}
- connectToServiceHelper(service.device().address(), service.serverChannel(), openMode);
- } else {
+ connectToServiceHelper(service.device().address(), quint16(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)) {
@@ -567,7 +606,13 @@ quint16 QBluetoothSocketPrivateWinRT::localPort() const
HString localPortString;
hr = info->get_LocalPort(localPortString.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
- return qt_QStringFromHString(localPortString).toInt();
+ bool ok = true;
+ const uint port = qt_QStringFromHString(localPortString).toUInt(&ok);
+ if (!ok || port > UINT16_MAX) {
+ qCWarning(QT_BT_WINRT) << "Unexpected local port";
+ return 0;
+ }
+ return quint16(port);
}
QString QBluetoothSocketPrivateWinRT::peerName() const
@@ -618,7 +663,13 @@ quint16 QBluetoothSocketPrivateWinRT::peerPort() const
HString remotePortString;
hr = info->get_LocalPort(remotePortString.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
- return qt_QStringFromHString(remotePortString).toInt();
+ bool ok = true;
+ const uint port = qt_QStringFromHString(remotePortString).toUInt(&ok);
+ if (!ok || port > UINT16_MAX) {
+ qCWarning(QT_BT_WINRT) << "Unexpected remote port";
+ return 0;
+ }
+ return quint16(port);
}
qint64 QBluetoothSocketPrivateWinRT::writeData(const char *data, qint64 maxSize)
@@ -657,8 +708,11 @@ qint64 QBluetoothSocketPrivateWinRT::readData(char *data, qint64 maxSize)
return -1;
}
- if (!buffer.isEmpty())
- return buffer.read(data, maxSize);
+ if (!buffer.isEmpty()) {
+ if (maxSize > INT_MAX)
+ maxSize = INT_MAX;
+ return buffer.read(data, int(maxSize));
+ }
return 0;
}
@@ -749,7 +803,7 @@ void QBluetoothSocketPrivateWinRT::addToPendingData(const QVector<QByteArray> &d
m_pendingData.append(data);
for (const QByteArray &newData : data) {
char *writePointer = buffer.reserve(newData.length());
- memcpy(writePointer, newData.data(), newData.length());
+ memcpy(writePointer, newData.data(), size_t(newData.length()));
}
locker.unlock();
emit q->readyRead();
@@ -767,17 +821,22 @@ HRESULT QBluetoothSocketPrivateWinRT::handleConnectOpFinished(ABI::Windows::Foun
HRESULT hr = action->GetResults();
switch (hr) {
- case 0x8007274c: // A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
+
+ // A connection attempt failed because the connected party did not properly respond after a
+ // period of time, or established connection failed because connected host has failed to respond.
+ case HRESULT_FROM_WIN32(WSAETIMEDOUT):
errorString = QBluetoothSocket::tr("Connection timed out");
q->setSocketError(QBluetoothSocket::NetworkError);
q->setSocketState(QBluetoothSocket::UnconnectedState);
return S_OK;
- case 0x80072751: // A socket operation was attempted to an unreachable host.
+ // A socket operation was attempted to an unreachable host.
+ case HRESULT_FROM_WIN32(WSAEHOSTUNREACH):
errorString = QBluetoothSocket::tr("Host not reachable");
q->setSocketError(QBluetoothSocket::HostNotFoundError);
q->setSocketState(QBluetoothSocket::UnconnectedState);
return S_OK;
- case 0x8007274d: // No connection could be made because the target machine actively refused it.
+ // No connection could be made because the target machine actively refused it.
+ case HRESULT_FROM_WIN32(WSAECONNREFUSED):
errorString = QBluetoothSocket::tr("Host refused connection");
q->setSocketError(QBluetoothSocket::HostNotFoundError);
q->setSocketState(QBluetoothSocket::UnconnectedState);
@@ -802,8 +861,7 @@ HRESULT QBluetoothSocketPrivateWinRT::handleConnectOpFinished(ABI::Windows::Foun
hr = info->Close();
Q_ASSERT_SUCCEEDED(hr);
}
- hr = m_connectOp.Reset();
- Q_ASSERT_SUCCEEDED(hr);
+ m_connectOp.Reset();
}
q->setOpenMode(requestedOpenMode);
diff --git a/src/bluetooth/qbluetoothsocket_winrt_p.h b/src/bluetooth/qbluetoothsocket_winrt_p.h
index 40e87f01..de8b7d67 100644
--- a/src/bluetooth/qbluetoothsocket_winrt_p.h
+++ b/src/bluetooth/qbluetoothsocket_winrt_p.h
@@ -57,6 +57,19 @@
QT_FORWARD_DECLARE_CLASS(SocketWorker)
+namespace ABI {
+ namespace Windows {
+ namespace Networking {
+ struct IHostName;
+ }
+ }
+}
+
+namespace Microsoft {
+ namespace WRL {
+ template <typename T> class ComPtr;
+ }
+}
QT_BEGIN_NAMESPACE
class QBluetoothSocketPrivateWinRT final: public QBluetoothSocketBasePrivate
@@ -125,6 +138,8 @@ private slots:
void handleError(QBluetoothSocket::SocketError error);
private:
+ void connectToService(Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostName> hostName,
+ const QString &serviceName, QIODevice::OpenMode openMode);
HRESULT handleConnectOpFinished(ABI::Windows::Foundation::IAsyncAction *action,
ABI::Windows::Foundation::AsyncStatus status);
diff --git a/src/bluetooth/qbluetoothsocketbase_p.h b/src/bluetooth/qbluetoothsocketbase_p.h
index 410dcbbd..d1894e96 100644
--- a/src/bluetooth/qbluetoothsocketbase_p.h
+++ b/src/bluetooth/qbluetoothsocketbase_p.h
@@ -89,7 +89,6 @@ QT_FORWARD_DECLARE_CLASS(QBluetoothServiceDiscoveryAgent)
QT_BEGIN_NAMESPACE
-#ifndef QT_OSX_BLUETOOTH
class QBluetoothSocketBasePrivate : public QObject
{
Q_OBJECT
@@ -198,26 +197,6 @@ static inline quint64 convertAddress(const quint8 (&from)[6], quint64 *to = null
return result;
}
-#else // QT_OSX_BLUETOOTH
-
-// QBluetoothSocketPrivate on macOS can not contain
-// Q_OBJECT (moc does not parse Objective-C syntax).
-// But QBluetoothSocket still requires QMetaObject::invokeMethod
-// to work. Here's the trick:
-class QBluetoothSocketBasePrivate : public QObject
-{
-// The most important part of it:
- Q_OBJECT
-public slots:
- virtual void _q_writeNotify() = 0;
-
-protected:
- Q_DECLARE_PUBLIC(QBluetoothSocket)
- QBluetoothSocket *q_ptr;
-};
-
-#endif // QT_OSX_BLUETOOTH
-
QT_END_NAMESPACE
#endif // QBLUETOOTHSOCKETBASE_P_H
diff --git a/src/bluetooth/qbluetoothutils_winrt.cpp b/src/bluetooth/qbluetoothutils_winrt.cpp
index 1d44221b..de4355c6 100644
--- a/src/bluetooth/qbluetoothutils_winrt.cpp
+++ b/src/bluetooth/qbluetoothutils_winrt.cpp
@@ -42,12 +42,15 @@
#include <QtCore/qfunctions_winrt.h>
+#include <robuffer.h>
#include <wrl.h>
#include <windows.foundation.metadata.h>
+#include <windows.storage.streams.h>
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::Foundation::Metadata;
+using namespace ABI::Windows::Storage::Streams;
QT_BEGIN_NAMESPACE
@@ -76,4 +79,26 @@ bool supportsNewLEApi()
return apiPresent;
}
+QByteArray byteArrayFromBuffer(const ComPtr<NativeBuffer> &buffer, bool isWCharString)
+{
+ if (!buffer) {
+ qErrnoWarning("nullptr passed to byteArrayFromBuffer");
+ return QByteArray();
+ }
+ ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
+ HRESULT hr = buffer.As(&byteAccess);
+ RETURN_IF_FAILED("Could not cast buffer", return QByteArray())
+ char *data;
+ hr = byteAccess->Buffer(reinterpret_cast<byte **>(&data));
+ RETURN_IF_FAILED("Could not obtain buffer data", return QByteArray())
+ UINT32 size;
+ hr = buffer->get_Length(&size);
+ RETURN_IF_FAILED("Could not obtain buffer size", return QByteArray())
+ if (isWCharString) {
+ QString valueString = QString::fromUtf16(reinterpret_cast<ushort *>(data)).left(size / 2);
+ return valueString.toUtf8();
+ }
+ return QByteArray(data, qint32(size));
+}
+
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothutils_winrt_p.h b/src/bluetooth/qbluetoothutils_winrt_p.h
index c272bae1..93950fc9 100644
--- a/src/bluetooth/qbluetoothutils_winrt_p.h
+++ b/src/bluetooth/qbluetoothutils_winrt_p.h
@@ -53,10 +53,26 @@
#include <QtCore/QtGlobal>
+#include <wrl/client.h>
+
+namespace ABI {
+ namespace Windows {
+ namespace Storage {
+ namespace Streams {
+ struct IBuffer;
+ }
+ }
+ }
+}
+
QT_BEGIN_NAMESPACE
bool supportsNewLEApi();
+using NativeBuffer = ABI::Windows::Storage::Streams::IBuffer;
+QByteArray byteArrayFromBuffer(const Microsoft::WRL::ComPtr<NativeBuffer> &buffer,
+ bool isWCharString = false);
+
QT_END_NAMESPACE
#endif // QBLUETOOTHSOCKET_WINRT_P_H
diff --git a/src/bluetooth/qlowenergycharacteristic.h b/src/bluetooth/qlowenergycharacteristic.h
index bb6487c4..fe9b73fa 100644
--- a/src/bluetooth/qlowenergycharacteristic.h
+++ b/src/bluetooth/qlowenergycharacteristic.h
@@ -101,8 +101,8 @@ protected:
friend class QLowEnergyControllerPrivateBluez;
friend class QLowEnergyControllerPrivateBluezDBus;
friend class QLowEnergyControllerPrivateCommon;
- friend class QLowEnergyControllerPrivateOSX;
friend class QLowEnergyControllerPrivateWin32;
+ friend class QLowEnergyControllerPrivateDarwin;
friend class QLowEnergyControllerPrivateWinRT;
friend class QLowEnergyControllerPrivateWinRTNew;
QLowEnergyCharacteristicPrivate *data = nullptr;
diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp
index 444bfb38..bd263812 100644
--- a/src/bluetooth/qlowenergycontroller.cpp
+++ b/src/bluetooth/qlowenergycontroller.cpp
@@ -62,6 +62,8 @@
#endif
#elif defined(QT_WIN_BLUETOOTH)
#include "qlowenergycontroller_win_p.h"
+#elif defined(Q_OS_DARWIN)
+#include "qlowenergycontroller_darwin_p.h"
#else
#include "qlowenergycontroller_p.h"
#endif
@@ -159,6 +161,9 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
This value was introduced by Qt 5.7.
\value RemoteHostClosedError The remote device closed the connection.
This value was introduced by Qt 5.10.
+ \value AuthorizationError The local Bluetooth device closed the connection due to
+ insufficient authorization.
+ This value was introduced by Qt 5.14.
*/
/*!
@@ -326,6 +331,9 @@ static QLowEnergyControllerPrivate *privateController(QLowEnergyController::Role
#elif defined(QT_WIN_BLUETOOTH)
Q_UNUSED(role);
return new QLowEnergyControllerPrivateWin32();
+#elif defined(Q_OS_DARWIN)
+ Q_UNUSED(role)
+ return new QLowEnergyControllerPrivateDarwin();
#else
Q_UNUSED(role);
return new QLowEnergyControllerPrivateCommon();
@@ -349,6 +357,9 @@ QLowEnergyController::QLowEnergyController(
QObject *parent)
: QObject(parent)
{
+ // Note: a central created using this ctor is useless
+ // on Darwin - no way to use addresses when connecting.
+
d_ptr = privateController(CentralRole);
Q_D(QLowEnergyController);
@@ -378,11 +389,12 @@ QLowEnergyController::QLowEnergyController(
QObject *parent)
: QObject(parent)
{
- d_ptr = privateController(CentralRole);
+ d_ptr = privateController(CentralRole);
Q_D(QLowEnergyController);
d->q_ptr = this;
d->role = CentralRole;
+ d->deviceUuid = remoteDeviceInfo.deviceUuid();
d->remoteDevice = remoteDeviceInfo.address();
d->localAdapter = QBluetoothLocalDevice().address();
d->addressType = QLowEnergyController::PublicAddress;
@@ -411,6 +423,8 @@ QLowEnergyController::QLowEnergyController(
QObject *parent)
: QObject(parent)
{
+ // Note: a central create using this ctor is useless on
+ // Darwin (CoreBluetooth does not work with addresses).
d_ptr = privateController(CentralRole);
Q_D(QLowEnergyController);
@@ -437,6 +451,29 @@ QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDevice
return new QLowEnergyController(remoteDevice, parent);
}
+/*!
+ Returns a new instance of this class with \a parent.
+
+ The \a remoteDevice must contain the address of the remote Bluetooth Low
+ Energy device to which this object should attempt to connect later on.
+
+ The connection is established via \a localDevice. If \a localDevice is invalid,
+ the local default device is automatically selected. If \a localDevice specifies
+ a local device that is not a local Bluetooth adapter, \l error() is set to
+ \l InvalidBluetoothAdapterError once \l connectToDevice() is called.
+
+ Note that specifying the local device to be used for the connection is only
+ possible when using BlueZ. All other platforms do not support this feature.
+
+ \since 5.13
+ */
+QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothAddress &remoteDevice,
+ const QBluetoothAddress &localDevice,
+ QObject *parent)
+{
+ return new QLowEnergyController(remoteDevice, localDevice, parent);
+}
+
/*!
Returns a new object of this class that is in the \l PeripheralRole and has the
@@ -516,7 +553,7 @@ QBluetoothAddress QLowEnergyController::remoteAddress() const
*/
QBluetoothUuid QLowEnergyController::remoteDeviceUuid() const
{
- return QBluetoothUuid();
+ return d_ptr->deviceUuid;
}
/*!
diff --git a/src/bluetooth/qlowenergycontroller.h b/src/bluetooth/qlowenergycontroller.h
index 9fe46fe5..37e7b82d 100644
--- a/src/bluetooth/qlowenergycontroller.h
+++ b/src/bluetooth/qlowenergycontroller.h
@@ -66,7 +66,8 @@ public:
InvalidBluetoothAdapterError,
ConnectionError,
AdvertisingError,
- RemoteHostClosedError
+ RemoteHostClosedError,
+ AuthorizationError
};
Q_ENUM(Error)
@@ -93,13 +94,16 @@ public:
explicit QLowEnergyController(const QBluetoothAddress &remoteDevice,
QObject *parent = nullptr); // TODO Qt 6 remove ctor
explicit QLowEnergyController(const QBluetoothDeviceInfo &remoteDevice,
- QObject *parent = nullptr);
+ QObject *parent = nullptr); // TODO Qt 6 make private
explicit QLowEnergyController(const QBluetoothAddress &remoteDevice,
const QBluetoothAddress &localDevice,
QObject *parent = nullptr); // TODO Qt 6 remove ctor
static QLowEnergyController *createCentral(const QBluetoothDeviceInfo &remoteDevice,
QObject *parent = nullptr);
+ static QLowEnergyController *createCentral(const QBluetoothAddress &remoteDevice,
+ const QBluetoothAddress &localDevice,
+ QObject *parent = nullptr);
static QLowEnergyController *createPeripheral(QObject *parent = nullptr);
// TODO: Allow to set connection timeout (disconnect when no data has been exchanged for n seconds).
diff --git a/src/bluetooth/qlowenergycontroller_android_p.h b/src/bluetooth/qlowenergycontroller_android_p.h
index 3f97e363..f05c63ca 100644
--- a/src/bluetooth/qlowenergycontroller_android_p.h
+++ b/src/bluetooth/qlowenergycontroller_android_p.h
@@ -60,15 +60,8 @@
#include "qlowenergycontroller.h"
#include "qlowenergycontrollerbase_p.h"
-#if QT_CONFIG(bluez) && !defined(QT_BLUEZ_NO_BTLE)
-#include <QtBluetooth/QBluetoothSocket>
-#elif defined(QT_ANDROID_BLUETOOTH)
#include <QtAndroidExtras/QAndroidJniObject>
#include "android/lowenergynotificationhub_p.h"
-#elif defined(QT_WINRT_BLUETOOTH)
-#include <wrl.h>
-#include <windows.devices.bluetooth.h>
-#endif
#include <functional>
@@ -77,16 +70,7 @@ QT_BEGIN_NAMESPACE
class QLowEnergyServiceData;
class QTimer;
-#if QT_CONFIG(bluez) && !defined(QT_BLUEZ_NO_BTLE)
-class HciManager;
-class LeCmacCalculator;
-class QSocketNotifier;
-class RemoteDeviceManager;
-#elif defined(QT_ANDROID_BLUETOOTH)
class LowEnergyNotificationHub;
-#elif defined(QT_WINRT_BLUETOOTH)
-class QWinRTLowEnergyServiceHandler;
-#endif
extern void registerQLowEnergyControllerMetaType();
diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp
index 65f4e0c2..dfa21004 100644
--- a/src/bluetooth/qlowenergycontroller_bluez.cpp
+++ b/src/bluetooth/qlowenergycontroller_bluez.cpp
@@ -3115,6 +3115,14 @@ void QLowEnergyControllerPrivateBluez::handleConnectionRequest()
if (connectionHandle == 0)
qCWarning(QT_BT_BLUEZ) << "Received client connection, but no connection complete event";
+ if (l2cpSocket) {
+ disconnect(l2cpSocket);
+ if (l2cpSocket->isOpen())
+ l2cpSocket->close();
+
+ l2cpSocket->deleteLater();
+ l2cpSocket = nullptr;
+ }
closeServerSocket();
QBluetoothSocketPrivateBluez *rawSocketPrivate = new QBluetoothSocketPrivateBluez();
diff --git a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp
index 441eca6b..4e5f3430 100644
--- a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp
+++ b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp
@@ -268,9 +268,13 @@ void QLowEnergyControllerPrivateBluezDBus::connectToDeviceHelper()
const QVariantMap &ifaceValues = jt.value();
if (iface == QStringLiteral("org.bluez.Device1")) {
- if (remoteDevice.toString() == ifaceValues.value(QStringLiteral("Address")).toString()) {
- devicePath = it.key().path();
- break;
+ if (remoteDevice.toString() == ifaceValues.value(QStringLiteral("Address")).toString())
+ {
+ const QVariant adapterForCurrentDevice = ifaceValues.value(QStringLiteral("Adapter"));
+ if (qvariant_cast<QDBusObjectPath>(adapterForCurrentDevice).path() == hostAdapterPath) {
+ devicePath = it.key().path();
+ break;
+ }
}
}
}
@@ -350,6 +354,9 @@ void QLowEnergyControllerPrivateBluezDBus::connectToDevice()
void QLowEnergyControllerPrivateBluezDBus::disconnectFromDevice()
{
+ if (!device)
+ return;
+
setState(QLowEnergyController::ClosingState);
QDBusPendingReply<> reply = device->Disconnect();
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_darwin.mm
index 8cef621c..253956e2 100644
--- a/src/bluetooth/qlowenergycontroller_osx.mm
+++ b/src/bluetooth/qlowenergycontroller_darwin.mm
@@ -38,13 +38,17 @@
**
****************************************************************************/
-#include "osx/osxbtnotifier_p.h"
#include "osx/osxbtutility_p.h"
#include "osx/uistrings_p.h"
+#ifndef Q_OS_TVOS
+#include "osx/osxbtperipheralmanager_p.h"
+#endif // Q_OS_TVOS
+#include "qlowenergycontroller_darwin_p.h"
#include "qlowenergyserviceprivate_p.h"
-#include "qlowenergycontroller_osx_p.h"
+#include "osx/osxbtcentralmanager_p.h"
+
#include "qlowenergyservicedata.h"
#include "qbluetoothlocaldevice.h"
#include "qbluetoothdeviceinfo.h"
@@ -58,30 +62,14 @@
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
-#define OSX_D_PTR QLowEnergyControllerPrivateOSX *osx_d_ptr = static_cast<QLowEnergyControllerPrivateOSX *>(d_ptr)
-
QT_BEGIN_NAMESPACE
namespace {
-static void registerQLowEnergyControllerMetaType()
-{
- static bool initDone = false;
- if (!initDone) {
- qRegisterMetaType<QLowEnergyController::ControllerState>();
- qRegisterMetaType<QLowEnergyController::Error>();
- qRegisterMetaType<QLowEnergyHandle>("QLowEnergyHandle");
- qRegisterMetaType<QSharedPointer<QLowEnergyServicePrivate> >();
- qRegisterMetaType<QLowEnergyCharacteristic>();
- qRegisterMetaType<QLowEnergyDescriptor>();
- initDone = true;
- }
-}
-
typedef QSharedPointer<QLowEnergyServicePrivate> ServicePrivate;
// Convenience function, can return a smart pointer that 'isNull'.
-ServicePrivate qt_createLEService(QLowEnergyControllerPrivateOSX *controller, CBService *cbService, bool included)
+ServicePrivate qt_createLEService(QLowEnergyControllerPrivateDarwin *controller, CBService *cbService, bool included)
{
Q_ASSERT_X(controller, Q_FUNC_INFO, "invalid controller (null)");
Q_ASSERT_X(cbService, Q_FUNC_INFO, "invalid service (nil)");
@@ -131,110 +119,276 @@ UUIDList qt_servicesUuids(NSArray *services)
return uuids;
}
-}
+} // unnamed namespace
-QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyController::Role r,
- QLowEnergyController *q,
- const QBluetoothDeviceInfo &deviceInfo)
- : q_ptr(q),
- deviceUuid(deviceInfo.deviceUuid()),
- deviceName(deviceInfo.name()),
- lastError(QLowEnergyController::NoError),
- controllerState(QLowEnergyController::UnconnectedState),
- addressType(QLowEnergyController::PublicAddress)
+#ifndef Q_OS_TVOS
+using ObjCPeripheralManager = QT_MANGLE_NAMESPACE(OSXBTPeripheralManager);
+#endif // Q_OS_TVOS
+
+using ObjCCentralManager = QT_MANGLE_NAMESPACE(OSXBTCentralManager);
+
+QLowEnergyControllerPrivateDarwin::QLowEnergyControllerPrivateDarwin()
{
+ void registerQLowEnergyControllerMetaType();
registerQLowEnergyControllerMetaType();
+ qRegisterMetaType<QLowEnergyHandle>("QLowEnergyHandle");
+ qRegisterMetaType<QSharedPointer<QLowEnergyServicePrivate>>();
+}
- Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)");
+QLowEnergyControllerPrivateDarwin::~QLowEnergyControllerPrivateDarwin()
+{
+ if (const auto leQueue = OSXBluetooth::qt_LE_queue()) {
+ if (role == QLowEnergyController::CentralRole) {
+ const auto manager = centralManager.getAs<ObjCCentralManager>();
+ dispatch_sync(leQueue, ^{
+ [manager detach];
+ });
+ } else {
+#ifndef Q_OS_TVOS
+ const auto manager = peripheralManager.getAs<ObjCPeripheralManager>();
+ dispatch_sync(leQueue, ^{
+ [manager detach];
+ });
+#endif
+ }
+ }
+}
- using OSXBluetooth::LECBManagerNotifier;
+bool QLowEnergyControllerPrivateDarwin::isValid() const
+{
+#ifdef Q_OS_TVOS
+ return centralManager;
+#else
+ return centralManager || peripheralManager;
+#endif
+}
- role = r;
+void QLowEnergyControllerPrivateDarwin::init()
+{
+ using OSXBluetooth::LECBManagerNotifier;
QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier);
if (role == QLowEnergyController::PeripheralRole) {
#ifndef Q_OS_TVOS
- peripheralManager.reset([[ObjCPeripheralManager alloc] initWith:notifier.data()]);
+ peripheralManager.reset([[ObjCPeripheralManager alloc] initWith:notifier.data()],
+ DarwinBluetooth::RetainPolicy::noInitialRetain);
if (!peripheralManager) {
- qCWarning(QT_BT_OSX) << "failed to initialize peripheral manager";
+ qCWarning(QT_BT_OSX) << "failed to create a peripheral manager";
return;
}
#else
- qCWarning(QT_BT_OSX) << "peripheral role is not supported on your platform";
+ qCWarning(QT_BT_OSX) << "the peripheral role is not supported on your platform";
return;
-#endif
+#endif // Q_OS_TVOS
} else {
- centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()]);
+ centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()],
+ DarwinBluetooth::RetainPolicy::noInitialRetain);
if (!centralManager) {
- qCWarning(QT_BT_OSX) << "failed to initialize central manager";
+ qCWarning(QT_BT_OSX) << "failed to initialize a central manager";
return;
}
}
- if (!connectSlots(notifier.data())) {
+ if (!connectSlots(notifier.data()))
qCWarning(QT_BT_OSX) << "failed to connect to notifier's signal(s)";
- }
+
// Ownership was taken by central manager.
notifier.take();
}
-QLowEnergyControllerPrivateOSX::~QLowEnergyControllerPrivateOSX()
+void QLowEnergyControllerPrivateDarwin::connectToDevice()
{
- if (const auto leQueue = OSXBluetooth::qt_LE_queue()) {
- if (role == QLowEnergyController::CentralRole) {
- const auto manager = centralManager.data();
- dispatch_sync(leQueue, ^{
- [manager detach];
+ Q_ASSERT_X(state == QLowEnergyController::UnconnectedState,
+ Q_FUNC_INFO, "invalid state");
+
+ if (!isValid()) {
+ // init() had failed for was never called.
+ return _q_CBManagerError(QLowEnergyController::UnknownError);
+ }
+
+ if (deviceUuid.isNull()) {
+ // Wrong constructor was used or invalid UUID was provided.
+ return _q_CBManagerError(QLowEnergyController::UnknownRemoteDeviceError);
+ }
+
+ // The logic enforcing the role is in the public class.
+ Q_ASSERT_X(role != QLowEnergyController::PeripheralRole,
+ Q_FUNC_INFO, "invalid role (peripheral)");
+
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << "no LE queue found";
+ setErrorDescription(QLowEnergyController::UnknownError);
+ return;
+ }
+
+ setErrorDescription(QLowEnergyController::NoError);
+ setState(QLowEnergyController::ConnectingState);
+
+ const QBluetoothUuid deviceUuidCopy(deviceUuid);
+ ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>();
+ dispatch_async(leQueue, ^{
+ [manager connectToDevice:deviceUuidCopy];
+ });
+}
+
+void QLowEnergyControllerPrivateDarwin::disconnectFromDevice()
+{
+ if (role == QLowEnergyController::PeripheralRole) {
+ // CoreBluetooth API intentionally does not provide any way of closing
+ // a connection. All we can do here is to stop the advertisement.
+ stopAdvertising();
+ return;
+ }
+
+ if (isValid()) {
+ const auto oldState = state;
+
+ if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) {
+ setState(QLowEnergyController::ClosingState);
+ invalidateServices();
+
+ auto manager = centralManager.getAs<ObjCCentralManager>();
+ dispatch_async(leQueue, ^{
+ [manager disconnectFromDevice];
});
+
+ if (oldState == QLowEnergyController::ConnectingState) {
+ // With a pending connect attempt there is no
+ // guarantee we'll ever have didDisconnect callback,
+ // set the state here and now to make sure we still
+ // can connect.
+ setState(QLowEnergyController::UnconnectedState);
+ }
} else {
-#ifndef Q_OS_TVOS
- const auto manager = peripheralManager.data();
- dispatch_sync(leQueue, ^{
- [manager detach];
- });
-#endif
+ qCCritical(QT_BT_OSX) << "qt LE queue is nil, "
+ "can not dispatch 'disconnect'";
}
}
}
-bool QLowEnergyControllerPrivateOSX::isValid() const
+void QLowEnergyControllerPrivateDarwin::discoverServices()
+{
+ Q_ASSERT_X(state != QLowEnergyController::UnconnectedState,
+ Q_FUNC_INFO, "not connected to peripheral");
+ Q_ASSERT_X(role != QLowEnergyController::PeripheralRole,
+ Q_FUNC_INFO, "invalid role (peripheral)");
+
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ Q_ASSERT_X(leQueue, Q_FUNC_INFO, "LE queue not found");
+
+ setState(QLowEnergyController::DiscoveringState);
+
+ ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>();
+ dispatch_async(leQueue, ^{
+ [manager discoverServices];
+ });
+}
+
+void QLowEnergyControllerPrivateDarwin::discoverServiceDetails(const QBluetoothUuid &serviceUuid)
{
+ if (state != QLowEnergyController::DiscoveredState) {
+ qCWarning(QT_BT_OSX) << "can not discover service details in the current state, "
+ "QLowEnergyController::DiscoveredState is expected";
+ return;
+ }
+
+ if (!serviceList.contains(serviceUuid)) {
+ qCWarning(QT_BT_OSX) << "unknown service: " << serviceUuid;
+ return;
+ }
+
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ Q_ASSERT(leQueue);
+
+ ServicePrivate qtService(serviceList.value(serviceUuid));
+ qtService->setState(QLowEnergyService::DiscoveringServices);
+ // Copy objects ...
+ ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>();
+ const QBluetoothUuid serviceUuidCopy(serviceUuid);
+ dispatch_async(leQueue, ^{
+ [manager discoverServiceDetails:serviceUuidCopy];
+ });
+}
+
+void QLowEnergyControllerPrivateDarwin::requestConnectionUpdate(const QLowEnergyConnectionParameters &params)
+{
+ Q_UNUSED(params);
+ // TODO: implement this, if possible.
+ qCWarning(QT_BT_OSX) << "Connection update not implemented on your platform";
+}
+
+void QLowEnergyControllerPrivateDarwin::addToGenericAttributeList(const QLowEnergyServiceData &service,
+ QLowEnergyHandle startHandle)
+{
+ Q_UNUSED(service);
+ Q_UNUSED(startHandle);
+ // TODO: check why I don't need this (apparently it is used in addServiceHelper
+ // of the base class).
+}
+
+QLowEnergyService * QLowEnergyControllerPrivateDarwin::addServiceHelper(const QLowEnergyServiceData &service)
+{
+ // Three checks below should be removed, they are done in the q_ptr's class.
#ifdef Q_OS_TVOS
- return centralManager;
+ Q_UNUSED(service);
+ qCDebug(QT_BT_OSX, "peripheral role is not supported on tvOS");
#else
- return centralManager || peripheralManager;
-#endif
+ if (role != QLowEnergyController::PeripheralRole) {
+ qCWarning(QT_BT_OSX) << "not in peripheral role";
+ return nullptr;
+ }
+
+ if (state != QLowEnergyController::UnconnectedState) {
+ qCWarning(QT_BT_OSX) << "invalid state";
+ return nullptr;
+ }
+
+ if (!service.isValid()) {
+ qCWarning(QT_BT_OSX) << "invalid service";
+ return nullptr;
+ }
+
+ for (auto includedService : service.includedServices())
+ includedService->d_ptr->type |= QLowEnergyService::IncludedService;
+
+ const auto manager = peripheralManager.getAs<ObjCPeripheralManager>();
+ Q_ASSERT(manager);
+ if (const auto servicePrivate = [manager addService:service]) {
+ servicePrivate->setController(this);
+ servicePrivate->state = QLowEnergyService::LocalService;
+ localServices.insert(servicePrivate->uuid, servicePrivate);
+ return new QLowEnergyService(servicePrivate);
+ }
+#endif // Q_OS_TVOS
+ return nullptr;
}
-void QLowEnergyControllerPrivateOSX::_q_connected()
+void QLowEnergyControllerPrivateDarwin::_q_connected()
{
- controllerState = QLowEnergyController::ConnectedState;
-
- emit q_ptr->stateChanged(QLowEnergyController::ConnectedState);
+ setState(QLowEnergyController::ConnectedState);
emit q_ptr->connected();
}
-void QLowEnergyControllerPrivateOSX::_q_disconnected()
+void QLowEnergyControllerPrivateDarwin::_q_disconnected()
{
- controllerState = QLowEnergyController::UnconnectedState;
-
if (role == QLowEnergyController::CentralRole)
invalidateServices();
- emit q_ptr->stateChanged(QLowEnergyController::UnconnectedState);
+ setState(QLowEnergyController::UnconnectedState);
emit q_ptr->disconnected();
}
-void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished()
+void QLowEnergyControllerPrivateDarwin::_q_serviceDiscoveryFinished()
{
- Q_ASSERT_X(controllerState == QLowEnergyController::DiscoveringState,
+ Q_ASSERT_X(state == QLowEnergyController::DiscoveringState,
Q_FUNC_INFO, "invalid state");
using namespace OSXBluetooth;
QT_BT_MAC_AUTORELEASEPOOL;
- NSArray *const services = [centralManager.data() peripheral].services;
+ NSArray *const services = [centralManager.getAs<ObjCCentralManager>() peripheral].services;
// Now we have to traverse the discovered services tree.
// Essentially it's an iterative version of more complicated code from the
// OSXBTCentralManager's code.
@@ -249,13 +403,13 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished()
const ServicePrivate newService(qt_createLEService(this, cbService, false));
if (!newService.data())
continue;
- if (discoveredServices.contains(newService->uuid)) {
+ if (serviceList.contains(newService->uuid)) {
// It's a bit stupid we first created it ...
qCDebug(QT_BT_OSX) << "discovered service with a duplicated UUID"
<< newService->uuid;
continue;
}
- discoveredServices.insert(newService->uuid, newService);
+ serviceList.insert(newService->uuid, newService);
discoveredCBServices.insert(newService->uuid, cbService);
}
@@ -273,8 +427,8 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished()
}
const QBluetoothUuid uuid(qt_uuid(s.UUID));
- if (discoveredServices.contains(uuid) && discoveredCBServices.value(uuid) == s) {
- ServicePrivate qtService(discoveredServices.value(uuid));
+ if (serviceList.contains(uuid) && discoveredCBServices.value(uuid) == s) {
+ ServicePrivate qtService(serviceList.value(uuid));
// Add included UUIDs:
qtService->includedServices.append(qt_servicesUuids(s.includedServices));
}// Else - we ignored this CBService object.
@@ -286,15 +440,15 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished()
for (NSUInteger i = 0, e = [toVisitNext count]; i < e; ++i) {
CBService *const s = [toVisitNext objectAtIndex:i];
const QBluetoothUuid uuid(qt_uuid(s.UUID));
- if (discoveredServices.contains(uuid)) {
+ if (serviceList.contains(uuid)) {
if (discoveredCBServices.value(uuid) == s) {
- ServicePrivate qtService(discoveredServices.value(uuid));
+ ServicePrivate qtService(serviceList.value(uuid));
qtService->type |= QLowEnergyService::IncludedService;
} // Else this is the duplicate we ignored already.
} else {
// Oh, we do not even have it yet???
ServicePrivate newService(qt_createLEService(this, s, true));
- discoveredServices.insert(newService->uuid, newService);
+ serviceList.insert(newService->uuid, newService);
discoveredCBServices.insert(newService->uuid, s);
}
}
@@ -306,31 +460,26 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished()
qCDebug(QT_BT_OSX) << "no services found";
}
- for (ServiceMap::const_iterator it = discoveredServices.constBegin(); it != discoveredServices.constEnd(); ++it) {
- const QBluetoothUuid &uuid = it.key();
- QMetaObject::invokeMethod(q_ptr, "serviceDiscovered", Qt::QueuedConnection,
- Q_ARG(QBluetoothUuid, uuid));
- }
+ for (ServiceMap::const_iterator it = serviceList.constBegin(); it != serviceList.constEnd(); ++it)
+ emit q_ptr->serviceDiscovered(it.key());
- controllerState = QLowEnergyController::DiscoveredState;
- QMetaObject::invokeMethod(q_ptr, "stateChanged", Qt::QueuedConnection,
- Q_ARG(QLowEnergyController::ControllerState, controllerState));
- QMetaObject::invokeMethod(q_ptr, "discoveryFinished", Qt::QueuedConnection);
+ setState(QLowEnergyController::DiscoveredState);
+ emit q_ptr->discoveryFinished();
}
-void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service)
+void QLowEnergyControllerPrivateDarwin::_q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service)
{
QT_BT_MAC_AUTORELEASEPOOL;
Q_ASSERT(service);
- if (!discoveredServices.contains(service->uuid)) {
+ if (!serviceList.contains(service->uuid)) {
qCDebug(QT_BT_OSX) << "unknown service uuid:"
<< service->uuid;
return;
}
- ServicePrivate qtService(discoveredServices.value(service->uuid));
+ ServicePrivate qtService(serviceList.value(service->uuid));
// Assert on handles?
qtService->startHandle = service->startHandle;
qtService->endHandle = service->endHandle;
@@ -339,8 +488,23 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedP
qtService->setState(QLowEnergyService::ServiceDiscovered);
}
-void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle charHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateDarwin::_q_servicesWereModified()
+{
+ if (!(state == QLowEnergyController::DiscoveringState
+ || state == QLowEnergyController::DiscoveredState)) {
+ qCWarning(QT_BT_OSX) << "services were modified while controller is not in Discovered/Discovering state";
+ return;
+ }
+
+ if (state == QLowEnergyController::DiscoveredState)
+ invalidateServices();
+
+ setState(QLowEnergyController::ConnectedState);
+ q_ptr->discoverServices();
+}
+
+void QLowEnergyControllerPrivateDarwin::_q_characteristicRead(QLowEnergyHandle charHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)");
@@ -360,8 +524,8 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle char
emit service->characteristicRead(characteristic, value);
}
-void QLowEnergyControllerPrivateOSX::_q_characteristicWritten(QLowEnergyHandle charHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateDarwin::_q_characteristicWritten(QLowEnergyHandle charHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)");
@@ -384,8 +548,8 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicWritten(QLowEnergyHandle c
emit service->characteristicWritten(characteristic, value);
}
-void QLowEnergyControllerPrivateOSX::_q_characteristicUpdated(QLowEnergyHandle charHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateDarwin::_q_characteristicUpdated(QLowEnergyHandle charHandle,
+ const QByteArray &value)
{
// TODO: write/update notifications are quite similar (except asserts/warnings messages
// and different signals emitted). Merge them into one function?
@@ -413,8 +577,8 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicUpdated(QLowEnergyHandle c
emit service->characteristicChanged(characteristic, value);
}
-void QLowEnergyControllerPrivateOSX::_q_descriptorRead(QLowEnergyHandle dHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateDarwin::_q_descriptorRead(QLowEnergyHandle dHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)");
@@ -429,8 +593,8 @@ void QLowEnergyControllerPrivateOSX::_q_descriptorRead(QLowEnergyHandle dHandle,
emit service->descriptorRead(qtDescriptor, value);
}
-void QLowEnergyControllerPrivateOSX::_q_descriptorWritten(QLowEnergyHandle dHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateDarwin::_q_descriptorWritten(QLowEnergyHandle dHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)");
@@ -446,8 +610,8 @@ void QLowEnergyControllerPrivateOSX::_q_descriptorWritten(QLowEnergyHandle dHand
emit service->descriptorWritten(qtDescriptor, value);
}
-void QLowEnergyControllerPrivateOSX::_q_notificationEnabled(QLowEnergyHandle charHandle,
- bool enabled)
+void QLowEnergyControllerPrivateDarwin::_q_notificationEnabled(QLowEnergyHandle charHandle,
+ bool enabled)
{
// CoreBluetooth in peripheral role does not allow mutable descriptors,
// in central we can only call setNotification:enabled/disabled.
@@ -489,7 +653,7 @@ void QLowEnergyControllerPrivateOSX::_q_notificationEnabled(QLowEnergyHandle cha
}
}
-void QLowEnergyControllerPrivateOSX::_q_LEnotSupported()
+void QLowEnergyControllerPrivateDarwin::_q_LEnotSupported()
{
// Report as an error. But this should not be possible
// actually: before connecting to any device, we have
@@ -497,32 +661,30 @@ void QLowEnergyControllerPrivateOSX::_q_LEnotSupported()
// be supported.
}
-void QLowEnergyControllerPrivateOSX::_q_CBManagerError(QLowEnergyController::Error errorCode)
+void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(QLowEnergyController::Error errorCode)
{
- // Errors reported during connect and general errors.
-
- setErrorDescription(errorCode);
- emit q_ptr->error(lastError);
-
- if (controllerState == QLowEnergyController::ConnectingState) {
- controllerState = QLowEnergyController::UnconnectedState;
- emit q_ptr->stateChanged(controllerState);
- } else if (controllerState == QLowEnergyController::DiscoveringState) {
- controllerState = QLowEnergyController::ConnectedState;
- emit q_ptr->stateChanged(controllerState);
- } // In any other case we stay in Discovered, it's
- // a service/characteristic - related error.
+ // This function handles errors reported while connecting to a remote device
+ // and also other errors in general.
+ setError(errorCode);
+
+ if (state == QLowEnergyController::ConnectingState)
+ setState(QLowEnergyController::UnconnectedState);
+ else if (state == QLowEnergyController::DiscoveringState)
+ setState(QLowEnergyController::ConnectedState);
+
+ // In any other case we stay in Discovered, it's
+ // a service/characteristic - related error.
}
-void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &serviceUuid,
- QLowEnergyController::Error errorCode)
+void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(const QBluetoothUuid &serviceUuid,
+ QLowEnergyController::Error errorCode)
{
// Errors reported while discovering service details etc.
Q_UNUSED(errorCode) // TODO: setError?
// We failed to discover any characteristics/descriptors.
- if (discoveredServices.contains(serviceUuid)) {
- ServicePrivate qtService(discoveredServices.value(serviceUuid));
+ if (serviceList.contains(serviceUuid)) {
+ ServicePrivate qtService(serviceList.value(serviceUuid));
qtService->setState(QLowEnergyService::InvalidService);
} else {
qCDebug(QT_BT_OSX) << "error reported for unknown service"
@@ -530,109 +692,24 @@ void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &ser
}
}
-void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &serviceUuid,
- QLowEnergyService::ServiceError errorCode)
+void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(const QBluetoothUuid &serviceUuid,
+ QLowEnergyService::ServiceError errorCode)
{
- if (!discoveredServices.contains(serviceUuid)) {
+ if (!serviceList.contains(serviceUuid)) {
qCDebug(QT_BT_OSX) << "unknown service uuid:"
<< serviceUuid;
return;
}
- ServicePrivate service(discoveredServices.value(serviceUuid));
+ ServicePrivate service(serviceList.value(serviceUuid));
service->setError(errorCode);
}
-void QLowEnergyControllerPrivateOSX::connectToDevice()
-{
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller");
- Q_ASSERT_X(controllerState == QLowEnergyController::UnconnectedState,
- Q_FUNC_INFO, "invalid state");
- Q_ASSERT_X(!deviceUuid.isNull(), Q_FUNC_INFO,
- "invalid private controller (no device uuid)");
- Q_ASSERT_X(role != QLowEnergyController::PeripheralRole,
- Q_FUNC_INFO, "invalid role (peripheral)");
-
- dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
- if (!leQueue) {
- qCWarning(QT_BT_OSX) << "no LE queue found";
- setErrorDescription(QLowEnergyController::UnknownError);
- return;
- }
-
- setErrorDescription(QLowEnergyController::NoError);
- controllerState = QLowEnergyController::ConnectingState;
-
- const QBluetoothUuid deviceUuidCopy(deviceUuid);
- ObjCCentralManager *manager = centralManager.data();
- dispatch_async(leQueue, ^{
- [manager connectToDevice:deviceUuidCopy];
- });
-}
-
-void QLowEnergyControllerPrivateOSX::discoverServices()
-{
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller");
- Q_ASSERT_X(controllerState != QLowEnergyController::UnconnectedState,
- Q_FUNC_INFO, "not connected to peripheral");
- Q_ASSERT_X(role != QLowEnergyController::PeripheralRole,
- Q_FUNC_INFO, "invalid role (peripheral)");
-
- dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
- if (!leQueue) {
- qCWarning(QT_BT_OSX) << "no LE queue found";
- setErrorDescription(QLowEnergyController::UnknownError);
- return;
- }
-
- controllerState = QLowEnergyController::DiscoveringState;
- emit q_ptr->stateChanged(QLowEnergyController::DiscoveringState);
-
- ObjCCentralManager *manager = centralManager.data();
- dispatch_async(leQueue, ^{
- [manager discoverServices];
- });
-}
-
-void QLowEnergyControllerPrivateOSX::discoverServiceDetails(const QBluetoothUuid &serviceUuid)
-{
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller");
-
- if (controllerState != QLowEnergyController::DiscoveredState) {
- // This will also exclude peripheral role, since controller
- // can never be in discovered state ...
- qCWarning(QT_BT_OSX) << "can not discover service details in the current state, "
- "QLowEnergyController::DiscoveredState is expected";
- return;
- }
-
- if (!discoveredServices.contains(serviceUuid)) {
- qCWarning(QT_BT_OSX) << "unknown service: " << serviceUuid;
- return;
- }
-
- dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
- if (!leQueue) {
- qCWarning(QT_BT_OSX) << "no LE queue found";
- return;
- }
-
- ServicePrivate qtService(discoveredServices.value(serviceUuid));
- qtService->setState(QLowEnergyService::DiscoveringServices);
- // Copy objects ...
- ObjCCentralManager *manager = centralManager.data();
- const QBluetoothUuid serviceUuidCopy(serviceUuid);
- dispatch_async(leQueue, ^{
- [manager discoverServiceDetails:serviceUuidCopy];
- });
-}
-
-void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergyServicePrivate> service,
- QLowEnergyHandle charHandle,
- const QByteArray &newValue)
+void QLowEnergyControllerPrivateDarwin::setNotifyValue(QSharedPointer<QLowEnergyServicePrivate> service,
+ QLowEnergyHandle charHandle,
+ const QByteArray &newValue)
{
Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller");
if (role == QLowEnergyController::PeripheralRole) {
qCWarning(QT_BT_OSX) << "invalid role (peripheral)";
@@ -651,7 +728,7 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer
return;
}
- if (!discoveredServices.contains(service->uuid)) {
+ if (!serviceList.contains(service->uuid)) {
qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << "found";
return;
}
@@ -663,11 +740,9 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer
}
dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
- if (!leQueue) {
- qCWarning(QT_BT_OSX) << "no LE queue found";
- return;
- }
- ObjCCentralManager *manager = centralManager.data();
+ Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found");
+
+ ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>();
const QBluetoothUuid serviceUuid(service->uuid);
const QByteArray newValueCopy(newValue);
dispatch_async(leQueue, ^{
@@ -677,18 +752,17 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer
});
}
-void QLowEnergyControllerPrivateOSX::readCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service,
- QLowEnergyHandle charHandle)
+void QLowEnergyControllerPrivateDarwin::readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle)
{
Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller");
if (role == QLowEnergyController::PeripheralRole) {
qCWarning(QT_BT_OSX) << "invalid role (peripheral)";
return;
}
- if (!discoveredServices.contains(service->uuid)) {
+ if (!serviceList.contains(service->uuid)) {
qCWarning(QT_BT_OSX) << "no service with uuid:"
<< service->uuid << "found";
return;
@@ -701,29 +775,26 @@ void QLowEnergyControllerPrivateOSX::readCharacteristic(QSharedPointer<QLowEnerg
}
dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
- if (!leQueue) {
- qCWarning(QT_BT_OSX) << "no LE queue found";
- return;
- }
+ Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found");
+
// Attention! We have to copy UUID.
- ObjCCentralManager *manager = centralManager.data();
+ ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>();
const QBluetoothUuid serviceUuid(service->uuid);
dispatch_async(leQueue, ^{
[manager readCharacteristic:charHandle onService:serviceUuid];
});
}
-void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service,
- QLowEnergyHandle charHandle, const QByteArray &newValue,
- QLowEnergyService::WriteMode mode)
+void QLowEnergyControllerPrivateDarwin::writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle, const QByteArray &newValue,
+ QLowEnergyService::WriteMode mode)
{
Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller");
// We can work only with services found on a given peripheral
// (== created by the given LE controller).
- if (!discoveredServices.contains(service->uuid)) {
+ if (!serviceList.contains(service->uuid) && !localServices.contains(service->uuid)) {
qCWarning(QT_BT_OSX) << "no service with uuid:"
<< service->uuid << " found";
return;
@@ -736,15 +807,12 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner
}
dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
- if (!leQueue) {
- qCWarning(QT_BT_OSX) << "no LE queue found";
- return;
- }
+ Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found");
// Attention! We have to copy objects!
const QByteArray newValueCopy(newValue);
if (role == QLowEnergyController::CentralRole) {
const QBluetoothUuid serviceUuid(service->uuid);
- const auto manager = centralManager.data();
+ const auto manager = centralManager.getAs<ObjCCentralManager>();
dispatch_async(leQueue, ^{
[manager write:newValueCopy
charHandle:charHandle
@@ -753,7 +821,7 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner
});
} else {
#ifndef Q_OS_TVOS
- const auto manager = peripheralManager.data();
+ const auto manager = peripheralManager.getAs<ObjCPeripheralManager>();
dispatch_async(leQueue, ^{
[manager write:newValueCopy charHandle:charHandle];
});
@@ -763,9 +831,9 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner
}
}
-quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHandle charHandle,
- const QByteArray &value,
- bool appendValue)
+quint16 QLowEnergyControllerPrivateDarwin::updateValueOfCharacteristic(QLowEnergyHandle charHandle,
+ const QByteArray &value,
+ bool appendValue)
{
QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
if (!service.isNull()) {
@@ -784,18 +852,20 @@ quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHa
return 0;
}
-void QLowEnergyControllerPrivateOSX::readDescriptor(QSharedPointer<QLowEnergyServicePrivate> service,
- QLowEnergyHandle descriptorHandle)
+void QLowEnergyControllerPrivateDarwin::readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle)
{
+ Q_UNUSED(charHandle) // Hehe, yes!
+
Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller");
if (role == QLowEnergyController::PeripheralRole) {
qCWarning(QT_BT_OSX) << "invalid role (peripheral)";
return;
}
- if (!discoveredServices.contains(service->uuid)) {
+ if (!serviceList.contains(service->uuid)) {
qCWarning(QT_BT_OSX) << "no service with uuid:"
<< service->uuid << "found";
return;
@@ -808,19 +878,21 @@ void QLowEnergyControllerPrivateOSX::readDescriptor(QSharedPointer<QLowEnergySer
}
// Attention! Copy objects!
const QBluetoothUuid serviceUuid(service->uuid);
- ObjCCentralManager * const manager = centralManager.data();
+ ObjCCentralManager * const manager = centralManager.getAs<ObjCCentralManager>();
dispatch_async(leQueue, ^{
[manager readDescriptor:descriptorHandle
onService:serviceUuid];
});
}
-void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service,
- QLowEnergyHandle descriptorHandle,
- const QByteArray &newValue)
+void QLowEnergyControllerPrivateDarwin::writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue)
{
+ Q_UNUSED(charHandle)
+
Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
- Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller");
if (role == QLowEnergyController::PeripheralRole) {
qCWarning(QT_BT_OSX) << "invalid role (peripheral)";
@@ -830,20 +902,17 @@ void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergySe
// We can work only with services found on a given peripheral
// (== created by the given LE controller),
// otherwise we can not write anything at all.
- if (!discoveredServices.contains(service->uuid)) {
+ if (!serviceList.contains(service->uuid)) {
qCWarning(QT_BT_OSX) << "no service with uuid:"
<< service->uuid << " found";
return;
}
dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
- if (!leQueue) {
- qCWarning(QT_BT_OSX) << "no LE queue found";
- return;
- }
+ Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found");
// Attention! Copy objects!
const QBluetoothUuid serviceUuid(service->uuid);
- ObjCCentralManager * const manager = centralManager.data();
+ ObjCCentralManager * const manager = centralManager.getAs<ObjCCentralManager>();
const QByteArray newValueCopy(newValue);
dispatch_async(leQueue, ^{
[manager write:newValueCopy
@@ -852,8 +921,8 @@ void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergySe
});
}
-quint16 QLowEnergyControllerPrivateOSX::updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descHandle,
- const QByteArray &value, bool appendValue)
+quint16 QLowEnergyControllerPrivateDarwin::updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descHandle,
+ const QByteArray &value, bool appendValue)
{
QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
if (!service.isNull()) {
@@ -878,66 +947,15 @@ quint16 QLowEnergyControllerPrivateOSX::updateValueOfDescriptor(QLowEnergyHandle
return 0;
}
-QSharedPointer<QLowEnergyServicePrivate> QLowEnergyControllerPrivateOSX::serviceForHandle(QLowEnergyHandle handle)
-{
- const QList<QSharedPointer<QLowEnergyServicePrivate>> services
- = discoveredServices.values();
- for (QSharedPointer<QLowEnergyServicePrivate> service : services) {
- if (service->startHandle <= handle && handle <= service->endHandle)
- return service;
- }
-
- return QSharedPointer<QLowEnergyServicePrivate>();
-}
-
-QLowEnergyCharacteristic QLowEnergyControllerPrivateOSX::characteristicForHandle(QLowEnergyHandle charHandle)
-{
- QSharedPointer<QLowEnergyServicePrivate> service(serviceForHandle(charHandle));
- if (service.isNull())
- return QLowEnergyCharacteristic();
-
- if (service->characteristicList.isEmpty())
- return QLowEnergyCharacteristic();
-
- // Check whether it is the handle of a characteristic header
- if (service->characteristicList.contains(charHandle))
- return QLowEnergyCharacteristic(service, charHandle);
-
- // Check whether it is the handle of the characteristic value or its descriptors
- QList<QLowEnergyHandle> charHandles(service->characteristicList.keys());
- std::sort(charHandles.begin(), charHandles.end());
-
- for (int i = charHandles.size() - 1; i >= 0; --i) {
- if (charHandles.at(i) > charHandle)
- continue;
-
- return QLowEnergyCharacteristic(service, charHandles.at(i));
- }
-
- return QLowEnergyCharacteristic();
-}
-
-QLowEnergyDescriptor QLowEnergyControllerPrivateOSX::descriptorForHandle(QLowEnergyHandle descriptorHandle)
-{
- const QLowEnergyCharacteristic ch(characteristicForHandle(descriptorHandle));
- if (!ch.isValid())
- return QLowEnergyDescriptor();
-
- const QLowEnergyServicePrivate::CharData charData = ch.d_ptr->characteristicList[ch.attributeHandle()];
-
- if (charData.descriptorList.contains(descriptorHandle))
- return QLowEnergyDescriptor(ch.d_ptr, ch.attributeHandle(), descriptorHandle);
-
- return QLowEnergyDescriptor();
-}
-
-void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::Error errorCode)
+void QLowEnergyControllerPrivateDarwin::setErrorDescription(QLowEnergyController::Error errorCode)
{
// This function does not emit!
+ // TODO: well, it is not a reason to duplicate a significant part of
+ // setError though!
- lastError = errorCode;
+ error = errorCode;
- switch (lastError) {
+ switch (error) {
case QLowEnergyController::NoError:
errorString.clear();
break;
@@ -963,46 +981,36 @@ void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::E
}
}
-void QLowEnergyControllerPrivateOSX::invalidateServices()
-{
- const QList<QSharedPointer<QLowEnergyServicePrivate>> services
- = discoveredServices.values();
- for (const QSharedPointer<QLowEnergyServicePrivate> service : services) {
- service->setController(nullptr);
- service->setState(QLowEnergyService::InvalidService);
- }
-
- discoveredServices.clear();
-}
-
-bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECBManagerNotifier *notifier)
+bool QLowEnergyControllerPrivateDarwin::connectSlots(OSXBluetooth::LECBManagerNotifier *notifier)
{
using OSXBluetooth::LECBManagerNotifier;
Q_ASSERT_X(notifier, Q_FUNC_INFO, "invalid notifier object (null)");
bool ok = connect(notifier, &LECBManagerNotifier::connected,
- this, &QLowEnergyControllerPrivateOSX::_q_connected);
+ this, &QLowEnergyControllerPrivateDarwin::_q_connected);
ok = ok && connect(notifier, &LECBManagerNotifier::disconnected,
- this, &QLowEnergyControllerPrivateOSX::_q_disconnected);
+ this, &QLowEnergyControllerPrivateDarwin::_q_disconnected);
ok = ok && connect(notifier, &LECBManagerNotifier::serviceDiscoveryFinished,
- this, &QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished);
+ this, &QLowEnergyControllerPrivateDarwin::_q_serviceDiscoveryFinished);
+ ok = ok && connect(notifier, &LECBManagerNotifier::servicesWereModified,
+ this, &QLowEnergyControllerPrivateDarwin::_q_servicesWereModified);
ok = ok && connect(notifier, &LECBManagerNotifier::serviceDetailsDiscoveryFinished,
- this, &QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished);
+ this, &QLowEnergyControllerPrivateDarwin::_q_serviceDetailsDiscoveryFinished);
ok = ok && connect(notifier, &LECBManagerNotifier::characteristicRead,
- this, &QLowEnergyControllerPrivateOSX::_q_characteristicRead);
+ this, &QLowEnergyControllerPrivateDarwin::_q_characteristicRead);
ok = ok && connect(notifier, &LECBManagerNotifier::characteristicWritten,
- this, &QLowEnergyControllerPrivateOSX::_q_characteristicWritten);
+ this, &QLowEnergyControllerPrivateDarwin::_q_characteristicWritten);
ok = ok && connect(notifier, &LECBManagerNotifier::characteristicUpdated,
- this, &QLowEnergyControllerPrivateOSX::_q_characteristicUpdated);
+ this, &QLowEnergyControllerPrivateDarwin::_q_characteristicUpdated);
ok = ok && connect(notifier, &LECBManagerNotifier::descriptorRead,
- this, &QLowEnergyControllerPrivateOSX::_q_descriptorRead);
+ this, &QLowEnergyControllerPrivateDarwin::_q_descriptorRead);
ok = ok && connect(notifier, &LECBManagerNotifier::descriptorWritten,
- this, &QLowEnergyControllerPrivateOSX::_q_descriptorWritten);
+ this, &QLowEnergyControllerPrivateDarwin::_q_descriptorWritten);
ok = ok && connect(notifier, &LECBManagerNotifier::notificationEnabled,
- this, &QLowEnergyControllerPrivateOSX::_q_notificationEnabled);
+ this, &QLowEnergyControllerPrivateDarwin::_q_notificationEnabled);
ok = ok && connect(notifier, &LECBManagerNotifier::LEnotSupported,
- this, &QLowEnergyControllerPrivateOSX::_q_LEnotSupported);
+ this, &QLowEnergyControllerPrivateDarwin::_q_LEnotSupported);
ok = ok && connect(notifier, SIGNAL(CBManagerError(QLowEnergyController::Error)),
this, SLOT(_q_CBManagerError(QLowEnergyController::Error)));
ok = ok && connect(notifier, SIGNAL(CBManagerError(const QBluetoothUuid &, QLowEnergyController::Error)),
@@ -1016,253 +1024,9 @@ bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECBManagerNotif
return ok;
}
-QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddress,
- QObject *parent)
- : QObject(parent),
- d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this))
-{
- OSX_D_PTR;
-
- osx_d_ptr->remoteAddress = remoteAddress;
- osx_d_ptr->localAddress = QBluetoothLocalDevice().address();
-
- qCWarning(QT_BT_OSX) << "construction with remote address "
- "is not supported!";
-}
-
-QLowEnergyController::QLowEnergyController(const QBluetoothDeviceInfo &remoteDevice,
- QObject *parent)
- : QObject(parent),
- d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this, remoteDevice))
-{
- OSX_D_PTR;
-
- osx_d_ptr->localAddress = QBluetoothLocalDevice().address();
- // That's the only "real" ctor - with Core Bluetooth we need a _valid_ deviceUuid
- // from 'remoteDevice'.
-}
-
-QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddress,
- const QBluetoothAddress &localAddress,
- QObject *parent)
- : QObject(parent),
- d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this))
-{
- OSX_D_PTR;
-
- osx_d_ptr->remoteAddress = remoteAddress;
- osx_d_ptr->localAddress = localAddress;
-
- qCWarning(QT_BT_OSX) << "construction with remote/local "
- "addresses is not supported!";
-}
-
-QLowEnergyController::QLowEnergyController(QObject *parent)
- : QObject(parent),
- d_ptr(new QLowEnergyControllerPrivateOSX(PeripheralRole, this))
-{
- OSX_D_PTR;
-
- osx_d_ptr->localAddress = QBluetoothLocalDevice().address();
-}
-
-QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDeviceInfo &remoteDevice,
- QObject *parent)
-{
- return new QLowEnergyController(remoteDevice, parent);
-}
-
-QLowEnergyController *QLowEnergyController::createPeripheral(QObject *parent)
-{
- return new QLowEnergyController(parent);
-}
-
-QLowEnergyController::~QLowEnergyController()
-{
- // Deleting a peripheral will also disconnect.
- delete d_ptr;
-}
-
-QLowEnergyController::Role QLowEnergyController::role() const
-{
- OSX_D_PTR;
-
- return osx_d_ptr->role;
-}
-
-QBluetoothAddress QLowEnergyController::localAddress() const
-{
- OSX_D_PTR;
-
- return osx_d_ptr->localAddress;
-}
-
-QBluetoothAddress QLowEnergyController::remoteAddress() const
-{
- OSX_D_PTR;
-
- return osx_d_ptr->remoteAddress;
-}
-
-QBluetoothUuid QLowEnergyController::remoteDeviceUuid() const
-{
- OSX_D_PTR;
-
- return osx_d_ptr->deviceUuid;
-}
-
-QString QLowEnergyController::remoteName() const
-{
- OSX_D_PTR;
-
- return osx_d_ptr->deviceName;
-}
-
-QLowEnergyController::ControllerState QLowEnergyController::state() const
-{
- OSX_D_PTR;
-
- return osx_d_ptr->controllerState;
-}
-
-QLowEnergyController::RemoteAddressType QLowEnergyController::remoteAddressType() const
-{
- OSX_D_PTR;
-
- return osx_d_ptr->addressType;
-}
-
-void QLowEnergyController::setRemoteAddressType(RemoteAddressType type)
-{
- Q_UNUSED(type)
-
- OSX_D_PTR;
-
- osx_d_ptr->addressType = type;
-}
-
-void QLowEnergyController::connectToDevice()
-{
- OSX_D_PTR;
-
- // A memory allocation problem.
- if (!osx_d_ptr->isValid())
- return osx_d_ptr->_q_CBManagerError(UnknownError);
-
- if (role() == PeripheralRole) {
- qCWarning(QT_BT_OSX) << "can not connect in peripheral role";
- return osx_d_ptr->_q_CBManagerError(ConnectionError);
- }
-
- // No QBluetoothDeviceInfo provided during construction.
- if (osx_d_ptr->deviceUuid.isNull())
- return osx_d_ptr->_q_CBManagerError(UnknownRemoteDeviceError);
-
- if (osx_d_ptr->controllerState != UnconnectedState)
- return;
-
- osx_d_ptr->connectToDevice();
-}
-
-void QLowEnergyController::disconnectFromDevice()
-{
- if (state() == UnconnectedState || state() == ClosingState)
- return;
-
- OSX_D_PTR;
-
- if (role() == PeripheralRole) {
- // CoreBluetooth API intentionally does not provide any way of closing
- // a connection. All we can do here is to stop the advertisement.
- stopAdvertising();
- return;
- }
-
- if (osx_d_ptr->isValid()) {
- const ControllerState oldState = osx_d_ptr->controllerState;
-
- if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) {
- osx_d_ptr->controllerState = ClosingState;
- emit stateChanged(ClosingState);
- osx_d_ptr->invalidateServices();
-
- QT_MANGLE_NAMESPACE(OSXBTCentralManager) *manager
- = osx_d_ptr->centralManager.data();
- dispatch_async(leQueue, ^{
- [manager disconnectFromDevice];
- });
-
- if (oldState == ConnectingState) {
- // With a pending connect attempt there is no
- // guarantee we'll ever have didDisconnect callback,
- // set the state here and now to make sure we still
- // can connect.
- osx_d_ptr->controllerState = UnconnectedState;
- emit stateChanged(UnconnectedState);
- }
- } else {
- qCCritical(QT_BT_OSX) << "qt LE queue is nil, "
- "can not dispatch 'disconnect'";
- }
- }
-}
-
-void QLowEnergyController::discoverServices()
-{
- if (role() == PeripheralRole) {
- qCWarning(QT_BT_OSX) << "invalid role (peripheral)";
- return;
- }
-
- if (state() != ConnectedState)
- return;
-
- OSX_D_PTR;
-
- osx_d_ptr->discoverServices();
-}
-
-QList<QBluetoothUuid> QLowEnergyController::services() const
-{
- OSX_D_PTR;
-
- return osx_d_ptr->discoveredServices.keys();
-}
-
-QLowEnergyService *QLowEnergyController::createServiceObject(const QBluetoothUuid &serviceUuid,
- QObject *parent)
-{
- OSX_D_PTR;
-
- QLowEnergyService *service = nullptr;
-
- QLowEnergyControllerPrivateOSX::ServiceMap::const_iterator it = osx_d_ptr->discoveredServices.constFind(serviceUuid);
- if (it != osx_d_ptr->discoveredServices.constEnd()) {
- const QSharedPointer<QLowEnergyServicePrivate> &serviceData = it.value();
-
- service = new QLowEnergyService(serviceData, parent);
- }
-
- return service;
-}
-
-QLowEnergyController::Error QLowEnergyController::error() const
-{
- OSX_D_PTR;
-
- return osx_d_ptr->lastError;
-}
-
-QString QLowEnergyController::errorString() const
-{
- OSX_D_PTR;
-
- return osx_d_ptr->errorString;
-}
-
-void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameters &params,
- const QLowEnergyAdvertisingData &advertisingData,
- const QLowEnergyAdvertisingData &scanResponseData)
+void QLowEnergyControllerPrivateDarwin::startAdvertising(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData)
{
#ifdef Q_OS_TVOS
Q_UNUSED(params)
@@ -1270,123 +1034,65 @@ void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameter
Q_UNUSED(scanResponseData)
qCWarning(QT_BT_OSX) << "advertising is not supported on your platform";
#else
- OSX_D_PTR;
- if (!osx_d_ptr->isValid())
- return osx_d_ptr->_q_CBManagerError(UnknownError);
+ if (!isValid())
+ return _q_CBManagerError(QLowEnergyController::UnknownError);
- if (role() != PeripheralRole) {
- qCWarning(QT_BT_OSX) << "invalid role";
+ if (role != QLowEnergyController::PeripheralRole) {
+ qCWarning(QT_BT_OSX) << "controller is not a peripheral, cannot start advertising";
return;
}
- if (state() != UnconnectedState) {
- qCWarning(QT_BT_OSX) << "invalid state" << state();
+ if (state != QLowEnergyController::UnconnectedState) {
+ qCWarning(QT_BT_OSX) << "invalid state" << state;
return;
}
auto leQueue(OSXBluetooth::qt_LE_queue());
if (!leQueue) {
qCWarning(QT_BT_OSX) << "no LE queue found";
- osx_d_ptr->setErrorDescription(QLowEnergyController::UnknownError);
+ setErrorDescription(QLowEnergyController::UnknownError);
return;
}
- [osx_d_ptr->peripheralManager setParameters:params
- data:advertisingData
- scanResponse:scanResponseData];
+ const auto manager = peripheralManager.getAs<ObjCPeripheralManager>();
+ [manager setParameters:params data:advertisingData scanResponse:scanResponseData];
- osx_d_ptr->controllerState = AdvertisingState;
- emit stateChanged(AdvertisingState);
+ setState(QLowEnergyController::AdvertisingState);
- const auto manager = osx_d_ptr->peripheralManager.data();
dispatch_async(leQueue, ^{
[manager startAdvertising];
});
#endif
}
-void QLowEnergyController::stopAdvertising()
+void QLowEnergyControllerPrivateDarwin::stopAdvertising()
{
#ifdef Q_OS_TVOS
qCWarning(QT_BT_OSX) << "advertising is not supported on your platform";
#else
- OSX_D_PTR;
+ if (!isValid())
+ return _q_CBManagerError(QLowEnergyController::UnknownError);
- if (!osx_d_ptr->isValid())
- return osx_d_ptr->_q_CBManagerError(UnknownError);
-
- if (state() != AdvertisingState) {
- qCDebug(QT_BT_OSX) << "cannot stop advertising, called in state" << state();
+ if (state != QLowEnergyController::AdvertisingState) {
+ qCDebug(QT_BT_OSX) << "cannot stop advertising, called in state" << state;
return;
}
if (const auto leQueue = OSXBluetooth::qt_LE_queue()) {
- const auto manager = osx_d_ptr->peripheralManager.data();
+ const auto manager = peripheralManager.getAs<ObjCPeripheralManager>();
dispatch_sync(leQueue, ^{
[manager stopAdvertising];
});
- osx_d_ptr->controllerState = UnconnectedState;
- emit stateChanged(UnconnectedState);
+ setState(QLowEnergyController::UnconnectedState);
} else {
qCWarning(QT_BT_OSX) << "no LE queue found";
- osx_d_ptr->setErrorDescription(QLowEnergyController::UnknownError);
+ setErrorDescription(QLowEnergyController::UnknownError);
return;
}
#endif
}
-QLowEnergyService *QLowEnergyController::addService(const QLowEnergyServiceData &data,
- QObject *parent)
-{
-#ifdef Q_OS_TVOS
- Q_UNUSED(data)
- Q_UNUSED(parent)
- qCWarning(QT_BT_OSX) << "peripheral role is not supported on your platform";
-#else
- OSX_D_PTR;
-
- if (!osx_d_ptr->isValid()) {
- osx_d_ptr->_q_CBManagerError(UnknownError);
- return nullptr;
- }
-
- if (role() != PeripheralRole) {
- qCWarning(QT_BT_OSX) << "not in peripheral role";
- return nullptr;
- }
-
- if (state() != UnconnectedState) {
- qCWarning(QT_BT_OSX) << "invalid state";
- return nullptr;
- }
-
- if (!data.isValid()) {
- qCWarning(QT_BT_OSX) << "invalid service";
- return nullptr;
- }
-
- for (auto includedService : data.includedServices())
- includedService->d_ptr->type |= QLowEnergyService::IncludedService;
-
- if (const auto servicePrivate = [osx_d_ptr->peripheralManager addService:data]) {
- servicePrivate->setController(osx_d_ptr);
- servicePrivate->state = QLowEnergyService::LocalService;
- osx_d_ptr->discoveredServices.insert(servicePrivate->uuid, servicePrivate);
- return new QLowEnergyService(servicePrivate, parent);
- }
-#endif
-
- return nullptr;
-}
-
-void QLowEnergyController::requestConnectionUpdate(const QLowEnergyConnectionParameters &params)
-{
- Q_UNUSED(params);
- qCWarning(QT_BT_OSX) << "Connection update not implemented on your platform";
-}
-
QT_END_NAMESPACE
-#include "moc_qlowenergycontroller_osx_p.cpp"
diff --git a/src/bluetooth/qlowenergycontroller_osx_p.h b/src/bluetooth/qlowenergycontroller_darwin_p.h
index 24b7c6e9..960d7fbc 100644
--- a/src/bluetooth/qlowenergycontroller_osx_p.h
+++ b/src/bluetooth/qlowenergycontroller_darwin_p.h
@@ -37,8 +37,8 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#ifndef QLOWENERGYCONTROLLER_OSX_P_H
-#define QLOWENERGYCONTROLLER_OSX_P_H
+#ifndef QLOWENERGYCONTROLLER_DARWIN_P_H
+#define QLOWENERGYCONTROLLER_DARWIN_P_H
//
// W A R N I N G
@@ -51,46 +51,64 @@
// We mean it.
//
-#include "osx/osxbtperipheralmanager_p.h"
#include "qlowenergyserviceprivate_p.h"
-#include "osx/osxbtcentralmanager_p.h"
#include "qlowenergycontrollerbase_p.h"
#include "qlowenergycontroller.h"
#include "osx/osxbtnotifier_p.h"
-#include "osx/osxbtutility_p.h"
#include "qbluetoothaddress.h"
#include "qbluetoothuuid.h"
+#include "osx/btraii_p.h"
#include <QtCore/qsharedpointer.h>
-#include <QtCore/qsysinfo.h>
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qmap.h>
QT_BEGIN_NAMESPACE
-namespace OSXBluetooth
-{
-
-class LECBManagerNotifier;
-
-}
-
class QByteArray;
-// Suffix 'OSX' is a legacy, it's also iOS.
-class QLowEnergyControllerPrivateOSX : public QLowEnergyControllerPrivate
+class QLowEnergyControllerPrivateDarwin : public QLowEnergyControllerPrivate
{
friend class QLowEnergyController;
friend class QLowEnergyService;
Q_OBJECT
public:
- QLowEnergyControllerPrivateOSX(QLowEnergyController::Role role, QLowEnergyController *q,
- const QBluetoothDeviceInfo &info = QBluetoothDeviceInfo());
- ~QLowEnergyControllerPrivateOSX();
-
- bool isValid() const;
+ QLowEnergyControllerPrivateDarwin();
+ ~QLowEnergyControllerPrivateDarwin();
+
+ void init() override;
+ void connectToDevice() override;
+ void disconnectFromDevice() override;
+ void discoverServices() override;
+ void discoverServiceDetails(const QBluetoothUuid &serviceUuid) override;
+
+ void readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle) override;
+ void readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle) override;
+
+ void writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle, const QByteArray &newValue,
+ QLowEnergyService::WriteMode mode) override;
+ void writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue) override;
+
+
+ void requestConnectionUpdate(const QLowEnergyConnectionParameters &params) override;
+ void addToGenericAttributeList(const QLowEnergyServiceData &service,
+ QLowEnergyHandle startHandle) override;
+
+ void startAdvertising(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData) override;
+ void stopAdvertising()override;
+ QLowEnergyService *addServiceHelper(const QLowEnergyServiceData &service) override;
+ bool isValid() const; // QT6 - delete this logic.
private Q_SLOTS:
void _q_connected();
@@ -98,6 +116,7 @@ private Q_SLOTS:
void _q_serviceDiscoveryFinished();
void _q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service);
+ void _q_servicesWereModified();
void _q_characteristicRead(QLowEnergyHandle charHandle, const QByteArray &value);
void _q_characteristicWritten(QLowEnergyHandle charHandle, const QByteArray &value);
@@ -112,75 +131,30 @@ private Q_SLOTS:
void _q_CBManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyService::ServiceError error);
private:
- void connectToDevice();
- void discoverServices();
- void discoverServiceDetails(const QBluetoothUuid &serviceUuid);
-
void setNotifyValue(QSharedPointer<QLowEnergyServicePrivate> service,
QLowEnergyHandle charHandle, const QByteArray &newValue);
- void readCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service,
- QLowEnergyHandle charHandle);
- void writeCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service,
- QLowEnergyHandle charHandle, const QByteArray &newValue,
- QLowEnergyService::WriteMode mode);
-
quint16 updateValueOfCharacteristic(QLowEnergyHandle charHandle,
const QByteArray &value,
bool appendValue);
- void readDescriptor(QSharedPointer<QLowEnergyServicePrivate> service,
- QLowEnergyHandle charHandle);
- void writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service,
- QLowEnergyHandle descriptorHandle,
- const QByteArray &newValue);
-
-
quint16 updateValueOfDescriptor(QLowEnergyHandle charHandle,
QLowEnergyHandle descHandle,
const QByteArray &value,
bool appendValue);
- // 'Lookup' functions:
- QSharedPointer<QLowEnergyServicePrivate> serviceForHandle(QLowEnergyHandle serviceHandle);
- QLowEnergyCharacteristic characteristicForHandle(QLowEnergyHandle charHandle);
- QLowEnergyDescriptor descriptorForHandle(QLowEnergyHandle descriptorHandle);
-
void setErrorDescription(QLowEnergyController::Error errorCode);
- void invalidateServices();
bool connectSlots(OSXBluetooth::LECBManagerNotifier *notifier);
- QLowEnergyController *q_ptr;
- QBluetoothUuid deviceUuid;
- QString deviceName;
-
- QString errorString;
- QLowEnergyController::Error lastError;
-
- QBluetoothAddress localAddress;
- QBluetoothAddress remoteAddress;
-
- QLowEnergyController::Role role;
-
- QLowEnergyController::ControllerState controllerState;
- QLowEnergyController::RemoteAddressType addressType;
-
- typedef QT_MANGLE_NAMESPACE(OSXBTCentralManager) ObjCCentralManager;
- typedef OSXBluetooth::ObjCScopedPointer<ObjCCentralManager> CentralManager;
- CentralManager centralManager;
+ DarwinBluetooth::ScopedPointer centralManager;
#ifndef Q_OS_TVOS
- typedef QT_MANGLE_NAMESPACE(OSXBTPeripheralManager) ObjCPeripheralManager;
- typedef OSXBluetooth::ObjCScopedPointer<ObjCPeripheralManager> PeripheralManager;
- PeripheralManager peripheralManager;
+ DarwinBluetooth::ScopedPointer peripheralManager;
#endif
- typedef QMap<QBluetoothUuid, QSharedPointer<QLowEnergyServicePrivate> > ServiceMap;
- typedef ServiceMap::const_iterator ConstServiceIterator;
- typedef ServiceMap::iterator ServiceIterator;
- ServiceMap discoveredServices;
+ using ServiceMap = QMap<QBluetoothUuid, QSharedPointer<QLowEnergyServicePrivate>>;
};
QT_END_NAMESPACE
-#endif
+#endif // QLOWENERGYCONTROLLER_DARWIN_P_H
diff --git a/src/bluetooth/qlowenergycontroller_winrt.cpp b/src/bluetooth/qlowenergycontroller_winrt.cpp
index ed279ffa..ab566bd9 100644
--- a/src/bluetooth/qlowenergycontroller_winrt.cpp
+++ b/src/bluetooth/qlowenergycontroller_winrt.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qlowenergycontroller_winrt_p.h"
+#include "qbluetoothutils_winrt_p.h"
#include <QtBluetooth/QLowEnergyCharacteristicData>
#include <QtBluetooth/QLowEnergyDescriptorData>
@@ -75,52 +76,7 @@ typedef GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConf
typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharConfigDescriptorResult;
Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
-
-static QVector<QBluetoothUuid> getIncludedServiceIds(const ComPtr<IGattDeviceService> &service)
-{
- QVector<QBluetoothUuid> result;
- ComPtr<IGattDeviceService2> service2;
- HRESULT hr = service.As(&service2);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IVectorView<GattDeviceService *>> includedServices;
- hr = service2->GetAllIncludedServices(&includedServices);
- Q_ASSERT_SUCCEEDED(hr);
-
- uint count;
- hr = includedServices->get_Size(&count);
- Q_ASSERT_SUCCEEDED(hr);
- for (uint i = 0; i < count; ++i) {
- ComPtr<IGattDeviceService> includedService;
- hr = includedServices->GetAt(i, &includedService);
- Q_ASSERT_SUCCEEDED(hr);
- GUID guuid;
- hr = includedService->get_Uuid(&guuid);
- Q_ASSERT_SUCCEEDED(hr);
- const QBluetoothUuid service(guuid);
- result << service;
-
- result << getIncludedServiceIds(includedService);
- }
- return result;
-}
-
-static QByteArray byteArrayFromBuffer(const ComPtr<IBuffer> &buffer, bool isWCharString = false)
-{
- ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
- HRESULT hr = buffer.As(&byteAccess);
- Q_ASSERT_SUCCEEDED(hr);
- char *data;
- hr = byteAccess->Buffer(reinterpret_cast<byte **>(&data));
- Q_ASSERT_SUCCEEDED(hr);
- UINT32 size;
- hr = buffer->get_Length(&size);
- Q_ASSERT_SUCCEEDED(hr);
- if (isWCharString) {
- QString valueString = QString::fromUtf16(reinterpret_cast<ushort *>(data)).left(size / 2);
- return valueString.toUtf8();
- }
- return QByteArray(data, size);
-}
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT_SERVICE_THREAD)
static QByteArray byteArrayFromGattResult(const ComPtr<IGattReadResult> &gattResult, bool isWCharString = false)
{
@@ -284,6 +240,9 @@ QLowEnergyControllerPrivateWinRT::QLowEnergyControllerPrivateWinRT()
qCDebug(QT_BT_WINRT) << __FUNCTION__;
registerQLowEnergyControllerMetaType();
+ connect(this, &QLowEnergyControllerPrivateWinRT::characteristicChanged,
+ this, &QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged,
+ Qt::QueuedConnection);
}
QLowEnergyControllerPrivateWinRT::~QLowEnergyControllerPrivateWinRT()
@@ -291,9 +250,7 @@ QLowEnergyControllerPrivateWinRT::~QLowEnergyControllerPrivateWinRT()
if (mDevice && mStatusChangedToken.value)
mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
- qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens";
- for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens))
- entry.characteristic->remove_ValueChanged(entry.token);
+ unregisterFromValueChanges();
}
void QLowEnergyControllerPrivateWinRT::init()
@@ -340,8 +297,10 @@ void QLowEnergyControllerPrivateWinRT::connectToDevice()
&& status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
setState(QLowEnergyController::ConnectedState);
emit q->connected();
- } else if (state == QLowEnergyController::ConnectedState
+ } else if (state != QLowEnergyController::UnconnectedState
&& status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) {
+ invalidateServices();
+ unregisterFromValueChanges();
setError(QLowEnergyController::RemoteHostClosedError);
setState(QLowEnergyController::UnconnectedState);
emit q->disconnected();
@@ -436,12 +395,17 @@ void QLowEnergyControllerPrivateWinRT::disconnectFromDevice()
{
qCDebug(QT_BT_WINRT) << __FUNCTION__;
Q_Q(QLowEnergyController);
+ setState(QLowEnergyController::ClosingState);
+ unregisterFromValueChanges();
+ if (mDevice) {
+ if (mStatusChangedToken.value) {
+ mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
+ mStatusChangedToken.value = 0;
+ }
+ mDevice = nullptr;
+ }
setState(QLowEnergyController::UnconnectedState);
emit q->disconnected();
- if (mDevice && mStatusChangedToken.value) {
- mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
- mStatusChangedToken.value = 0;
- }
}
ComPtr<IGattDeviceService> QLowEnergyControllerPrivateWinRT::getNativeService(const QBluetoothUuid &serviceUuid)
@@ -493,7 +457,7 @@ void QLowEnergyControllerPrivateWinRT::registerForValueChanges(const QBluetoothU
ComPtr<IBuffer> buffer;
hr = args->get_CharacteristicValue(&buffer);
Q_ASSERT_SUCCEEDED(hr);
- characteristicChanged(handle, byteArrayFromBuffer(buffer));
+ emit characteristicChanged(handle, byteArrayFromBuffer(buffer));
return S_OK;
}).Get(), &token);
Q_ASSERT_SUCCEEDED(hr);
@@ -502,6 +466,17 @@ void QLowEnergyControllerPrivateWinRT::registerForValueChanges(const QBluetoothU
<< serviceUuid << "registered for value changes";
}
+void QLowEnergyControllerPrivateWinRT::unregisterFromValueChanges()
+{
+ qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens";
+ HRESULT hr;
+ for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) {
+ hr = entry.characteristic->remove_ValueChanged(entry.token);
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+ mValueChangedTokens.clear();
+}
+
void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer,
ComPtr<IGattDeviceService> service)
{
@@ -527,6 +502,9 @@ void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(QSharedPointer<QLo
Q_ASSERT_SUCCEEDED(hr);
const QBluetoothUuid includedUuid(guuid);
QSharedPointer<QLowEnergyServicePrivate> includedPointer;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__
+ << "Changing service pointer from thread"
+ << QThread::currentThread();
if (serviceList.contains(includedUuid)) {
includedPointer = serviceList.value(includedUuid);
} else {
@@ -537,6 +515,9 @@ void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(QSharedPointer<QLo
includedPointer = QSharedPointer<QLowEnergyServicePrivate>(priv);
serviceList.insert(includedUuid, includedPointer);
}
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__
+ << "Changing service pointer from thread"
+ << QThread::currentThread();
includedPointer->type |= QLowEnergyService::IncludedService;
servicePointer->includedServices.append(includedUuid);
@@ -566,6 +547,9 @@ void QLowEnergyControllerPrivateWinRT::discoverServices()
Q_ASSERT_SUCCEEDED(hr);
const QBluetoothUuid service(guuid);
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__
+ << "Changing service pointer from thread"
+ << QThread::currentThread();
QSharedPointer<QLowEnergyServicePrivate> pointer;
if (serviceList.contains(service)) {
pointer = serviceList.value(service);
@@ -605,6 +589,8 @@ void QLowEnergyControllerPrivateWinRT::discoverServiceDetails(const QBluetoothUu
//update service data
QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
pointer->setState(QLowEnergyService::DiscoveringServices);
ComPtr<IGattDeviceService2> deviceService2;
@@ -698,6 +684,8 @@ void QLowEnergyControllerPrivateWinRT::readCharacteristic(const QSharedPointer<Q
const QLowEnergyHandle charHandle)
{
qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
Q_ASSERT(!service.isNull());
if (role == QLowEnergyController::PeripheralRole) {
service->setError(QLowEnergyService::CharacteristicReadError);
@@ -763,6 +751,8 @@ void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointer<QLowE
const QLowEnergyHandle descHandle)
{
qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
Q_ASSERT(!service.isNull());
if (role == QLowEnergyController::PeripheralRole) {
service->setError(QLowEnergyService::DescriptorReadError);
@@ -779,7 +769,7 @@ void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointer<QLowE
HRESULT hr;
hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, descHandle, service, this]() {
- QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
+ const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid);
if (!characteristic) {
qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid
@@ -791,12 +781,13 @@ void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointer<QLowE
// Get native descriptor
if (!charData.descriptorList.contains(descHandle))
qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "cannot be found in characteristic" << charHandle;
- QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle);
- if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
+ const QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle);
+ const QBluetoothUuid descUuid = descData.uuid;
+ if (descUuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp;
HRESULT hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp);
Q_ASSERT_SUCCEEDED(hr);
- auto readCompletedLambda = [&charData, charHandle, &descData, descHandle, service]
+ auto readCompletedLambda = [charHandle, descHandle, service]
(IAsyncOperation<ClientCharConfigDescriptorResult *> *op, AsyncStatus status)
{
if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
@@ -837,9 +828,11 @@ void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointer<QLowE
service->setError(QLowEnergyService::DescriptorReadError);
return S_OK;
}
+ QLowEnergyServicePrivate::DescData descData;
+ descData.uuid = QBluetoothUuid::ClientCharacteristicConfiguration;
descData.value = QByteArray(2, Qt::Uninitialized);
qToLittleEndian(result, descData.value.data());
- charData.descriptorList.insert(descHandle, descData);
+ service->characteristicList[charHandle].descriptorList[descHandle] = descData;
emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
descData.value);
return S_OK;
@@ -857,7 +850,7 @@ void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointer<QLowE
ComPtr<IAsyncOperation<GattReadResult*>> readOp;
hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp);
Q_ASSERT_SUCCEEDED(hr);
- auto readCompletedLambda = [&charData, charHandle, &descData, descHandle, service]
+ auto readCompletedLambda = [charHandle, descHandle, descUuid, service]
(IAsyncOperation<GattReadResult*> *op, AsyncStatus status)
{
if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
@@ -873,11 +866,12 @@ void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointer<QLowE
service->setError(QLowEnergyService::DescriptorReadError);
return S_OK;
}
- if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription)
+ QLowEnergyServicePrivate::DescData descData;
+ if (descUuid == QBluetoothUuid::CharacteristicUserDescription)
descData.value = byteArrayFromGattResult(descriptorValue, true);
else
descData.value = byteArrayFromGattResult(descriptorValue);
- charData.descriptorList.insert(descHandle, descData);
+ service->characteristicList[charHandle].descriptorList[descHandle] = descData;
emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
descData.value);
return S_OK;
@@ -895,6 +889,8 @@ void QLowEnergyControllerPrivateWinRT::writeCharacteristic(const QSharedPointer<
QLowEnergyService::WriteMode mode)
{
qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << newValue << mode;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
Q_ASSERT(!service.isNull());
if (role == QLowEnergyController::PeripheralRole) {
service->setError(QLowEnergyService::CharacteristicWriteError);
@@ -985,6 +981,8 @@ void QLowEnergyControllerPrivateWinRT::writeDescriptor(
const QByteArray &newValue)
{
qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle << newValue;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
Q_ASSERT(!service.isNull());
if (role == QLowEnergyController::PeripheralRole) {
service->setError(QLowEnergyService::DescriptorWriteError);
@@ -1128,9 +1126,12 @@ void QLowEnergyControllerPrivateWinRT::addToGenericAttributeList(const QLowEnerg
Q_UNIMPLEMENTED();
}
-void QLowEnergyControllerPrivateWinRT::characteristicChanged(
- int charHandle, const QByteArray &data)
+void QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged(
+ quint16 charHandle, const QByteArray &data)
{
+ qCDebug(QT_BT_WINRT) << __FUNCTION__ << charHandle << data;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
QSharedPointer<QLowEnergyServicePrivate> service =
serviceForHandle(charHandle);
if (service.isNull())
diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp
index 1f70807e..a22064fd 100644
--- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp
+++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp
@@ -39,7 +39,9 @@
#include "qlowenergycontroller_winrt_new_p.h"
#include "qlowenergycontroller_winrt_p.h"
+#include "qbluetoothutils_winrt_p.h"
+#include <QtBluetooth/qbluetoothlocaldevice.h>
#include <QtBluetooth/QLowEnergyCharacteristicData>
#include <QtBluetooth/QLowEnergyDescriptorData>
#include <QtBluetooth/private/qbluetoothutils_winrt_p.h>
@@ -56,6 +58,7 @@
#include <robuffer.h>
#include <windows.devices.enumeration.h>
#include <windows.devices.bluetooth.h>
+#include <windows.devices.bluetooth.genericattributeprofile.h>
#include <windows.foundation.collections.h>
#include <windows.foundation.metadata.h>
#include <windows.storage.streams.h>
@@ -78,7 +81,39 @@ typedef ITypedEventHandler<GattCharacteristic *, GattValueChangedEventArgs *> Va
typedef GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConfigDescriptorResult;
typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharConfigDescriptorResult;
+#define EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, ret) \
+ if (FAILED(hr)) { \
+ emitErrorAndQuitThread(hr); \
+ ret; \
+ }
+
+#define WARN_AND_CONTINUE_IF_FAILED(hr, msg) \
+ if (FAILED(hr)) { \
+ qCWarning(QT_BT_WINRT) << msg; \
+ continue; \
+ }
+
+#define CHECK_FOR_DEVICE_CONNECTION_ERROR_IMPL(this, hr, msg, ret) \
+ if (FAILED(hr)) { \
+ qCWarning(QT_BT_WINRT) << msg; \
+ this->unregisterFromStatusChanges(); \
+ this->setError(QLowEnergyController::ConnectionError); \
+ this->setState(QLowEnergyController::UnconnectedState); \
+ ret; \
+ }
+
+#define CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, msg, ret) \
+ CHECK_FOR_DEVICE_CONNECTION_ERROR_IMPL(this, hr, msg, ret)
+
+#define CHECK_HR_AND_SET_SERVICE_ERROR(hr, msg, service, error, ret) \
+ if (FAILED(hr)) { \
+ qCDebug(QT_BT_WINRT) << msg; \
+ service->setError(error); \
+ ret; \
+ }
+
Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT_SERVICE_THREAD)
QLowEnergyControllerPrivate *createWinRTLowEnergyController()
{
@@ -91,31 +126,16 @@ QLowEnergyControllerPrivate *createWinRTLowEnergyController()
return new QLowEnergyControllerPrivateWinRT();
}
-static QByteArray byteArrayFromBuffer(const ComPtr<IBuffer> &buffer, bool isWCharString = false)
-{
- ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
- HRESULT hr = buffer.As(&byteAccess);
- Q_ASSERT_SUCCEEDED(hr);
- char *data;
- hr = byteAccess->Buffer(reinterpret_cast<byte **>(&data));
- Q_ASSERT_SUCCEEDED(hr);
- UINT32 size;
- hr = buffer->get_Length(&size);
- Q_ASSERT_SUCCEEDED(hr);
- if (isWCharString) {
- QString valueString = QString::fromUtf16(reinterpret_cast<ushort *>(data)).left(size / 2);
- return valueString.toUtf8();
- }
- return QByteArray(data, int(size));
-}
-
static QByteArray byteArrayFromGattResult(const ComPtr<IGattReadResult> &gattResult,
bool isWCharString = false)
{
ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
HRESULT hr;
hr = gattResult->get_Value(&buffer);
- Q_ASSERT_SUCCEEDED(hr);
+ if (FAILED(hr) || !buffer) {
+ qCWarning(QT_BT_WINRT) << "Could not obtain buffer from GattReadResult";
+ return QByteArray();
+ }
return byteArrayFromBuffer(buffer, isWCharString);
}
@@ -124,7 +144,7 @@ class QWinRTLowEnergyServiceHandlerNew : public QObject
Q_OBJECT
public:
QWinRTLowEnergyServiceHandlerNew(const QBluetoothUuid &service,
- const ComPtr<IGattDeviceService2> &deviceService)
+ const ComPtr<IGattDeviceService3> &deviceService)
: mService(service)
, mDeviceService(deviceService)
{
@@ -138,58 +158,47 @@ public:
public slots:
void obtainCharList()
{
- QVector<QBluetoothUuid> indicateChars;
- quint16 startHandle = 0;
- quint16 endHandle = 0;
+ mIndicateChars.clear();
qCDebug(QT_BT_WINRT) << __FUNCTION__;
- ComPtr<IVectorView<GattCharacteristic *>> characteristics;
- HRESULT hr = mDeviceService->GetAllCharacteristics(&characteristics);
- Q_ASSERT_SUCCEEDED(hr);
- if (!characteristics) {
- emit charListObtained(mService, mCharacteristicList, indicateChars, startHandle, endHandle);
- QThread::currentThread()->quit();
+ ComPtr<IAsyncOperation<GattCharacteristicsResult *>> characteristicsOp;
+ ComPtr<IGattCharacteristicsResult> characteristicsResult;
+ HRESULT hr = mDeviceService->GetCharacteristicsAsync(&characteristicsOp);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
+ hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(),
+ QWinRTFunctions::ProcessMainThreadEvents, 5000);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
+ GattCommunicationStatus status;
+ hr = characteristicsResult->get_Status(&status);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
+ if (status != GattCommunicationStatus_Success) {
+ emitErrorAndQuitThread(QLatin1String("Could not obtain char list"));
return;
}
+ ComPtr<IVectorView<GattCharacteristic *>> characteristics;
+ hr = characteristicsResult->get_Characteristics(&characteristics);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
uint characteristicsCount;
hr = characteristics->get_Size(&characteristicsCount);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
- // If there are no characteristics, we assume that the device is not paired (and not
- // discovered by Windows) and we use new API (GetCharacteristicsAsync) to discover them
- // without pairing.
- if (characteristicsCount == 0) {
- ComPtr<IGattDeviceService3> deviceService3;
- hr = mDeviceService.As(&deviceService3);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IAsyncOperation<GattCharacteristicsResult*>> asyncResult;
- deviceService3->GetCharacteristicsAsync(&asyncResult);
- hr = asyncResult->put_Completed(
- Callback<IAsyncOperationCompletedHandler<GattCharacteristicsResult*>>(
- [this](IAsyncOperation<GattCharacteristicsResult*> *, AsyncStatus status) {
- if (status != AsyncStatus::Completed) {
- qCDebug(QT_BT_WINRT) << "Could not obtain characteristics";
- return S_OK;
- }
- // TODO We should check if we found any characteristics. It makes no sense but
- // there is a possibility that device doesn't state any characteristics under a service.
- // So, for sanity, we should not continue endless loop here.
- obtainCharList();
- return S_OK;
- }).Get());
- Q_ASSERT_SUCCEEDED(hr);
- return;
- }
-
- Q_ASSERT_SUCCEEDED(hr);
mCharacteristicsCountToBeDiscovered = characteristicsCount;
for (uint i = 0; i < characteristicsCount; ++i) {
ComPtr<IGattCharacteristic> characteristic;
hr = characteristics->GetAt(i, &characteristic);
- Q_ASSERT_SUCCEEDED(hr);
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not obtain characteristic at" << i;
+ --mCharacteristicsCountToBeDiscovered;
+ continue;
+ }
ComPtr<IGattCharacteristic3> characteristic3;
hr = characteristic.As(&characteristic3);
- Q_ASSERT_SUCCEEDED(hr);
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not cast characteristic";
+ --mCharacteristicsCountToBeDiscovered;
+ continue;
+ }
// For some strange reason, Windows doesn't discover descriptors of characteristics (if not paired).
// Qt API assumes that all characteristics and their descriptors are discovered in one go.
@@ -197,157 +206,246 @@ public slots:
// when GetDescriptorsAsync for all characteristics return.
ComPtr<IAsyncOperation<GattDescriptorsResult*>> descAsyncResult;
hr = characteristic3->GetDescriptorsAsync(&descAsyncResult);
- Q_ASSERT_SUCCEEDED(hr);
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors";
+ --mCharacteristicsCountToBeDiscovered;
+ continue;
+ }
hr = descAsyncResult->put_Completed(
- Callback<IAsyncOperationCompletedHandler<GattDescriptorsResult*>>(
- [this, characteristic](IAsyncOperation<GattDescriptorsResult*> *, AsyncStatus status) {
- if (status != AsyncStatus::Completed) {
- qCDebug(QT_BT_WINRT) << "Could not obtain descriptors";
+ Callback<IAsyncOperationCompletedHandler<GattDescriptorsResult*>>(
+ [this, characteristic]
+ (IAsyncOperation<GattDescriptorsResult *> *op,
+ AsyncStatus status) {
+ if (status != AsyncStatus::Completed) {
+ qCWarning(QT_BT_WINRT) << "Descriptor operation unsuccessful";
+ --mCharacteristicsCountToBeDiscovered;
+ checkAllCharacteristicsDiscovered();
+ return S_OK;
+ }
+ quint16 handle;
+
+ HRESULT hr = characteristic->get_AttributeHandle(&handle);
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's attribute handle";
+ --mCharacteristicsCountToBeDiscovered;
+ checkAllCharacteristicsDiscovered();
+ return S_OK;
+ }
+ QLowEnergyServicePrivate::CharData charData;
+ charData.valueHandle = handle + 1;
+ if (mStartHandle == 0 || mStartHandle > handle)
+ mStartHandle = handle;
+ if (mEndHandle == 0 || mEndHandle < handle)
+ mEndHandle = handle;
+ GUID guuid;
+ hr = characteristic->get_Uuid(&guuid);
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's Uuid";
+ --mCharacteristicsCountToBeDiscovered;
+ checkAllCharacteristicsDiscovered();
+ return S_OK;
+ }
+ charData.uuid = QBluetoothUuid(guuid);
+ GattCharacteristicProperties properties;
+ hr = characteristic->get_CharacteristicProperties(&properties);
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's properties";
+ --mCharacteristicsCountToBeDiscovered;
+ checkAllCharacteristicsDiscovered();
+ return S_OK;
+ }
+ charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties & 0xff);
+ if (charData.properties & QLowEnergyCharacteristic::Read) {
+ ComPtr<IAsyncOperation<GattReadResult *>> readOp;
+ hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached,
+ &readOp);
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not read characteristic";
+ --mCharacteristicsCountToBeDiscovered;
+ checkAllCharacteristicsDiscovered();
return S_OK;
}
- quint16 handle;
-
- HRESULT hr = characteristic->get_AttributeHandle(&handle);
- Q_ASSERT_SUCCEEDED(hr);
- QLowEnergyServicePrivate::CharData charData;
- charData.valueHandle = handle + 1;
- if (mStartHandle == 0 || mStartHandle > handle)
- mStartHandle = handle;
- if (mEndHandle == 0 || mEndHandle < handle)
- mEndHandle = handle;
- GUID guuid;
- hr = characteristic->get_Uuid(&guuid);
- Q_ASSERT_SUCCEEDED(hr);
- charData.uuid = QBluetoothUuid(guuid);
- GattCharacteristicProperties properties;
- hr = characteristic->get_CharacteristicProperties(&properties);
- Q_ASSERT_SUCCEEDED(hr);
- charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties & 0xff);
- if (charData.properties & QLowEnergyCharacteristic::Read) {
- ComPtr<IAsyncOperation<GattReadResult *>> readOp;
- hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached,
- &readOp);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IGattReadResult> readResult;
- hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- if (readResult)
- charData.value = byteArrayFromGattResult(readResult);
+ ComPtr<IGattReadResult> readResult;
+ hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not obtain characteristic read result";
+ --mCharacteristicsCountToBeDiscovered;
+ checkAllCharacteristicsDiscovered();
+ return S_OK;
}
+ if (!readResult)
+ qCWarning(QT_BT_WINRT) << "Characteristic read result is null";
+ else
+ charData.value = byteArrayFromGattResult(readResult);
+ }
+ mCharacteristicList.insert(handle, charData);
- QVector<QBluetoothUuid> indicateChars;
- ComPtr<IVectorView<GattDescriptor *>> descriptors;
-
- ComPtr<IGattCharacteristic2> characteristic2;
- hr = characteristic.As(&characteristic2);
- Q_ASSERT_SUCCEEDED(hr);
-
- hr = characteristic2->GetAllDescriptors(&descriptors);
- Q_ASSERT_SUCCEEDED(hr);
-
-
- uint descriptorCount;
- hr = descriptors->get_Size(&descriptorCount);
- Q_ASSERT_SUCCEEDED(hr);
- for (uint j = 0; j < descriptorCount; ++j) {
- QLowEnergyServicePrivate::DescData descData;
- ComPtr<IGattDescriptor> descriptor;
- hr = descriptors->GetAt(j, &descriptor);
- Q_ASSERT_SUCCEEDED(hr);
- quint16 descHandle;
- hr = descriptor->get_AttributeHandle(&descHandle);
- Q_ASSERT_SUCCEEDED(hr);
- GUID descriptorUuid;
- hr = descriptor->get_Uuid(&descriptorUuid);
- Q_ASSERT_SUCCEEDED(hr);
- descData.uuid = QBluetoothUuid(descriptorUuid);
- if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
- ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp;
- hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IClientCharConfigDescriptorResult> readResult;
- hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- GattClientCharacteristicConfigurationDescriptorValue value;
- hr = readResult->get_ClientCharacteristicConfigurationDescriptor(&value);
- Q_ASSERT_SUCCEEDED(hr);
- quint16 result = 0;
- bool correct = false;
- if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
- result |= GattClientCharacteristicConfigurationDescriptorValue_Indicate;
- correct = true;
- }
- if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
- result |= GattClientCharacteristicConfigurationDescriptorValue_Notify;
- correct = true;
- }
- if (value == GattClientCharacteristicConfigurationDescriptorValue_None) {
- correct = true;
- }
- if (!correct)
- continue;
-
- descData.value = QByteArray(2, Qt::Uninitialized);
- qToLittleEndian(result, descData.value.data());
- indicateChars << charData.uuid;
- } else {
- ComPtr<IAsyncOperation<GattReadResult *>> readOp;
- hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached,
- &readOp);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IGattReadResult> readResult;
- hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription)
- descData.value = byteArrayFromGattResult(readResult, true);
- else
- descData.value = byteArrayFromGattResult(readResult);
+ ComPtr<IVectorView<GattDescriptor *>> descriptors;
+
+ ComPtr<IGattDescriptorsResult> result;
+ hr = op->GetResults(&result);
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not obtain descriptor read result";
+ --mCharacteristicsCountToBeDiscovered;
+ checkAllCharacteristicsDiscovered();
+ return S_OK;
+ }
+ GattCommunicationStatus commStatus;
+ hr = result->get_Status(&commStatus);
+ if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
+ qCWarning(QT_BT_WINRT) << "Descriptor operation failed";
+ --mCharacteristicsCountToBeDiscovered;
+ checkAllCharacteristicsDiscovered();
+ return S_OK;
+ }
+
+ hr = result->get_Descriptors(&descriptors);
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors";
+ --mCharacteristicsCountToBeDiscovered;
+ checkAllCharacteristicsDiscovered();
+ return S_OK;
+ }
+
+ uint descriptorCount;
+ hr = descriptors->get_Size(&descriptorCount);
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors' size";
+ --mCharacteristicsCountToBeDiscovered;
+ checkAllCharacteristicsDiscovered();
+ return S_OK;
+ }
+ for (uint j = 0; j < descriptorCount; ++j) {
+ QLowEnergyServicePrivate::DescData descData;
+ ComPtr<IGattDescriptor> descriptor;
+ hr = descriptors->GetAt(j, &descriptor);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor")
+ quint16 descHandle;
+ hr = descriptor->get_AttributeHandle(&descHandle);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor's attribute handle")
+ GUID descriptorUuid;
+ hr = descriptor->get_Uuid(&descriptorUuid);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor's Uuid")
+ descData.uuid = QBluetoothUuid(descriptorUuid);
+ charData.descriptorList.insert(descHandle, descData);
+ if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
+ ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp;
+ hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read descriptor value")
+ ComPtr<IClientCharConfigDescriptorResult> readResult;
+ hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not await descriptor read result")
+ GattClientCharacteristicConfigurationDescriptorValue value;
+ hr = readResult->get_ClientCharacteristicConfigurationDescriptor(&value);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not get descriptor value from result")
+ quint16 result = 0;
+ bool correct = false;
+ if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
+ result |= GattClientCharacteristicConfigurationDescriptorValue_Indicate;
+ correct = true;
}
- charData.descriptorList.insert(descHandle, descData);
- }
+ if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
+ result |= GattClientCharacteristicConfigurationDescriptorValue_Notify;
+ correct = true;
+ }
+ if (value == GattClientCharacteristicConfigurationDescriptorValue_None) {
+ correct = true;
+ }
+ if (!correct)
+ continue;
- mCharacteristicList.insert(handle, charData);
- mCharacteristicsCountToBeDiscovered--;
- if (mCharacteristicsCountToBeDiscovered == 0) {
- emit charListObtained(mService, mCharacteristicList, indicateChars,
- mStartHandle, mEndHandle);
- QThread::currentThread()->quit();
+ descData.value = QByteArray(2, Qt::Uninitialized);
+ qToLittleEndian(result, descData.value.data());
+ mIndicateChars << charData.uuid;
+ } else {
+ ComPtr<IAsyncOperation<GattReadResult *>> readOp;
+ hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached,
+ &readOp);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read descriptor value")
+ ComPtr<IGattReadResult> readResult;
+ hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could await descriptor read result")
+ if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription)
+ descData.value = byteArrayFromGattResult(readResult, true);
+ else
+ descData.value = byteArrayFromGattResult(readResult);
}
- return S_OK;
- }).Get());
- Q_ASSERT_SUCCEEDED(hr);
+ charData.descriptorList.insert(descHandle, descData);
+ }
+
+ mCharacteristicList.insert(handle, charData);
+ --mCharacteristicsCountToBeDiscovered;
+ checkAllCharacteristicsDiscovered();
+ return S_OK;
+ }).Get());
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << "Could not register descriptor callback";
+ --mCharacteristicsCountToBeDiscovered;
+ continue;
+ }
}
+ checkAllCharacteristicsDiscovered();
}
+private:
+ bool checkAllCharacteristicsDiscovered();
+ void emitErrorAndQuitThread(HRESULT hr);
+ void emitErrorAndQuitThread(const QString &error);
+
public:
QBluetoothUuid mService;
- ComPtr<IGattDeviceService2> mDeviceService;
+ ComPtr<IGattDeviceService3> mDeviceService;
QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> mCharacteristicList;
uint mCharacteristicsCountToBeDiscovered;
quint16 mStartHandle = 0;
quint16 mEndHandle = 0;
+ QVector<QBluetoothUuid> mIndicateChars;
signals:
void charListObtained(const QBluetoothUuid &service, QHash<QLowEnergyHandle,
QLowEnergyServicePrivate::CharData> charList,
QVector<QBluetoothUuid> indicateChars,
QLowEnergyHandle startHandle, QLowEnergyHandle endHandle);
+ void errorOccured(const QString &error);
};
+bool QWinRTLowEnergyServiceHandlerNew::checkAllCharacteristicsDiscovered()
+{
+ if (mCharacteristicsCountToBeDiscovered == 0) {
+ emit charListObtained(mService, mCharacteristicList, mIndicateChars,
+ mStartHandle, mEndHandle);
+ QThread::currentThread()->quit();
+ return true;
+ }
+
+ return false;
+}
+
+void QWinRTLowEnergyServiceHandlerNew::emitErrorAndQuitThread(HRESULT hr)
+{
+ emitErrorAndQuitThread(qt_error_string(hr));
+}
+
+void QWinRTLowEnergyServiceHandlerNew::emitErrorAndQuitThread(const QString &error)
+{
+ emit errorOccured(error);
+ QThread::currentThread()->quit();
+}
+
QLowEnergyControllerPrivateWinRTNew::QLowEnergyControllerPrivateWinRTNew()
: QLowEnergyControllerPrivate()
{
registerQLowEnergyControllerMetaType();
+ connect(this, &QLowEnergyControllerPrivateWinRTNew::characteristicChanged,
+ this, &QLowEnergyControllerPrivateWinRTNew::handleCharacteristicChanged,
+ Qt::QueuedConnection);
}
QLowEnergyControllerPrivateWinRTNew::~QLowEnergyControllerPrivateWinRTNew()
{
- if (mDevice && mStatusChangedToken.value)
- mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
-
- qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens";
- for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens))
- entry.characteristic->remove_ValueChanged(entry.token);
+ unregisterFromStatusChanges();
+ unregisterFromValueChanges();
+ mAbortPending = true;
}
void QLowEnergyControllerPrivateWinRTNew::init()
@@ -357,6 +455,7 @@ void QLowEnergyControllerPrivateWinRTNew::init()
void QLowEnergyControllerPrivateWinRTNew::connectToDevice()
{
qCDebug(QT_BT_WINRT) << __FUNCTION__;
+ mAbortPending = false;
Q_Q(QLowEnergyController);
if (remoteDevice.isNull()) {
qWarning() << "Invalid/null remote device address";
@@ -370,162 +469,46 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice()
HRESULT hr = GetActivationFactory(
HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(),
&deviceStatics);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain device factory", return)
ComPtr<IAsyncOperation<BluetoothLEDevice *>> deviceFromIdOperation;
hr = deviceStatics->FromBluetoothAddressAsync(remoteDevice.toUInt64(), &deviceFromIdOperation);
- Q_ASSERT_SUCCEEDED(hr);
- hr = QWinRTFunctions::await(deviceFromIdOperation, mDevice.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
-
- if (!mDevice) {
- qCDebug(QT_BT_WINRT) << "Could not find LE device";
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not find LE device from address", return)
+ hr = QWinRTFunctions::await(deviceFromIdOperation, mDevice.GetAddressOf(),
+ QWinRTFunctions::ProcessMainThreadEvents, 5000);
+ if (FAILED(hr) || !mDevice) {
+ qCWarning(QT_BT_WINRT) << "Could not find LE device";
setError(QLowEnergyController::InvalidBluetoothAdapterError);
setState(QLowEnergyController::UnconnectedState);
+ return;
}
BluetoothConnectionStatus status;
hr = mDevice->get_ConnectionStatus(&status);
- Q_ASSERT_SUCCEEDED(hr);
- hr = QEventDispatcherWinRT::runOnXamlThread([this, q]() {
- HRESULT hr;
- hr = mDevice->add_ConnectionStatusChanged(
- Callback<StatusHandler>([this, q](IBluetoothLEDevice *dev, IInspectable *) {
- BluetoothConnectionStatus status;
- HRESULT hr;
- hr = dev->get_ConnectionStatus(&status);
- Q_ASSERT_SUCCEEDED(hr);
- if (state == QLowEnergyController::ConnectingState
- && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
- setState(QLowEnergyController::ConnectedState);
- emit q->connected();
- } else if (state == QLowEnergyController::ConnectedState
- && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) {
- setError(QLowEnergyController::RemoteHostClosedError);
- setState(QLowEnergyController::UnconnectedState);
- emit q->disconnected();
- }
- return S_OK;
- }).Get(), &mStatusChangedToken);
- Q_ASSERT_SUCCEEDED(hr);
- return S_OK;
- });
- Q_ASSERT_SUCCEEDED(hr);
-
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain device's connection status", return)
if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
setState(QLowEnergyController::ConnectedState);
emit q->connected();
return;
}
- ComPtr<IVectorView <GattDeviceService *>> deviceServices;
- hr = mDevice->get_GattServices(&deviceServices);
- Q_ASSERT_SUCCEEDED(hr);
- uint serviceCount;
- hr = deviceServices->get_Size(&serviceCount);
- Q_ASSERT_SUCCEEDED(hr);
-
- // Windows doesn't provide any explicit connect/reconnect. We need to 'start using' the device
- // and windows will initiate connection as a cause of that.
- if (serviceCount == 0) {
- // If we don't have any services discovered yet (for devices not paired), the simplest
- // way to initiate connect is to start discovering services. It's not exactly how Qt API
- // expects it to be but IMHO doesn't do any harm either. Services will already be discovered
- // when coonnection state changes to 'connected'.
- ComPtr<IBluetoothLEDevice3> device3;
- hr = mDevice.As(&device3);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *>> asyncResult;
- hr = device3->GetGattServicesAsync(&asyncResult);
- Q_ASSERT_SUCCEEDED(hr);
- hr = asyncResult->put_Completed(
- Callback<IAsyncOperationCompletedHandler<GenericAttributeProfile::GattDeviceServicesResult *>>(
- [this, q](IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *> *, AsyncStatus status) {
- if (status != AsyncStatus::Completed) {
- qCDebug(QT_BT_WINRT) << "Could not obtain services";
- return S_OK;
- }
- setState(QLowEnergyController::ConnectedState);
- emit q->connected();
- return S_OK;
- }).Get());
- Q_ASSERT_SUCCEEDED(hr);
- } else {
- // Windows Phone automatically connects to the device as soon as a service value is read/written.
- // Thus we read one value in order to establish the connection.
- for (uint i = 0; i < serviceCount; ++i) {
- ComPtr<IGattDeviceService> service;
- hr = deviceServices->GetAt(i, &service);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IGattDeviceService2> service2;
- hr = service.As(&service2);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IVectorView<GattCharacteristic *>> characteristics;
- hr = service2->GetAllCharacteristics(&characteristics);
- if (hr == E_ACCESSDENIED) {
- // Everything will work as expected up until this point if the manifest capabilties
- // for bluetooth LE are not set.
- qCWarning(QT_BT_WINRT) << "Could not obtain characteristic list. Please check your "
- "manifest capabilities";
- setState(QLowEnergyController::UnconnectedState);
- setError(QLowEnergyController::ConnectionError);
- return;
- } else if (FAILED(hr)) {
- qCWarning(QT_BT_WINRT) << "Connecting to device failed: "
- << qt_error_string(hr);
- setError(QLowEnergyController::ConnectionError);
- setState(QLowEnergyController::UnconnectedState);
- return;
- }
- uint characteristicsCount;
- hr = characteristics->get_Size(&characteristicsCount);
- Q_ASSERT_SUCCEEDED(hr);
- for (uint j = 0; j < characteristicsCount; ++j) {
- ComPtr<IGattCharacteristic> characteristic;
- hr = characteristics->GetAt(j, &characteristic);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IAsyncOperation<GattReadResult *>> op;
- GattCharacteristicProperties props;
- hr = characteristic->get_CharacteristicProperties(&props);
- Q_ASSERT_SUCCEEDED(hr);
- if (!(props & GattCharacteristicProperties_Read))
- continue;
- hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IGattReadResult> result;
- hr = QWinRTFunctions::await(op, result.GetAddressOf());
- if (hr == E_INVALIDARG) {
- // E_INVALIDARG happens when user tries to connect to a device that was paired
- // before but is not available.
- qCDebug(QT_BT_WINRT) << "Could not obtain characteristic read result that triggers"
- "device connection. Is the device reachable?";
- setError(QLowEnergyController::ConnectionError);
- setState(QLowEnergyController::UnconnectedState);
- return;
- }
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
- hr = result->get_Value(&buffer);
- Q_ASSERT_SUCCEEDED(hr);
- if (!buffer) {
- qCDebug(QT_BT_WINRT) << "Problem reading value";
- setError(QLowEnergyController::ConnectionError);
- setState(QLowEnergyController::UnconnectedState);
- }
- return;
- }
- }
- }
+ QBluetoothLocalDevice localDevice;
+ QBluetoothLocalDevice::Pairing pairing = localDevice.pairingStatus(remoteDevice);
+ if (pairing == QBluetoothLocalDevice::Unpaired)
+ connectToUnpairedDevice();
+ else
+ connectToPairedDevice();
}
void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice()
{
qCDebug(QT_BT_WINRT) << __FUNCTION__;
Q_Q(QLowEnergyController);
+ setState(QLowEnergyController::ClosingState);
+ unregisterFromValueChanges();
+ unregisterFromStatusChanges();
+ mAbortPending = true;
+ mDevice = nullptr;
setState(QLowEnergyController::UnconnectedState);
emit q->disconnected();
- if (mDevice && mStatusChangedToken.value) {
- mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
- mStatusChangedToken.value = 0;
- }
}
ComPtr<IGattDeviceService> QLowEnergyControllerPrivateWinRTNew::getNativeService(
@@ -546,9 +529,30 @@ ComPtr<IGattCharacteristic> QLowEnergyControllerPrivateWinRTNew::getNativeCharac
if (!service)
return nullptr;
- ComPtr<IVectorView<GattCharacteristic *>> characteristics;
- HRESULT hr = service->GetCharacteristics(charUuid, &characteristics);
+ ComPtr<IGattDeviceService3> service3;
+ HRESULT hr = service.As(&service3);
+ RETURN_IF_FAILED("Could not cast service", return nullptr);
+
+ ComPtr<IAsyncOperation<GattCharacteristicsResult *>> op;
+ ComPtr<IGattCharacteristicsResult> result;
+ hr = service3->GetCharacteristicsForUuidAsync(charUuid, &op);
RETURN_IF_FAILED("Could not obtain native characteristics for service", return nullptr);
+ hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000);
+ RETURN_IF_FAILED("Could not await completion of characteristic operation", return nullptr);
+ GattCommunicationStatus status;
+ hr = result->get_Status(&status);
+ if (FAILED(hr) || status != GattCommunicationStatus_Success) {
+ qErrnoWarning(hr, "Native characteristic operation failed.");
+ return nullptr;
+ }
+ ComPtr<IVectorView<GattCharacteristic *>> characteristics;
+ hr = result->get_Characteristics(&characteristics);
+ RETURN_IF_FAILED("Could not obtain characteristic list.", return nullptr);
+ uint size;
+ hr = characteristics->get_Size(&size);
+ RETURN_IF_FAILED("Could not obtain characteristic list's size.", return nullptr);
+ if (size != 1)
+ qErrnoWarning("More than 1 characteristic found.");
ComPtr<IGattCharacteristic> characteristic;
hr = characteristics->GetAt(0, &characteristic);
RETURN_IF_FAILED("Could not obtain first characteristic for service", return nullptr);
@@ -564,59 +568,152 @@ void QLowEnergyControllerPrivateWinRTNew::registerForValueChanges(const QBluetoo
GUID guuid;
HRESULT hr;
hr = entry.characteristic->get_Uuid(&guuid);
- Q_ASSERT_SUCCEEDED(hr);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic's Uuid")
if (QBluetoothUuid(guuid) == charUuid)
return;
}
ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(serviceUuid, charUuid);
+ if (!characteristic) {
+ qCDebug(QT_BT_WINRT).nospace() << "Could not obtain native characteristic " << charUuid
+ << " from service " << serviceUuid << ". Qt will not be able to signal"
+ << " changes for this characteristic.";
+ return;
+ }
EventRegistrationToken token;
HRESULT hr;
hr = characteristic->add_ValueChanged(
- Callback<ValueChangedHandler>(
- [this](IGattCharacteristic *characteristic, IGattValueChangedEventArgs *args) {
- HRESULT hr;
- quint16 handle;
- hr = characteristic->get_AttributeHandle(&handle);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IBuffer> buffer;
- hr = args->get_CharacteristicValue(&buffer);
- Q_ASSERT_SUCCEEDED(hr);
- characteristicChanged(handle, byteArrayFromBuffer(buffer));
- return S_OK;
- }).Get(), &token);
- Q_ASSERT_SUCCEEDED(hr);
+ Callback<ValueChangedHandler>(this, &QLowEnergyControllerPrivateWinRTNew::onValueChange).Get(),
+ &token);
+ RETURN_IF_FAILED("Could not register characteristic for value changes", return)
mValueChangedTokens.append(ValueChangedEntry(characteristic, token));
qCDebug(QT_BT_WINRT) << "Characteristic" << charUuid << "in service"
<< serviceUuid << "registered for value changes";
}
+void QLowEnergyControllerPrivateWinRTNew::unregisterFromValueChanges()
+{
+ qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens";
+ HRESULT hr;
+ for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) {
+ if (!entry.characteristic) {
+ qCWarning(QT_BT_WINRT) << "Unregistering from value changes for characteristic failed."
+ << "Characteristic has been deleted";
+ continue;
+ }
+ hr = entry.characteristic->remove_ValueChanged(entry.token);
+ if (FAILED(hr))
+ qCWarning(QT_BT_WINRT) << "Unregistering from value changes for characteristic failed.";
+ }
+ mValueChangedTokens.clear();
+}
+
+HRESULT QLowEnergyControllerPrivateWinRTNew::onValueChange(IGattCharacteristic *characteristic, IGattValueChangedEventArgs *args)
+{
+ HRESULT hr;
+ quint16 handle;
+ hr = characteristic->get_AttributeHandle(&handle);
+ RETURN_IF_FAILED("Could not obtain characteristic's handle", return S_OK)
+ ComPtr<IBuffer> buffer;
+ hr = args->get_CharacteristicValue(&buffer);
+ RETURN_IF_FAILED("Could not obtain characteristic's value", return S_OK)
+ emit characteristicChanged(handle, byteArrayFromBuffer(buffer));
+ return S_OK;
+}
+
+bool QLowEnergyControllerPrivateWinRTNew::registerForStatusChanges()
+{
+ if (!mDevice)
+ return false;
+
+ qCDebug(QT_BT_WINRT) << __FUNCTION__;
+
+ HRESULT hr;
+ hr = QEventDispatcherWinRT::runOnXamlThread([this]() {
+ HRESULT hr;
+ hr = mDevice->add_ConnectionStatusChanged(
+ Callback<StatusHandler>(this, &QLowEnergyControllerPrivateWinRTNew::onStatusChange).Get(),
+ &mStatusChangedToken);
+ RETURN_IF_FAILED("Could not register connection status callback", return hr)
+ return S_OK;
+ });
+ RETURN_FALSE_IF_FAILED("Could not add status callback on Xaml thread")
+ return true;
+}
+
+void QLowEnergyControllerPrivateWinRTNew::unregisterFromStatusChanges()
+{
+ qCDebug(QT_BT_WINRT) << __FUNCTION__;
+ if (mDevice && mStatusChangedToken.value) {
+ mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
+ mStatusChangedToken.value = 0;
+ }
+}
+
+HRESULT QLowEnergyControllerPrivateWinRTNew::onStatusChange(IBluetoothLEDevice *dev, IInspectable *)
+{
+ Q_Q(QLowEnergyController);
+ BluetoothConnectionStatus status;
+ HRESULT hr;
+ hr = dev->get_ConnectionStatus(&status);
+ RETURN_IF_FAILED("Could not obtain connection status", return S_OK)
+ if (state == QLowEnergyController::ConnectingState
+ && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
+ setState(QLowEnergyController::ConnectedState);
+ emit q->connected();
+ } else if (state != QLowEnergyController::UnconnectedState
+ && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) {
+ invalidateServices();
+ unregisterFromValueChanges();
+ unregisterFromStatusChanges();
+ mDevice = nullptr;
+ setError(QLowEnergyController::RemoteHostClosedError);
+ setState(QLowEnergyController::UnconnectedState);
+ emit q->disconnected();
+ }
+ return S_OK;
+}
+
void QLowEnergyControllerPrivateWinRTNew::obtainIncludedServices(
QSharedPointer<QLowEnergyServicePrivate> servicePointer,
ComPtr<IGattDeviceService> service)
{
Q_Q(QLowEnergyController);
- ComPtr<IGattDeviceService2> service2;
- HRESULT hr = service.As(&service2);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IVectorView<GattDeviceService *>> includedServices;
- hr = service2->GetAllIncludedServices(&includedServices);
+ ComPtr<IGattDeviceService3> service3;
+ HRESULT hr = service.As(&service3);
+ RETURN_IF_FAILED("Could not cast service", return);
+ ComPtr<IAsyncOperation<GattDeviceServicesResult *>> op;
+ hr = service3->GetIncludedServicesAsync(&op);
// Some devices return ERROR_ACCESS_DISABLED_BY_POLICY
- if (FAILED(hr))
+ RETURN_IF_FAILED("Could not obtain included services", return);
+ ComPtr<IGattDeviceServicesResult> result;
+ hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000);
+ RETURN_IF_FAILED("Could not await service operation", return);
+ GattCommunicationStatus status;
+ hr = result->get_Status(&status);
+ if (FAILED(hr) || status != GattCommunicationStatus_Success) {
+ qErrnoWarning("Could not obtain list of included services");
return;
+ }
+ ComPtr<IVectorView<GattDeviceService *>> includedServices;
+ hr = result->get_Services(&includedServices);
+ RETURN_IF_FAILED("Could not obtain service list", return);
uint count;
hr = includedServices->get_Size(&count);
- Q_ASSERT_SUCCEEDED(hr);
+ RETURN_IF_FAILED("Could not obtain service list's size", return);
for (uint i = 0; i < count; ++i) {
ComPtr<IGattDeviceService> includedService;
hr = includedServices->GetAt(i, &includedService);
- Q_ASSERT_SUCCEEDED(hr);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service from list");
GUID guuid;
hr = includedService->get_Uuid(&guuid);
- Q_ASSERT_SUCCEEDED(hr);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain included service's Uuid");
const QBluetoothUuid includedUuid(guuid);
QSharedPointer<QLowEnergyServicePrivate> includedPointer;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__
+ << "Changing service pointer from thread"
+ << QThread::currentThread();
if (serviceList.contains(includedUuid)) {
includedPointer = serviceList.value(includedUuid);
} else {
@@ -636,68 +733,89 @@ void QLowEnergyControllerPrivateWinRTNew::obtainIncludedServices(
}
}
-void QLowEnergyControllerPrivateWinRTNew::discoverServices()
+HRESULT QLowEnergyControllerPrivateWinRTNew::onServiceDiscoveryFinished(ABI::Windows::Foundation::IAsyncOperation<GattDeviceServicesResult *> *op, AsyncStatus status)
{
Q_Q(QLowEnergyController);
+ if (status != AsyncStatus::Completed) {
+ qCDebug(QT_BT_WINRT) << "Could not obtain services";
+ return S_OK;
+ }
+ ComPtr<IGattDeviceServicesResult> result;
+ ComPtr<IVectorView<GattDeviceService *>> deviceServices;
+ HRESULT hr = op->GetResults(&result);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery result",
+ return S_OK);
+ GattCommunicationStatus commStatus;
+ hr = result->get_Status(&commStatus);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery status",
+ return S_OK);
+ if (commStatus != GattCommunicationStatus_Success)
+ return S_OK;
+
+ hr = result->get_Services(&deviceServices);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list",
+ return S_OK);
+
+ uint serviceCount;
+ hr = deviceServices->get_Size(&serviceCount);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list size",
+ return S_OK);
+ for (uint i = 0; i < serviceCount; ++i) {
+ ComPtr<IGattDeviceService> deviceService;
+ hr = deviceServices->GetAt(i, &deviceService);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service");
+ GUID guuid;
+ hr = deviceService->get_Uuid(&guuid);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service's Uuid");
+ const QBluetoothUuid service(guuid);
+
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__
+ << "Changing service pointer from thread"
+ << QThread::currentThread();
+ QSharedPointer<QLowEnergyServicePrivate> pointer;
+ if (serviceList.contains(service)) {
+ pointer = serviceList.value(service);
+ } else {
+ QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate();
+ priv->uuid = service;
+ priv->setController(this);
+
+ pointer = QSharedPointer<QLowEnergyServicePrivate>(priv);
+ serviceList.insert(service, pointer);
+ }
+ pointer->type |= QLowEnergyService::PrimaryService;
+ obtainIncludedServices(pointer, deviceService);
+
+ emit q->serviceDiscovered(service);
+ }
+
+ setState(QLowEnergyController::DiscoveredState);
+ emit q->discoveryFinished();
+
+ return S_OK;
+}
+
+void QLowEnergyControllerPrivateWinRTNew::discoverServices()
+{
qCDebug(QT_BT_WINRT) << "Service discovery initiated";
ComPtr<IBluetoothLEDevice3> device3;
HRESULT hr = mDevice.As(&device3);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return);
ComPtr<IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *>> asyncResult;
hr = device3->GetGattServicesAsync(&asyncResult);
- Q_ASSERT_SUCCEEDED(hr);
- hr = QEventDispatcherWinRT::runOnXamlThread( [asyncResult, q, this] () {
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return);
+ hr = QEventDispatcherWinRT::runOnXamlThread( [asyncResult, this] () {
HRESULT hr = asyncResult->put_Completed(
Callback<IAsyncOperationCompletedHandler<GenericAttributeProfile::GattDeviceServicesResult *>>(
- [this, q](IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *> *, AsyncStatus status) {
- if (status != AsyncStatus::Completed) {
- qCDebug(QT_BT_WINRT) << "Could not obtain services";
- return S_OK;
- }
- ComPtr<IVectorView<GattDeviceService *>> deviceServices;
- HRESULT hr = mDevice->get_GattServices(&deviceServices);
- Q_ASSERT_SUCCEEDED(hr);
- uint serviceCount;
- hr = deviceServices->get_Size(&serviceCount);
- Q_ASSERT_SUCCEEDED(hr);
- for (uint i = 0; i < serviceCount; ++i) {
- ComPtr<IGattDeviceService> deviceService;
- hr = deviceServices->GetAt(i, &deviceService);
- Q_ASSERT_SUCCEEDED(hr);
- GUID guuid;
- hr = deviceService->get_Uuid(&guuid);
- Q_ASSERT_SUCCEEDED(hr);
- const QBluetoothUuid service(guuid);
-
- QSharedPointer<QLowEnergyServicePrivate> pointer;
- if (serviceList.contains(service)) {
- pointer = serviceList.value(service);
- } else {
- QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate();
- priv->uuid = service;
- priv->setController(this);
-
- pointer = QSharedPointer<QLowEnergyServicePrivate>(priv);
- serviceList.insert(service, pointer);
- }
- pointer->type |= QLowEnergyService::PrimaryService;
-
- obtainIncludedServices(pointer, deviceService);
-
- emit q->serviceDiscovered(service);
- }
-
- setState(QLowEnergyController::DiscoveredState);
- emit q->discoveryFinished();
-
- return S_OK;
- }).Get());
- Q_ASSERT_SUCCEEDED(hr);
+ this, &QLowEnergyControllerPrivateWinRTNew::onServiceDiscoveryFinished).Get());
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not register service discovery callback",
+ return S_OK)
return hr;
});
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not run registration in Xaml thread",
+ return)
}
void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoothUuid &service)
@@ -717,34 +835,48 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoot
//update service data
QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
-
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
pointer->setState(QLowEnergyService::DiscoveringServices);
- ComPtr<IGattDeviceService2> deviceService2;
- HRESULT hr = deviceService.As(&deviceService2);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IVectorView<GattDeviceService *>> deviceServices;
- hr = deviceService2->GetAllIncludedServices(&deviceServices);
- if (FAILED(hr)) { // ERROR_ACCESS_DISABLED_BY_POLICY
- qCDebug(QT_BT_WINRT) << "Could not obtain included services list for" << service;
+ ComPtr<IGattDeviceService3> deviceService3;
+ HRESULT hr = deviceService.As(&deviceService3);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast service",
+ pointer, QLowEnergyService::UnknownError, return)
+ ComPtr<IAsyncOperation<GattDeviceServicesResult *>> op;
+ hr = deviceService3->GetIncludedServicesAsync(&op);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain included service list",
+ pointer, QLowEnergyService::UnknownError, return)
+ ComPtr<IGattDeviceServicesResult> result;
+ hr = QWinRTFunctions::await(op, result.GetAddressOf());
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await service operation",
+ pointer, QLowEnergyService::UnknownError, return)
+ GattCommunicationStatus status;
+ hr = result->get_Status(&status);
+ if (FAILED(hr) || status != GattCommunicationStatus_Success) {
+ qCDebug(QT_BT_WINRT) << "Obtaining list of included services failed";
pointer->setError(QLowEnergyService::UnknownError);
- pointer->setState(QLowEnergyService::InvalidService);
return;
}
+ ComPtr<IVectorView<GattDeviceService *>> deviceServices;
+ hr = result->get_Services(&deviceServices);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain service list from result",
+ pointer, QLowEnergyService::UnknownError, return)
uint serviceCount;
hr = deviceServices->get_Size(&serviceCount);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain included service list's size",
+ pointer, QLowEnergyService::UnknownError, return)
for (uint i = 0; i < serviceCount; ++i) {
ComPtr<IGattDeviceService> includedService;
hr = deviceServices->GetAt(i, &includedService);
- Q_ASSERT_SUCCEEDED(hr);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service from list")
GUID guuid;
hr = includedService->get_Uuid(&guuid);
- Q_ASSERT_SUCCEEDED(hr);
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service Uuid")
const QBluetoothUuid service(guuid);
if (service.isNull()) {
qCDebug(QT_BT_WINRT) << "Could not find service";
- return;
+ continue;
}
pointer->includedServices.append(service);
@@ -756,12 +888,14 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoot
}
QWinRTLowEnergyServiceHandlerNew *worker
- = new QWinRTLowEnergyServiceHandlerNew(service, deviceService2);
+ = new QWinRTLowEnergyServiceHandlerNew(service, deviceService3);
QThread *thread = new QThread;
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandlerNew::obtainCharList);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
connect(thread, &QThread::finished, worker, &QObject::deleteLater);
+ connect(worker, &QWinRTLowEnergyServiceHandlerNew::errorOccured,
+ this, &QLowEnergyControllerPrivateWinRTNew::handleServiceHandlerError);
connect(worker, &QWinRTLowEnergyServiceHandlerNew::charListObtained,
[this, thread](const QBluetoothUuid &service, QHash<QLowEnergyHandle,
QLowEnergyServicePrivate::CharData> charList, QVector<QBluetoothUuid> indicateChars,
@@ -783,7 +917,8 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoot
registerForValueChanges(service, indicateChar);
return S_OK;
});
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register for value changes in Xaml thread",
+ pointer, QLowEnergyService::UnknownError, return)
pointer->setState(QLowEnergyService::ServiceDiscovered);
thread->exit(0);
@@ -815,6 +950,8 @@ void QLowEnergyControllerPrivateWinRTNew::readCharacteristic(
const QLowEnergyHandle charHandle)
{
qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
Q_ASSERT(!service.isNull());
if (role == QLowEnergyController::PeripheralRole) {
service->setError(QLowEnergyService::CharacteristicReadError);
@@ -843,7 +980,8 @@ void QLowEnergyControllerPrivateWinRTNew::readCharacteristic(
}
ComPtr<IAsyncOperation<GattReadResult*>> readOp;
HRESULT hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read characteristic",
+ service, QLowEnergyService::CharacteristicReadError, return S_OK)
auto readCompletedLambda = [charData, charHandle, service]
(IAsyncOperation<GattReadResult*> *op, AsyncStatus status)
{
@@ -855,11 +993,8 @@ void QLowEnergyControllerPrivateWinRTNew::readCharacteristic(
ComPtr<IGattReadResult> characteristicValue;
HRESULT hr;
hr = op->GetResults(&characteristicValue);
- if (FAILED(hr)) {
- qCDebug(QT_BT_WINRT) << "Could not obtain result for characteristic" << charHandle;
- service->setError(QLowEnergyService::CharacteristicReadError);
- return S_OK;
- }
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for characteristic",
+ service, QLowEnergyService::CharacteristicReadError, return S_OK)
const QByteArray value = byteArrayFromGattResult(characteristicValue);
QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
@@ -870,10 +1005,12 @@ void QLowEnergyControllerPrivateWinRTNew::readCharacteristic(
};
hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>(
readCompletedLambda).Get());
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register characteristic read callback",
+ service, QLowEnergyService::CharacteristicReadError, return S_OK)
return S_OK;
});
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread",
+ service, QLowEnergyService::CharacteristicReadError, return)
}
void QLowEnergyControllerPrivateWinRTNew::readDescriptor(
@@ -882,6 +1019,8 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor(
const QLowEnergyHandle descHandle)
{
qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
Q_ASSERT(!service.isNull());
if (role == QLowEnergyController::PeripheralRole) {
service->setError(QLowEnergyService::DescriptorReadError);
@@ -898,7 +1037,7 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor(
HRESULT hr;
hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, descHandle, service, this]() {
- QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
+ const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid);
if (!characteristic) {
qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid
@@ -910,12 +1049,14 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor(
// Get native descriptor
if (!charData.descriptorList.contains(descHandle))
qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "cannot be found in characteristic" << charHandle;
- QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle);
- if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
+ const QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle);
+ const QBluetoothUuid descUuid = descData.uuid;
+ if (descUuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp;
HRESULT hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp);
- Q_ASSERT_SUCCEEDED(hr);
- auto readCompletedLambda = [&charData, charHandle, &descData, descHandle, service]
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read client characteristic configuration",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
+ auto readCompletedLambda = [charHandle, descHandle, service]
(IAsyncOperation<ClientCharConfigDescriptorResult *> *op, AsyncStatus status)
{
if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
@@ -926,18 +1067,12 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor(
ComPtr<IClientCharConfigDescriptorResult> iValue;
HRESULT hr;
hr = op->GetResults(&iValue);
- if (FAILED(hr)) {
- qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle;
- service->setError(QLowEnergyService::DescriptorReadError);
- return S_OK;
- }
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
GattClientCharacteristicConfigurationDescriptorValue value;
hr = iValue->get_ClientCharacteristicConfigurationDescriptor(&value);
- if (FAILED(hr)) {
- qCDebug(QT_BT_WINRT) << "Could not obtain value for descriptor" << descHandle;
- service->setError(QLowEnergyService::DescriptorReadError);
- return S_OK;
- }
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain value for descriptor",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
quint16 result = 0;
bool correct = false;
if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
@@ -956,9 +1091,11 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor(
service->setError(QLowEnergyService::DescriptorReadError);
return S_OK;
}
+ QLowEnergyServicePrivate::DescData descData;
+ descData.uuid = QBluetoothUuid::ClientCharacteristicConfiguration;
descData.value = QByteArray(2, Qt::Uninitialized);
qToLittleEndian(result, descData.value.data());
- charData.descriptorList.insert(descHandle, descData);
+ service->characteristicList[charHandle].descriptorList[descHandle] = descData;
emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
descData.value);
return S_OK;
@@ -966,19 +1103,56 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor(
hr = readOp->put_Completed(
Callback<IAsyncOperationCompletedHandler<ClientCharConfigDescriptorResult *>>(
readCompletedLambda).Get());
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor read callback",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
return S_OK;
} else {
+ ComPtr<IGattCharacteristic3> characteristic3;
+ HRESULT hr = characteristic.As(&characteristic3);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast characteristic",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
+ ComPtr<IAsyncOperation<GattDescriptorsResult *>> op;
+ hr = characteristic3->GetDescriptorsForUuidAsync(descData.uuid, &op);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor for uuid",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
+ ComPtr<IGattDescriptorsResult> result;
+ hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descritpor read result",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
+
+ GattCommunicationStatus commStatus;
+ hr = result->get_Status(&commStatus);
+ if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
+ qErrnoWarning("Could not obtain list of descriptors");
+ service->setError(QLowEnergyService::DescriptorReadError);
+ return S_OK;
+ }
+
ComPtr<IVectorView<GattDescriptor *>> descriptors;
- HRESULT hr = characteristic->GetDescriptors(descData.uuid, &descriptors);
- Q_ASSERT_SUCCEEDED(hr);
+ hr = result->get_Descriptors(&descriptors);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor list",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
+ uint size;
+ hr = descriptors->get_Size(&size);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descritpor list's size",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
+ if (size == 0) {
+ qCWarning(QT_BT_WINRT) << "No descriptor with uuid" << descData.uuid << "was found.";
+ service->setError(QLowEnergyService::DescriptorReadError);
+ return S_OK;
+ } else if (size > 1) {
+ qCWarning(QT_BT_WINRT) << "There is more than 1 descriptor with uuid" << descData.uuid;
+ }
+
ComPtr<IGattDescriptor> descriptor;
hr = descriptors->GetAt(0, &descriptor);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descritpor from list",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
ComPtr<IAsyncOperation<GattReadResult*>> readOp;
hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp);
- Q_ASSERT_SUCCEEDED(hr);
- auto readCompletedLambda = [&charData, charHandle, &descData, descHandle, service]
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read descriptor value",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
+ auto readCompletedLambda = [charHandle, descHandle, descUuid, service]
(IAsyncOperation<GattReadResult*> *op, AsyncStatus status)
{
if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
@@ -994,22 +1168,26 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor(
service->setError(QLowEnergyService::DescriptorReadError);
return S_OK;
}
+ QLowEnergyServicePrivate::DescData descData;
+ descData.uuid = descUuid;
if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription)
descData.value = byteArrayFromGattResult(descriptorValue, true);
else
descData.value = byteArrayFromGattResult(descriptorValue);
- charData.descriptorList.insert(descHandle, descData);
+ service->characteristicList[charHandle].descriptorList[descHandle] = descData;
emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
descData.value);
return S_OK;
};
hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>(
readCompletedLambda).Get());
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor read callback",
+ service, QLowEnergyService::DescriptorReadError, return S_OK)
return S_OK;
}
});
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread",
+ service, QLowEnergyService::DescriptorReadError, return)
}
void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic(
@@ -1019,6 +1197,8 @@ void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic(
QLowEnergyService::WriteMode mode)
{
qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << newValue << mode;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
Q_ASSERT(!service.isNull());
if (role == QLowEnergyController::PeripheralRole) {
service->setError(QLowEnergyService::CharacteristicWriteError);
@@ -1053,26 +1233,33 @@ void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic(
HRESULT hr = GetActivationFactory(
HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
&bufferFactory);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain buffer factory",
+ service, QLowEnergyService::CharacteristicWriteError, return S_OK)
ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
const quint32 length = quint32(newValue.length());
hr = bufferFactory->Create(length, &buffer);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not create buffer",
+ service, QLowEnergyService::CharacteristicWriteError, return S_OK)
hr = buffer->put_Length(length);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer length",
+ service, QLowEnergyService::CharacteristicWriteError, return S_OK)
ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
hr = buffer.As(&byteAccess);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast buffer",
+ service, QLowEnergyService::CharacteristicWriteError, return S_OK)
byte *bytes;
hr = byteAccess->Buffer(&bytes);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer",
+ service, QLowEnergyService::CharacteristicWriteError, return S_OK)
memcpy(bytes, newValue, length);
ComPtr<IAsyncOperation<GattCommunicationStatus>> writeOp;
GattWriteOption option = writeWithResponse ? GattWriteOption_WriteWithResponse
: GattWriteOption_WriteWithoutResponse;
hr = characteristic->WriteValueWithOptionAsync(buffer.Get(), option, &writeOp);
- Q_ASSERT_SUCCEEDED(hr);
- auto writeCompletedLambda =[charData, charHandle, newValue, service, writeWithResponse, this]
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could write characteristic",
+ service, QLowEnergyService::CharacteristicWriteError, return S_OK)
+ QPointer<QLowEnergyControllerPrivateWinRTNew> thisPtr(this);
+ auto writeCompletedLambda = [charData, charHandle, newValue, service, writeWithResponse, thisPtr]
(IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
{
if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
@@ -1089,7 +1276,8 @@ void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic(
service->setError(QLowEnergyService::CharacteristicWriteError);
return S_OK;
}
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain characteristic write result",
+ service, QLowEnergyService::CharacteristicWriteError, return S_OK)
if (result != GattCommunicationStatus_Success) {
qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation failed";
service->setError(QLowEnergyService::CharacteristicWriteError);
@@ -1098,7 +1286,7 @@ void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic(
// only update cache when property is readable. Otherwise it remains
// empty.
if (charData.properties & QLowEnergyCharacteristic::Read)
- updateValueOfCharacteristic(charHandle, newValue, false);
+ thisPtr->updateValueOfCharacteristic(charHandle, newValue, false);
if (writeWithResponse)
emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle),
newValue);
@@ -1107,10 +1295,12 @@ void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic(
hr = writeOp->put_Completed(
Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>(
writeCompletedLambda).Get());
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register characteristic write callback",
+ service, QLowEnergyService::CharacteristicWriteError, return S_OK)
return S_OK;
});
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread",
+ service, QLowEnergyService::CharacteristicWriteError, return)
}
void QLowEnergyControllerPrivateWinRTNew::writeDescriptor(
@@ -1120,6 +1310,8 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor(
const QByteArray &newValue)
{
qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle << newValue;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
Q_ASSERT(!service.isNull());
if (role == QLowEnergyController::PeripheralRole) {
service->setError(QLowEnergyService::DescriptorWriteError);
@@ -1174,8 +1366,10 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor(
}
ComPtr<IAsyncOperation<enum GattCommunicationStatus>> writeOp;
HRESULT hr = characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(value, &writeOp);
- Q_ASSERT_SUCCEEDED(hr);
- auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this]
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write client characteristic configuration",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
+ QPointer<QLowEnergyControllerPrivateWinRTNew> thisPtr(this);
+ auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
(IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
{
if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
@@ -1186,17 +1380,14 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor(
GattCommunicationStatus result;
HRESULT hr;
hr = op->GetResults(&result);
- if (FAILED(hr)) {
- qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle;
- service->setError(QLowEnergyService::DescriptorWriteError);
- return S_OK;
- }
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
if (result != GattCommunicationStatus_Success) {
- qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed";
+ qCWarning(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed";
service->setError(QLowEnergyService::DescriptorWriteError);
return S_OK;
}
- updateValueOfDescriptor(charHandle, descHandle, newValue, false);
+ thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
newValue);
return S_OK;
@@ -1204,36 +1395,75 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor(
hr = writeOp->put_Completed(
Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus >>(
writeCompletedLambda).Get());
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor write callback",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
} else {
+ ComPtr<IGattCharacteristic3> characteristic3;
+ HRESULT hr = characteristic.As(&characteristic3);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast characteristic",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
+ ComPtr<IAsyncOperation<GattDescriptorsResult *>> op;
+ hr = characteristic3->GetDescriptorsForUuidAsync(descData.uuid, &op);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor from Uuid",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
+ ComPtr<IGattDescriptorsResult> result;
+ hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descriptor operation",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
+ GattCommunicationStatus commStatus;
+ hr = result->get_Status(&commStatus);
+ if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
+ qCWarning(QT_BT_WINRT) << "Descriptor operation failed";
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return S_OK;
+ }
ComPtr<IVectorView<GattDescriptor *>> descriptors;
- HRESULT hr = characteristic->GetDescriptors(descData.uuid, &descriptors);
- Q_ASSERT_SUCCEEDED(hr);
+ hr = result->get_Descriptors(&descriptors);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain list of descriptors",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
+ uint size;
+ hr = descriptors->get_Size(&size);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain list of descriptors' size",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
+ if (size == 0) {
+ qCWarning(QT_BT_WINRT) << "No descriptor with uuid" << descData.uuid << "was found.";
+ return S_OK;
+ } else if (size > 1) {
+ qCWarning(QT_BT_WINRT) << "There is more than 1 descriptor with uuid" << descData.uuid;
+ }
ComPtr<IGattDescriptor> descriptor;
hr = descriptors->GetAt(0, &descriptor);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory;
hr = GetActivationFactory(
HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
&bufferFactory);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain buffer factory",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
const quint32 length = quint32(newValue.length());
hr = bufferFactory->Create(length, &buffer);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not create buffer",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
hr = buffer->put_Length(length);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer length",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
hr = buffer.As(&byteAccess);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast buffer",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
byte *bytes;
hr = byteAccess->Buffer(&bytes);
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
memcpy(bytes, newValue, length);
ComPtr<IAsyncOperation<GattCommunicationStatus>> writeOp;
hr = descriptor->WriteValueAsync(buffer.Get(), &writeOp);
- Q_ASSERT_SUCCEEDED(hr);
- auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this]
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write descriptor value",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
+ QPointer<QLowEnergyControllerPrivateWinRTNew> thisPtr(this);
+ auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
(IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
{
if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
@@ -1244,17 +1474,14 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor(
GattCommunicationStatus result;
HRESULT hr;
hr = op->GetResults(&result);
- if (FAILED(hr)) {
- qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle;
- service->setError(QLowEnergyService::DescriptorWriteError);
- return S_OK;
- }
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
if (result != GattCommunicationStatus_Success) {
qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed";
service->setError(QLowEnergyService::DescriptorWriteError);
return S_OK;
}
- updateValueOfDescriptor(charHandle, descHandle, newValue, false);
+ thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
newValue);
return S_OK;
@@ -1262,12 +1489,14 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor(
hr = writeOp->put_Completed(
Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>(
writeCompletedLambda).Get());
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor write callback",
+ service, QLowEnergyService::DescriptorWriteError, return S_OK)
return S_OK;
}
return S_OK;
});
- Q_ASSERT_SUCCEEDED(hr);
+ CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread",
+ service, QLowEnergyService::DescriptorWriteError, return)
}
@@ -1277,9 +1506,12 @@ void QLowEnergyControllerPrivateWinRTNew::addToGenericAttributeList(const QLowEn
Q_UNIMPLEMENTED();
}
-void QLowEnergyControllerPrivateWinRTNew::characteristicChanged(
+void QLowEnergyControllerPrivateWinRTNew::handleCharacteristicChanged(
quint16 charHandle, const QByteArray &data)
{
+ qCDebug(QT_BT_WINRT) << __FUNCTION__ << charHandle << data;
+ qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
+ << QThread::currentThread();
QSharedPointer<QLowEnergyServicePrivate> service =
serviceForHandle(charHandle);
if (service.isNull())
@@ -1302,6 +1534,170 @@ void QLowEnergyControllerPrivateWinRTNew::characteristicChanged(
emit service->characteristicChanged(characteristic, data);
}
+void QLowEnergyControllerPrivateWinRTNew::handleServiceHandlerError(const QString &error)
+{
+ if (state != QLowEnergyController::DiscoveringState)
+ return;
+
+ qCWarning(QT_BT_WINRT) << "Error while discovering services:" << error;
+ setState(QLowEnergyController::UnconnectedState);
+ setError(QLowEnergyController::ConnectionError);
+}
+
+void QLowEnergyControllerPrivateWinRTNew::connectToPairedDevice()
+{
+ Q_Q(QLowEnergyController);
+ ComPtr<IBluetoothLEDevice3> device3;
+ HRESULT hr = mDevice.As(&device3);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return)
+ ComPtr<IAsyncOperation<GattDeviceServicesResult *>> deviceServicesOp;
+ while (!mAbortPending) {
+ hr = device3->GetGattServicesAsync(&deviceServicesOp);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return)
+ ComPtr<IGattDeviceServicesResult> deviceServicesResult;
+ hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(),
+ QWinRTFunctions::ProcessThreadEvents, 5000);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await services operation", return)
+
+ GattCommunicationStatus commStatus;
+ hr = deviceServicesResult->get_Status(&commStatus);
+ if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
+ qCWarning(QT_BT_WINRT()) << "Service operation failed";
+ setError(QLowEnergyController::ConnectionError);
+ setState(QLowEnergyController::UnconnectedState);
+ unregisterFromStatusChanges();
+ return;
+ }
+
+ ComPtr<IVectorView <GattDeviceService *>> deviceServices;
+ hr = deviceServicesResult->get_Services(&deviceServices);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain list of services", return)
+ uint serviceCount;
+ hr = deviceServices->get_Size(&serviceCount);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service count", return)
+
+ if (serviceCount == 0) {
+ qCWarning(QT_BT_WINRT()) << "Found devices without services";
+ setError(QLowEnergyController::ConnectionError);
+ setState(QLowEnergyController::UnconnectedState);
+ unregisterFromStatusChanges();
+ return;
+ }
+
+ // Windows automatically connects to the device as soon as a service value is read/written.
+ // Thus we read one value in order to establish the connection.
+ for (uint i = 0; i < serviceCount; ++i) {
+ ComPtr<IGattDeviceService> service;
+ hr = deviceServices->GetAt(i, &service);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service", return);
+ ComPtr<IGattDeviceService3> service3;
+ hr = service.As(&service3);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast service", return);
+ ComPtr<IAsyncOperation<GattCharacteristicsResult *>> characteristicsOp;
+ hr = service3->GetCharacteristicsAsync(&characteristicsOp);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic", return);
+ ComPtr<IGattCharacteristicsResult> characteristicsResult;
+ hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(),
+ QWinRTFunctions::ProcessThreadEvents, 5000);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await characteristic operation", return);
+ GattCommunicationStatus commStatus;
+ hr = characteristicsResult->get_Status(&commStatus);
+ if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
+ qCWarning(QT_BT_WINRT) << "Characteristic operation failed";
+ break;
+ }
+ ComPtr<IVectorView<GattCharacteristic *>> characteristics;
+ hr = characteristicsResult->get_Characteristics(&characteristics);
+ if (hr == E_ACCESSDENIED) {
+ // Everything will work as expected up until this point if the manifest capabilties
+ // for bluetooth LE are not set.
+ qCWarning(QT_BT_WINRT) << "Could not obtain characteristic list. Please check your "
+ "manifest capabilities";
+ setState(QLowEnergyController::UnconnectedState);
+ setError(QLowEnergyController::ConnectionError);
+ unregisterFromStatusChanges();
+ return;
+ }
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic list", return);
+ uint characteristicsCount;
+ hr = characteristics->get_Size(&characteristicsCount);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic list's size", return);
+ for (uint j = 0; j < characteristicsCount; ++j) {
+ ComPtr<IGattCharacteristic> characteristic;
+ hr = characteristics->GetAt(j, &characteristic);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic", return);
+ ComPtr<IAsyncOperation<GattReadResult *>> op;
+ GattCharacteristicProperties props;
+ hr = characteristic->get_CharacteristicProperties(&props);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic's properties", return);
+ if (!(props & GattCharacteristicProperties_Read))
+ continue;
+ hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not read characteristic value", return);
+ ComPtr<IGattReadResult> result;
+ hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessThreadEvents, 500);
+ // E_ILLEGAL_METHOD_CALL will be the result for a device, that is not reachable at
+ // the moment. In this case we should jump back into the outer loop and keep trying.
+ if (hr == E_ILLEGAL_METHOD_CALL)
+ break;
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await characteristic read", return);
+ ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
+ hr = result->get_Value(&buffer);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic value", return);
+ if (!buffer) {
+ qCDebug(QT_BT_WINRT) << "Problem reading value";
+ break;
+ }
+
+ setState(QLowEnergyController::ConnectedState);
+ emit q->connected();
+ if (!registerForStatusChanges()) {
+ setError(QLowEnergyController::ConnectionError);
+ setState(QLowEnergyController::UnconnectedState);
+ return;
+ }
+ return;
+ }
+ }
+ }
+}
+
+void QLowEnergyControllerPrivateWinRTNew::connectToUnpairedDevice()
+{
+ if (!registerForStatusChanges()) {
+ setError(QLowEnergyController::ConnectionError);
+ setState(QLowEnergyController::UnconnectedState);
+ return;
+ }
+ ComPtr<IBluetoothLEDevice3> device3;
+ HRESULT hr = mDevice.As(&device3);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return)
+ ComPtr<IGattDeviceServicesResult> deviceServicesResult;
+ while (!mAbortPending) {
+ ComPtr<IAsyncOperation<GattDeviceServicesResult *>> deviceServicesOp;
+ hr = device3->GetGattServicesAsync(&deviceServicesOp);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return)
+ hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(),
+ QWinRTFunctions::ProcessMainThreadEvents);
+ CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await services operation", return)
+
+ GattCommunicationStatus commStatus;
+ hr = deviceServicesResult->get_Status(&commStatus);
+ if (commStatus == GattCommunicationStatus_Unreachable)
+ continue;
+
+ if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
+ qCWarning(QT_BT_WINRT()) << "Service operation failed";
+ setError(QLowEnergyController::ConnectionError);
+ setState(QLowEnergyController::UnconnectedState);
+ unregisterFromStatusChanges();
+ return;
+ }
+
+ break;
+ }
+}
+
QT_END_NAMESPACE
#include "qlowenergycontroller_winrt_new.moc"
diff --git a/src/bluetooth/qlowenergycontroller_winrt_new_p.h b/src/bluetooth/qlowenergycontroller_winrt_new_p.h
index 716d2d07..c31408be 100644
--- a/src/bluetooth/qlowenergycontroller_winrt_new_p.h
+++ b/src/bluetooth/qlowenergycontroller_winrt_new_p.h
@@ -60,8 +60,22 @@
#include "qlowenergycontroller.h"
#include "qlowenergycontrollerbase_p.h"
+namespace ABI {
+ namespace Windows {
+ namespace Devices {
+ namespace Bluetooth {
+ struct IBluetoothLEDevice;
+ }
+ }
+ namespace Foundation {
+ template <typename T> struct IAsyncOperation;
+ enum class AsyncStatus;
+ }
+ }
+}
+
#include <wrl.h>
-#include <windows.devices.bluetooth.h>
+#include <windows.devices.bluetooth.genericattributeprofile.h>
#include <functional>
@@ -116,10 +130,18 @@ public:
void addToGenericAttributeList(const QLowEnergyServiceData &service,
QLowEnergyHandle startHandle) override;
-private slots:
+signals:
void characteristicChanged(quint16 charHandle, const QByteArray &data);
+private slots:
+ void handleCharacteristicChanged(quint16 charHandle, const QByteArray &data);
+ void handleServiceHandlerError(const QString &error);
+
private:
+ void connectToPairedDevice();
+ void connectToUnpairedDevice();
+
+ bool mAbortPending = false;
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice> mDevice;
EventRegistrationToken mStatusChangedToken;
struct ValueChangedEntry {
@@ -140,10 +162,18 @@ private:
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattCharacteristic> getNativeCharacteristic(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid);
void registerForValueChanges(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid);
+ void unregisterFromValueChanges();
+ HRESULT onValueChange(ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattCharacteristic *characteristic,
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattValueChangedEventArgs *args);
+
+ bool registerForStatusChanges();
+ void unregisterFromStatusChanges();
+ HRESULT onStatusChange(ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice *dev, IInspectable *);
void obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer,
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattDeviceService> nativeService);
-
+ HRESULT onServiceDiscoveryFinished(ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDeviceServicesResult *> *op,
+ ABI::Windows::Foundation::AsyncStatus status);
};
QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_winrt_p.h b/src/bluetooth/qlowenergycontroller_winrt_p.h
index 783a71fa..fedc52d9 100644
--- a/src/bluetooth/qlowenergycontroller_winrt_p.h
+++ b/src/bluetooth/qlowenergycontroller_winrt_p.h
@@ -114,8 +114,11 @@ public:
void addToGenericAttributeList(const QLowEnergyServiceData &service,
QLowEnergyHandle startHandle) override;
+signals:
+ void characteristicChanged(quint16 charHandle, const QByteArray &data);
+
private slots:
- void characteristicChanged(int charHandle, const QByteArray &data);
+ void handleCharacteristicChanged(quint16 charHandle, const QByteArray &data);
private:
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice> mDevice;
@@ -138,6 +141,7 @@ private:
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattCharacteristic> getNativeCharacteristic(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid);
void registerForValueChanges(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid);
+ void unregisterFromValueChanges();
void obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer,
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattDeviceService> nativeService);
diff --git a/src/bluetooth/qlowenergycontrollerbase.cpp b/src/bluetooth/qlowenergycontrollerbase.cpp
index 86108648..059bd41b 100644
--- a/src/bluetooth/qlowenergycontrollerbase.cpp
+++ b/src/bluetooth/qlowenergycontrollerbase.cpp
@@ -61,7 +61,7 @@ QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate()
bool QLowEnergyControllerPrivate::isValidLocalAdapter()
{
-#ifdef QT_WINRT_BLUETOOTH
+#if defined(QT_WINRT_BLUETOOTH) || defined(Q_OS_DARWIN)
return true;
#endif
if (localAdapter.isNull())
@@ -106,6 +106,9 @@ void QLowEnergyControllerPrivate::setError(
case QLowEnergyController::RemoteHostClosedError:
errorString = QLowEnergyController::tr("Remote device closed the connection");
break;
+ case QLowEnergyController::AuthorizationError:
+ errorString = QLowEnergyController::tr("Failed to authorize on the remote device");
+ break;
case QLowEnergyController::NoError:
return;
default:
diff --git a/src/bluetooth/qlowenergycontrollerbase_p.h b/src/bluetooth/qlowenergycontrollerbase_p.h
index a8d1c676..169ba07b 100644
--- a/src/bluetooth/qlowenergycontrollerbase_p.h
+++ b/src/bluetooth/qlowenergycontrollerbase_p.h
@@ -51,24 +51,6 @@
// We mean it.
//
-#if defined(QT_OSX_BLUETOOTH) || defined(QT_IOS_BLUETOOTH)
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qobject.h>
-
-QT_BEGIN_NAMESPACE
-
-class QLowEnergyControllerPrivate : public QObject
-{
-public:
- // This class is required to make shared pointer machinery and
- // moc (== Obj-C syntax) happy on both OS X and iOS.
-};
-
-QT_END_NAMESPACE
-
-#else
-
#include <qglobal.h>
#include <QtCore/qobject.h>
@@ -135,7 +117,6 @@ public:
virtual QLowEnergyService *addServiceHelper(
const QLowEnergyServiceData &service);
-
// common backend methods
bool isValidLocalAdapter();
void setError(QLowEnergyController::Error newError);
@@ -174,6 +155,7 @@ protected:
QLowEnergyHandle lastLocalHandle{};
QString remoteName; // device name of the remote
+ QBluetoothUuid deviceUuid; // quite useless anywhere but Darwin (CoreBluetooth).
Q_DECLARE_PUBLIC(QLowEnergyController)
QLowEnergyController *q_ptr;
@@ -181,6 +163,4 @@ protected:
QT_END_NAMESPACE
-#endif //defined(QT_OSX_BLUETOOTH) || defined(QT_IOS_BLUETOOTH)
-
#endif // QLOWENERGYCONTROLLERPRIVATEBASE_P_H
diff --git a/src/bluetooth/qlowenergydescriptor.h b/src/bluetooth/qlowenergydescriptor.h
index adfe1203..18bb53c0 100644
--- a/src/bluetooth/qlowenergydescriptor.h
+++ b/src/bluetooth/qlowenergydescriptor.h
@@ -83,8 +83,8 @@ protected:
friend class QLowEnergyControllerPrivateBluez;
friend class QLowEnergyControllerPrivateBluezDBus;
friend class QLowEnergyControllerPrivateCommon;
- friend class QLowEnergyControllerPrivateOSX;
friend class QLowEnergyControllerPrivateWin32;
+ friend class QLowEnergyControllerPrivateDarwin;
friend class QLowEnergyControllerPrivateWinRT;
friend class QLowEnergyControllerPrivateWinRTNew;
QLowEnergyDescriptorPrivate *data = nullptr;
diff --git a/src/bluetooth/qlowenergyservice.cpp b/src/bluetooth/qlowenergyservice.cpp
index 1529d3c2..2e6d1f9b 100644
--- a/src/bluetooth/qlowenergyservice.cpp
+++ b/src/bluetooth/qlowenergyservice.cpp
@@ -47,6 +47,10 @@
#include "qlowenergycontrollerbase_p.h"
#include "qlowenergyserviceprivate_p.h"
+#ifdef Q_OS_DARWIN
+#include "qlowenergycontroller_darwin_p.h"
+#endif // Q_OS_DARWIN
+
QT_BEGIN_NAMESPACE
/*!
@@ -809,6 +813,21 @@ void QLowEnergyService::writeDescriptor(const QLowEnergyDescriptor &descriptor,
d->setError(QLowEnergyService::OperationError);
return;
}
+#ifdef Q_OS_DARWIN
+ if (descriptor.uuid() == QBluetoothUuid::ClientCharacteristicConfiguration) {
+ // We have to identify a special case - ClientCharacteristicConfiguration
+ // since with CoreBluetooth:
+ //
+ // "You cannot use this method to write the value of a client configuration descriptor
+ // (represented by the CBUUIDClientCharacteristicConfigurationString constant),
+ // which describes how notification or indications are configured for a
+ // characteristic’s value with respect to a client. If you want to manage
+ // notifications or indications for a characteristic’s value, you must
+ // use the setNotifyValue:forCharacteristic: method instead."
+ auto controller = static_cast<QLowEnergyControllerPrivateDarwin *>(d->controller.data());
+ return controller->setNotifyValue(descriptor.d_ptr, descriptor.characteristicHandle(), newValue);
+ }
+#endif // Q_OS_DARWIN
d->controller->writeDescriptor(descriptor.d_ptr,
descriptor.characteristicHandle(),
diff --git a/src/bluetooth/qlowenergyservice.h b/src/bluetooth/qlowenergyservice.h
index 9de65a84..a2715471 100644
--- a/src/bluetooth/qlowenergyservice.h
+++ b/src/bluetooth/qlowenergyservice.h
@@ -136,6 +136,7 @@ private:
friend class QLowEnergyControllerPrivate;
friend class QLowEnergyControllerPrivateBluez;
friend class QLowEnergyControllerPrivateAndroid;
+ friend class QLowEnergyControllerPrivateDarwin;
QLowEnergyService(QSharedPointer<QLowEnergyServicePrivate> p,
QObject *parent = nullptr);
};
diff --git a/src/bluetooth/qlowenergyservice_osx.mm b/src/bluetooth/qlowenergyservice_osx.mm
deleted file mode 100644
index c294b693..00000000
--- a/src/bluetooth/qlowenergyservice_osx.mm
+++ /dev/null
@@ -1,277 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Javier S. Pedro <maemo@javispedro.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtBluetooth module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qlowenergycontroller_osx_p.h"
-#include "qlowenergyserviceprivate_p.h"
-#include "qlowenergycharacteristic.h"
-#include "qlowenergydescriptor.h"
-#include "qlowenergyservice.h"
-#include "qbluetoothuuid.h"
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qlist.h>
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-namespace {
-
-QLowEnergyControllerPrivateOSX *qt_mac_le_controller(QSharedPointer<QLowEnergyServicePrivate> d_ptr)
-{
- if (d_ptr.isNull())
- return nullptr;
-
- return static_cast<QLowEnergyControllerPrivateOSX *>(d_ptr->controller.data());
-}
-
-}
-
-QLowEnergyService::QLowEnergyService(QSharedPointer<QLowEnergyServicePrivate> d, QObject *parent)
- : QObject(parent),
- d_ptr(d)
-{
- qRegisterMetaType<QLowEnergyService::ServiceState>();
- qRegisterMetaType<QLowEnergyService::ServiceError>();
-
- connect(d.data(), SIGNAL(error(QLowEnergyService::ServiceError)),
- this, SIGNAL(error(QLowEnergyService::ServiceError)));
- connect(d.data(), SIGNAL(stateChanged(QLowEnergyService::ServiceState)),
- this, SIGNAL(stateChanged(QLowEnergyService::ServiceState)));
- connect(d.data(), SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)),
- this, SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)));
- connect(d.data(), SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)),
- this, SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)));
- connect(d.data(), SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray)),
- this, SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray)));
- connect(d.data(), SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray)),
- this, SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray)));
- connect(d.data(), SIGNAL(descriptorRead(QLowEnergyDescriptor,QByteArray)),
- this, SIGNAL(descriptorRead(QLowEnergyDescriptor,QByteArray)));
-
-}
-
-QLowEnergyService::~QLowEnergyService()
-{
-}
-
-QList<QBluetoothUuid> QLowEnergyService::includedServices() const
-{
- return d_ptr->includedServices;
-}
-
-QLowEnergyService::ServiceTypes QLowEnergyService::type() const
-{
- return d_ptr->type;
-}
-
-QLowEnergyService::ServiceState QLowEnergyService::state() const
-{
- return d_ptr->state;
-}
-
-QLowEnergyCharacteristic QLowEnergyService::characteristic(const QBluetoothUuid &uuid) const
-{
- CharacteristicDataMap::const_iterator charIt = d_ptr->characteristicList.constBegin();
- for ( ; charIt != d_ptr->characteristicList.constEnd(); ++charIt) {
- const QLowEnergyHandle charHandle = charIt.key();
- const QLowEnergyServicePrivate::CharData &charDetails = charIt.value();
-
- if (charDetails.uuid == uuid)
- return QLowEnergyCharacteristic(d_ptr, charHandle);
- }
-
- return QLowEnergyCharacteristic();
-}
-
-QList<QLowEnergyCharacteristic> QLowEnergyService::characteristics() const
-{
- QList<QLowEnergyCharacteristic> result;
- QList<QLowEnergyHandle> handles(d_ptr->characteristicList.keys());
-
- std::sort(handles.begin(), handles.end());
-
- for (const QLowEnergyHandle &handle : qAsConst(handles)) {
- QLowEnergyCharacteristic characteristic(d_ptr, handle);
- result.append(characteristic);
- }
-
- return result;
-}
-
-QBluetoothUuid QLowEnergyService::serviceUuid() const
-{
- return d_ptr->uuid;
-}
-
-QString QLowEnergyService::serviceName() const
-{
- bool ok = false;
- const quint16 clsId = d_ptr->uuid.toUInt16(&ok);
- if (ok) {
- QBluetoothUuid::ServiceClassUuid uuid
- = static_cast<QBluetoothUuid::ServiceClassUuid>(clsId);
- const QString name = QBluetoothUuid::serviceClassToString(uuid);
- if (!name.isEmpty())
- return name;
- }
-
- return qApp ? qApp->translate("QBluetoothServiceDiscoveryAgent", "Unknown Service") :
- QStringLiteral("Unknown Service");
-}
-
-void QLowEnergyService::discoverDetails()
-{
- QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr);
-
- if (!controller || d_ptr->state == InvalidService) {
- d_ptr->setError(OperationError);
- return;
- }
-
- if (d_ptr->state != DiscoveryRequired)
- return;
-
- d_ptr->setState(QLowEnergyService::DiscoveringServices);
- controller->discoverServiceDetails(d_ptr->uuid);
-}
-
-QLowEnergyService::ServiceError QLowEnergyService::error() const
-{
- return d_ptr->lastError;
-}
-
-bool QLowEnergyService::contains(const QLowEnergyCharacteristic &characteristic) const
-{
- if (characteristic.d_ptr.isNull() || !characteristic.data)
- return false;
-
- if (d_ptr == characteristic.d_ptr
- && d_ptr->characteristicList.contains(characteristic.attributeHandle())) {
- return true;
- }
-
- return false;
-}
-
-void QLowEnergyService::readCharacteristic(const QLowEnergyCharacteristic &characteristic)
-{
- QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr);
- if (controller == nullptr || state() != ServiceDiscovered || !contains(characteristic)) {
- d_ptr->setError(OperationError);
- return;
- }
-
- controller->readCharacteristic(characteristic.d_ptr, characteristic.attributeHandle());
-}
-
-
-void QLowEnergyService::writeCharacteristic(const QLowEnergyCharacteristic &ch, const QByteArray &newValue,
- WriteMode mode)
-{
- QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr);
- if (controller == nullptr ||
- (controller->role == QLowEnergyController::CentralRole && state() != ServiceDiscovered) ||
- !contains(ch)) {
- d_ptr->setError(QLowEnergyService::OperationError);
- return;
- }
-
- controller->writeCharacteristic(ch.d_ptr, ch.attributeHandle(), newValue, mode);
-}
-
-bool QLowEnergyService::contains(const QLowEnergyDescriptor &descriptor) const
-{
- if (descriptor.d_ptr.isNull() || !descriptor.data)
- return false;
-
- const QLowEnergyHandle charHandle = descriptor.characteristicHandle();
- if (!charHandle)
- return false;
-
- if (d_ptr == descriptor.d_ptr && d_ptr->characteristicList.contains(charHandle)
- && d_ptr->characteristicList[charHandle].descriptorList.contains(descriptor.handle()))
- {
- return true;
- }
-
- return false;
-}
-
-void QLowEnergyService::readDescriptor(const QLowEnergyDescriptor &descriptor)
-{
- QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr);
- if (controller == nullptr || state() != ServiceDiscovered || !contains(descriptor)) {
- d_ptr->setError(OperationError);
- return;
- }
-
- controller->readDescriptor(descriptor.d_ptr, descriptor.handle());
-}
-
-void QLowEnergyService::writeDescriptor(const QLowEnergyDescriptor &descriptor,
- const QByteArray &newValue)
-{
- QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr);
- if (controller == nullptr || state() != ServiceDiscovered || !contains(descriptor)) {
- // This operation error also includes LE controller in the peripheral role:
- // on iOS/OS X - descriptors are immutable.
- d_ptr->setError(OperationError);
- return;
- }
-
- if (descriptor.uuid() == QBluetoothUuid::ClientCharacteristicConfiguration) {
- // We have to identify a special case - ClientCharacteristicConfiguration
- // since with Core Bluetooth:
- //
- // "You cannot use this method to write the value of a client configuration descriptor
- // (represented by the CBUUIDClientCharacteristicConfigurationString constant),
- // which describes how notification or indications are configured for a
- // characteristic’s value with respect to a client. If you want to manage
- // notifications or indications for a characteristic’s value, you must
- // use the setNotifyValue:forCharacteristic: method instead."
- controller->setNotifyValue(descriptor.d_ptr, descriptor.characteristicHandle(), newValue);
- } else {
- controller->writeDescriptor(descriptor.d_ptr, descriptor.handle(), newValue);
- }
-}
-
-QT_END_NAMESPACE