summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf4
-rw-r--r--config.tests/bluez/main.cpp5
-rw-r--r--examples/bluetooth/btchat/main.cpp1
-rw-r--r--examples/bluetooth/btchat/remoteselector.cpp10
-rw-r--r--examples/bluetooth/btfiletransfer/main.cpp1
-rw-r--r--examples/bluetooth/btfiletransfer/pindisplay.h2
-rw-r--r--examples/bluetooth/btfiletransfer/progress.h6
-rw-r--r--examples/bluetooth/btfiletransfer/remoteselector.cpp25
-rw-r--r--examples/bluetooth/btfiletransfer/remoteselector.h4
-rw-r--r--examples/bluetooth/btscanner/device.h2
-rw-r--r--examples/bluetooth/btscanner/main.cpp1
-rw-r--r--examples/bluetooth/btscanner/service.h2
-rw-r--r--examples/bluetooth/chat/qmlchat.cpp1
-rw-r--r--examples/bluetooth/heartrate-game/main.cpp1
-rw-r--r--examples/bluetooth/lowenergyscanner/device.cpp24
-rw-r--r--examples/bluetooth/lowenergyscanner/main.cpp1
-rw-r--r--examples/bluetooth/picturetransfer/filetransfer.h2
-rw-r--r--examples/bluetooth/picturetransfer/main.cpp1
-rw-r--r--examples/bluetooth/pingpong/main.cpp1
-rw-r--r--examples/bluetooth/scanner/qmlscanner.cpp1
-rw-r--r--examples/nfc/corkboard/android/AndroidManifest.xml6
-rw-r--r--src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java58
-rw-r--r--src/bluetooth/android/devicediscoverybroadcastreceiver.cpp4
-rw-r--r--src/bluetooth/bluetooth.pro50
-rw-r--r--src/bluetooth/configure.json16
-rw-r--r--src/bluetooth/doc/src/bluetooth-index.qdoc19
-rw-r--r--src/bluetooth/doc/src/bluetooth-overview.qdoc5
-rw-r--r--src/bluetooth/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.h136
-rw-r--r--src/bluetooth/osx/osxbt.pri15
-rw-r--r--src/bluetooth/osx/osxbtcentralmanager.mm21
-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/osxbtnotifier_p.h1
-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.cpp2
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent.cpp6
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent.h1
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp3
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp173
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm (renamed from src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm)331
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm458
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_p.h110
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp557
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp11
-rw-r--r--src/bluetooth/qbluetoothdeviceinfo.cpp57
-rw-r--r--src/bluetooth/qbluetoothdeviceinfo.h8
-rw-r--r--src/bluetooth/qbluetoothdeviceinfo_p.h2
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_bluez.cpp2
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_p.h24
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_win.cpp260
-rw-r--r--src/bluetooth/qbluetoothserver.cpp5
-rw-r--r--src/bluetooth/qbluetoothserver_android.cpp5
-rw-r--r--src/bluetooth/qbluetoothserver_bluez.cpp5
-rw-r--r--src/bluetooth/qbluetoothserver_osx.mm230
-rw-r--r--src/bluetooth/qbluetoothserver_p.cpp6
-rw-r--r--src/bluetooth/qbluetoothserver_p.h72
-rw-r--r--src/bluetooth/qbluetoothserver_win.cpp240
-rw-r--r--src/bluetooth/qbluetoothserver_winrt.cpp6
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent.cpp11
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent.h1
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_osx.mm416
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_p.h52
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_win.cpp437
-rw-r--r--src/bluetooth/qbluetoothserviceinfo.cpp5
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_osx.mm390
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_p.h35
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_win.cpp120
-rw-r--r--src/bluetooth/qbluetoothsocket.cpp19
-rw-r--r--src/bluetooth/qbluetoothsocket.h17
-rw-r--r--src/bluetooth/qbluetoothsocket_osx.mm853
-rw-r--r--src/bluetooth/qbluetoothsocket_osx_p.h82
-rw-r--r--src/bluetooth/qbluetoothsocket_win.cpp597
-rw-r--r--src/bluetooth/qbluetoothsocket_win_p.h (renamed from src/bluetooth/qbluetoothserver_osx_p.h)101
-rw-r--r--src/bluetooth/qbluetoothsocketbase_p.h21
-rw-r--r--src/bluetooth/qbluetoothtransfermanager.cpp4
-rw-r--r--src/bluetooth/qbluetoothutils_win.cpp2
-rw-r--r--src/bluetooth/qlowenergycharacteristic.h3
-rw-r--r--src/bluetooth/qlowenergycontroller.cpp46
-rw-r--r--src/bluetooth/qlowenergycontroller.h8
-rw-r--r--src/bluetooth/qlowenergycontroller_bluezdbus.cpp10
-rw-r--r--src/bluetooth/qlowenergycontroller_darwin.mm (renamed from src/bluetooth/qlowenergycontroller_osx.mm)1021
-rw-r--r--src/bluetooth/qlowenergycontroller_darwin_p.h (renamed from src/bluetooth/qlowenergycontroller_osx_p.h)111
-rw-r--r--src/bluetooth/qlowenergycontroller_win.cpp1356
-rw-r--r--src/bluetooth/qlowenergycontroller_win_p.h185
-rw-r--r--src/bluetooth/qlowenergycontrollerbase.cpp5
-rw-r--r--src/bluetooth/qlowenergycontrollerbase_p.h22
-rw-r--r--src/bluetooth/qlowenergydescriptor.h3
-rw-r--r--src/bluetooth/qlowenergyservice.cpp19
-rw-r--r--src/bluetooth/qlowenergyservice.h1
-rw-r--r--src/bluetooth/qlowenergyservice_osx.mm277
-rw-r--r--src/bluetooth/qlowenergyserviceprivate_p.h9
-rw-r--r--src/bluetooth/windows/qwinlowenergybluetooth_p.h231
-rw-r--r--src/bluetooth/windows/windows.pri2
-rw-r--r--src/imports/bluetooth/plugins.qmltypes276
-rw-r--r--src/imports/nfc/plugins.qmltypes2
-rw-r--r--src/nfc/qnearfieldtagtype2.cpp7
-rw-r--r--src/nfc/qnearfieldtarget.cpp13
-rw-r--r--src/nfc/qqmlndefrecord.cpp5
-rw-r--r--tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp44
-rw-r--r--tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp13
-rw-r--r--tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp2
-rw-r--r--tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp6
-rw-r--r--tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp79
113 files changed, 6639 insertions, 3681 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 543d0a84..f6895bb0 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,3 +1,5 @@
load(qt_build_config)
-MODULE_VERSION = 5.13.2
+DEFINES += QT_NO_FOREACH QT_NO_JAVA_STYLE_ITERATORS QT_NO_LINKED_LIST
+
+MODULE_VERSION = 5.14.0
diff --git a/config.tests/bluez/main.cpp b/config.tests/bluez/main.cpp
index 2e978b85..63d7f8eb 100644
--- a/config.tests/bluez/main.cpp
+++ b/config.tests/bluez/main.cpp
@@ -35,5 +35,10 @@ int main()
bacmp(&anyTmp, &localTmp);
+ uint32_t field0 = 1;
+ uint16_t field1 = 1;
+
+ field0 = htonl(field0);
+ field1 = htons(field1);
return 0;
}
diff --git a/examples/bluetooth/btchat/main.cpp b/examples/bluetooth/btchat/main.cpp
index 5c7bbf75..e728efd2 100644
--- a/examples/bluetooth/btchat/main.cpp
+++ b/examples/bluetooth/btchat/main.cpp
@@ -56,6 +56,7 @@
int main(int argc, char *argv[])
{
//QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+ QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
Chat d;
diff --git a/examples/bluetooth/btchat/remoteselector.cpp b/examples/bluetooth/btchat/remoteselector.cpp
index 2bd1efcc..ec5d207c 100644
--- a/examples/bluetooth/btchat/remoteselector.cpp
+++ b/examples/bluetooth/btchat/remoteselector.cpp
@@ -114,17 +114,15 @@ void RemoteSelector::serviceDiscovered(const QBluetoothServiceInfo &serviceInfo)
<< serviceInfo.protocolServiceMultiplexer();
qDebug() << "\tRFCOMM server channel:" << serviceInfo.serverChannel();
#endif
- QMapIterator<QListWidgetItem *, QBluetoothServiceInfo> i(m_discoveredServices);
- while (i.hasNext()){
- i.next();
- if (serviceInfo.device().address() == i.value().device().address()){
+ const QBluetoothAddress address = serviceInfo.device().address();
+ for (const QBluetoothServiceInfo &info : qAsConst(m_discoveredServices)) {
+ if (info.device().address() == address)
return;
- }
}
QString remoteName;
if (serviceInfo.device().name().isEmpty())
- remoteName = serviceInfo.device().address().toString();
+ remoteName = address.toString();
else
remoteName = serviceInfo.device().name();
diff --git a/examples/bluetooth/btfiletransfer/main.cpp b/examples/bluetooth/btfiletransfer/main.cpp
index 2c2b6df5..1871e207 100644
--- a/examples/bluetooth/btfiletransfer/main.cpp
+++ b/examples/bluetooth/btfiletransfer/main.cpp
@@ -54,6 +54,7 @@
int main(int argc, char *argv[])
{
+ QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
RemoteSelector d;
diff --git a/examples/bluetooth/btfiletransfer/pindisplay.h b/examples/bluetooth/btfiletransfer/pindisplay.h
index b7ad8866..7acc517e 100644
--- a/examples/bluetooth/btfiletransfer/pindisplay.h
+++ b/examples/bluetooth/btfiletransfer/pindisplay.h
@@ -64,7 +64,7 @@ class pinDisplay : public QDialog
Q_OBJECT
public:
- explicit pinDisplay(QString title, QString pin, QWidget *parent = 0);
+ explicit pinDisplay(QString title, QString pin, QWidget *parent = nullptr);
~pinDisplay();
void setOkCancel();
diff --git a/examples/bluetooth/btfiletransfer/progress.h b/examples/bluetooth/btfiletransfer/progress.h
index 64a19ca0..afd72153 100644
--- a/examples/bluetooth/btfiletransfer/progress.h
+++ b/examples/bluetooth/btfiletransfer/progress.h
@@ -52,7 +52,7 @@
#define PROGRESS_H
#include <QDialog>
-#include <QTime>
+#include <QElapsedTimer>
QT_FORWARD_DECLARE_CLASS(QBluetoothTransferReply)
@@ -69,7 +69,7 @@ class Progress : public QDialog
Q_OBJECT
public:
- explicit Progress(QWidget *parent = 0);
+ explicit Progress(QWidget *parent = nullptr);
~Progress();
void setStatus(QString title, QString filename);
@@ -81,7 +81,7 @@ public Q_SLOTS:
private:
Ui::Progress *ui;
- QTime start;
+ QElapsedTimer start;
};
#endif // PROGRESS_H
diff --git a/examples/bluetooth/btfiletransfer/remoteselector.cpp b/examples/bluetooth/btfiletransfer/remoteselector.cpp
index 766bd028..b3c60cb3 100644
--- a/examples/bluetooth/btfiletransfer/remoteselector.cpp
+++ b/examples/bluetooth/btfiletransfer/remoteselector.cpp
@@ -171,18 +171,17 @@ void RemoteSelector::serviceDiscovered(const QBluetoothServiceInfo &serviceInfo)
// new QListWidgetItem(QString::fromLatin1("%1\t%2\t%3").arg(serviceInfo.device().address().toString(),
// serviceInfo.device().name(), serviceInfo.serviceName()));
- QMutableMapIterator<int, QBluetoothServiceInfo> i(m_discoveredServices);
- while (i.hasNext()){
- i.next();
- if (serviceInfo.device().address() == i.value().device().address()){
- i.setValue(serviceInfo);
+ const QBluetoothAddress address = serviceInfo.device().address();
+ for (QBluetoothServiceInfo &info : m_discoveredServices) {
+ if (info.device().address() == address){
+ info = serviceInfo;
return;
}
}
int row = ui->remoteDevices->rowCount();
ui->remoteDevices->insertRow(row);
- QTableWidgetItem *item = new QTableWidgetItem(serviceInfo.device().address().toString());
+ QTableWidgetItem *item = new QTableWidgetItem(address.toString());
ui->remoteDevices->setItem(row, 0, item);
item = new QTableWidgetItem(serviceInfo.device().name());
ui->remoteDevices->setItem(row, 1, item);
@@ -190,9 +189,7 @@ void RemoteSelector::serviceDiscovered(const QBluetoothServiceInfo &serviceInfo)
ui->remoteDevices->setItem(row, 2, item);
- QBluetoothLocalDevice::Pairing p;
-
- p = m_localDevice->pairingStatus(serviceInfo.device().address());
+ QBluetoothLocalDevice::Pairing p = m_localDevice->pairingStatus(address);
ui->remoteDevices->blockSignals(true);
@@ -275,13 +272,11 @@ void RemoteSelector::on_stopButton_clicked()
m_discoveryAgent->stop();
}
-QString RemoteSelector::addressToName(const QBluetoothAddress &address)
+QString RemoteSelector::addressToName(const QBluetoothAddress &address) const
{
- QMapIterator<int, QBluetoothServiceInfo> i(m_discoveredServices);
- while (i.hasNext()){
- i.next();
- if (i.value().device().address() == address)
- return i.value().device().name();
+ for (const QBluetoothServiceInfo &info : m_discoveredServices) {
+ if (info.device().address() == address)
+ return info.device().name();
}
return address.toString();
}
diff --git a/examples/bluetooth/btfiletransfer/remoteselector.h b/examples/bluetooth/btfiletransfer/remoteselector.h
index b0850c41..8266c8c4 100644
--- a/examples/bluetooth/btfiletransfer/remoteselector.h
+++ b/examples/bluetooth/btfiletransfer/remoteselector.h
@@ -78,7 +78,7 @@ class RemoteSelector : public QDialog
Q_OBJECT
public:
- explicit RemoteSelector(QWidget *parent = 0);
+ explicit RemoteSelector(QWidget *parent = nullptr);
~RemoteSelector();
void startDiscovery(const QBluetoothUuid &uuid);
@@ -95,7 +95,7 @@ private:
QPointer<pinDisplay> m_pindisplay;
bool m_pairingError;
- QString addressToName(const QBluetoothAddress &address);
+ QString addressToName(const QBluetoothAddress &address) const;
public Q_SLOTS:
void startDiscovery();
diff --git a/examples/bluetooth/btscanner/device.h b/examples/bluetooth/btscanner/device.h
index 7111e1ec..35dc7965 100644
--- a/examples/bluetooth/btscanner/device.h
+++ b/examples/bluetooth/btscanner/device.h
@@ -67,7 +67,7 @@ class DeviceDiscoveryDialog : public QDialog
Q_OBJECT
public:
- DeviceDiscoveryDialog(QWidget *parent = 0);
+ DeviceDiscoveryDialog(QWidget *parent = nullptr);
~DeviceDiscoveryDialog();
public slots:
diff --git a/examples/bluetooth/btscanner/main.cpp b/examples/bluetooth/btscanner/main.cpp
index ca762802..a84dcda9 100644
--- a/examples/bluetooth/btscanner/main.cpp
+++ b/examples/bluetooth/btscanner/main.cpp
@@ -54,6 +54,7 @@
int main(int argc, char *argv[])
{
+ QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
DeviceDiscoveryDialog d;
diff --git a/examples/bluetooth/btscanner/service.h b/examples/bluetooth/btscanner/service.h
index 94748dad..93d1e12b 100644
--- a/examples/bluetooth/btscanner/service.h
+++ b/examples/bluetooth/btscanner/service.h
@@ -66,7 +66,7 @@ class ServiceDiscoveryDialog : public QDialog
Q_OBJECT
public:
- ServiceDiscoveryDialog(const QString &name, const QBluetoothAddress &address, QWidget *parent = 0);
+ ServiceDiscoveryDialog(const QString &name, const QBluetoothAddress &address, QWidget *parent = nullptr);
~ServiceDiscoveryDialog();
public slots:
diff --git a/examples/bluetooth/chat/qmlchat.cpp b/examples/bluetooth/chat/qmlchat.cpp
index e7ce3f3a..6c8d2fa7 100644
--- a/examples/bluetooth/chat/qmlchat.cpp
+++ b/examples/bluetooth/chat/qmlchat.cpp
@@ -62,6 +62,7 @@
int main(int argc, char *argv[])
{
//QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+ QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication application(argc, argv);
QList<QBluetoothHostInfo> infos = QBluetoothLocalDevice::allDevices();
diff --git a/examples/bluetooth/heartrate-game/main.cpp b/examples/bluetooth/heartrate-game/main.cpp
index 099f82a7..05557c05 100644
--- a/examples/bluetooth/heartrate-game/main.cpp
+++ b/examples/bluetooth/heartrate-game/main.cpp
@@ -60,6 +60,7 @@
int main(int argc, char *argv[])
{
QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+ QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
ConnectionHandler connectionHandler;
diff --git a/examples/bluetooth/lowenergyscanner/device.cpp b/examples/bluetooth/lowenergyscanner/device.cpp
index 82179db1..e1d96be3 100644
--- a/examples/bluetooth/lowenergyscanner/device.cpp
+++ b/examples/bluetooth/lowenergyscanner/device.cpp
@@ -108,16 +108,18 @@ void Device::startDeviceDiscovery()
//! [les-devicediscovery-3]
void Device::addDevice(const QBluetoothDeviceInfo &info)
{
- if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
- auto d = new DeviceInfo(info);
- devices.append(d);
- setUpdate("Last device added: " + d->getName());
- }
+ if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration)
+ setUpdate("Last device added: " + info.name());
}
//! [les-devicediscovery-3]
void Device::deviceScanFinished()
{
+ const QList<QBluetoothDeviceInfo> foundDevices = discoveryAgent->discoveredDevices();
+ for (auto nextDevice : foundDevices)
+ if (nextDevice.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration)
+ devices.append(new DeviceInfo(nextDevice));
+
emit devicesUpdated();
m_deviceScanState = false;
emit stateChanged();
@@ -152,12 +154,12 @@ void Device::scanServices(const QString &address)
// We need the current device for service discovery.
for (auto d: qAsConst(devices)) {
- auto device = qobject_cast<DeviceInfo *>(d);
- if (!device)
- continue;
-
- if (device->getAddress() == address )
- currentDevice.setDevice(device->getDevice());
+ if (auto device = qobject_cast<DeviceInfo *>(d)) {
+ if (device->getAddress() == address ) {
+ currentDevice.setDevice(device->getDevice());
+ break;
+ }
+ }
}
if (!currentDevice.getDevice().isValid()) {
diff --git a/examples/bluetooth/lowenergyscanner/main.cpp b/examples/bluetooth/lowenergyscanner/main.cpp
index 351ab13a..f223b028 100644
--- a/examples/bluetooth/lowenergyscanner/main.cpp
+++ b/examples/bluetooth/lowenergyscanner/main.cpp
@@ -59,6 +59,7 @@
int main(int argc, char *argv[])
{
//QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+ QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Device d;
diff --git a/examples/bluetooth/picturetransfer/filetransfer.h b/examples/bluetooth/picturetransfer/filetransfer.h
index 5e2e08f9..4dee32fe 100644
--- a/examples/bluetooth/picturetransfer/filetransfer.h
+++ b/examples/bluetooth/picturetransfer/filetransfer.h
@@ -59,7 +59,7 @@ class FileTransfer : public QObject
Q_OBJECT
Q_PROPERTY(float progress READ getProgress NOTIFY progressChanged)
public:
- explicit FileTransfer(QObject *parent = 0);
+ explicit FileTransfer(QObject *parent = nullptr);
float getProgress() { return m_progress;}
signals:
diff --git a/examples/bluetooth/picturetransfer/main.cpp b/examples/bluetooth/picturetransfer/main.cpp
index b4a1e670..4622e87e 100644
--- a/examples/bluetooth/picturetransfer/main.cpp
+++ b/examples/bluetooth/picturetransfer/main.cpp
@@ -58,6 +58,7 @@
int main(int argc, char *argv[])
{
+ QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication application(argc, argv);
//! [Transfer-2]
QQuickView view;
diff --git a/examples/bluetooth/pingpong/main.cpp b/examples/bluetooth/pingpong/main.cpp
index fc1029de..7c34b135 100644
--- a/examples/bluetooth/pingpong/main.cpp
+++ b/examples/bluetooth/pingpong/main.cpp
@@ -58,6 +58,7 @@
int main(int argc, char *argv[])
{
//QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+ QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
PingPong pingPong;
QQmlApplicationEngine engine;
diff --git a/examples/bluetooth/scanner/qmlscanner.cpp b/examples/bluetooth/scanner/qmlscanner.cpp
index 4ead2364..2dbc0c27 100644
--- a/examples/bluetooth/scanner/qmlscanner.cpp
+++ b/examples/bluetooth/scanner/qmlscanner.cpp
@@ -56,6 +56,7 @@
int main(int argc, char *argv[])
{
//QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+ QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication application(argc, argv);
const QString mainQmlApp(QStringLiteral("qrc:/scanner.qml"));
QQuickView view;
diff --git a/examples/nfc/corkboard/android/AndroidManifest.xml b/examples/nfc/corkboard/android/AndroidManifest.xml
index ac0c5a67..3aec51ae 100644
--- a/examples/nfc/corkboard/android/AndroidManifest.xml
+++ b/examples/nfc/corkboard/android/AndroidManifest.xml
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
-<manifest package="org.qtproject.example" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
+<manifest package="org.qtproject.example" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto" android:extractNativeLibs="true">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
android:name="org.qtproject.qt5.android.bindings.QtActivity"
@@ -27,12 +27,10 @@
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
- <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
- <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
- <meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
+ <meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
diff --git a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java
index 5e8bf484..8a69b4c7 100644
--- a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java
+++ b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java
@@ -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.
@@ -47,6 +47,11 @@ import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
@@ -90,6 +95,9 @@ public class QtBluetoothLE {
private final int RUNNABLE_TIMEOUT = 3000; // 3 seconds
private final Handler timeoutHandler = new Handler(Looper.getMainLooper());
+ /* New BTLE scanner setup since Android SDK v21 */
+ private BluetoothLeScanner mBluetoothLeScanner = null;
+
private class TimeoutRunnable implements Runnable {
public TimeoutRunnable(int handle) { pendingJobHandle = handle; }
@Override
@@ -123,6 +131,7 @@ public class QtBluetoothLE {
@SuppressWarnings("WeakerAccess")
public QtBluetoothLE() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
}
public QtBluetoothLE(final String remoteAddress, Context context) {
@@ -131,7 +140,6 @@ public class QtBluetoothLE {
mRemoteGattAddress = remoteAddress;
}
-
/*************************************************************/
/* Device scan */
/*************************************************************/
@@ -144,27 +152,45 @@ public class QtBluetoothLE {
return true;
if (isEnabled) {
- mLeScanRunning = mBluetoothAdapter.startLeScan(leScanCallback);
+ Log.d(TAG, "New BTLE scanning API");
+ ScanSettings.Builder settingsBuilder = new ScanSettings.Builder();
+ settingsBuilder = settingsBuilder.setScanMode(ScanSettings.SCAN_MODE_BALANCED);
+ ScanSettings settings = settingsBuilder.build();
+
+ List<ScanFilter> filterList = new ArrayList<ScanFilter>(2);
+
+ mBluetoothLeScanner.startScan(filterList, settings, leScanCallback21);
+ mLeScanRunning = true;
} else {
- mBluetoothAdapter.stopLeScan(leScanCallback);
+ mBluetoothLeScanner.stopScan(leScanCallback21);
mLeScanRunning = false;
}
return (mLeScanRunning == isEnabled);
}
- // Device scan callback
- private final BluetoothAdapter.LeScanCallback leScanCallback =
- new BluetoothAdapter.LeScanCallback() {
+ // Device scan callback (SDK v21+)
+ private final ScanCallback leScanCallback21 = new ScanCallback() {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ super.onScanResult(callbackType, result);
+ leScanResult(qtObject, result.getDevice(), result.getRssi(), result.getScanRecord().getBytes());
+ }
- @Override
- public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
- if (qtObject == 0)
- return;
+ @Override
+ public void onBatchScanResults(List<ScanResult> results) {
+ super.onBatchScanResults(results);
+ for (ScanResult result : results)
+ leScanResult(qtObject, result.getDevice(), result.getRssi(), result.getScanRecord().getBytes());
- leScanResult(qtObject, device, rssi, scanRecord);
- }
- };
+ }
+
+ @Override
+ public void onScanFailed(int errorCode) {
+ super.onScanFailed(errorCode);
+ Log.d(TAG, "BTLE device scan failed with " + errorCode);
+ }
+ };
public native void leScanResult(long qtObject, BluetoothDevice device, int rssi, byte[] scanRecord);
@@ -203,7 +229,6 @@ public class QtBluetoothLE {
case BluetoothGatt.GATT_FAILURE: // Android's equivalent of "do not know what error it is"
errorCode = 1; break; //QLowEnergyController::UnknownError
case 8: // BLE_HCI_CONNECTION_TIMEOUT
- case 22: // BLE_HCI_LOCAL_HOST_TERMINATED_CONNECTION
Log.w(TAG, "Connection Error: Try to delay connect() call after previous activity");
errorCode = 5; break; //QLowEnergyController::ConnectionError
case 19: // BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION
@@ -212,6 +237,9 @@ public class QtBluetoothLE {
Log.w(TAG, "The remote host closed the connection");
errorCode = 7; //QLowEnergyController::RemoteHostClosedError
break;
+ case 22: // BLE_HCI_LOCAL_HOST_TERMINATED_CONNECTION
+ // Internally, Android maps PIN_OR_KEY_MISSING to GATT_CONN_TERMINATE_LOCAL_HOST
+ errorCode = 8; break; //QLowEnergyController::AuthorizationError
default:
Log.w(TAG, "Unhandled error code on connectionStateChanged: " + status + " " + newState);
errorCode = status; break; //TODO deal with all errors
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/bluetooth.pro b/src/bluetooth/bluetooth.pro
index b104a902..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 {
@@ -245,6 +225,24 @@ qtConfig(bluez) {
DEFINES += QT_WINRT_LIMITED_SERVICEDISCOVERY
DEFINES += QT_UCRTVERSION=$$WINDOWS_SDK_VERSION
}
+} else:win32 {
+ QT_PRIVATE = concurrent
+ DEFINES += QT_WIN_BLUETOOTH
+ LIBS += -lbthprops -lws2_32 -lsetupapi
+
+ include(windows/windows.pri)
+
+ SOURCES += \
+ qbluetoothdevicediscoveryagent_win.cpp \
+ qbluetoothlocaldevice_win.cpp \
+ qbluetoothserviceinfo_win.cpp \
+ qbluetoothservicediscoveryagent_win.cpp \
+ qbluetoothsocket_win.cpp \
+ qbluetoothserver_win.cpp \
+ qlowenergycontroller_win.cpp
+
+ PRIVATE_HEADERS += qlowenergycontroller_win_p.h \
+ qbluetoothsocket_win_p.h
} else {
message("Unsupported Bluetooth platform, will not build a working QtBluetooth library.")
message("Either no Qt D-Bus found or no BlueZ headers available.")
diff --git a/src/bluetooth/configure.json b/src/bluetooth/configure.json
index 53923e12..3bd95903 100644
--- a/src/bluetooth/configure.json
+++ b/src/bluetooth/configure.json
@@ -2,6 +2,12 @@
"module": "bluetooth",
"testDir": "../../config.tests",
+ "commandline": {
+ "options": {
+ "native-win32-bluetooth": "boolean"
+ }
+ },
+
"libraries": {
"bluez": {
"label": "BlueZ",
@@ -52,9 +58,16 @@
"condition": "features.bluez_le && tests.linux_crypto_api",
"output": [ "privateFeature" ]
},
+ "native-win32-bluetooth": {
+ "label": "Native Win32 Bluetooth",
+ "purpose": "Uses the native Win32 Bluetooth backend.",
+ "section": "Qt Bluetooth",
+ "autoDetect": false,
+ "output": [ "publicFeature", "feature" ]
+ },
"winrt_bt": {
"label": "WinRT Bluetooth API (desktop & UWP)",
- "condition": "config.win32 && tests.winrt_bt",
+ "condition": "config.win32 && !features.native-win32-bluetooth && tests.winrt_bt",
"output": [ "privateFeature" ]
},
"winrt_btle_no_pairing": {
@@ -85,6 +98,7 @@ Only classic Bluetooth will be available."
"bluez",
"bluez_le",
"linux_crypto_api",
+ "native-win32-bluetooth",
"winrt_bt",
"winrt_btle_no_pairing"
]
diff --git a/src/bluetooth/doc/src/bluetooth-index.qdoc b/src/bluetooth/doc/src/bluetooth-index.qdoc
index 08c57e40..0f5b96d6 100644
--- a/src/bluetooth/doc/src/bluetooth-index.qdoc
+++ b/src/bluetooth/doc/src/bluetooth-index.qdoc
@@ -51,7 +51,7 @@ Currently, the API is supported on the following platforms:
\li x
\li x
\li x
- \li
+ \li x
\row
\li Bluetooth LE Central
\li x
@@ -59,7 +59,7 @@ Currently, the API is supported on the following platforms:
\li x
\li x
\li x
- \li
+ \li x
\row
\li Bluetooth LE Peripheral
\li x
@@ -78,10 +78,12 @@ Currently, the API is supported on the following platforms:
\li
\endtable
-Despite there not being a Win32 port yet, the UWP backend is automatically used
-if the win32 target platform supports the required UWP APIs. Minimal requirement is Windows 10 version 1507
-with slightly improved service discovery since Windows 10 version 1607. Therefore Windows 7 and 8.x
-targets are excluded.
+Qt 5.14 adds a native Win32 port supporting Classic Bluetooth on Windows 7 or newer,
+and Bluetooth LE on Windows 8 or newer. It must be enabled at build time by configuration
+option -native-win32-bluetooth. The UWP backend is used by default if this option is not
+set and the Win32 target platform supports the required UWP APIs (minimal requirement is
+Windows 10 version 1507, with slightly improved service discovery since Windows 10 version
+1607).
\section1 Overview
@@ -115,7 +117,7 @@ import statement in your \c .qml file:
\section2 Building Qt Bluetooth
Despite the fact that the module can be built for all Qt platforms,
-the module is not ported to all of them. Not supported platforms such as Windows desktop
+the module is not ported to all of them. Not supported platforms
employ a fake or dummy backend which is automatically selected when the
platform is not supported. The dummy backend reports appropriate error messages
and values which allow the Qt Bluetooth developer to detect at runtime that the
@@ -168,6 +170,9 @@ The \l QtBluetooth module exports the following
\li qt.bluetooth.winrt
\li Enables logging of the \l {Qt for UWP}{UWP (Universal Windows Platform)}
implementation
+\row
+ \li qt.bluetooth.windows
+ \li Enables logging of the \l {Qt for Windows} {Win32} implementation
\endtable
Logging categories can be used to enable additional warning and debug output
diff --git a/src/bluetooth/doc/src/bluetooth-overview.qdoc b/src/bluetooth/doc/src/bluetooth-overview.qdoc
index e27dc0ed..33ccaa33 100644
--- a/src/bluetooth/doc/src/bluetooth-overview.qdoc
+++ b/src/bluetooth/doc/src/bluetooth-overview.qdoc
@@ -54,6 +54,11 @@
Windows. Therefore registering a service with custom values for these fields might not yield the
expected result on Windows.
+ Note that the Received Signal Strength Indicator (RSSI), as well as
+ the Manufacturer Specific Data advertised by Bluetooth LE devices are
+ not supported by the Win32 backend. Also, it is only possible to find
+ devices that have been previously paired through Windows Settings.
+
The following sections describe how to use the Qt Bluetooth C++ API classes
for the above use cases.
diff --git a/src/bluetooth/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/osx/btraii_p.h b/src/bluetooth/osx/btraii_p.h
new file mode 100644
index 00000000..6053d63b
--- /dev/null
+++ b/src/bluetooth/osx/btraii_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** 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 BTRAII_P_H
+#define 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 <QtCore/qglobal.h>
+
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+namespace DarwinBluetooth {
+
+enum class RetainPolicy
+{
+ 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:
+ StrongReference() = default;
+ StrongReference(void *object, RetainPolicy policy);
+ StrongReference(const StrongReference &other);
+ StrongReference(StrongReference &&other);
+
+ ~StrongReference();
+
+ StrongReference &operator = (const StrongReference &other) noexcept;
+ StrongReference &operator = (StrongReference &&other) noexcept;
+
+ void swap(StrongReference &other) noexcept
+ {
+ std::swap(objCInstance, other.objCInstance);
+ }
+
+ void reset();
+ void reset(void *newInstance, RetainPolicy policy);
+
+ template<class ObjCType>
+ ObjCType *getAs() const
+ {
+ return static_cast<ObjCType *>(objCInstance);
+ }
+
+ operator bool() const
+ {
+ return !!objCInstance;
+ }
+
+private:
+ void *objCInstance = nullptr;
+};
+
+class ScopedPointer final : public StrongReference
+{
+public:
+ ScopedPointer() = default;
+ ScopedPointer(void *instance, RetainPolicy policy)
+ : StrongReference(instance, policy)
+ {
+ }
+
+private:
+ Q_DISABLE_COPY_MOVE(ScopedPointer)
+};
+
+} // namespace DarwinBluetooth
+
+QT_END_NAMESPACE
+
+#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 6b742684..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
@@ -1540,6 +1557,7 @@ QT_USE_NAMESPACE
currentReadHandle = 0;
requestPending = false;
emit notifier->CBManagerError(qtUuid, QLowEnergyService::CharacteristicReadError);
+ [self handleReadWriteError:error];
[self performNextRequest];
}
return;
@@ -1658,6 +1676,7 @@ QT_USE_NAMESPACE
currentReadHandle = 0;
requestPending = false;
emit notifier->CBManagerError(qtUuid, QLowEnergyService::DescriptorReadError);
+ [self handleReadWriteError:error];
[self performNextRequest];
}
return;
@@ -1747,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);
@@ -1781,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/osxbtnotifier_p.h b/src/bluetooth/osx/osxbtnotifier_p.h
index dca6c268..397214d0 100644
--- a/src/bluetooth/osx/osxbtnotifier_p.h
+++ b/src/bluetooth/osx/osxbtnotifier_p.h
@@ -96,7 +96,6 @@ Q_SIGNALS:
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/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 b14c79cd..1e8ce0b8 100644
--- a/src/bluetooth/qbluetooth.cpp
+++ b/src/bluetooth/qbluetooth.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -101,6 +102,7 @@ namespace QBluetooth {
Q_LOGGING_CATEGORY(QT_BT, "qt.bluetooth")
Q_LOGGING_CATEGORY(QT_BT_ANDROID, "qt.bluetooth.android")
Q_LOGGING_CATEGORY(QT_BT_BLUEZ, "qt.bluetooth.bluez")
+Q_LOGGING_CATEGORY(QT_BT_WINDOWS, "qt.bluetooth.windows")
Q_LOGGING_CATEGORY(QT_BT_WINRT, "qt.bluetooth.winrt")
Q_LOGGING_CATEGORY(QT_BT_WINRT_SERVICE_THREAD, "qt.bluetooth.winrt.service.thread")
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp
index fb14850e..f38f8994 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp
@@ -78,6 +78,10 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT)
\note Due to API limitations it is only possible to find devices that have been paired using
Windows' settings on Windows.
+
+ \note The Win32 backend currently does not support the Received Signal Strength
+ Indicator (RSSI), as well as the Manufacturer Specific Data, or other data
+ updates advertised by Bluetooth LE devices after discovery.
*/
/*!
@@ -171,8 +175,6 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT)
This signal informs you that if your application is displaying this data, it
can be updated, rather than waiting until the discovery has finished.
- \note This signal is only emitted on Android, iOS, macOS, and BlueZ 5.x.
-
\sa QBluetoothDeviceInfo::rssi(), lowEnergyDiscoveryTimeout()
*/
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.h b/src/bluetooth/qbluetoothdevicediscoveryagent.h
index 59a8b456..e566d895 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent.h
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp
index ce3b8f10..d8cc5ace 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp
@@ -361,7 +361,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_bluez.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp
index a7def3d0..7dce9dae 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp
@@ -135,6 +135,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent
}
discoveredDevices.clear();
+ devicesProperties.clear();
if (managerBluez5) {
startBluez5(methods);
@@ -309,7 +310,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startBluez5(QBluetoothDeviceDiscover
if (path.path().indexOf(adapterBluez5->path()) != 0)
continue; //devices whose path doesn't start with same path we skip
- deviceFoundBluez5(path.path());
+ deviceFoundBluez5(path.path(), jt.value());
if (!isActive()) // Can happen if stop() was called from a slot in user code.
return;
}
@@ -403,50 +404,23 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_deviceFound(const QString &addres
emit q->deviceDiscovered(device);
}
-void QBluetoothDeviceDiscoveryAgentPrivate::deviceFoundBluez5(const QString& devicePath)
+// Returns invalid QBluetoothDeviceInfo in case of error
+static QBluetoothDeviceInfo createDeviceInfoFromBluez5Device(const QVariantMap& properties)
{
- Q_Q(QBluetoothDeviceDiscoveryAgent);
-
- if (!q->isActive())
- return;
-
- OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), devicePath,
- QDBusConnection::systemBus());
-
- if (device.adapter().path() != adapterBluez5->path())
- return;
-
- const QBluetoothAddress btAddress(device.address());
- if (btAddress.isNull()) // no point reporting an empty address
- return;
+ const QBluetoothAddress btAddress(properties[QStringLiteral("Address")].toString());
+ if (btAddress.isNull())
+ return QBluetoothDeviceInfo();
- const QString btName = device.alias();
- quint32 btClass = device.classProperty();
+ const QString btName = properties[QStringLiteral("Alias")].toString();
+ quint32 btClass = properties[QStringLiteral("Class")].toUInt();
- qCDebug(QT_BT_BLUEZ) << "Discovered: " << btAddress.toString() << btName
- << "Num UUIDs" << device.uUIDs().count()
- << "total device" << discoveredDevices.count() << "cached"
- << "RSSI" << device.rSSI() << "Class" << btClass
- << "Num ManufacturerData" << device.manufacturerData().size();
-
- OrgFreedesktopDBusPropertiesInterface *prop = new OrgFreedesktopDBusPropertiesInterface(
- QStringLiteral("org.bluez"), devicePath, QDBusConnection::systemBus(), q);
- QObject::connect(prop, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged,
- q, [this](const QString &interface, const QVariantMap &changedProperties,
- const QStringList &invalidatedProperties) {
- this->_q_PropertiesChanged(interface, changedProperties, invalidatedProperties);
- });
-
- // remember what we have to cleanup
- propertyMonitors.append(prop);
-
- // read information
QBluetoothDeviceInfo deviceInfo(btAddress, btName, btClass);
- deviceInfo.setRssi(device.rSSI());
+ deviceInfo.setRssi(qvariant_cast<short>(properties[QStringLiteral("RSSI")]));
QVector<QBluetoothUuid> uuids;
bool foundLikelyLowEnergyUuid = false;
- for (const auto &u: device.uUIDs()) {
+ const QStringList foundUuids = qvariant_cast<QStringList>(properties[QStringLiteral("UUIDs")]);
+ for (const auto &u: foundUuids) {
const QBluetoothUuid id(u);
if (id.isNull())
continue;
@@ -470,16 +444,56 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFoundBluez5(const QString& dev
deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration);
}
- const ManufacturerDataList deviceManufacturerData = device.manufacturerData();
+ const ManufacturerDataList deviceManufacturerData = qdbus_cast<ManufacturerDataList>(properties[QStringLiteral("ManufacturerData")]);
const QList<quint16> keys = deviceManufacturerData.keys();
for (quint16 key : keys)
deviceInfo.setManufacturerData(
key, deviceManufacturerData.value(key).variant().toByteArray());
+ return deviceInfo;
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::deviceFoundBluez5(const QString &devicePath,
+ const QVariantMap &properties)
+{
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+
+ if (!q->isActive())
+ return;
+
+ auto deviceAdapter = qvariant_cast<QDBusObjectPath>(properties[QStringLiteral("Adapter")]);
+ if (deviceAdapter.path() != adapterBluez5->path())
+ return;
+
+ // read information
+ QBluetoothDeviceInfo deviceInfo = createDeviceInfoFromBluez5Device(properties);
+ if (!deviceInfo.isValid()) // no point reporting an empty address
+ return;
+
+ qCDebug(QT_BT_BLUEZ) << "Discovered: " << deviceInfo.name() << deviceInfo.address()
+ << "Num UUIDs" << deviceInfo.serviceUuids().count()
+ << "total device" << discoveredDevices.count() << "cached"
+ << "RSSI" << deviceInfo.rssi()
+ << "Num ManufacturerData" << deviceInfo.manufacturerData().size();
+
+ OrgFreedesktopDBusPropertiesInterface *prop = new OrgFreedesktopDBusPropertiesInterface(
+ QStringLiteral("org.bluez"), devicePath, QDBusConnection::systemBus(), q);
+ QObject::connect(prop, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged,
+ q, [this](const QString &interface, const QVariantMap &changedProperties,
+ const QStringList &invalidatedProperties) {
+ this->_q_PropertiesChanged(interface, changedProperties, invalidatedProperties);
+ });
+
+ // remember what we have to cleanup
+ propertyMonitors.append(prop);
+
+ // Cache the properties so we do not have to access dbus every time to get a value
+ devicesProperties[devicePath] = properties;
+
for (int i = 0; i < discoveredDevices.size(); i++) {
if (discoveredDevices[i].address() == deviceInfo.address()) {
- if (discoveredDevices[i] == deviceInfo && lowEnergySearchTimeout > 0) {
- qCDebug(QT_BT_BLUEZ) << "Duplicate: " << btAddress.toString();
+ if (lowEnergySearchTimeout > 0 && discoveredDevices[i] == deviceInfo) {
+ qCDebug(QT_BT_BLUEZ) << "Duplicate: " << deviceInfo.address();
return;
}
discoveredDevices.replace(i, deviceInfo);
@@ -567,7 +581,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_InterfacesAdded(const QDBusObject
if (interfaces_and_properties.contains(QStringLiteral("org.bluez.Device1"))) {
// device interfaces belonging to different adapter
// will be filtered out by deviceFoundBluez5();
- deviceFoundBluez5(object_path.path());
+ deviceFoundBluez5(object_path.path(), interfaces_and_properties[QStringLiteral("org.bluez.Device1")]);
}
}
@@ -628,40 +642,84 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_discoveryInterrupted(const QStrin
void QBluetoothDeviceDiscoveryAgentPrivate::_q_PropertiesChanged(const QString &interface,
const QVariantMap &changed_properties,
- const QStringList &)
+ const QStringList &invalidated_properties)
{
Q_Q(QBluetoothDeviceDiscoveryAgent);
- if (interface == QStringLiteral("org.bluez.Device1")
- && (changed_properties.contains(QStringLiteral("RSSI"))
- || changed_properties.contains(QStringLiteral("ManufacturerData")))) {
- OrgFreedesktopDBusPropertiesInterface *props =
- qobject_cast<OrgFreedesktopDBusPropertiesInterface *>(q->sender());
- if (!props)
- return;
+ if (interface != QStringLiteral("org.bluez.Device1"))
+ return;
+
+ OrgFreedesktopDBusPropertiesInterface *props =
+ qobject_cast<OrgFreedesktopDBusPropertiesInterface *>(q->sender());
+ if (!props)
+ return;
+
+ const QString path = props->path();
+ if (!devicesProperties.contains(path))
+ return;
+
+ // Update the cached properties before checking changed_properties for RSSI and ManufacturerData
+ // so the cached properties are always up to date.
+ QVariantMap & properties = devicesProperties[path];
+ for (QVariantMap::const_iterator it = changed_properties.constBegin();
+ it != changed_properties.constEnd(); ++it) {
+ properties[it.key()] = it.value();
+ }
+
+ for (const QString & property : invalidated_properties)
+ properties.remove(property);
+
+ const auto info = createDeviceInfoFromBluez5Device(properties);
+ if (!info.isValid())
+ return;
+
+ if (changed_properties.contains(QStringLiteral("RSSI"))
+ || changed_properties.contains(QStringLiteral("ManufacturerData"))) {
- OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), props->path(),
- QDBusConnection::systemBus());
for (int i = 0; i < discoveredDevices.size(); i++) {
- if (discoveredDevices[i].address().toString() == device.address()) {
+ if (discoveredDevices[i].address() == info.address()) {
QBluetoothDeviceInfo::Fields updatedFields = QBluetoothDeviceInfo::Field::None;
if (changed_properties.contains(QStringLiteral("RSSI"))) {
- qCDebug(QT_BT_BLUEZ) << "Updating RSSI for" << device.address()
+ qCDebug(QT_BT_BLUEZ) << "Updating RSSI for" << info.address()
<< changed_properties.value(QStringLiteral("RSSI"));
discoveredDevices[i].setRssi(
changed_properties.value(QStringLiteral("RSSI")).toInt());
updatedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI);
}
if (changed_properties.contains(QStringLiteral("ManufacturerData"))) {
- qCDebug(QT_BT_BLUEZ) << "Updating ManufacturerData for" << device.address();
+ qCDebug(QT_BT_BLUEZ) << "Updating ManufacturerData for" << info.address();
ManufacturerDataList changedManufacturerData =
qdbus_cast< ManufacturerDataList >(changed_properties.value(QStringLiteral("ManufacturerData")));
const QList<quint16> keys = changedManufacturerData.keys();
+ bool wasNewValue = false;
for (quint16 key : keys) {
- if (discoveredDevices[i].setManufacturerData(key, changedManufacturerData.value(key).variant().toByteArray()))
- updatedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData);
+ bool added = discoveredDevices[i].setManufacturerData(key, changedManufacturerData.value(key).variant().toByteArray());
+ wasNewValue = (wasNewValue || added);
}
+
+ if (wasNewValue)
+ updatedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData);
}
+
+ if (lowEnergySearchTimeout > 0) {
+ if (discoveredDevices[i] != info) { // field other than manufacturer or rssi changed
+ if (discoveredDevices.at(i).name() == info.name()) {
+ qCDebug(QT_BT_BLUEZ) << "Almost Duplicate " << info.address()
+ << info.name() << "- replacing in place";
+ discoveredDevices.replace(i, info);
+ emit q->deviceDiscovered(info);
+ }
+ } else {
+ if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None))
+ emit q->deviceUpdated(discoveredDevices[i], updatedFields);
+ }
+
+ return;
+ }
+
+ discoveredDevices.replace(i, info);
+ emit q_ptr->deviceDiscovered(discoveredDevices[i]);
+
if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None))
emit q->deviceUpdated(discoveredDevices[i], updatedFields);
return;
@@ -669,5 +727,4 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_PropertiesChanged(const QString &
}
}
}
-
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm
index 08c1dbfa..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,117 +79,51 @@ 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()
{
- if (inquiryLE.data() && agentState != NonActive) {
+ if (inquiryLE && agentState != NonActive) {
// 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.data() && [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,8 +236,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE()
this, DeviceMemFunPtr(&QBluetoothDeviceDiscoveryAgentPrivate::deviceFound));
// Check queue and create scanner:
- inquiryLE.reset([[LEDeviceInquiryObjC alloc] initWithNotifier:notifier.data()]);
- if (inquiryLE.data())
+ inquiryLE.reset([[LEInquiryObjC alloc] initWithNotifier:notifier.data()],
+ DarwinBluetooth::RetainPolicy::noInitialRetain);
+ if (inquiryLE)
notifier.take(); // Whatever happens next, inquiryLE is already the owner ...
dispatch_queue_t leQueue(qt_LE_queue());
@@ -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 71ebc88e..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.data()) {
- // 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.data();
-}
-
-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.data())
- 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 3e76c13d..27220a09 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -58,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>
@@ -78,7 +84,20 @@ class QDBusVariant;
QT_END_NAMESPACE
#endif
-#ifdef QT_WINRT_BLUETOOTH
+#ifdef QT_WIN_BLUETOOTH
+QT_BEGIN_NAMESPACE
+class QThread;
+
+class ThreadWorkerDeviceDiscovery : public QObject
+{
+ Q_OBJECT
+signals:
+ void discoveryCompleted(const QVariant res);
+};
+
+QT_END_NAMESPACE
+
+#elif defined(QT_WINRT_BLUETOOTH)
#include <QtCore/QPointer>
#include <QtCore/QTimer>
@@ -93,11 +112,15 @@ class QWinRTBluetoothDeviceDiscoveryWorker;
#endif
class QBluetoothDeviceDiscoveryAgentPrivate
-#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH)
+#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(QT_WIN_BLUETOOTH) \
+ || 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)
@@ -160,11 +183,39 @@ private:
QTimer *discoveryTimer = nullptr;
QList<OrgFreedesktopDBusPropertiesInterface *> propertyMonitors;
- void deviceFoundBluez5(const QString& devicePath);
+ void deviceFoundBluez5(const QString &devicePath, const QVariantMap &properties);
void startBluez5(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods);
bool useExtendedDiscovery;
QTimer extendedDiscoveryTimer;
+ QMap<QString, QVariantMap> devicesProperties;
+#endif
+
+#ifdef QT_WIN_BLUETOOTH
+public:
+ static QString discoveredLeDeviceSystemPath(const QBluetoothAddress &deviceAddress);
+
+private:
+ void cancelDiscovery();
+ void restartDiscovery();
+ void finishDiscovery(QBluetoothDeviceDiscoveryAgent::Error errorCode, const QString &errorText);
+
+ void startLeDevicesDiscovery();
+ void completeLeDevicesDiscovery(const QVariant &res);
+ void startClassicDevicesDiscovery(Qt::HANDLE hSearch = nullptr);
+ void completeClassicDevicesDiscovery(const QVariant &res);
+
+ void processDiscoveredDevice(const QBluetoothDeviceInfo &foundDevice);
+
+ QBluetoothAddress adapterAddress;
+ bool pendingCancel;
+ bool pendingStart;
+ bool active;
+
+ QThread *threadLE = nullptr;
+ QThread *threadClassic = nullptr;
+ ThreadWorkerDeviceDiscovery *threadWorkerLE = nullptr;
+ ThreadWorkerDeviceDiscovery *threadWorkerClassic = nullptr;
#endif
#ifdef QT_WINRT_BLUETOOTH
@@ -180,6 +231,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_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp
new file mode 100644
index 00000000..159428d4
--- /dev/null
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp
@@ -0,0 +1,557 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothdevicediscoveryagent.h"
+#include "qbluetoothuuid.h"
+#include "qbluetoothdevicediscoveryagent_p.h"
+#include "qbluetoothlocaldevice_p.h"
+
+#include <QtCore/qmutex.h>
+#include <QtCore/QThread>
+#include <QtCore/QLoggingCategory>
+
+#include <qt_windows.h>
+#include <setupapi.h>
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+struct LeDeviceEntry {
+ QString devicePath;
+ QBluetoothAddress deviceAddress;
+};
+
+Q_GLOBAL_STATIC(QVector<LeDeviceEntry>, cachedLeDeviceEntries)
+Q_GLOBAL_STATIC(QMutex, cachedLeDeviceEntriesGuard)
+
+static QString devicePropertyString(
+ HDEVINFO hDeviceInfo,
+ const PSP_DEVINFO_DATA deviceInfoData,
+ DWORD registryProperty)
+{
+ DWORD propertyRegDataType = 0;
+ DWORD propertyBufferSize = 0;
+ QByteArray propertyBuffer;
+
+ while (!::SetupDiGetDeviceRegistryProperty(
+ hDeviceInfo,
+ deviceInfoData,
+ registryProperty,
+ &propertyRegDataType,
+ propertyBuffer.isEmpty() ? nullptr : reinterpret_cast<PBYTE>(propertyBuffer.data()),
+ propertyBuffer.size(),
+ &propertyBufferSize)) {
+
+ const DWORD error = ::GetLastError();
+ if (error != ERROR_INSUFFICIENT_BUFFER
+ || (propertyRegDataType != REG_SZ
+ && propertyRegDataType != REG_EXPAND_SZ)) {
+ return QString();
+ }
+
+ // add +2 byte to allow to successfully convert to qstring
+ propertyBuffer.fill(0, propertyBufferSize + sizeof(wchar_t));
+ }
+
+ return QString::fromWCharArray(reinterpret_cast<const wchar_t *>(
+ propertyBuffer.constData()));
+}
+
+static QString deviceName(HDEVINFO hDeviceInfo, PSP_DEVINFO_DATA deviceInfoData)
+{
+ return devicePropertyString(hDeviceInfo, deviceInfoData, SPDRP_FRIENDLYNAME);
+}
+
+static QString deviceSystemPath(const PSP_INTERFACE_DEVICE_DETAIL_DATA detailData)
+{
+ return QString::fromWCharArray(detailData->DevicePath);
+}
+
+static QBluetoothAddress deviceAddress(const QString &devicePath)
+{
+ const int firstbound = devicePath.indexOf(QStringLiteral("dev_"));
+ const int lastbound = devicePath.indexOf(QLatin1Char('#'), firstbound);
+ const QString hex = devicePath.mid(firstbound + 4, lastbound - firstbound - 4);
+ bool ok = false;
+ return QBluetoothAddress(hex.toULongLong(&ok, 16));
+}
+
+static QBluetoothDeviceInfo createClassicDeviceInfo(const BLUETOOTH_DEVICE_INFO &foundDevice)
+{
+ QBluetoothDeviceInfo deviceInfo(
+ QBluetoothAddress(foundDevice.Address.ullLong),
+ QString::fromWCharArray(foundDevice.szName),
+ foundDevice.ulClassofDevice);
+
+ deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration);
+
+ if (foundDevice.fRemembered)
+ deviceInfo.setCached(true);
+ return deviceInfo;
+}
+
+static QBluetoothDeviceInfo findFirstClassicDevice(
+ DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND *hSearch)
+{
+ BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = {};
+ searchParams.dwSize = sizeof(searchParams);
+ searchParams.cTimeoutMultiplier = 10; // 12.8 sec
+ searchParams.fIssueInquiry = TRUE;
+ searchParams.fReturnAuthenticated = TRUE;
+ searchParams.fReturnConnected = TRUE;
+ searchParams.fReturnRemembered = TRUE;
+ searchParams.fReturnUnknown = TRUE;
+ searchParams.hRadio = nullptr;
+
+ BLUETOOTH_DEVICE_INFO deviceInfo = {};
+ deviceInfo.dwSize = sizeof(deviceInfo);
+
+ const HBLUETOOTH_DEVICE_FIND hFind = ::BluetoothFindFirstDevice(
+ &searchParams, &deviceInfo);
+
+ QBluetoothDeviceInfo foundDevice;
+ if (hFind) {
+ *hSearch = hFind;
+ *systemErrorCode = NO_ERROR;
+ foundDevice = createClassicDeviceInfo(deviceInfo);
+ } else {
+ *systemErrorCode = int(::GetLastError());
+ }
+
+ return foundDevice;
+}
+
+static QBluetoothDeviceInfo findNextClassicDevice(
+ DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND hSearch)
+{
+ BLUETOOTH_DEVICE_INFO deviceInfo = {};
+ deviceInfo.dwSize = sizeof(deviceInfo);
+
+ QBluetoothDeviceInfo foundDevice;
+ if (!::BluetoothFindNextDevice(hSearch, &deviceInfo)) {
+ *systemErrorCode = int(::GetLastError());
+ } else {
+ *systemErrorCode = NO_ERROR;
+ foundDevice = createClassicDeviceInfo(deviceInfo);
+ }
+
+ return foundDevice;
+}
+
+static void closeClassicSearch(HBLUETOOTH_DEVICE_FIND hSearch)
+{
+ if (hSearch)
+ ::BluetoothFindDeviceClose(hSearch);
+}
+
+static QVector<QBluetoothDeviceInfo> enumerateLeDevices(
+ DWORD *systemErrorCode)
+{
+ // GUID_BLUETOOTHLE_DEVICE_INTERFACE
+ const QUuid deviceInterfaceGuid("781aee18-7733-4ce4-add0-91f41c67b592");
+ const HDEVINFO hDeviceInfo = ::SetupDiGetClassDevs(
+ reinterpret_cast<const GUID *>(&deviceInterfaceGuid),
+ nullptr,
+ nullptr,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+
+ if (hDeviceInfo == INVALID_HANDLE_VALUE) {
+ *systemErrorCode = int(::GetLastError());
+ return QVector<QBluetoothDeviceInfo>();
+ }
+
+ QVector<QBluetoothDeviceInfo> foundDevices;
+ DWORD index = 0;
+
+ QVector<LeDeviceEntry> cachedEntries;
+
+ for (;;) {
+ SP_DEVICE_INTERFACE_DATA deviceInterfaceData = {};
+ deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
+
+ if (!::SetupDiEnumDeviceInterfaces(
+ hDeviceInfo,
+ nullptr,
+ reinterpret_cast<const GUID *>(&deviceInterfaceGuid),
+ index++,
+ &deviceInterfaceData)) {
+ *systemErrorCode = int(::GetLastError());
+ break;
+ }
+
+ DWORD deviceInterfaceDetailDataSize = 0;
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ hDeviceInfo,
+ &deviceInterfaceData,
+ nullptr,
+ deviceInterfaceDetailDataSize,
+ &deviceInterfaceDetailDataSize,
+ nullptr)) {
+ if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ *systemErrorCode = int(::GetLastError());
+ break;
+ }
+ }
+
+ SP_DEVINFO_DATA deviceInfoData = {};
+ deviceInfoData.cbSize = sizeof(deviceInfoData);
+
+ QByteArray deviceInterfaceDetailDataBuffer(
+ deviceInterfaceDetailDataSize, 0);
+
+ PSP_INTERFACE_DEVICE_DETAIL_DATA deviceInterfaceDetailData =
+ reinterpret_cast<PSP_INTERFACE_DEVICE_DETAIL_DATA>
+ (deviceInterfaceDetailDataBuffer.data());
+
+ deviceInterfaceDetailData->cbSize =
+ sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
+
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ hDeviceInfo,
+ &deviceInterfaceData,
+ deviceInterfaceDetailData,
+ deviceInterfaceDetailDataBuffer.size(),
+ &deviceInterfaceDetailDataSize,
+ &deviceInfoData)) {
+ *systemErrorCode = int(::GetLastError());
+ break;
+ }
+
+ const QString systemPath = deviceSystemPath(deviceInterfaceDetailData);
+ const QBluetoothAddress address = deviceAddress(systemPath);
+ if (address.isNull())
+ continue;
+ const QString name = deviceName(hDeviceInfo, &deviceInfoData);
+
+ QBluetoothDeviceInfo deviceInfo(address, name, QBluetoothDeviceInfo::MiscellaneousDevice);
+ deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
+ deviceInfo.setCached(true);
+
+ foundDevices << deviceInfo;
+ cachedEntries << LeDeviceEntry{systemPath, address};
+ }
+
+ QMutexLocker locker(cachedLeDeviceEntriesGuard());
+ cachedLeDeviceEntries()->swap(cachedEntries);
+
+ ::SetupDiDestroyDeviceInfoList(hDeviceInfo);
+ return foundDevices;
+}
+
+struct DiscoveryResult {
+ QVector<QBluetoothDeviceInfo> devices;
+ DWORD systemErrorCode;
+ HBLUETOOTH_DEVICE_FIND hSearch; // Used only for classic devices
+};
+
+QString QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath(
+ const QBluetoothAddress &deviceAddress)
+{
+ // update LE devices cache
+ DWORD dummyErrorCode;
+ enumerateLeDevices(&dummyErrorCode);
+
+ QMutexLocker locker(cachedLeDeviceEntriesGuard());
+ for (const LeDeviceEntry &e: *cachedLeDeviceEntries) {
+ if (e.deviceAddress == deviceAddress)
+ return e.devicePath;
+ }
+ return QString();
+}
+
+QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(
+ const QBluetoothAddress &deviceAdapter,
+ QBluetoothDeviceDiscoveryAgent *parent)
+ : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry)
+ , lastError(QBluetoothDeviceDiscoveryAgent::NoError)
+ , adapterAddress(deviceAdapter)
+ , pendingCancel(false)
+ , pendingStart(false)
+ , active(false)
+ , lowEnergySearchTimeout(-1) // remains -1 -> timeout not supported
+ , q_ptr(parent)
+{
+ threadLE = new QThread;
+ threadWorkerLE = new ThreadWorkerDeviceDiscovery;
+ threadWorkerLE->moveToThread(threadLE);
+ connect(threadWorkerLE, &ThreadWorkerDeviceDiscovery::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery);
+ connect(threadLE, &QThread::finished, threadWorkerLE, &ThreadWorkerDeviceDiscovery::deleteLater);
+ connect(threadLE, &QThread::finished, threadLE, &QThread::deleteLater);
+ threadLE->start();
+
+ threadClassic = new QThread;
+ threadWorkerClassic = new ThreadWorkerDeviceDiscovery;
+ threadWorkerClassic->moveToThread(threadClassic);
+ connect(threadWorkerClassic, &ThreadWorkerDeviceDiscovery::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery);
+ connect(threadClassic, &QThread::finished, threadWorkerClassic, &ThreadWorkerDeviceDiscovery::deleteLater);
+ connect(threadClassic, &QThread::finished, threadClassic, &QThread::deleteLater);
+ threadClassic->start();
+}
+
+QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
+{
+ if (active)
+ stop();
+ if (threadLE)
+ threadLE->quit();
+ if (threadClassic)
+ threadClassic->quit();
+}
+
+bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const
+{
+ if (pendingStart)
+ return true;
+ if (pendingCancel)
+ return false;
+ return active;
+}
+
+QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods()
+{
+ return (LowEnergyMethod | ClassicMethod);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods)
+{
+ requestedMethods = methods;
+
+ if (pendingCancel == true) {
+ pendingStart = true;
+ return;
+ }
+
+ const QList<QBluetoothHostInfo> foundLocalAdapters =
+ QBluetoothLocalDevicePrivate::localAdapters();
+
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+
+ if (foundLocalAdapters.isEmpty()) {
+ qCWarning(QT_BT_WINDOWS) << "Device does not support Bluetooth";
+ lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError;
+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Device does not support Bluetooth");
+ emit q->error(lastError);
+ return;
+ }
+
+ // Check for the local adapter address.
+ auto equals = [this](const QBluetoothHostInfo &adapterInfo) {
+ return adapterAddress == QBluetoothAddress()
+ || adapterAddress == adapterInfo.address();
+ };
+ const auto end = foundLocalAdapters.cend();
+ const auto it = std::find_if(foundLocalAdapters.cbegin(), end, equals);
+ if (it == end) {
+ qCWarning(QT_BT_WINDOWS) << "Incorrect local adapter passed.";
+ lastError = QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError;
+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Passed address is not a local device.");
+ emit q->error(lastError);
+ return;
+ }
+
+ discoveredDevices.clear();
+ active = true;
+
+ // We run LE search first, as it is fast on windows.
+ if (requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)
+ startLeDevicesDiscovery();
+ else if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod)
+ startClassicDevicesDiscovery();
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::stop()
+{
+ if (!active)
+ return;
+
+ pendingCancel = true;
+ pendingStart = false;
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::cancelDiscovery()
+{
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ active = false;
+ pendingCancel = false;
+ emit q->canceled();
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::restartDiscovery()
+{
+ pendingStart = false;
+ pendingCancel = false;
+ start(requestedMethods);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::finishDiscovery(QBluetoothDeviceDiscoveryAgent::Error errorCode, const QString &errorText)
+{
+ active = false;
+ pendingStart = false;
+ pendingCancel = false;
+ lastError = errorCode;
+ errorString = errorText;
+
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ if (errorCode == QBluetoothDeviceDiscoveryAgent::NoError)
+ emit q->finished();
+ else
+ emit q->error(lastError);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::startLeDevicesDiscovery()
+{
+ const auto threadWorker = threadWorkerLE;
+ QMetaObject::invokeMethod(threadWorkerLE, [threadWorker]()
+ {
+ DiscoveryResult result; // Do not use hSearch here!
+ result.systemErrorCode = NO_ERROR;
+ result.devices = enumerateLeDevices(&result.systemErrorCode);
+ emit threadWorker->discoveryCompleted(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery(const QVariant &res)
+{
+ if (pendingCancel && !pendingStart) {
+ cancelDiscovery();
+ } else if (pendingStart) {
+ restartDiscovery();
+ } else {
+ const DiscoveryResult result = res.value<DiscoveryResult>();
+ if (result.systemErrorCode == NO_ERROR || result.systemErrorCode == ERROR_NO_MORE_ITEMS) {
+ for (const QBluetoothDeviceInfo &device : result.devices)
+ processDiscoveredDevice(device);
+
+ // We run classic search at second, as it is slow on windows.
+ if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod)
+ startClassicDevicesDiscovery();
+ else
+ finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, qt_error_string(NO_ERROR));
+ } else {
+ const QBluetoothDeviceDiscoveryAgent::Error error = (result.systemErrorCode == ERROR_INVALID_HANDLE)
+ ? QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError
+ : QBluetoothDeviceDiscoveryAgent::InputOutputError;
+ finishDiscovery(error, qt_error_string(result.systemErrorCode));
+ }
+ }
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::startClassicDevicesDiscovery(Qt::HANDLE hSearch)
+{
+ const auto threadWorker = threadWorkerClassic;
+ QMetaObject::invokeMethod(threadWorker, [threadWorker, hSearch]()
+ {
+ DiscoveryResult result;
+ result.hSearch = hSearch;
+ result.systemErrorCode = NO_ERROR;
+
+ const QBluetoothDeviceInfo device = hSearch
+ ? findNextClassicDevice(&result.systemErrorCode, result.hSearch)
+ : findFirstClassicDevice(&result.systemErrorCode, &result.hSearch);
+
+ result.devices.append(device);
+ emit threadWorker->discoveryCompleted(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery(const QVariant &res)
+{
+ const DiscoveryResult result = res.value<DiscoveryResult>();
+ if (pendingCancel && !pendingStart) {
+ closeClassicSearch(result.hSearch);
+ cancelDiscovery();
+ } else if (pendingStart) {
+ closeClassicSearch(result.hSearch);
+ restartDiscovery();
+ } else {
+ if (result.systemErrorCode == ERROR_NO_MORE_ITEMS) {
+ closeClassicSearch(result.hSearch);
+ finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, QString());
+ } else if (result.systemErrorCode == NO_ERROR) {
+ if (result.hSearch) {
+ for (const QBluetoothDeviceInfo &device : result.devices)
+ processDiscoveredDevice(device);
+
+ startClassicDevicesDiscovery(result.hSearch);
+ } else {
+ finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, QString());
+ }
+ } else {
+ closeClassicSearch(result.hSearch);
+ const QBluetoothDeviceDiscoveryAgent::Error error = (result.systemErrorCode == ERROR_INVALID_HANDLE)
+ ? QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError
+ : QBluetoothDeviceDiscoveryAgent::InputOutputError;
+ finishDiscovery(error, qt_error_string(result.systemErrorCode));
+ }
+ }
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevice(
+ const QBluetoothDeviceInfo &foundDevice)
+{
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+
+ auto equalAddress = [foundDevice](const QBluetoothDeviceInfo &targetDevice) {
+ return foundDevice.address() == targetDevice.address(); };
+ auto end = discoveredDevices.end();
+ auto deviceIt = std::find_if(discoveredDevices.begin(), end, equalAddress);
+ if (deviceIt == end) {
+ qCDebug(QT_BT_WINDOWS) << "Found device: " << foundDevice.name() << foundDevice.address();
+ discoveredDevices.append(foundDevice);
+ emit q->deviceDiscovered(foundDevice);
+ } else {
+ qCDebug(QT_BT_WINDOWS) << "Updating device:" << deviceIt->name() << deviceIt->address();
+ // merge service uuids
+ QList<QBluetoothUuid> uuids = deviceIt->serviceUuids();
+ uuids.append(foundDevice.serviceUuids());
+ const QSet<QBluetoothUuid> uuidSet = uuids.toSet();
+ if (deviceIt->serviceUuids().count() != uuidSet.count())
+ deviceIt->setServiceUuids(uuidSet.toList().toVector());
+ if (deviceIt->coreConfigurations() != foundDevice.coreConfigurations())
+ deviceIt->setCoreConfigurations(
+ QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration);
+ }
+}
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(DiscoveryResult)
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
index 30aa3fcc..2562395a 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
@@ -50,6 +50,7 @@
#include <QtBluetooth/private/qtbluetoothglobal_p.h>
#include <QtBluetooth/private/qbluetoothutils_winrt_p.h>
#include <QtCore/QLoggingCategory>
+#include <QtCore/qmutex.h>
#include <QtCore/private/qeventdispatcher_winrt_p.h>
#include <QtCore/qmutex.h>
@@ -765,13 +766,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 = adInfo.services.toList();
+ uuids = adInfo.services;
} else {
IVectorView <GenericAttributeProfile::GattDeviceService *> *deviceServices;
hr = device->get_GattServices(&deviceServices);
@@ -797,7 +798,7 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IB
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));
@@ -887,9 +888,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::registerDevice(const QBluetoothDevic
// merge service uuids
QList<QBluetoothUuid> uuids = iter->serviceUuids();
uuids.append(info.serviceUuids());
- const QSet<QBluetoothUuid> uuidSet = uuids.toSet();
+ const QSet<QBluetoothUuid> uuidSet(uuids.begin(), uuids.end());
if (iter->serviceUuids().count() != uuidSet.count())
- iter->setServiceUuids(uuidSet.toList().toVector());
+ iter->setServiceUuids(uuidSet.values().toVector());
if (iter->coreConfigurations() != info.coreConfigurations())
iter->setCoreConfigurations(QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration);
return;
diff --git a/src/bluetooth/qbluetoothdeviceinfo.cpp b/src/bluetooth/qbluetoothdeviceinfo.cpp
index 46df5c7b..443902dc 100644
--- a/src/bluetooth/qbluetoothdeviceinfo.cpp
+++ b/src/bluetooth/qbluetoothdeviceinfo.cpp
@@ -282,7 +282,9 @@ QBluetoothDeviceInfoPrivate::QBluetoothDeviceInfoPrivate() :
serviceClasses(QBluetoothDeviceInfo::NoService),
majorDeviceClass(QBluetoothDeviceInfo::MiscellaneousDevice),
minorDeviceClass(0),
+#if QT_DEPRECATED_SINCE(5, 13)
serviceUuidsCompleteness(QBluetoothDeviceInfo::DataUnavailable),
+#endif
deviceCoreConfiguration(QBluetoothDeviceInfo::UnknownCoreConfiguration)
{
}
@@ -322,7 +324,9 @@ QBluetoothDeviceInfo::QBluetoothDeviceInfo(const QBluetoothAddress &address, con
d->majorDeviceClass = static_cast<MajorDeviceClass>((classOfDevice >> 8) & 0x1f);
d->serviceClasses = static_cast<ServiceClasses>((classOfDevice >> 13) & 0x7ff);
+#if QT_DEPRECATED_SINCE(5, 13)
d->serviceUuidsCompleteness = DataUnavailable;
+#endif
d->valid = true;
d->cached = false;
@@ -353,7 +357,9 @@ QBluetoothDeviceInfo::QBluetoothDeviceInfo(const QBluetoothUuid &uuid, const QSt
d->majorDeviceClass = static_cast<MajorDeviceClass>((classOfDevice >> 8) & 0x1f);
d->serviceClasses = static_cast<ServiceClasses>((classOfDevice >> 13) & 0x7ff);
+#if QT_DEPRECATED_SINCE(5, 13)
d->serviceUuidsCompleteness = DataUnavailable;
+#endif
d->valid = true;
d->cached = false;
@@ -420,7 +426,9 @@ QBluetoothDeviceInfo &QBluetoothDeviceInfo::operator=(const QBluetoothDeviceInfo
d->serviceClasses = other.d_func()->serviceClasses;
d->valid = other.d_func()->valid;
d->cached = other.d_func()->cached;
+#if QT_DEPRECATED_SINCE(5, 13)
d->serviceUuidsCompleteness = other.d_func()->serviceUuidsCompleteness;
+#endif
d->serviceUuids = other.d_func()->serviceUuids;
d->manufacturerData = other.d_func()->manufacturerData;
d->rssi = other.d_func()->rssi;
@@ -451,8 +459,10 @@ bool QBluetoothDeviceInfo::operator==(const QBluetoothDeviceInfo &other) const
return false;
if (d->address != other.d_func()->address)
return false;
+#if QT_DEPRECATED_SINCE(5, 13)
if (d->serviceUuidsCompleteness != other.d_func()->serviceUuidsCompleteness)
return false;
+#endif
if (d->serviceUuids.count() != other.d_func()->serviceUuids.count())
return false;
if (d->serviceUuids != other.d_func()->serviceUuids)
@@ -538,6 +548,7 @@ quint8 QBluetoothDeviceInfo::minorDeviceClass() const
return d->minorDeviceClass;
}
+#if QT_DEPRECATED_SINCE(5, 13)
/*!
\deprecated
@@ -551,6 +562,7 @@ void QBluetoothDeviceInfo::setServiceUuids(const QList<QBluetoothUuid> &uuids,
d->serviceUuids = uuids.toVector();
d->serviceUuidsCompleteness = completeness;
}
+#endif
/*!
Sets the list of service UUIDs to \a uuids.
@@ -577,7 +589,7 @@ QVector<QBluetoothUuid> QBluetoothDeviceInfo::serviceUuids() const
return d->serviceUuids;
}
-#else
+#elif QT_DEPRECATED_SINCE(5, 13)
/*!
Returns the list of service UUIDS supported by the device. If \a completeness is not 0 it will
@@ -596,8 +608,23 @@ QList<QBluetoothUuid> QBluetoothDeviceInfo::serviceUuids(DataCompleteness *compl
return d->serviceUuids.toList();
}
+
+#else
+
+/*!
+ Returns the list of service UUIDS supported by the device. Most commonly this
+ list of uuids represents custom uuids or a uuid value specified by
+ \l QBluetoothUuid::ServiceClassUuid.
+*/
+QList<QBluetoothUuid> QBluetoothDeviceInfo::serviceUuids() const
+{
+ Q_D(const QBluetoothDeviceInfo);
+ return d->serviceUuids.toList();
+}
+
#endif //QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#if QT_DEPRECATED_SINCE(5, 13)
/*!
\deprecated
@@ -611,7 +638,7 @@ QBluetoothDeviceInfo::DataCompleteness QBluetoothDeviceInfo::serviceUuidsComplet
Q_D(const QBluetoothDeviceInfo);
return d->serviceUuidsCompleteness;
}
-
+#endif
/*!
Returns all manufacturer ids attached to this device information.
@@ -642,6 +669,10 @@ QVector<quint16> QBluetoothDeviceInfo::manufacturerIds() const
The interpretation of the data octets is defined by the manufacturer
specified by the company identifier.
+ \note The remote device may provide multiple data entries per manufacturerId.
+ This function only returns the first entry. If all entries are needed use
+ \l manufacturerData() which returns a multi hash.
+
\sa manufacturerIds(), setManufacturerData()
\since 5.12
*/
@@ -653,7 +684,10 @@ QByteArray QBluetoothDeviceInfo::manufacturerData(quint16 manufacturerId) const
/*!
Sets the advertised manufacturer \a data for the given \a manufacturerId.
- Returns true if it was inserted or changed, false if it was already known.
+ Returns \c true if it was inserted, \c false if it was already known.
+
+ Since Qt 5.14, different values for \a data and the same \a manufacturerId no longer
+ replace each other but are accumulated for the duration of a device scan.
\sa manufacturerData
\since 5.12
@@ -661,16 +695,25 @@ QByteArray QBluetoothDeviceInfo::manufacturerData(quint16 manufacturerId) const
bool QBluetoothDeviceInfo::setManufacturerData(quint16 manufacturerId, const QByteArray &data)
{
Q_D(QBluetoothDeviceInfo);
- const auto it = d->manufacturerData.find(manufacturerId);
- if (it != d->manufacturerData.end() && *it == data)
- return false;
- d->manufacturerData.insert(manufacturerId, data);
+ QHash<quint16, QByteArray>::const_iterator it = d->manufacturerData.find(manufacturerId);
+ while (it != d->manufacturerData.end() && it.key() == manufacturerId) {
+ if (*it == data)
+ return false;
+ it++;
+ }
+
+ d->manufacturerData.insertMulti(manufacturerId, data);
return true;
}
/*!
Returns the complete set of all manufacturer data.
+ Some devices may provide multiple manufacturer data entries per manufacturer ID.
+ An example might be a Bluetooth Low Energy device that sends a different manufacturer data via
+ advertisement packets and scan response packets respectively. Therefore the returned hash table
+ may have multiple entries per manufacturer ID or hash key.
+
\sa setManufacturerData
\since 5.12
*/
diff --git a/src/bluetooth/qbluetoothdeviceinfo.h b/src/bluetooth/qbluetoothdeviceinfo.h
index 11cb2bea..c4fa01ec 100644
--- a/src/bluetooth/qbluetoothdeviceinfo.h
+++ b/src/bluetooth/qbluetoothdeviceinfo.h
@@ -255,11 +255,17 @@ public:
#ifndef Q_QDOC //suppress qdoc warnings
QVector<QBluetoothUuid> serviceUuids() const;
#endif // Q_QDOC
-#else
+#elif QT_DEPRECATED_SINCE(5, 13)
QList<QBluetoothUuid> serviceUuids(DataCompleteness *completeness = nullptr) const;
+#else
+ QList<QBluetoothUuid> serviceUuids() const;
#endif
void setServiceUuids(const QVector<QBluetoothUuid> &uuids);
+ // TODO Qt6 manufacturerData()
+ // manufacturerData() and manufacturerData(quint16) return types should be modified to
+ // cope with multiple data entires per manufacturer ID. QHash<quint16, QByteArray>
+ // may stay though if it retains insertMulti() in Qt 6.
QVector<quint16> manufacturerIds() const;
QByteArray manufacturerData(quint16 manufacturerId) const;
bool setManufacturerData(quint16 manufacturerId, const QByteArray &data);
diff --git a/src/bluetooth/qbluetoothdeviceinfo_p.h b/src/bluetooth/qbluetoothdeviceinfo_p.h
index 3c19b10f..80fd1472 100644
--- a/src/bluetooth/qbluetoothdeviceinfo_p.h
+++ b/src/bluetooth/qbluetoothdeviceinfo_p.h
@@ -77,7 +77,9 @@ public:
QBluetoothDeviceInfo::MajorDeviceClass majorDeviceClass;
quint8 minorDeviceClass;
+#if QT_DEPRECATED_SINCE(5, 13)
QBluetoothDeviceInfo::DataCompleteness serviceUuidsCompleteness;
+#endif
QVector<QBluetoothUuid> serviceUuids;
QHash<quint16, QByteArray> manufacturerData;
QBluetoothDeviceInfo::CoreConfigurations deviceCoreConfiguration;
diff --git a/src/bluetooth/qbluetoothlocaldevice_bluez.cpp b/src/bluetooth/qbluetoothlocaldevice_bluez.cpp
index f02c6ab9..f48717a6 100644
--- a/src/bluetooth/qbluetoothlocaldevice_bluez.cpp
+++ b/src/bluetooth/qbluetoothlocaldevice_bluez.cpp
@@ -1133,7 +1133,7 @@ void QBluetoothLocalDevicePrivate::createCache()
QList<QBluetoothAddress> QBluetoothLocalDevicePrivate::connectedDevices() const
{
- return connectedDevicesSet.toList();
+ return connectedDevicesSet.values();
}
void QBluetoothLocalDevice::pairingConfirmation(bool confirmation)
diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h
index b2f03b9f..d86ab22f 100644
--- a/src/bluetooth/qbluetoothlocaldevice_p.h
+++ b/src/bluetooth/qbluetoothlocaldevice_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -222,6 +223,29 @@ private:
void initializeAdapter();
void initializeAdapterBluez5();
};
+
+#elif defined(QT_WIN_BLUETOOTH)
+
+class QBluetoothLocalDevicePrivate : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PUBLIC(QBluetoothLocalDevice)
+public:
+ QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q,
+ const QBluetoothAddress &address = QBluetoothAddress());
+
+ ~QBluetoothLocalDevicePrivate();
+ bool isValid() const;
+ void initialize(const QBluetoothAddress &address);
+
+ static QList<QBluetoothHostInfo> localAdapters();
+
+ QBluetoothAddress deviceAddress;
+ QString deviceName;
+ bool deviceValid;
+private:
+ QBluetoothLocalDevice *q_ptr;
+};
#elif defined(QT_WINRT_BLUETOOTH)
class QBluetoothLocalDevicePrivate : public QObject
{
diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp
new file mode 100644
index 00000000..cf17cad1
--- /dev/null
+++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothlocaldevice.h"
+#include "qbluetoothaddress.h"
+
+#include "qbluetoothlocaldevice_p.h"
+
+#include <QtCore/QLoggingCategory>
+
+#include <qt_windows.h>
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothLocalDevicePrivate(this))
+{
+}
+
+QBluetoothLocalDevice::QBluetoothLocalDevice(
+ const QBluetoothAddress &address, QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothLocalDevicePrivate(this, address))
+{
+}
+
+QString QBluetoothLocalDevice::name() const
+{
+ Q_D(const QBluetoothLocalDevice);
+ return d->deviceName;
+}
+
+QBluetoothAddress QBluetoothLocalDevice::address() const
+{
+ Q_D(const QBluetoothLocalDevice);
+ return d->deviceAddress;
+}
+
+void QBluetoothLocalDevice::powerOn()
+{
+ if (hostMode() != HostPoweredOff)
+ return;
+
+ setHostMode(HostConnectable);
+}
+
+void QBluetoothLocalDevice::setHostMode(
+ QBluetoothLocalDevice::HostMode requestedMode)
+{
+ if (!isValid()) {
+ qCWarning(QT_BT_WINDOWS) << "The local device is not initialized correctly";
+ return;
+ }
+
+ if (requestedMode == HostDiscoverableLimitedInquiry)
+ requestedMode = HostDiscoverable;
+
+ if (requestedMode == hostMode())
+ return;
+
+ if (requestedMode == QBluetoothLocalDevice::HostPoweredOff) {
+ if (::BluetoothIsDiscoverable(nullptr)
+ && !::BluetoothEnableDiscovery(nullptr, FALSE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to disable the discoverable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ if (::BluetoothIsConnectable(nullptr)
+ && !::BluetoothEnableIncomingConnections(nullptr, FALSE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to disable the connectable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ } else if (requestedMode == QBluetoothLocalDevice::HostConnectable) {
+ if (!::BluetoothIsConnectable(nullptr)
+ && !::BluetoothEnableIncomingConnections(nullptr, TRUE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to enable the connectable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ } else if (requestedMode == QBluetoothLocalDevice::HostDiscoverable
+ || requestedMode == QBluetoothLocalDevice::HostDiscoverableLimitedInquiry) {
+ if (!::BluetoothIsConnectable(nullptr)
+ && !::BluetoothEnableIncomingConnections(nullptr, TRUE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to enable the connectable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ if (!::BluetoothEnableDiscovery(nullptr, TRUE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to enable the discoverable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ }
+
+ emit hostModeStateChanged(requestedMode);
+}
+
+QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const
+{
+ if (!isValid()) {
+ qCWarning(QT_BT_WINDOWS) << "The local device is not initialized correctly";
+ return HostPoweredOff;
+ }
+
+ if (::BluetoothIsDiscoverable(nullptr))
+ return HostDiscoverable;
+ if (::BluetoothIsConnectable(nullptr))
+ return HostConnectable;
+ return HostPoweredOff;
+}
+
+QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
+{
+ return QList<QBluetoothAddress>();
+}
+
+QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
+{
+ return QBluetoothLocalDevicePrivate::localAdapters();
+}
+
+void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing)
+{
+ Q_UNUSED(address);
+ Q_UNUSED(pairing);
+ Q_UNIMPLEMENTED();
+}
+
+QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(
+ const QBluetoothAddress &address) const
+{
+ Q_UNUSED(address);
+ Q_UNIMPLEMENTED();
+ return Unpaired;
+}
+
+void QBluetoothLocalDevice::pairingConfirmation(bool confirmation)
+{
+ Q_UNUSED(confirmation);
+ Q_UNIMPLEMENTED();
+}
+
+QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(
+ QBluetoothLocalDevice *q, const QBluetoothAddress &address)
+ : deviceValid(false)
+ , q_ptr(q)
+{
+ initialize(address);
+}
+
+QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate()
+{
+}
+
+bool QBluetoothLocalDevicePrivate::isValid() const
+{
+ return deviceValid;
+}
+
+void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address)
+{
+ Q_Q(QBluetoothLocalDevice);
+
+ const QList<QBluetoothHostInfo> adapterInfos = QBluetoothLocalDevicePrivate::localAdapters();
+ for (const QBluetoothHostInfo &adapterInfo : adapterInfos) {
+ if (address == QBluetoothAddress()
+ || address == adapterInfo.address()) {
+ deviceAddress = adapterInfo.address();
+ deviceName = adapterInfo.name();
+ deviceValid = true;
+ return;
+ }
+ }
+
+ qCWarning(QT_BT_WINDOWS) << "Unable to find classic local radio: " << address;
+ QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection,
+ Q_ARG(QBluetoothLocalDevice::Error,
+ QBluetoothLocalDevice::UnknownError));
+}
+
+QList<QBluetoothHostInfo> QBluetoothLocalDevicePrivate::localAdapters()
+{
+ BLUETOOTH_FIND_RADIO_PARAMS params;
+ ::ZeroMemory(&params, sizeof(params));
+ params.dwSize = sizeof(params);
+
+ QList<QBluetoothHostInfo> foundAdapters;
+
+ HANDLE hRadio = nullptr;
+ if (const HBLUETOOTH_RADIO_FIND hSearch = ::BluetoothFindFirstRadio(&params, &hRadio)) {
+ for (;;) {
+ BLUETOOTH_RADIO_INFO radio;
+ ::ZeroMemory(&radio, sizeof(radio));
+ radio.dwSize = sizeof(radio);
+
+ const DWORD retval = ::BluetoothGetRadioInfo(hRadio, &radio);
+ ::CloseHandle(hRadio);
+
+ if (retval != ERROR_SUCCESS)
+ break;
+
+ QBluetoothHostInfo adapterInfo;
+ adapterInfo.setAddress(QBluetoothAddress(radio.address.ullLong));
+ adapterInfo.setName(QString::fromWCharArray(radio.szName));
+
+ foundAdapters << adapterInfo;
+
+ if (!::BluetoothFindNextRadio(hSearch, &hRadio))
+ break;
+ }
+
+ ::BluetoothFindRadioClose(hSearch);
+ }
+
+ return foundAdapters;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserver.cpp b/src/bluetooth/qbluetoothserver.cpp
index 6991518f..daed5dc2 100644
--- a/src/bluetooth/qbluetoothserver.cpp
+++ b/src/bluetooth/qbluetoothserver.cpp
@@ -167,9 +167,8 @@ QT_BEGIN_NAMESPACE
Constructs a bluetooth server with \a parent and \a serverType.
*/
QBluetoothServer::QBluetoothServer(QBluetoothServiceInfo::Protocol serverType, QObject *parent)
- : QObject(parent), d_ptr(new QBluetoothServerPrivate(serverType))
+ : QObject(parent), d_ptr(new QBluetoothServerPrivate(serverType, this))
{
- d_ptr->q_ptr = this;
}
/*!
@@ -266,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_android.cpp b/src/bluetooth/qbluetoothserver_android.cpp
index 4c469c76..eed3a1ea 100644
--- a/src/bluetooth/qbluetoothserver_android.cpp
+++ b/src/bluetooth/qbluetoothserver_android.cpp
@@ -53,9 +53,10 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
QHash<QBluetoothServerPrivate*, int> __fakeServerPorts;
-QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType)
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType,
+ QBluetoothServer *parent)
: socket(0),maxPendingConnections(1), securityFlags(QBluetooth::NoSecurity), serverType(sType),
- m_lastError(QBluetoothServer::NoError)
+ m_lastError(QBluetoothServer::NoError), q_ptr(parent)
{
thread = new ServerAcceptanceThread();
thread->setMaxPendingConnections(maxPendingConnections);
diff --git a/src/bluetooth/qbluetoothserver_bluez.cpp b/src/bluetooth/qbluetoothserver_bluez.cpp
index 54bc85a0..a98f5398 100644
--- a/src/bluetooth/qbluetoothserver_bluez.cpp
+++ b/src/bluetooth/qbluetoothserver_bluez.cpp
@@ -66,9 +66,10 @@ QBluetoothSocket *QBluetoothServerPrivate::createSocketForServer(
return socket;
}
-QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType)
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType,
+ QBluetoothServer *parent)
: maxPendingConnections(1), securityFlags(QBluetooth::Authorization), serverType(sType),
- m_lastError(QBluetoothServer::NoError)
+ m_lastError(QBluetoothServer::NoError), q_ptr(parent)
{
if (sType == QBluetoothServiceInfo::RfcommProtocol)
socket = createSocketForServer(QBluetoothServiceInfo::RfcommProtocol);
diff --git a/src/bluetooth/qbluetoothserver_osx.mm b/src/bluetooth/qbluetoothserver_osx.mm
index 5d3b8fc4..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)
{
- Q_ASSERT_X(listener.data(), Q_FUNC_INFO, "invalid listener (nil)");
+ 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)
{
- Q_ASSERT_X(listener.data(), Q_FUNC_INFO, "invalid listener (nil)");
+ 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,11 +288,9 @@ void QBluetoothServer::close()
bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
{
- typedef QBluetoothServerPrivate::ObjCListener ObjCListener;
-
OSXBluetooth::qt_test_iobluetooth_runloop();
- if (d_ptr->listener.data()) {
+ if (d_ptr->listener) {
qCWarning(QT_BT_OSX) << "already in listen mode, close server first";
return false;
}
@@ -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.cpp b/src/bluetooth/qbluetoothserver_p.cpp
index 4f28c9b1..6657e151 100644
--- a/src/bluetooth/qbluetoothserver_p.cpp
+++ b/src/bluetooth/qbluetoothserver_p.cpp
@@ -46,8 +46,10 @@
QT_BEGIN_NAMESPACE
-QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType)
- : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError)
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType,
+ QBluetoothServer *parent)
+ : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError),
+ q_ptr(parent)
{
#ifndef QT_IOS_BLUETOOTH
printDummyWarning();
diff --git a/src/bluetooth/qbluetoothserver_p.h b/src/bluetooth/qbluetoothserver_p.h
index d78eee5f..d14dc7b4 100644
--- a/src/bluetooth/qbluetoothserver_p.h
+++ b/src/bluetooth/qbluetoothserver_p.h
@@ -57,7 +57,7 @@
#include "qbluetoothserver.h"
#include "qbluetooth.h"
-#if QT_CONFIG(bluez)
+#if QT_CONFIG(bluez) || defined(QT_WIN_BLUETOOTH)
QT_FORWARD_DECLARE_CLASS(QSocketNotifier)
#endif
@@ -77,21 +77,30 @@ 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)
public:
- QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol serverType);
+ QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol serverType, QBluetoothServer *parent);
~QBluetoothServerPrivate();
#if QT_CONFIG(bluez)
@@ -101,6 +110,9 @@ public:
static QBluetoothSocket *createSocketForServer(
QBluetoothServiceInfo::Protocol socketType = QBluetoothServiceInfo::RfcommProtocol);
#endif
+#if defined(QT_WIN_BLUETOOTH)
+ void _q_newConnection();
+#endif
public:
QBluetoothSocket *socket;
@@ -114,7 +126,7 @@ protected:
private:
QBluetoothServer::Error m_lastError;
-#if QT_CONFIG(bluez)
+#if QT_CONFIG(bluez) || defined(QT_WIN_BLUETOOTH)
QSocketNotifier *socketNotifier = nullptr;
#elif defined(QT_ANDROID_BLUETOOTH)
ServerAcceptanceThread *thread;
@@ -140,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/qbluetoothserver_win.cpp b/src/bluetooth/qbluetoothserver_win.cpp
new file mode 100644
index 00000000..70695112
--- /dev/null
+++ b/src/bluetooth/qbluetoothserver_win.cpp
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothserver.h"
+#include "qbluetoothserver_p.h"
+#include "qbluetoothsocket.h"
+#include "qbluetoothlocaldevice.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QSocketNotifier>
+
+#include <winsock2.h>
+#include <ws2bth.h>
+#include <bluetoothapis.h>
+#include <errno.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType,
+ QBluetoothServer *parent)
+ : maxPendingConnections(1), serverType(sType), q_ptr(parent),
+ m_lastError(QBluetoothServer::NoError)
+{
+ Q_Q(QBluetoothServer);
+ Q_ASSERT(sType == QBluetoothServiceInfo::RfcommProtocol);
+ socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, q);
+}
+
+QBluetoothServerPrivate::~QBluetoothServerPrivate()
+{
+}
+
+void QBluetoothServerPrivate::_q_newConnection()
+{
+ // disable socket notifier until application calls nextPendingConnection().
+ socketNotifier->setEnabled(false);
+
+ emit q_ptr->newConnection();
+}
+
+void QBluetoothServer::close()
+{
+ Q_D(QBluetoothServer);
+
+ delete d->socketNotifier;
+ d->socketNotifier = nullptr;
+
+ d->socket->close();
+}
+
+bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
+{
+ Q_D(QBluetoothServer);
+
+ if (d->serverType != QBluetoothServiceInfo::RfcommProtocol) {
+ qCWarning(QT_BT_WINDOWS) << "Protocol is not supported.";
+ d->m_lastError = QBluetoothServer::UnsupportedProtocolError;
+ emit error(d->m_lastError);
+ return false;
+ }
+
+ if (d->socket->state() == QBluetoothSocket::ListeningState) {
+ qCWarning(QT_BT_WINDOWS) << "Socket already in listen mode, close server first";
+ return false;
+ }
+
+ const QBluetoothLocalDevice device(address);
+ if (!device.isValid()) {
+ qCWarning(QT_BT_WINDOWS) << "Device does not support Bluetooth or"
+ << address.toString() << "is not a valid local adapter";
+ d->m_lastError = QBluetoothServer::UnknownError;
+ emit error(d->m_lastError);
+ return false;
+ }
+
+ const QBluetoothLocalDevice::HostMode hostMode = device.hostMode();
+ if (hostMode == QBluetoothLocalDevice::HostPoweredOff) {
+ d->m_lastError = QBluetoothServer::PoweredOffError;
+ emit error(d->m_lastError);
+ qCWarning(QT_BT_WINDOWS) << "Bluetooth device is powered off";
+ return false;
+ }
+
+ int sock = d->socket->socketDescriptor();
+ if (sock < 0) {
+ /* Negative socket descriptor is not always an error case.
+ * Another cause could be a call to close()/abort().
+ * Check whether we can recover by re-creating the socket.
+ */
+ delete d->socket;
+ d->socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this);
+ sock = d->socket->socketDescriptor();
+ if (sock < 0) {
+ d->m_lastError = InputOutputError;
+ emit error(d->m_lastError);
+ return false;
+ }
+ }
+
+ if (sock < 0)
+ return false;
+
+ SOCKADDR_BTH addr = {};
+ addr.addressFamily = AF_BTH;
+ addr.port = (port == 0) ? BT_PORT_ANY : port;
+ addr.btAddr = address.toUInt64();
+
+ if (::bind(sock, reinterpret_cast<sockaddr *>(&addr), sizeof(SOCKADDR_BTH)) < 0) {
+ if (errno == EADDRINUSE)
+ d->m_lastError = ServiceAlreadyRegisteredError;
+ else
+ d->m_lastError = InputOutputError;
+ emit error(d->m_lastError);
+ return false;
+ }
+
+ if (::listen(sock, d->maxPendingConnections) < 0) {
+ d->m_lastError = InputOutputError;
+ emit error(d->m_lastError);
+ return false;
+ }
+
+ d->socket->setSocketState(QBluetoothSocket::ListeningState);
+
+ if (!d->socketNotifier) {
+ d->socketNotifier = new QSocketNotifier(d->socket->socketDescriptor(),
+ QSocketNotifier::Read, this);
+ connect(d->socketNotifier, &QSocketNotifier::activated, this, [d](){
+ d->_q_newConnection();
+ });
+ }
+
+ return true;
+}
+
+void QBluetoothServer::setMaxPendingConnections(int numConnections)
+{
+ Q_UNUSED(numConnections);
+}
+
+bool QBluetoothServer::hasPendingConnections() const
+{
+ Q_D(const QBluetoothServer);
+
+ if (!d || !d->socketNotifier)
+ return false;
+
+ // if the socket notifier is disabled there is a pending connection waiting for us to accept.
+ return !d->socketNotifier->isEnabled();
+}
+
+QBluetoothSocket *QBluetoothServer::nextPendingConnection()
+{
+ Q_D(QBluetoothServer);
+
+ if (!hasPendingConnections())
+ return nullptr;
+
+ if (d->serverType != QBluetoothServiceInfo::RfcommProtocol)
+ return nullptr;
+
+ SOCKADDR_BTH addr = {};
+ int length = sizeof(SOCKADDR_BTH);
+ int pending = ::accept(d->socket->socketDescriptor(),
+ reinterpret_cast<sockaddr *>(&addr), &length);
+
+ QBluetoothSocket *newSocket = nullptr;
+
+ if (pending >= 0) {
+ newSocket = new QBluetoothSocket();
+ newSocket->setSocketDescriptor(pending, QBluetoothServiceInfo::RfcommProtocol);
+ }
+
+ d->socketNotifier->setEnabled(true);
+ return newSocket;
+}
+
+QBluetoothAddress QBluetoothServer::serverAddress() const
+{
+ Q_D(const QBluetoothServer);
+
+ return d->socket->localAddress();
+}
+
+quint16 QBluetoothServer::serverPort() const
+{
+ Q_D(const QBluetoothServer);
+
+ return d->socket->localPort();
+}
+
+void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security)
+{
+ Q_UNUSED(security);
+}
+
+QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const
+{
+ return QBluetooth::NoSecurity;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserver_winrt.cpp b/src/bluetooth/qbluetoothserver_winrt.cpp
index 3bcd6b33..b9d98eb7 100644
--- a/src/bluetooth/qbluetoothserver_winrt.cpp
+++ b/src/bluetooth/qbluetoothserver_winrt.cpp
@@ -70,8 +70,10 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
QHash<QBluetoothServerPrivate *, int> __fakeServerPorts;
-QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType)
- : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError), socket(0)
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType,
+ QBluetoothServer *parent)
+ : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError),
+ socket(0), q_ptr(parent)
{
#ifdef CLASSIC_APP_BUILD
CoInitialize(NULL);
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.cpp b/src/bluetooth/qbluetoothservicediscoveryagent.cpp
index 9750b2aa..e76c2311 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent.cpp
+++ b/src/bluetooth/qbluetoothservicediscoveryagent.cpp
@@ -308,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.
@@ -318,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) {
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.h b/src/bluetooth/qbluetoothservicediscoveryagent.h
index 1db05f55..f1fa4640 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent.h
@@ -43,6 +43,7 @@
#include <QtBluetooth/qtbluetoothglobal.h>
#include <QtCore/QObject>
+#include <QtCore/QVariant>
#include <QtBluetooth/QBluetoothServiceInfo>
#include <QtBluetooth/QBluetoothUuid>
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_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 dbf8b1d4..41410b70 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
@@ -72,10 +72,29 @@ class QXmlStreamReader;
QT_END_NAMESPACE
#endif
-#ifdef QT_WINRT_BLUETOOTH
+#ifdef QT_WIN_BLUETOOTH
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+class QThread;
+
+class ThreadWorkerFind : public QObject
+{
+ Q_OBJECT
+signals:
+ void findFinished(QVariant result);
+};
+QT_END_NAMESPACE
+
+#elif defined(QT_WINRT_BLUETOOTH)
#include <QtCore/QPointer>
#endif
+#ifdef QT_OSX_BLUETOOTH
+#include "osx/btdelegates_p.h"
+#include "osx/btraii_p.h"
+#endif
+
QT_BEGIN_NAMESPACE
class QBluetoothDeviceDiscoveryAgent;
@@ -91,10 +110,13 @@ class QWinRTBluetoothServiceDiscoveryWorker;
#endif
class QBluetoothServiceDiscoveryAgentPrivate
-#if defined QT_WINRT_BLUETOOTH
+#if defined QT_WINRT_BLUETOOTH || defined QT_WIN_BLUETOOTH
: public QObject
{
Q_OBJECT
+#elif defined(QT_OSX_BLUETOOTH)
+ : public QObject, public DarwinBluetooth::SDPInquiryDelegate
+{
#else
{
#endif
@@ -149,6 +171,10 @@ public:
void _q_fetchUuidsTimeout();
void _q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state);
#endif
+#ifdef QT_WIN_BLUETOOTH
+ void _q_nextSdpScan(const QVariant &input);
+ bool serviceMatches(const QBluetoothServiceInfo &info);
+#endif
private:
void start(const QBluetoothAddress &address);
@@ -200,6 +226,15 @@ private:
QMap<QBluetoothAddress,QPair<QBluetoothDeviceInfo,QList<QBluetoothUuid> > > sdpCache;
#endif
+#ifdef QT_WIN_BLUETOOTH
+private:
+ bool pendingStop;
+ bool pendingFinish;
+
+ QThread *threadFind = nullptr;
+ ThreadWorkerFind *threadWorkerFind = nullptr;
+#endif
+
#ifdef QT_WINRT_BLUETOOTH
private slots:
void processFoundService(quint64 deviceAddress, const QBluetoothServiceInfo &info);
@@ -211,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_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp
new file mode 100644
index 00000000..c34443aa
--- /dev/null
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp
@@ -0,0 +1,437 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothservicediscoveryagent.h"
+#include "qbluetoothservicediscoveryagent_p.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QLibrary>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QThread>
+#include <QtCore/QUrl>
+
+#include <initguid.h>
+#include <winsock2.h>
+#include <qt_windows.h>
+
+#if defined(Q_CC_MINGW)
+// Workaround for MinGW headers declaring BluetoothSdpGetElementData incorrectly.
+# define BluetoothSdpGetElementData _BluetoothSdpGetElementData_notok
+# include <bluetoothapis.h>
+# undef BluetoothSdpGetElementData
+ extern "C" DWORD WINAPI BluetoothSdpGetElementData(LPBYTE, ULONG, PSDP_ELEMENT_DATA);
+#else
+# include <bluetoothapis.h>
+#endif
+
+#include <ws2bth.h>
+#include <iostream>
+
+QT_BEGIN_NAMESPACE
+
+struct FindServiceResult {
+ QBluetoothServiceInfo info;
+ Qt::HANDLE hSearch = INVALID_HANDLE_VALUE;
+ int systemError = NO_ERROR;
+};
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+static QList<QVariant> spdContainerToVariantList(LPBYTE containerStream, ULONG containerLength);
+
+static QVariant spdElementToVariant(const SDP_ELEMENT_DATA &element)
+{
+ QVariant variant;
+
+ switch (element.type) {
+ case SDP_TYPE_UINT: {
+ switch (element.specificType) {
+ case SDP_ST_UINT128:
+ //Not supported!!!
+ break;
+ case SDP_ST_UINT64:
+ variant = QVariant::fromValue<quint64>(element.data.uint64);
+ break;
+ case SDP_ST_UINT32:
+ variant = QVariant::fromValue<quint32>(element.data.uint32);
+ break;
+ case SDP_ST_UINT16:
+ variant = QVariant::fromValue<quint16>(element.data.uint16);
+ break;
+ case SDP_ST_UINT8:
+ variant = QVariant::fromValue<quint8>(element.data.uint8);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case SDP_TYPE_INT: {
+ switch (element.specificType) {
+ case SDP_ST_INT128: {
+ //Not supported!!!
+ break;
+ }
+ case SDP_ST_INT64:
+ variant = QVariant::fromValue<qint64>(element.data.int64);
+ break;
+ case SDP_ST_INT32:
+ variant = QVariant::fromValue<qint32>(element.data.int32);
+ break;
+ case SDP_ST_INT16:
+ variant = QVariant::fromValue<qint16>(element.data.int16);
+ break;
+ case SDP_ST_INT8:
+ variant = QVariant::fromValue<qint8>(element.data.int8);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case SDP_TYPE_UUID: {
+ switch (element.specificType) {
+ case SDP_ST_UUID128:
+ variant = QVariant::fromValue(QBluetoothUuid(element.data.uuid128));
+ break;
+ case SDP_ST_UUID32:
+ variant = QVariant::fromValue(QBluetoothUuid(quint32(element.data.uuid32)));
+ break;
+ case SDP_ST_UUID16:
+ variant = QVariant::fromValue(QBluetoothUuid(quint16(element.data.uuid16)));
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case SDP_TYPE_STRING: {
+ const QByteArray stringBuffer(reinterpret_cast<const char*>(element.data.string.value), element.data.string.length);
+ variant = QVariant::fromValue<QString>(QString::fromLocal8Bit(stringBuffer));
+ break;
+ }
+ case SDP_TYPE_URL: {
+ const QString urlString = QString::fromLocal8Bit(reinterpret_cast<const char*>(element.data.url.value),
+ int(element.data.url.length));
+ const QUrl url(urlString);
+ if (url.isValid())
+ variant = QVariant::fromValue<QUrl>(url);
+ break;
+ }
+ case SDP_TYPE_SEQUENCE: {
+ const QList<QVariant> sequenceList = spdContainerToVariantList(element.data.sequence.value,
+ element.data.sequence.length);
+ const QBluetoothServiceInfo::Sequence sequence(sequenceList);
+ variant = QVariant::fromValue(sequence);
+ break;
+ }
+ case SDP_TYPE_ALTERNATIVE: {
+ const QList<QVariant> alternativeList = spdContainerToVariantList(element.data.alternative.value,
+ element.data.alternative.length);
+ const QBluetoothServiceInfo::Alternative alternative(alternativeList);
+ variant = QVariant::fromValue(alternative);
+ break;
+ }
+ case SDP_TYPE_BOOLEAN:
+ variant = QVariant::fromValue<bool>(bool(element.data.booleanVal));
+ break;
+ case SDP_TYPE_NIL:
+ break;
+ default:
+ break;
+ }
+
+ return variant;
+}
+
+static QList<QVariant> spdContainerToVariantList(LPBYTE containerStream, ULONG containerLength)
+{
+ HBLUETOOTH_CONTAINER_ELEMENT iter = nullptr;
+ SDP_ELEMENT_DATA element = {};
+
+ QList<QVariant> sequence;
+
+ for (;;) {
+ const DWORD result = ::BluetoothSdpGetContainerElementData(containerStream,
+ containerLength,
+ &iter,
+ &element);
+
+ if (result == ERROR_SUCCESS) {
+ const QVariant variant = spdElementToVariant(element);
+ sequence.append(variant);
+ } else if (result == ERROR_NO_MORE_ITEMS) {
+ break;
+ } else if (result == ERROR_INVALID_PARAMETER) {
+ break;
+ }
+ }
+
+ return sequence;
+}
+
+#if defined(Q_CC_MINGW)
+# define SDP_CALLBACK
+#else
+# define SDP_CALLBACK QT_WIN_CALLBACK
+#endif
+static BOOL SDP_CALLBACK bluetoothSdpCallback(ULONG attributeId, LPBYTE valueStream, ULONG streamSize, LPVOID param)
+{
+ QBluetoothServiceInfo *result = static_cast<QBluetoothServiceInfo*>(param);
+
+ SDP_ELEMENT_DATA element = {};
+
+ if (::BluetoothSdpGetElementData(valueStream, streamSize, &element) == ERROR_SUCCESS) {
+ switch (element.type) {
+ case SDP_TYPE_UINT:
+ case SDP_TYPE_INT:
+ case SDP_TYPE_UUID:
+ case SDP_TYPE_STRING:
+ case SDP_TYPE_URL:
+ case SDP_TYPE_BOOLEAN:
+ case SDP_TYPE_SEQUENCE:
+ case SDP_TYPE_ALTERNATIVE: {
+ const QVariant variant = spdElementToVariant(element);
+
+ result->setAttribute(attributeId, variant);
+ break;
+ }
+ case SDP_TYPE_NIL:
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+enum {
+ WSAControlFlags = LUP_FLUSHCACHE
+ | LUP_RETURN_NAME
+ | LUP_RETURN_TYPE
+ | LUP_RETURN_ADDR
+ | LUP_RETURN_BLOB
+ | LUP_RETURN_COMMENT
+};
+
+static FindServiceResult findNextService(HANDLE hSearch)
+{
+ FindServiceResult result;
+ result.hSearch = hSearch;
+
+ QByteArray resultBuffer(2048, 0);
+ WSAQUERYSET *resultQuery = reinterpret_cast<WSAQUERYSET*>(resultBuffer.data());
+ DWORD resultBufferSize = DWORD(resultBuffer.size());
+ const int resultCode = ::WSALookupServiceNext(hSearch,
+ WSAControlFlags,
+ &resultBufferSize,
+ resultQuery);
+
+ if (resultCode == SOCKET_ERROR) {
+ result.systemError = ::WSAGetLastError();
+ if (result.systemError == WSA_E_NO_MORE)
+ ::WSALookupServiceEnd(hSearch);
+ return result;
+ }
+
+ if (resultQuery->lpBlob
+ && ::BluetoothSdpEnumAttributes(resultQuery->lpBlob->pBlobData,
+ resultQuery->lpBlob->cbSize,
+ bluetoothSdpCallback,
+ &result.info)) {
+ return result;
+ } else {
+ result.systemError = GetLastError();
+ }
+ return result;
+}
+
+static FindServiceResult findFirstService(const QBluetoothAddress &address)
+{
+ WSAData wsadata = {};
+ FindServiceResult result;
+
+ // IPv6 requires Winsock v2.0 or better.
+ if (::WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) {
+ result.systemError = ::WSAGetLastError();
+ return result;
+ }
+
+ const QString addressAsString = QStringLiteral("(%1)").arg(address.toString());
+ QVector<WCHAR> addressAsWChar(addressAsString.size() + 1);
+ addressAsString.toWCharArray(addressAsWChar.data());
+
+ GUID protocol = L2CAP_PROTOCOL_UUID; //Search for L2CAP and RFCOMM services
+
+ WSAQUERYSET serviceQuery = {};
+ serviceQuery.dwSize = sizeof(WSAQUERYSET);
+ serviceQuery.lpServiceClassId = &protocol;
+ serviceQuery.dwNameSpace = NS_BTH;
+ serviceQuery.dwNumberOfCsAddrs = 0;
+ serviceQuery.lpszContext = addressAsWChar.data();
+
+ HANDLE hSearch = nullptr;
+ const int resultCode = ::WSALookupServiceBegin(&serviceQuery,
+ WSAControlFlags,
+ &hSearch);
+ if (resultCode == SOCKET_ERROR) {
+ result.systemError = ::WSAGetLastError();
+ ::WSALookupServiceEnd(hSearch);
+ return result;
+ }
+ return findNextService(hSearch);
+}
+
+QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
+ QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter)
+ : error(QBluetoothServiceDiscoveryAgent::NoError),
+ state(Inactive),
+ deviceDiscoveryAgent(0),
+ mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery),
+ singleDevice(false),
+ pendingStop(false),
+ pendingFinish(false),
+ q_ptr(qp)
+{
+ Q_UNUSED(deviceAdapter);
+
+ threadFind = new QThread;
+ threadWorkerFind = new ThreadWorkerFind;
+ threadWorkerFind->moveToThread(threadFind);
+ connect(threadWorkerFind, &ThreadWorkerFind::findFinished, this, &QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan);
+ connect(threadFind, &QThread::finished, threadWorkerFind, &ThreadWorkerFind::deleteLater);
+ connect(threadFind, &QThread::finished, threadFind, &QThread::deleteLater);
+ threadFind->start();
+}
+
+QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate()
+{
+ if (pendingFinish)
+ stop();
+ if (threadFind)
+ threadFind->quit();
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address)
+{
+ if (!pendingFinish) {
+ pendingFinish = true;
+ pendingStop = false;
+
+ const auto threadWorker = threadWorkerFind;
+ QMetaObject::invokeMethod(threadWorkerFind, [threadWorker, address]()
+ {
+ const FindServiceResult result = findFirstService(address);
+ emit threadWorker->findFinished(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+ }
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::stop()
+{
+ pendingStop = true;
+}
+
+bool QBluetoothServiceDiscoveryAgentPrivate::serviceMatches(const QBluetoothServiceInfo &info)
+{
+ if (uuidFilter.isEmpty())
+ return true;
+
+ if (uuidFilter.contains(info.serviceUuid()))
+ return true;
+
+ const QList<QBluetoothUuid> serviceClassUuids = info.serviceClassUuids();
+ for (const QBluetoothUuid &uuid : serviceClassUuids)
+ if (uuidFilter.contains(uuid))
+ return true;
+
+ return false;
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &input)
+{
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ auto result = input.value<FindServiceResult>();
+
+ if (pendingStop) {
+ ::WSALookupServiceEnd(result.hSearch);
+ pendingStop = false;
+ pendingFinish = false;
+ emit q->canceled();
+ } else {
+ if (result.systemError == WSA_E_NO_MORE) {
+ result.systemError = NO_ERROR;
+ } else if (result.systemError != NO_ERROR) {
+ if (result.hSearch != INVALID_HANDLE_VALUE)
+ ::WSALookupServiceEnd(result.hSearch);
+ error = (result.systemError == ERROR_INVALID_HANDLE) ?
+ QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError
+ : QBluetoothServiceDiscoveryAgent::InputOutputError;
+ errorString = qt_error_string(result.systemError);
+ qCWarning(QT_BT_WINDOWS) << errorString;
+ emit q->error(this->error);
+ } else {
+
+ if (serviceMatches(result.info)) {
+ result.info.setDevice(discoveredDevices.at(0));
+ if (result.info.isValid()) {
+ if (!isDuplicatedService(result.info)) {
+ discoveredServices.append(result.info);
+ emit q->serviceDiscovered(result.info);
+ }
+ }
+ }
+
+ const auto threadWorker = threadWorkerFind;
+ const auto hSearch = result.hSearch;
+ QMetaObject::invokeMethod(threadWorkerFind, [threadWorker, hSearch]()
+ {
+ FindServiceResult result = findNextService(hSearch);
+ emit threadWorker->findFinished(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+ return;
+ }
+ pendingFinish = false;
+ _q_serviceDiscoveryFinished();
+ }
+}
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(FindServiceResult)
diff --git a/src/bluetooth/qbluetoothserviceinfo.cpp b/src/bluetooth/qbluetoothserviceinfo.cpp
index 7c3780ec..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
}
/*!
diff --git a/src/bluetooth/qbluetoothserviceinfo_osx.mm b/src/bluetooth/qbluetoothserviceinfo_osx.mm
index 7ce4c645..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)
@@ -152,19 +184,20 @@ bool QBluetoothServiceInfoPrivate::unregisterService()
if (!registered)
return false;
- Q_ASSERT_X(serviceRecord.data(), Q_FUNC_INFO, "service registered, but serviceRecord is nil");
+ 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,281 +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(QDebug dbg, const QVariant &var, const QString& indent)
-{
- switch (int(var.type())) {
- case QMetaType::Void:
- dbg << QString::asprintf("%sEmpty\n", indent.toUtf8().constData());
- break;
- case QMetaType::UChar:
- dbg << QString::asprintf("%suchar %u\n", indent.toUtf8().constData(), var.toUInt());
- break;
- case QMetaType::UShort:
- dbg << QString::asprintf("%sushort %u\n", indent.toUtf8().constData(), var.toUInt());
- break;
- case QMetaType::UInt:
- dbg << QString::asprintf("%suint %u\n", indent.toUtf8().constData(), var.toUInt());
- break;
- case QMetaType::Char:
- dbg << QString::asprintf("%schar %d\n", indent.toUtf8().constData(), var.toInt());
- break;
- case QMetaType::Short:
- dbg << QString::asprintf("%sshort %d\n", indent.toUtf8().constData(), var.toInt());
- break;
- case QMetaType::Int:
- dbg << QString::asprintf("%sint %d\n", indent.toUtf8().constData(), var.toInt());
- break;
- case QMetaType::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;
- case QMetaType::QUrl:
- dbg << QString::asprintf("%surl %s\n", indent.toUtf8().constData(),
- var.toUrl().toString().toUtf8().constData());
- break;
- case QVariant::UserType:
- if (var.userType() == qMetaTypeId<QBluetoothUuid>()) {
- QBluetoothUuid uuid = var.value<QBluetoothUuid>();
- switch (uuid.minimumSize()) {
- case 0:
- dbg << QString::asprintf("%suuid NULL\n", indent.toUtf8().constData());
- break;
- case 2:
- dbg << QString::asprintf("%suuid2 %04x\n", indent.toUtf8().constData(),
- uuid.toUInt16());
- break;
- case 4:
- dbg << QString::asprintf("%suuid %08x\n", indent.toUtf8().constData(),
- uuid.toUInt32());
- break;
- case 16:
- dbg << QString::asprintf("%suuid %s\n",
- indent.toUtf8().constData(),
- QByteArray(reinterpret_cast<const char *>(uuid.toUInt128().data), 16).toHex().constData());
- break;
- default:
- dbg << QString::asprintf("%suuid ???\n", indent.toUtf8().constData());
- }
- } else if (var.userType() == qMetaTypeId<QBluetoothServiceInfo::Sequence>()) {
- dbg << QString::asprintf("%sSequence\n", indent.toUtf8().constData());
- const QBluetoothServiceInfo::Sequence *sequence = static_cast<const QBluetoothServiceInfo::Sequence *>(var.data());
- for (const QVariant &v : *sequence)
- dumpAttributeVariant(dbg, v, indent + QLatin1Char('\t'));
- } else if (var.userType() == qMetaTypeId<QBluetoothServiceInfo::Alternative>()) {
- dbg << QString::asprintf("%sAlternative\n", indent.toUtf8().constData());
- const QBluetoothServiceInfo::Alternative *alternative = static_cast<const QBluetoothServiceInfo::Alternative *>(var.data());
- for (const QVariant &v : *alternative)
- dumpAttributeVariant(dbg, v, indent + QLatin1Char('\t'));
- }
- break;
- default:
- dbg << QString::asprintf("%sunknown variant type %d\n", indent.toUtf8().constData(),
- var.userType());
- }
-}
-
-QDebug operator << (QDebug dbg, const QBluetoothServiceInfo &info)
-{
- QDebugStateSaver saver(dbg);
- dbg.noquote() << "\n";
- const QList<quint16> attributes = info.attributes();
- for (quint16 id : attributes) {
- dumpAttributeVariant(dbg, 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 e4e835e4..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;
@@ -78,11 +82,15 @@ namespace ABI {
}
#endif
+#ifdef QT_WIN_BLUETOOTH
+#include <winsock2.h>
+#include <ws2bth.h>
+#endif
+
QT_BEGIN_NAMESPACE
class QBluetoothServiceInfo;
-#ifndef QT_OSX_BLUETOOTH
class QBluetoothServiceInfoPrivate
: public QObject
@@ -120,11 +128,28 @@ private:
bool writeSdpAttributes();
#endif
- mutable bool registered;
-};
-
+#ifdef QT_WIN_BLUETOOTH
+ SOCKADDR_BTH sockaddr = {};
+ CSADDR_INFO addrinfo = {};
+ WSAQUERYSET regInfo = {};
+ QVector<WCHAR> serviceName;
+ QVector<WCHAR> serviceDescription;
#endif
+#if QT_OSX_BLUETOOTH
+public:
+ bool registerService(const QBluetoothServiceInfo &info);
+
+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_win.cpp b/src/bluetooth/qbluetoothserviceinfo_win.cpp
new file mode 100644
index 00000000..1f1293ad
--- /dev/null
+++ b/src/bluetooth/qbluetoothserviceinfo_win.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothserviceinfo.h"
+#include "qbluetoothserviceinfo_p.h"
+#include "qbluetoothserver_p.h"
+#include "qbluetoothserver.h"
+
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate()
+ : registered(false)
+{
+}
+
+QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate()
+{
+}
+
+bool QBluetoothServiceInfoPrivate::isRegistered() const
+{
+ return registered;
+}
+
+bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAdapter)
+{
+ if (registered)
+ return false;
+
+ GUID serviceUuid = attributes.value(QBluetoothServiceInfo::ServiceId).value<QBluetoothUuid>();
+ const QString name = attributes.value(QBluetoothServiceInfo::ServiceName).toString();
+ const QString description = attributes.value(QBluetoothServiceInfo::ServiceDescription).toString();
+
+ ::memset(&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.addressFamily = AF_BTH;
+ sockaddr.port = serverChannel();
+ sockaddr.btAddr = localAdapter.toUInt64();
+
+ ::memset(&addrinfo, 0, sizeof(addrinfo));
+ addrinfo.LocalAddr.iSockaddrLength = sizeof(SOCKADDR_BTH);
+ addrinfo.LocalAddr.lpSockaddr = (LPSOCKADDR)&sockaddr;
+ addrinfo.RemoteAddr.iSockaddrLength = sizeof(SOCKADDR_BTH);
+ addrinfo.RemoteAddr.lpSockaddr = (LPSOCKADDR)&sockaddr;
+ addrinfo.iSocketType = SOCK_STREAM;
+ addrinfo.iProtocol = BTHPROTO_RFCOMM;
+
+ serviceName.resize(name.size() + 1);
+ name.toWCharArray(serviceName.data());
+ serviceName[name.size()] = WCHAR(0);
+ serviceDescription.resize(description.size() + 1);
+ description.toWCharArray(serviceDescription.data());
+ serviceDescription[description.size()] = WCHAR(0);
+
+ ::memset(&regInfo, 0, sizeof(regInfo));
+ regInfo.dwSize = sizeof(WSAQUERYSET);
+ regInfo.dwNameSpace = NS_BTH;
+ regInfo.dwNumberOfCsAddrs = 1;
+ regInfo.lpcsaBuffer = &addrinfo;
+ regInfo.lpszServiceInstanceName = serviceName.data();
+ regInfo.lpszComment = serviceDescription.data();
+ regInfo.lpServiceClassId = &serviceUuid;
+
+ if (::WSASetService(&regInfo, RNRSERVICE_REGISTER, 0) == SOCKET_ERROR)
+ return false;
+
+ registered = true;
+ return true;
+}
+
+bool QBluetoothServiceInfoPrivate::unregisterService()
+{
+ if (!registered)
+ return false;
+
+ if (::WSASetService(&regInfo, RNRSERVICE_DELETE, 0) == SOCKET_ERROR)
+ return false;
+
+ registered = false;
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp
index db7c8be4..e4d85447 100644
--- a/src/bluetooth/qbluetoothsocket.cpp
+++ b/src/bluetooth/qbluetoothsocket.cpp
@@ -47,6 +47,10 @@
#include "qbluetoothsocket_android_p.h"
#elif defined(QT_WINRT_BLUETOOTH)
#include "qbluetoothsocket_winrt_p.h"
+#elif defined(QT_WIN_BLUETOOTH)
+#include "qbluetoothsocket_win_p.h"
+#elif defined(QT_OSX_BLUETOOTH)
+#include "qbluetoothsocket_osx_p.h"
#else
#include "qbluetoothsocket_dummy_p.h"
#endif
@@ -267,6 +271,10 @@ static QBluetoothSocketBasePrivate *createSocketPrivate()
return new QBluetoothSocketPrivateAndroid();
#elif defined(QT_WINRT_BLUETOOTH)
return new QBluetoothSocketPrivateWinRT();
+#elif defined(QT_WIN_BLUETOOTH)
+ return new QBluetoothSocketPrivateWin();
+#elif defined(QT_OSX_BLUETOOTH)
+ return new QBluetoothSocketPrivate();
#else
return new QBluetoothSocketPrivateDummy();
#endif
@@ -513,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;
@@ -534,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
}
/*!
@@ -559,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) {
diff --git a/src/bluetooth/qbluetoothsocket.h b/src/bluetooth/qbluetoothsocket.h
index d2535544..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;
@@ -75,6 +68,7 @@ class Q_BLUETOOTH_EXPORT QBluetoothSocket : public QIODevice
friend class QBluetoothSocketPrivateBluez;
friend class QBluetoothSocketPrivateBluezDBus;
friend class QBluetoothSocketPrivateDummy;
+ friend class QBluetoothSocketPrivateWin;
friend class QBluetoothSocketPrivateWinRT;
public:
@@ -187,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_osx.mm b/src/bluetooth/qbluetoothsocket_osx.mm
index 2a856092..8af085ac 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, [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]);
- if (rfcommChannel.data())
- status = [rfcommChannel connectAsyncToDevice:address withChannelID:port];
+ rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this], RetainPolicy::noInitialRetain);
+ if (rfcommChannel)
+ status = [rfcommChannel.getAs<ObjCRFCOMMChannel>() connectAsyncToDevice:address withChannelID:port];
else
status = kIOReturnNoMemory;
} else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
- l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this]);
- if (l2capChannel.data())
- status = [l2capChannel connectAsyncToDevice:address withPSM:port];
+ l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this], RetainPolicy::noInitialRetain);
+ if (l2capChannel)
+ status = [l2capChannel.getAs<ObjCL2CAPChannel>() connectAsyncToDevice:address withPSM:port];
else
status = kIOReturnNoMemory;
}
@@ -148,104 +415,26 @@ 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.data())
- nsName = [rfcommChannel peerName];
- } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
- if (l2capChannel.data())
- nsName = [l2capChannel peerName];
- }
-
- if (nsName)
- return QString::fromNSString(nsName);
-
- return QString();
-}
-
-QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const
-{
- BluetoothDeviceAddress addr = {};
- if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
- if (rfcommChannel.data())
- addr = [rfcommChannel peerAddress];
- } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
- if (l2capChannel.data())
- addr = [l2capChannel peerAddress];
- }
-
- return OSXBluetooth::qt_address(&addr);
-}
-
-quint16 QBluetoothSocketPrivate::peerPort() const
-{
- if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
- if (rfcommChannel.data())
- return [rfcommChannel getChannelID];
- } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
- if (l2capChannel.data())
- return [l2capChannel getPSM];
- }
-
- return 0;
-}
-
-void QBluetoothSocketPrivate::_q_readNotify()
-{
- // Noop.
-}
-
void QBluetoothSocketPrivate::_q_writeNotify()
{
Q_ASSERT_X(socketType == QBluetoothServiceInfo::L2capProtocol
|| socketType == QBluetoothServiceInfo::RfcommProtocol,
Q_FUNC_INFO, "invalid socket type");
- Q_ASSERT_X(l2capChannel.data() || rfcommChannel.data(), Q_FUNC_INFO,
+ Q_ASSERT_X(l2capChannel || rfcommChannel, Q_FUNC_INFO,
"invalid socket (no open channel)");
Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
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,37 +449,39 @@ 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]);
- if (rfcommChannel.data()) {// We do not handle errors, up to an external user.
+ 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;
socketType = QBluetoothServiceInfo::RfcommProtocol;
}
- return rfcommChannel.data();
+ 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,17 +489,16 @@ bool QBluetoothSocketPrivate::setChannel(IOBluetoothL2CAPChannel *channel)
Q_FUNC_INFO, "unexpected socket state");
openMode = QIODevice::ReadWrite;
- l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this channel:channel]);
- if (l2capChannel.data()) {// We do not handle errors, up to an external user.
+ 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;
socketType = QBluetoothServiceInfo::L2capProtocol;
}
- return l2capChannel.data();
+ 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_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp
new file mode 100644
index 00000000..83855323
--- /dev/null
+++ b/src/bluetooth/qbluetoothsocket_win.cpp
@@ -0,0 +1,597 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothsocket.h"
+#include "qbluetoothsocket_win_p.h"
+
+#include <QtCore/qloggingcategory.h>
+
+#include <QtBluetooth/qbluetoothdeviceinfo.h>
+#include <QtBluetooth/QBluetoothLocalDevice>
+#include <QtCore/QSocketNotifier>
+
+#include <winsock2.h>
+#include <ws2bth.h>
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+QBluetoothSocketPrivateWin::QBluetoothSocketPrivateWin()
+ : QBluetoothSocketBasePrivate()
+{
+ WSAData wsadata = {};
+ ::WSAStartup(MAKEWORD(2, 0), &wsadata);
+}
+
+QBluetoothSocketPrivateWin::~QBluetoothSocketPrivateWin()
+{
+ abort();
+}
+
+bool QBluetoothSocketPrivateWin::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (static_cast<SOCKET>(socket) != INVALID_SOCKET) {
+ if (socketType == type)
+ return true;
+ abort();
+ }
+ socketType = type;
+
+ if (type != QBluetoothServiceInfo::RfcommProtocol) {
+ socket = int(INVALID_SOCKET);
+ errorString = QBluetoothSocket::tr("Unsupported protocol. Win32 only supports RFCOMM sockets");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return false;
+ }
+
+ socket = ::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
+
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to create socket:" << error << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Failed to create socket");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+
+ if (!createNotifiers())
+ return false;
+
+ return true;
+}
+
+void QBluetoothSocketPrivateWin::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET && !ensureNativeSocket(socketType))
+ return;
+
+ if (!configureSecurity())
+ return;
+
+ SOCKADDR_BTH addr = {};
+ addr.addressFamily = AF_BTH;
+ addr.port = port;
+ addr.btAddr = address.toUInt64();
+
+ switch (socketType) {
+ case QBluetoothServiceInfo::RfcommProtocol:
+ addr.serviceClassId = RFCOMM_PROTOCOL_UUID;
+ break;
+ default:
+ errorString = QBluetoothSocket::tr("Socket type not handled: %1").arg(socketType);
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ connectWriteNotifier->setEnabled(true);
+ readNotifier->setEnabled(true);
+ exceptNotifier->setEnabled(true);
+
+ const int result = ::connect(socket, reinterpret_cast<sockaddr *>(&addr), sizeof(addr));
+
+ const int error = ::WSAGetLastError();
+ if (result != SOCKET_ERROR || error == WSAEWOULDBLOCK) {
+ q->setSocketState(QBluetoothSocket::ConnectingState);
+ q->setOpenMode(openMode);
+ } else {
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ }
+}
+
+void QBluetoothSocketPrivateWin::connectToService(
+ const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (q->state() != QBluetoothSocket::UnconnectedState
+ && q->state() != QBluetoothSocket::ServiceLookupState) {
+ //qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWIN::connectToService called on busy socket";
+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ // we are checking the service protocol and not socketType()
+ // socketType will change in ensureNativeSocket()
+ if (service.socketProtocol() != QBluetoothServiceInfo::RfcommProtocol) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocket::connectToService called with unsupported protocol";
+ errorString = QBluetoothSocket::tr("Socket type not supported");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ if (service.serverChannel() > 0) {
+ if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return;
+ }
+ connectToServiceHelper(service.device().address(), service.serverChannel(), openMode);
+ } else {
+ // try doing service discovery to see if we can find the socket
+ if (service.serviceUuid().isNull()
+ && !service.serviceClassUuids().contains(QBluetoothUuid::SerialPort)) {
+ qCWarning(QT_BT_WINDOWS) << "No port, no PSM, and no UUID provided. Unable to connect";
+ return;
+ }
+ qCDebug(QT_BT_WINDOWS) << "Need a port/psm, doing discovery";
+ q->doDeviceDiscovery(service, openMode);
+ }
+}
+
+void QBluetoothSocketPrivateWin::_q_writeNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state == QBluetoothSocket::ConnectingState) {
+ q->setSocketState(QBluetoothSocket::ConnectedState);
+ connectWriteNotifier->setEnabled(false);
+ } else {
+ if (txBuffer.isEmpty()) {
+ connectWriteNotifier->setEnabled(false);
+ return;
+ }
+
+ char buf[1024];
+ const int size = txBuffer.read(&buf[0], sizeof(buf));
+ const int writtenBytes = ::send(socket, &buf[0], size, 0);
+ if (writtenBytes == SOCKET_ERROR) {
+ // every other case returns error
+ const int error = ::WSAGetLastError();
+ errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error));
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ } else if (writtenBytes <= size) {
+ // add remainder back to buffer
+ const char *remainder = &buf[writtenBytes];
+ txBuffer.ungetBlock(remainder, size - writtenBytes);
+ if (writtenBytes > 0)
+ emit q->bytesWritten(writtenBytes);
+ } else {
+ errorString = QBluetoothSocket::tr("Logic error: more bytes sent than passed to ::send");
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ }
+
+ if (!txBuffer.isEmpty()) {
+ connectWriteNotifier->setEnabled(true);
+ } else if (state == QBluetoothSocket::ClosingState) {
+ connectWriteNotifier->setEnabled(false);
+ this->close();
+ }
+ }
+}
+
+void QBluetoothSocketPrivateWin::_q_readNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ char *writePointer = buffer.reserve(QPRIVATELINEARBUFFER_BUFFERSIZE);
+ const int bytesRead = ::recv(socket, writePointer, QPRIVATELINEARBUFFER_BUFFERSIZE, 0);
+ if (bytesRead == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ buffer.chop(QPRIVATELINEARBUFFER_BUFFERSIZE);
+ readNotifier->setEnabled(false);
+ connectWriteNotifier->setEnabled(false);
+ errorString = qt_error_string(error);
+ qCWarning(QT_BT_WINDOWS) << Q_FUNC_INFO << socket << "error:" << error << errorString;
+ switch (error) {
+ case WSAEHOSTDOWN:
+ q->setSocketError(QBluetoothSocket::HostNotFoundError);
+ break;
+ case WSAECONNRESET:
+ q->setSocketError(QBluetoothSocket::RemoteHostClosedError);
+ break;
+ default:
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ break;
+ }
+
+ q->disconnectFromService();
+ } else if (bytesRead == 0) {
+ q->setSocketError(QBluetoothSocket::RemoteHostClosedError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ } else {
+ const int unusedBytes = QPRIVATELINEARBUFFER_BUFFERSIZE - bytesRead;
+ buffer.chop(unusedBytes);
+ if (bytesRead > 0)
+ emit q->readyRead();
+ }
+}
+
+void QBluetoothSocketPrivateWin::_q_exceptNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ const int error = ::WSAGetLastError();
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+
+ if (state == QBluetoothSocket::ConnectingState)
+ abort();
+}
+
+void QBluetoothSocketPrivateWin::connectToService(
+ const QBluetoothAddress &address, const QBluetoothUuid &uuid,
+ QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (q->state() != QBluetoothSocket::UnconnectedState) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService called on busy socket";
+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService cannot "
+ "connect with 'UnknownProtocol' (type provided by given service)";
+ errorString = QBluetoothSocket::tr("Socket type not supported");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ QBluetoothServiceInfo service;
+ QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice);
+ service.setDevice(device);
+ service.setServiceUuid(uuid);
+ q->doDeviceDiscovery(service, openMode);
+}
+
+void QBluetoothSocketPrivateWin::connectToService(
+ const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService cannot "
+ "connect with 'UnknownProtocol' (type provided by given service)";
+ errorString = QBluetoothSocket::tr("Socket type not supported");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ if (q->state() != QBluetoothSocket::UnconnectedState) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService called on busy socket";
+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ q->setOpenMode(openMode);
+ connectToServiceHelper(address, port, openMode);
+}
+
+void QBluetoothSocketPrivateWin::abort()
+{
+ delete readNotifier;
+ readNotifier = nullptr;
+ delete connectWriteNotifier;
+ connectWriteNotifier = nullptr;
+ delete exceptNotifier;
+ exceptNotifier = nullptr;
+
+ // We don't transition through Closing for abort, so
+ // we don't call disconnectFromService or QBluetoothSocket::close
+ ::closesocket(socket);
+ socket = int(INVALID_SOCKET);
+
+ Q_Q(QBluetoothSocket);
+
+ const bool wasConnected = q->state() == QBluetoothSocket::ConnectedState;
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ if (wasConnected) {
+ q->setOpenMode(QIODevice::NotOpen);
+ emit q->readChannelFinished();
+ }
+}
+
+QString QBluetoothSocketPrivateWin::localName() const
+{
+ const QBluetoothAddress localAddr = localAddress();
+ if (localAddr == QBluetoothAddress())
+ return {};
+ const QBluetoothLocalDevice device(localAddr);
+ return device.name();
+}
+
+QBluetoothAddress QBluetoothSocketPrivateWin::localAddress() const
+{
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET)
+ return {};
+ SOCKADDR_BTH localAddr = {};
+ int localAddrLength = sizeof(localAddr);
+ const int localResult = ::getsockname(socket, reinterpret_cast<sockaddr *>(&localAddr), &localAddrLength);
+ if (localResult == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting local address" << error << qt_error_string(error);
+ return {};
+ }
+ return QBluetoothAddress(localAddr.btAddr);
+}
+
+quint16 QBluetoothSocketPrivateWin::localPort() const
+{
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET)
+ return {};
+ SOCKADDR_BTH localAddr = {};
+ int localAddrLength = sizeof(localAddr);
+ const int localResult = ::getsockname(socket, reinterpret_cast<sockaddr *>(&localAddr), &localAddrLength);
+ if (localResult == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting local port" << error << qt_error_string(error);
+ return {};
+ }
+ return localAddr.port;
+}
+
+QString QBluetoothSocketPrivateWin::peerName() const
+{
+ const QBluetoothAddress peerAddr = peerAddress();
+ if (peerAddr == QBluetoothAddress())
+ return {};
+ BLUETOOTH_DEVICE_INFO bdi = {};
+ bdi.dwSize = sizeof(bdi);
+ bdi.Address.ullLong = peerAddr.toUInt64();
+ const DWORD res = ::BluetoothGetDeviceInfo(nullptr, &bdi);
+ if (res != ERROR_SUCCESS) {
+ qCWarning(QT_BT_WINDOWS) << "Error calling BluetoothGetDeviceInfo" << res << qt_error_string(res);
+ return {};
+ }
+ return QString::fromWCharArray(&bdi.szName[0]);
+}
+
+QBluetoothAddress QBluetoothSocketPrivateWin::peerAddress() const
+{
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET)
+ return {};
+ SOCKADDR_BTH peerAddr = {};
+ int peerAddrLength = sizeof(peerAddr);
+ const int peerResult = ::getpeername(socket, reinterpret_cast<sockaddr *>(&peerAddr), &peerAddrLength);
+ if (peerResult == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error);
+ return {};
+ }
+ return QBluetoothAddress(peerAddr.btAddr);
+}
+
+quint16 QBluetoothSocketPrivateWin::peerPort() const
+{
+ if (static_cast<SOCKET>(socket) == INVALID_SOCKET)
+ return {};
+ SOCKADDR_BTH peerAddr = {};
+ int peerAddrLength = sizeof(peerAddr);
+ const int peerResult = ::getpeername(socket, reinterpret_cast<sockaddr *>(&peerAddr), &peerAddrLength);
+ if (peerResult == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error);
+ return {};
+ }
+ return peerAddr.port;
+}
+
+qint64 QBluetoothSocketPrivateWin::writeData(const char *data, qint64 maxSize)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot write while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ if (q->openMode() & QIODevice::Unbuffered) {
+ const int bytesWritten = ::send(socket, data, maxSize, 0);
+
+ if (bytesWritten == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error));
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ }
+
+ if (bytesWritten > 0)
+ emit q->bytesWritten(bytesWritten);
+
+ return bytesWritten;
+ } else {
+
+ if (!connectWriteNotifier)
+ return -1;
+
+ if (txBuffer.isEmpty()) {
+ connectWriteNotifier->setEnabled(true);
+ QMetaObject::invokeMethod(this, "_q_writeNotify", Qt::QueuedConnection);
+ }
+
+ char *txbuf = txBuffer.reserve(maxSize);
+ ::memcpy(txbuf, data, maxSize);
+
+ return maxSize;
+ }
+}
+
+qint64 QBluetoothSocketPrivateWin::readData(char *data, qint64 maxSize)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot read while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ const int bytesRead = buffer.read(data, maxSize);
+ return bytesRead;
+}
+
+void QBluetoothSocketPrivateWin::close()
+{
+ if (txBuffer.isEmpty())
+ abort();
+ else
+ connectWriteNotifier->setEnabled(true);
+}
+
+bool QBluetoothSocketPrivateWin::setSocketDescriptor(int socketDescriptor,
+ QBluetoothServiceInfo::Protocol protocol,
+ QBluetoothSocket::SocketState socketState,
+ QBluetoothSocket::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ abort();
+
+ socketType = protocol;
+ socket = socketDescriptor;
+
+ if (!createNotifiers())
+ return false;
+ q->setSocketState(socketState);
+ q->setOpenMode(openMode);
+ if (socketState == QBluetoothSocket::ConnectedState) {
+ connectWriteNotifier->setEnabled(true);
+ readNotifier->setEnabled(true);
+ exceptNotifier->setEnabled(true);
+ }
+
+ return true;
+}
+
+qint64 QBluetoothSocketPrivateWin::bytesAvailable() const
+{
+ return buffer.size();
+}
+
+bool QBluetoothSocketPrivateWin::canReadLine() const
+{
+ return buffer.canReadLine();
+}
+
+qint64 QBluetoothSocketPrivateWin::bytesToWrite() const
+{
+ return txBuffer.size();
+}
+
+bool QBluetoothSocketPrivateWin::createNotifiers()
+{
+ Q_Q(QBluetoothSocket);
+
+ ULONG mode = 1; // 1 to enable non-blocking socket
+ const int result = ::ioctlsocket(socket, FIONBIO, &mode);
+
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ qCWarning(QT_BT_WINDOWS) << "Error setting socket to non-blocking" << error << errorString;
+ abort();
+ return false;
+ }
+ readNotifier = new QSocketNotifier(socket, QSocketNotifier::Read);
+ QObject::connect(readNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_readNotify);
+ connectWriteNotifier = new QSocketNotifier(socket, QSocketNotifier::Write, q);
+ QObject::connect(connectWriteNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_writeNotify);
+ exceptNotifier = new QSocketNotifier(socket, QSocketNotifier::Exception, q);
+ QObject::connect(exceptNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_exceptNotify);
+
+ connectWriteNotifier->setEnabled(false);
+ readNotifier->setEnabled(false);
+ exceptNotifier->setEnabled(false);
+ return true;
+}
+
+bool QBluetoothSocketPrivateWin::configureSecurity()
+{
+ Q_Q(QBluetoothSocket);
+
+ if (secFlags & QBluetooth::Authorization) {
+ ULONG authenticate = TRUE;
+ const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_AUTHENTICATE, reinterpret_cast<const char*>(&authenticate), sizeof(authenticate));
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error;
+ qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot set connection security level");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+ }
+
+ if (secFlags & QBluetooth::Encryption) {
+ ULONG encrypt = TRUE;
+ const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_ENCRYPT, reinterpret_cast<const char*>(&encrypt), sizeof(encrypt));
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error;
+ qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot set connection security level");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+ }
+ return true;
+}
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserver_osx_p.h b/src/bluetooth/qbluetoothsocket_win_p.h
index 3116ca02..77f4842e 100644
--- a/src/bluetooth/qbluetoothserver_osx_p.h
+++ b/src/bluetooth/qbluetoothsocket_win_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QBLUETOOTHSERVER_OSX_P_H
-#define QBLUETOOTHSERVER_OSX_P_H
+#ifndef QBLUETOOTHSOCKET_WIN_H
+#define QBLUETOOTHSOCKET_WIN_H
//
// W A R N I N G
@@ -51,77 +51,68 @@
// 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 "qbluetoothsocket.h"
+#include "qbluetoothsocketbase_p.h"
+#include <QtGlobal>
QT_BEGIN_NAMESPACE
-class QMutex;
-
-class QBluetoothServerPrivate : public OSXBluetooth::SocketListener
+class QBluetoothSocketPrivateWin final : public QBluetoothSocketBasePrivate
{
- friend class QBluetoothServer;
- friend class QBluetoothServiceInfoPrivate;
+ Q_OBJECT
+ friend class QBluetoothServerPrivate;
public:
- QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol type, QBluetoothServer *q);
- ~QBluetoothServerPrivate();
+ QBluetoothSocketPrivateWin();
+ ~QBluetoothSocketPrivateWin() override;
- void _q_newConnection();
-private:
- bool startListener(quint16 realPort);
- void stopListener();
+ void connectToServiceHelper(const QBluetoothAddress &address,
+ quint16 port,
+ QIODevice::OpenMode openMode) override;
- // SocketListener (delegate):
- void openNotify(IOBluetoothRFCOMMChannel *channel) override;
- void openNotify(IOBluetoothL2CAPChannel *channel) 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;
- QBluetoothServiceInfo::Protocol serverType;
- QBluetoothServer *q_ptr;
- QBluetoothServer::Error lastError;
+ bool ensureNativeSocket(QBluetoothServiceInfo::Protocol type) 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;
+ QString localName() const override;
+ QBluetoothAddress localAddress() const override;
+ quint16 localPort() const override;
- typedef OSXBluetooth::ObjCScopedPointer<ObjCListener> Listener;
- Listener listener;
+ QString peerName() const override;
+ QBluetoothAddress peerAddress() const override;
+ quint16 peerPort() const override;
- int maxPendingConnections;
+ void abort() override;
+ void close() override;
- // 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();
+ qint64 writeData(const char *data, qint64 maxSize) override;
+ qint64 readData(char *data, qint64 maxSize) override;
- static bool channelIsBusy(quint16 channelID);
- static quint16 findFreeChannel();
+ bool setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
+ QBluetoothSocket::SocketState socketState = QBluetoothSocket::ConnectedState,
+ QBluetoothSocket::OpenMode openMode = QBluetoothSocket::ReadWrite) override;
- static bool psmIsBusy(quint16 psm);
- static quint16 findFreePSM();
+ qint64 bytesAvailable() const override;
+ bool canReadLine() const override;
+ qint64 bytesToWrite() const override;
- static void registerServer(QBluetoothServerPrivate *server, quint16 port);
- static QBluetoothServerPrivate *registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol);
- static void unregisterServer(QBluetoothServerPrivate *server);
+private slots:
+ void _q_readNotify();
+ void _q_writeNotify();
+ void _q_exceptNotify();
- typedef OSXBluetooth::ObjCStrongReference<NSObject> PendingConnection;
- QList<PendingConnection> pendingConnections;
+private:
+ bool createNotifiers();
+ bool configureSecurity();
+ QSocketNotifier *exceptNotifier = nullptr;
};
QT_END_NAMESPACE
-#endif //QT_OSX_BLUETOOTH
-
-#endif
+#endif // QBLUETOOTHSOCKET_WIN_H
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/qbluetoothtransfermanager.cpp b/src/bluetooth/qbluetoothtransfermanager.cpp
index 37a3191a..d84f726c 100644
--- a/src/bluetooth/qbluetoothtransfermanager.cpp
+++ b/src/bluetooth/qbluetoothtransfermanager.cpp
@@ -121,8 +121,8 @@ QBluetoothTransferReply *QBluetoothTransferManager::put(const QBluetoothTransfer
connect(reply, SIGNAL(finished(QBluetoothTransferReply*)), this, SIGNAL(finished(QBluetoothTransferReply*)));
return reply;
#else
- // Android, iOS, and WinRT have no implementation
-#if !defined(QT_ANDROID_BLUETOOTH) && !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH)
+ // Android, iOS, and Win/WinRT have no implementation
+#if !defined(QT_ANDROID_BLUETOOTH) && !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH) && !defined(QT_WIN_BLUETOOTH)
printDummyWarning();
#endif
Q_UNUSED(request);
diff --git a/src/bluetooth/qbluetoothutils_win.cpp b/src/bluetooth/qbluetoothutils_win.cpp
index fa3127cb..a5151d82 100644
--- a/src/bluetooth/qbluetoothutils_win.cpp
+++ b/src/bluetooth/qbluetoothutils_win.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
diff --git a/src/bluetooth/qlowenergycharacteristic.h b/src/bluetooth/qlowenergycharacteristic.h
index 9b27d621..fe9b73fa 100644
--- a/src/bluetooth/qlowenergycharacteristic.h
+++ b/src/bluetooth/qlowenergycharacteristic.h
@@ -101,7 +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 8fc044fb..ec140a85 100644
--- a/src/bluetooth/qlowenergycontroller.cpp
+++ b/src/bluetooth/qlowenergycontroller.cpp
@@ -60,6 +60,10 @@
#if QT_CONFIG(winrt_btle_no_pairing)
#include "qlowenergycontroller_winrt_new_p.h"
#endif
+#elif defined(QT_WIN_BLUETOOTH)
+#include "qlowenergycontroller_win_p.h"
+#elif defined(Q_OS_DARWIN)
+#include "qlowenergycontroller_darwin_p.h"
#else
#include "qlowenergycontroller_p.h"
#endif
@@ -157,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.
*/
/*!
@@ -321,6 +328,12 @@ static QLowEnergyControllerPrivate *privateController(QLowEnergyController::Role
qCDebug(QT_BT_WINRT) << "Using pre 15063 low energy controller";
return new QLowEnergyControllerPrivateWinRT();
#endif
+#elif defined(QT_WIN_BLUETOOTH)
+ Q_UNUSED(role);
+ return new QLowEnergyControllerPrivateWin32();
+#elif defined(Q_OS_DARWIN)
+ Q_UNUSED(role)
+ return new QLowEnergyControllerPrivateDarwin();
#else
Q_UNUSED(role);
return new QLowEnergyControllerPrivateCommon();
@@ -344,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);
@@ -373,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;
@@ -406,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);
@@ -432,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.14
+ */
+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
@@ -511,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_bluezdbus.cpp b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp
index 26ceefb0..2a0fafdf 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;
+ }
}
}
}
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_darwin.mm
index 9aaee855..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
+
+#ifndef Q_OS_TVOS
+using ObjCPeripheralManager = QT_MANGLE_NAMESPACE(OSXBTPeripheralManager);
+#endif // Q_OS_TVOS
+
+using ObjCCentralManager = QT_MANGLE_NAMESPACE(OSXBTCentralManager);
-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)
+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()]);
- if (!centralManager.data()) {
- qCWarning(QT_BT_OSX) << "failed to initialize central manager";
+ centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()],
+ DarwinBluetooth::RetainPolicy::noInitialRetain);
+ if (!centralManager) {
+ 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.data();
+ Q_UNUSED(service);
+ qCDebug(QT_BT_OSX, "peripheral role is not supported on tvOS");
#else
- return centralManager.data() || peripheralManager.data();
-#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,23 +488,23 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedP
qtService->setState(QLowEnergyService::ServiceDiscovered);
}
-void QLowEnergyControllerPrivateOSX::_q_servicesWereModified()
+void QLowEnergyControllerPrivateDarwin::_q_servicesWereModified()
{
- if (!(controllerState == QLowEnergyController::DiscoveringState
- || controllerState == QLowEnergyController::DiscoveredState)) {
+ if (!(state == QLowEnergyController::DiscoveringState
+ || state == QLowEnergyController::DiscoveredState)) {
qCWarning(QT_BT_OSX) << "services were modified while controller is not in Discovered/Discovering state";
return;
}
- if (controllerState == QLowEnergyController::DiscoveredState)
+ if (state == QLowEnergyController::DiscoveredState)
invalidateServices();
- controllerState = QLowEnergyController::ConnectedState;
+ setState(QLowEnergyController::ConnectedState);
q_ptr->discoverServices();
}
-void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle charHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateDarwin::_q_characteristicRead(QLowEnergyHandle charHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)");
@@ -375,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)");
@@ -399,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?
@@ -428,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)");
@@ -444,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)");
@@ -461,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.
@@ -504,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
@@ -512,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"
@@ -545,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)";
@@ -666,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;
}
@@ -678,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, ^{
@@ -692,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;
@@ -716,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;
@@ -751,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
@@ -768,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];
});
@@ -778,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()) {
@@ -799,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;
@@ -823,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)";
@@ -845,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
@@ -867,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()) {
@@ -893,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;
@@ -978,48 +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);
- ok = ok && connect(notifier, &LECBManagerNotifier::serviceDetailsDiscoveryFinished,
- this, &QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished);
+ this, &QLowEnergyControllerPrivateDarwin::_q_serviceDiscoveryFinished);
ok = ok && connect(notifier, &LECBManagerNotifier::servicesWereModified,
- this, &QLowEnergyControllerPrivateOSX::_q_servicesWereModified);
+ this, &QLowEnergyControllerPrivateDarwin::_q_servicesWereModified);
+ ok = ok && connect(notifier, &LECBManagerNotifier::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)),
@@ -1033,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)
@@ -1287,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 (!osx_d_ptr->isValid())
- return osx_d_ptr->_q_CBManagerError(UnknownError);
+ if (!isValid())
+ return _q_CBManagerError(QLowEnergyController::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 da959895..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();
@@ -113,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_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp
new file mode 100644
index 00000000..ced69685
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_win.cpp
@@ -0,0 +1,1356 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlowenergycontroller_win_p.h"
+#include "qbluetoothdevicediscoveryagent_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QIODevice> // for open modes
+#include <QtCore/QEvent>
+#include <QtCore/QMutex>
+#include <QtCore/QThread>
+#include <QtCore/QDataStream>
+#include <QtCore/QCoreApplication>
+
+#include <algorithm> // for std::max
+
+#include <SetupAPI.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+Q_GLOBAL_STATIC(QLibrary, bluetoothapis)
+
+Q_GLOBAL_STATIC(QVector<QLowEnergyControllerPrivateWin32 *>, qControllers)
+static QMutex controllersGuard(QMutex::NonRecursive);
+
+const QEvent::Type CharactericticValueEventType = static_cast<QEvent::Type>(QEvent::User + 1);
+
+class CharactericticValueEvent : public QEvent
+{
+public:
+ explicit CharactericticValueEvent(const PBLUETOOTH_GATT_VALUE_CHANGED_EVENT gattValueChangedEvent)
+ : QEvent(CharactericticValueEventType)
+ , m_handle(0)
+ {
+ if (!gattValueChangedEvent || gattValueChangedEvent->CharacteristicValueDataSize == 0)
+ return;
+
+ m_handle = gattValueChangedEvent->ChangedAttributeHandle;
+
+ const PBTH_LE_GATT_CHARACTERISTIC_VALUE gattValue = gattValueChangedEvent->CharacteristicValue;
+ if (!gattValue)
+ return;
+
+ m_value = QByteArray(reinterpret_cast<const char *>(&gattValue->Data[0]),
+ int(gattValue->DataSize));
+ }
+
+ QByteArray m_value;
+ QLowEnergyHandle m_handle;
+};
+
+// Bit masks of ClientCharacteristicConfiguration value, see btle spec.
+namespace ClientCharacteristicConfigurationValue {
+enum { UseNotifications = 0x1, UseIndications = 0x2 };
+}
+
+static bool gattFunctionsResolved = false;
+
+static QBluetoothAddress getDeviceAddress(const QString &servicePath)
+{
+ const int firstbound = servicePath.lastIndexOf(QStringLiteral("_"));
+ const int lastbound = servicePath.indexOf(QLatin1Char('#'), firstbound);
+ const QString hex = servicePath.mid(firstbound + 1, lastbound - firstbound - 1);
+ bool ok = false;
+ return QBluetoothAddress(hex.toULongLong(&ok, 16));
+}
+
+static QString getServiceSystemPath(const QBluetoothAddress &deviceAddress,
+ const QBluetoothUuid &serviceUuid, int *systemErrorCode)
+{
+ const HDEVINFO deviceInfoSet = ::SetupDiGetClassDevs(
+ reinterpret_cast<const GUID *>(&serviceUuid),
+ nullptr,
+ nullptr,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+
+ if (deviceInfoSet == INVALID_HANDLE_VALUE) {
+ *systemErrorCode = int(::GetLastError());
+ return QString();
+ }
+
+ QString foundSystemPath;
+ DWORD index = 0;
+
+ for (;;) {
+ SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
+ ::ZeroMemory(&deviceInterfaceData, sizeof(deviceInterfaceData));
+ deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
+
+ if (!::SetupDiEnumDeviceInterfaces(
+ deviceInfoSet,
+ nullptr,
+ reinterpret_cast<const GUID *>(&serviceUuid),
+ index++,
+ &deviceInterfaceData)) {
+ *systemErrorCode = int(::GetLastError());
+ break;
+ }
+
+ DWORD deviceInterfaceDetailDataSize = 0;
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ deviceInfoSet,
+ &deviceInterfaceData,
+ nullptr,
+ deviceInterfaceDetailDataSize,
+ &deviceInterfaceDetailDataSize,
+ nullptr)) {
+ const int error = int(::GetLastError());
+ if (error != ERROR_INSUFFICIENT_BUFFER) {
+ *systemErrorCode = error;
+ break;
+ }
+ }
+
+ SP_DEVINFO_DATA deviceInfoData;
+ ::ZeroMemory(&deviceInfoData, sizeof(deviceInfoData));
+ deviceInfoData.cbSize = sizeof(deviceInfoData);
+
+ QByteArray deviceInterfaceDetailDataBuffer(
+ int(deviceInterfaceDetailDataSize), 0);
+
+ PSP_INTERFACE_DEVICE_DETAIL_DATA deviceInterfaceDetailData =
+ reinterpret_cast<PSP_INTERFACE_DEVICE_DETAIL_DATA>
+ (deviceInterfaceDetailDataBuffer.data());
+
+ deviceInterfaceDetailData->cbSize =
+ sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
+
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ deviceInfoSet,
+ &deviceInterfaceData,
+ deviceInterfaceDetailData,
+ DWORD(deviceInterfaceDetailDataBuffer.size()),
+ &deviceInterfaceDetailDataSize,
+ &deviceInfoData)) {
+ *systemErrorCode = int(::GetLastError());
+ break;
+ }
+
+ // We need to check on required device address which contains in a
+ // system path. As it is not enough to use only service UUID for this.
+ const auto candidateSystemPath = QString::fromWCharArray(deviceInterfaceDetailData->DevicePath);
+ const auto candidateDeviceAddress = getDeviceAddress(candidateSystemPath);
+ if (candidateDeviceAddress == deviceAddress) {
+ foundSystemPath = candidateSystemPath;
+ *systemErrorCode = NO_ERROR;
+ break;
+ }
+ }
+
+ ::SetupDiDestroyDeviceInfoList(deviceInfoSet);
+ return foundSystemPath;
+}
+
+static HANDLE openSystemDevice(
+ const QString &systemPath, QIODevice::OpenMode openMode, int *systemErrorCode)
+{
+ DWORD desiredAccess = 0;
+ DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+ if (openMode & QIODevice::ReadOnly) {
+ desiredAccess |= GENERIC_READ;
+ }
+
+ if (openMode & QIODevice::WriteOnly) {
+ desiredAccess |= GENERIC_WRITE;
+ shareMode &= ~DWORD(FILE_SHARE_WRITE);
+ }
+
+ const HANDLE hDevice = ::CreateFile(
+ reinterpret_cast<const wchar_t *>(systemPath.utf16()),
+ desiredAccess,
+ shareMode,
+ nullptr,
+ OPEN_EXISTING,
+ 0,
+ nullptr);
+
+ *systemErrorCode = (INVALID_HANDLE_VALUE == hDevice)
+ ? int(::GetLastError()) : NO_ERROR;
+ return hDevice;
+}
+
+static HANDLE openSystemService(const QBluetoothAddress &deviceAddress,
+ const QBluetoothUuid &service, QIODevice::OpenMode openMode, int *systemErrorCode)
+{
+ const QString serviceSystemPath = getServiceSystemPath(
+ deviceAddress, service, systemErrorCode);
+
+ if (*systemErrorCode != NO_ERROR)
+ return INVALID_HANDLE_VALUE;
+
+ const HANDLE hService = openSystemDevice(
+ serviceSystemPath, openMode, systemErrorCode);
+
+ if (*systemErrorCode != NO_ERROR)
+ return INVALID_HANDLE_VALUE;
+
+ return hService;
+}
+
+static void closeSystemDevice(HANDLE hDevice)
+{
+ if (hDevice && hDevice != INVALID_HANDLE_VALUE)
+ ::CloseHandle(hDevice);
+}
+
+static QVector<BTH_LE_GATT_SERVICE> enumeratePrimaryGattServices(
+ HANDLE hDevice, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QVector<BTH_LE_GATT_SERVICE>();
+ }
+
+ QVector<BTH_LE_GATT_SERVICE> foundServices;
+ USHORT servicesCount = 0;
+ for (;;) {
+ const HRESULT hr = ::BluetoothGATTGetServices(
+ hDevice,
+ servicesCount,
+ foundServices.isEmpty() ? nullptr : &foundServices[0],
+ &servicesCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return foundServices;
+ } else {
+ const int error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ foundServices.resize(servicesCount);
+ } else {
+ *systemErrorCode = error;
+ return QVector<BTH_LE_GATT_SERVICE>();
+ }
+ }
+ }
+}
+
+static QVector<BTH_LE_GATT_CHARACTERISTIC> enumerateGattCharacteristics(
+ HANDLE hService, PBTH_LE_GATT_SERVICE gattService, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QVector<BTH_LE_GATT_CHARACTERISTIC>();
+ }
+
+ QVector<BTH_LE_GATT_CHARACTERISTIC> foundCharacteristics;
+ USHORT characteristicsCount = 0;
+ for (;;) {
+ const HRESULT hr = ::BluetoothGATTGetCharacteristics(
+ hService,
+ gattService,
+ characteristicsCount,
+ foundCharacteristics.isEmpty() ? nullptr : &foundCharacteristics[0],
+ &characteristicsCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return foundCharacteristics;
+ } else {
+ const int error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ foundCharacteristics.resize(characteristicsCount);
+ } else {
+ *systemErrorCode = error;
+ return QVector<BTH_LE_GATT_CHARACTERISTIC>();
+ }
+ }
+ }
+}
+
+static QByteArray getGattCharacteristicValue(
+ HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QByteArray();
+ }
+
+ QByteArray valueBuffer;
+ USHORT valueBufferSize = 0;
+ for (;;) {
+ const auto valuePtr = valueBuffer.isEmpty()
+ ? nullptr
+ : reinterpret_cast<PBTH_LE_GATT_CHARACTERISTIC_VALUE>(valueBuffer.data());
+
+ const HRESULT hr = ::BluetoothGATTGetCharacteristicValue(
+ hService,
+ gattCharacteristic,
+ valueBufferSize,
+ valuePtr,
+ &valueBufferSize,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return QByteArray(reinterpret_cast<const char *>(&valuePtr->Data[0]),
+ int(valuePtr->DataSize));
+ } else {
+ const int error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ valueBuffer.resize(valueBufferSize);
+ valueBuffer.fill(0);
+ } else {
+ *systemErrorCode = error;
+ return QByteArray();
+ }
+ }
+ }
+}
+
+static void setGattCharacteristicValue(
+ HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic,
+ const QByteArray &value, DWORD flags, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return;
+ }
+
+ QByteArray valueBuffer;
+ QDataStream out(&valueBuffer, QIODevice::WriteOnly);
+ ULONG dataSize = ULONG(value.size());
+ out.writeRawData(reinterpret_cast<const char *>(&dataSize), sizeof(dataSize));
+ out.writeRawData(value.constData(), value.size());
+
+ BTH_LE_GATT_RELIABLE_WRITE_CONTEXT reliableWriteContext = 0;
+
+ const HRESULT hr = ::BluetoothGATTSetCharacteristicValue(
+ hService,
+ gattCharacteristic,
+ reinterpret_cast<PBTH_LE_GATT_CHARACTERISTIC_VALUE>(valueBuffer.data()),
+ reliableWriteContext,
+ flags);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+}
+
+static QVector<BTH_LE_GATT_DESCRIPTOR> enumerateGattDescriptors(
+ HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QVector<BTH_LE_GATT_DESCRIPTOR>();
+ }
+
+ QVector<BTH_LE_GATT_DESCRIPTOR> foundDescriptors;
+ USHORT descriptorsCount = 0;
+ for (;;) {
+ const HRESULT hr = ::BluetoothGATTGetDescriptors(
+ hService,
+ gattCharacteristic,
+ descriptorsCount,
+ foundDescriptors.isEmpty() ? nullptr : &foundDescriptors[0],
+ &descriptorsCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return foundDescriptors;
+ } else {
+ const int error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ foundDescriptors.resize(descriptorsCount);
+ } else {
+ *systemErrorCode = error;
+ return QVector<BTH_LE_GATT_DESCRIPTOR>();
+ }
+ }
+ }
+}
+
+static QByteArray getGattDescriptorValue(
+ HANDLE hService, PBTH_LE_GATT_DESCRIPTOR gattDescriptor, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QByteArray();
+ }
+
+ QByteArray valueBuffer;
+ USHORT valueBufferSize = 0;
+ for (;;) {
+ const auto valuePtr = valueBuffer.isEmpty()
+ ? nullptr
+ : reinterpret_cast<PBTH_LE_GATT_DESCRIPTOR_VALUE>(valueBuffer.data());
+
+ const HRESULT hr = ::BluetoothGATTGetDescriptorValue(
+ hService,
+ gattDescriptor,
+ valueBufferSize,
+ valuePtr,
+ &valueBufferSize,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ if (gattDescriptor->DescriptorType == CharacteristicUserDescription) {
+ QString valueString = QString::fromUtf16(reinterpret_cast<const ushort *>(&valuePtr->Data[0]),
+ valuePtr->DataSize/2);
+ return valueString.toUtf8();
+ }
+ return QByteArray(reinterpret_cast<const char *>(&valuePtr->Data[0]),
+ int(valuePtr->DataSize));
+ } else {
+ const int error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ valueBuffer.resize(valueBufferSize);
+ valueBuffer.fill(0);
+ } else {
+ *systemErrorCode = error;
+ return QByteArray();
+ }
+ }
+ }
+}
+
+static void setGattDescriptorValue(
+ HANDLE hService, PBTH_LE_GATT_DESCRIPTOR gattDescriptor,
+ QByteArray value, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return;
+ }
+
+ const int requiredValueBufferSize = int(sizeof(BTH_LE_GATT_DESCRIPTOR_VALUE))
+ + value.size();
+
+ QByteArray valueBuffer(requiredValueBufferSize, 0);
+
+ PBTH_LE_GATT_DESCRIPTOR_VALUE gattValue = reinterpret_cast<
+ PBTH_LE_GATT_DESCRIPTOR_VALUE>(valueBuffer.data());
+
+ gattValue->DescriptorType = gattDescriptor->DescriptorType;
+
+ if (gattValue->DescriptorType == ClientCharacteristicConfiguration) {
+ QDataStream in(value);
+ quint8 u;
+ in >> u;
+
+ // We need to setup appropriate fields that allow to subscribe for events.
+ gattValue->ClientCharacteristicConfiguration.IsSubscribeToNotification =
+ bool(u & ClientCharacteristicConfigurationValue::UseNotifications);
+ gattValue->ClientCharacteristicConfiguration.IsSubscribeToIndication =
+ bool(u & ClientCharacteristicConfigurationValue::UseIndications);
+ }
+
+ gattValue->DataSize = ULONG(value.size());
+ ::memcpy(gattValue->Data, value.constData(), size_t(value.size()));
+
+ const HRESULT hr = ::BluetoothGATTSetDescriptorValue(
+ hService,
+ gattDescriptor,
+ gattValue,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+}
+
+static void WINAPI eventChangedCallbackEntry(
+ BTH_LE_GATT_EVENT_TYPE eventType, PVOID eventOutParameter, PVOID context)
+{
+ if ((eventType != CharacteristicValueChangedEvent) || !eventOutParameter || !context)
+ return;
+
+ QMutexLocker locker(&controllersGuard);
+ const auto target = static_cast<QLowEnergyControllerPrivateWin32 *>(context);
+ if (!qControllers->contains(target))
+ return;
+
+ CharactericticValueEvent *e = new CharactericticValueEvent(
+ reinterpret_cast<const PBLUETOOTH_GATT_VALUE_CHANGED_EVENT>(eventOutParameter));
+
+ QCoreApplication::postEvent(target, e);
+}
+
+static HANDLE registerEvent(
+ HANDLE hService, BTH_LE_GATT_CHARACTERISTIC gattCharacteristic,
+ PVOID context, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return INVALID_HANDLE_VALUE;
+ }
+
+ HANDLE hEvent = INVALID_HANDLE_VALUE;
+
+ BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION registration;
+ ::ZeroMemory(&registration, sizeof(registration));
+ registration.NumCharacteristics = 1;
+ registration.Characteristics[0] = gattCharacteristic;
+
+ const HRESULT hr = ::BluetoothGATTRegisterEvent(
+ hService,
+ CharacteristicValueChangedEvent,
+ &registration,
+ eventChangedCallbackEntry,
+ context,
+ &hEvent,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+
+ return hEvent;
+}
+
+static void unregisterEvent(HANDLE hEvent, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return;
+ }
+
+ const HRESULT hr = ::BluetoothGATTUnregisterEvent(
+ hEvent,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+}
+
+static QBluetoothUuid qtBluetoothUuidFromNativeLeUuid(const BTH_LE_UUID &uuid)
+{
+ return uuid.IsShortUuid ? QBluetoothUuid(uuid.Value.ShortUuid)
+ : QBluetoothUuid(uuid.Value.LongUuid);
+}
+
+static BTH_LE_UUID nativeLeUuidFromQtBluetoothUuid(const QBluetoothUuid &uuid)
+{
+ BTH_LE_UUID gattUuid;
+ ::ZeroMemory(&gattUuid, sizeof(gattUuid));
+ if (uuid.minimumSize() == 2) {
+ gattUuid.IsShortUuid = TRUE;
+ gattUuid.Value.ShortUuid = USHORT(uuid.data1); // other fields should be empty!
+ } else {
+ gattUuid.Value.LongUuid = uuid;
+ }
+ return gattUuid;
+}
+
+static BTH_LE_GATT_CHARACTERISTIC recoverNativeLeGattCharacteristic(
+ QLowEnergyHandle serviceHandle, QLowEnergyHandle characteristicHandle,
+ const QLowEnergyServicePrivate::CharData &characteristicData)
+{
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic;
+
+ gattCharacteristic.ServiceHandle = serviceHandle;
+ gattCharacteristic.AttributeHandle = characteristicHandle;
+ gattCharacteristic.CharacteristicValueHandle = characteristicData.valueHandle;
+
+ gattCharacteristic.CharacteristicUuid = nativeLeUuidFromQtBluetoothUuid(
+ characteristicData.uuid);
+
+ gattCharacteristic.HasExtendedProperties = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::ExtendedProperty);
+ gattCharacteristic.IsBroadcastable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Broadcasting);
+ gattCharacteristic.IsIndicatable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Indicate);
+ gattCharacteristic.IsNotifiable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Notify);
+ gattCharacteristic.IsReadable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Read);
+ gattCharacteristic.IsSignedWritable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::WriteSigned);
+ gattCharacteristic.IsWritable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Write);
+ gattCharacteristic.IsWritableWithoutResponse = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::WriteNoResponse);
+
+ return gattCharacteristic;
+}
+
+static BTH_LE_GATT_DESCRIPTOR_TYPE nativeLeGattDescriptorTypeFromUuid(
+ const QBluetoothUuid &uuid)
+{
+ switch (uuid.toUInt16()) {
+ case QBluetoothUuid::CharacteristicExtendedProperties:
+ return CharacteristicExtendedProperties;
+ case QBluetoothUuid::CharacteristicUserDescription:
+ return CharacteristicUserDescription;
+ case QBluetoothUuid::ClientCharacteristicConfiguration:
+ return ClientCharacteristicConfiguration;
+ case QBluetoothUuid::ServerCharacteristicConfiguration:
+ return ServerCharacteristicConfiguration;
+ case QBluetoothUuid::CharacteristicPresentationFormat:
+ return CharacteristicFormat;
+ case QBluetoothUuid::CharacteristicAggregateFormat:
+ return CharacteristicAggregateFormat;
+ default:
+ return CustomDescriptor;
+ }
+}
+
+static BTH_LE_GATT_DESCRIPTOR recoverNativeLeGattDescriptor(
+ QLowEnergyHandle serviceHandle, QLowEnergyHandle characteristicHandle,
+ QLowEnergyHandle descriptorHandle,
+ const QLowEnergyServicePrivate::DescData &descriptorData)
+{
+ BTH_LE_GATT_DESCRIPTOR gattDescriptor;
+
+ gattDescriptor.ServiceHandle = serviceHandle;
+ gattDescriptor.CharacteristicHandle = characteristicHandle;
+ gattDescriptor.AttributeHandle = descriptorHandle;
+
+ gattDescriptor.DescriptorUuid = nativeLeUuidFromQtBluetoothUuid(
+ descriptorData.uuid);
+
+ gattDescriptor.DescriptorType = nativeLeGattDescriptorTypeFromUuid
+ (descriptorData.uuid);
+
+ return gattDescriptor;
+}
+
+void QLowEnergyControllerPrivateWin32::customEvent(QEvent *e)
+{
+ if (e->type() != CharactericticValueEventType)
+ return;
+
+ const CharactericticValueEvent *characteristicEvent
+ = static_cast<CharactericticValueEvent *>(e);
+
+ updateValueOfCharacteristic(characteristicEvent->m_handle,
+ characteristicEvent->m_value, false);
+
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(
+ characteristicEvent->m_handle);
+ if (service.isNull())
+ return;
+
+ const QLowEnergyCharacteristic ch(service, characteristicEvent->m_handle);
+ emit service->characteristicChanged(ch, characteristicEvent->m_value);
+}
+
+QLowEnergyControllerPrivateWin32::QLowEnergyControllerPrivateWin32()
+ : QLowEnergyControllerPrivate()
+{
+ QMutexLocker locker(&controllersGuard);
+ qControllers()->append(this);
+
+ gattFunctionsResolved = resolveFunctions(bluetoothapis());
+ if (!gattFunctionsResolved) {
+ qCWarning(QT_BT_WINDOWS) << "LE is not supported on this OS";
+ return;
+ }
+}
+
+QLowEnergyControllerPrivateWin32::~QLowEnergyControllerPrivateWin32()
+{
+ QMutexLocker locker(&controllersGuard);
+ qControllers()->removeAll(this);
+}
+
+void QLowEnergyControllerPrivateWin32::init()
+{
+}
+
+void QLowEnergyControllerPrivateWin32::connectToDevice()
+{
+ // required to pass unit test on default backend
+ if (remoteDevice.isNull()) {
+ qWarning() << "Invalid/null remote device address";
+ setError(QLowEnergyController::UnknownRemoteDeviceError);
+ return;
+ }
+
+ if (!deviceSystemPath.isEmpty()) {
+ qCDebug(QT_BT_WINDOWS) << "Already is connected";
+ return;
+ }
+
+ setState(QLowEnergyController::ConnectingState);
+
+ deviceSystemPath =
+ QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath(
+ remoteDevice);
+
+ if (deviceSystemPath.isEmpty()) {
+ qCWarning(QT_BT_WINDOWS) << qt_error_string(ERROR_PATH_NOT_FOUND);
+ setError(QLowEnergyController::UnknownRemoteDeviceError);
+ setState(QLowEnergyController::UnconnectedState);
+ return;
+ }
+
+ setState(QLowEnergyController::ConnectedState);
+
+ thread = new QThread;
+ threadWorker = new ThreadWorker;
+ threadWorker->moveToThread(thread);
+ connect(threadWorker, &ThreadWorker::jobFinished, this, &QLowEnergyControllerPrivateWin32::jobFinished);
+ connect(thread, &QThread::finished, threadWorker, &ThreadWorker::deleteLater);
+ connect(thread, &QThread::finished, thread, &QThread::deleteLater);
+ thread->start();
+
+ Q_Q(QLowEnergyController);
+ emit q->connected();
+}
+
+void QLowEnergyControllerPrivateWin32::disconnectFromDevice()
+{
+ if (deviceSystemPath.isEmpty()) {
+ qCDebug(QT_BT_WINDOWS) << "Already is disconnected";
+ return;
+ }
+
+ setState(QLowEnergyController::ClosingState);
+ deviceSystemPath.clear();
+ setState(QLowEnergyController::UnconnectedState);
+
+ if (thread) {
+ disconnect(threadWorker, &ThreadWorker::jobFinished, this, &QLowEnergyControllerPrivateWin32::jobFinished);
+ thread->quit();
+ thread = nullptr;
+ }
+
+ for (const auto &servicePrivate: serviceList)
+ closeSystemDevice(servicePrivate->hService);
+
+ Q_Q(QLowEnergyController);
+ emit q->disconnected();
+}
+
+void QLowEnergyControllerPrivateWin32::discoverServices()
+{
+ int systemErrorCode = NO_ERROR;
+
+ const HANDLE hDevice = openSystemDevice(
+ deviceSystemPath, QIODevice::ReadOnly, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << qt_error_string(systemErrorCode);
+ setError(QLowEnergyController::NetworkError);
+ setState(QLowEnergyController::ConnectedState);
+ return;
+ }
+
+ const QVector<BTH_LE_GATT_SERVICE> foundServices =
+ enumeratePrimaryGattServices(hDevice, &systemErrorCode);
+
+ closeSystemDevice(hDevice);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << qt_error_string(systemErrorCode);
+ setError(QLowEnergyController::NetworkError);
+ setState(QLowEnergyController::ConnectedState);
+ return;
+ }
+
+ setState(QLowEnergyController::DiscoveringState);
+
+ Q_Q(QLowEnergyController);
+
+ for (const BTH_LE_GATT_SERVICE &service : foundServices) {
+ const QBluetoothUuid uuid = qtBluetoothUuidFromNativeLeUuid(
+ service.ServiceUuid);
+ qCDebug(QT_BT_WINDOWS) << "Found uuid:" << uuid;
+
+ QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate();
+ priv->uuid = uuid;
+ priv->type = QLowEnergyService::PrimaryService;
+ priv->startHandle = service.AttributeHandle;
+ priv->setController(this);
+
+ QSharedPointer<QLowEnergyServicePrivate> pointer(priv);
+ serviceList.insert(uuid, pointer);
+
+ emit q->serviceDiscovered(uuid);
+ }
+
+ setState(QLowEnergyController::DiscoveredState);
+ emit q->discoveryFinished();
+}
+
+void QLowEnergyControllerPrivateWin32::discoverServiceDetails(
+ const QBluetoothUuid &service)
+{
+ if (!serviceList.contains(service)) {
+ qCWarning(QT_BT_WINDOWS) << "Discovery of unknown service" << service.toString()
+ << "not possible";
+ return;
+ }
+
+ const QSharedPointer<QLowEnergyServicePrivate> servicePrivate =
+ serviceList.value(service);
+
+ int systemErrorCode = NO_ERROR;
+
+ // Only open a service once and close it in the QLowEnergyServicePrivate destructor
+ if (!servicePrivate->hService || servicePrivate->hService == INVALID_HANDLE_VALUE) {
+ servicePrivate->hService = openSystemService(remoteDevice, service,
+ QIODevice::ReadOnly | QIODevice::WriteOnly,
+ &systemErrorCode);
+ if (systemErrorCode != NO_ERROR) {
+ servicePrivate->hService = openSystemService(remoteDevice, service,
+ QIODevice::ReadOnly,
+ &systemErrorCode);
+ }
+ }
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::UnknownError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+
+ // We assume that the service does not have any characteristics with descriptors.
+ servicePrivate->endHandle = servicePrivate->startHandle;
+
+ const QVector<BTH_LE_GATT_CHARACTERISTIC> foundCharacteristics =
+ enumerateGattCharacteristics(servicePrivate->hService, nullptr, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get characteristics for service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::CharacteristicReadError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+
+ for (const BTH_LE_GATT_CHARACTERISTIC &gattCharacteristic : foundCharacteristics) {
+ const QLowEnergyHandle characteristicHandle = gattCharacteristic.AttributeHandle;
+
+ QLowEnergyServicePrivate::CharData detailsData;
+
+ detailsData.hValueChangeEvent = nullptr;
+
+ detailsData.uuid = qtBluetoothUuidFromNativeLeUuid(
+ gattCharacteristic.CharacteristicUuid);
+ detailsData.valueHandle = gattCharacteristic.CharacteristicValueHandle;
+
+ QLowEnergyCharacteristic::PropertyTypes properties = QLowEnergyCharacteristic::Unknown;
+ if (gattCharacteristic.HasExtendedProperties)
+ properties |= QLowEnergyCharacteristic::ExtendedProperty;
+ if (gattCharacteristic.IsBroadcastable)
+ properties |= QLowEnergyCharacteristic::Broadcasting;
+ if (gattCharacteristic.IsIndicatable)
+ properties |= QLowEnergyCharacteristic::Indicate;
+ if (gattCharacteristic.IsNotifiable)
+ properties |= QLowEnergyCharacteristic::Notify;
+ if (gattCharacteristic.IsReadable)
+ properties |= QLowEnergyCharacteristic::Read;
+ if (gattCharacteristic.IsSignedWritable)
+ properties |= QLowEnergyCharacteristic::WriteSigned;
+ if (gattCharacteristic.IsWritable)
+ properties |= QLowEnergyCharacteristic::Write;
+ if (gattCharacteristic.IsWritableWithoutResponse)
+ properties |= QLowEnergyCharacteristic::WriteNoResponse;
+
+ detailsData.properties = properties;
+ detailsData.value = getGattCharacteristicValue(
+ servicePrivate->hService, const_cast<PBTH_LE_GATT_CHARACTERISTIC>(
+ &gattCharacteristic), &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ // We do not interrupt enumerating of characteristics
+ // if value can not be read
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for characteristic"
+ << detailsData.uuid.toString()
+ << "of the service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ }
+
+ // We assume that the characteristic has no any descriptors. So, the
+ // biggest characteristic + 1 will indicate an end handle of service.
+ servicePrivate->endHandle = std::max(
+ servicePrivate->endHandle,
+ QLowEnergyHandle(gattCharacteristic.AttributeHandle + 1));
+
+ const QVector<BTH_LE_GATT_DESCRIPTOR> foundDescriptors = enumerateGattDescriptors(
+ servicePrivate->hService, const_cast<PBTH_LE_GATT_CHARACTERISTIC>(
+ &gattCharacteristic), &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ if (systemErrorCode != ERROR_NOT_FOUND) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get descriptor for characteristic"
+ << detailsData.uuid.toString()
+ << "of the service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::DescriptorReadError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+ }
+
+ for (const BTH_LE_GATT_DESCRIPTOR &gattDescriptor : foundDescriptors) {
+ const QLowEnergyHandle descriptorHandle = gattDescriptor.AttributeHandle;
+
+ QLowEnergyServicePrivate::DescData data;
+ data.uuid = qtBluetoothUuidFromNativeLeUuid(
+ gattDescriptor.DescriptorUuid);
+
+ data.value = getGattDescriptorValue(servicePrivate->hService, const_cast<PBTH_LE_GATT_DESCRIPTOR>(
+ &gattDescriptor), &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for descriptor"
+ << data.uuid.toString()
+ << "for characteristic"
+ << detailsData.uuid.toString()
+ << "of the service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::DescriptorReadError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+
+ // Biggest descriptor will contain an end handle of service.
+ servicePrivate->endHandle = std::max(
+ servicePrivate->endHandle,
+ QLowEnergyHandle(gattDescriptor.AttributeHandle));
+
+ detailsData.descriptorList.insert(descriptorHandle, data);
+ }
+
+ servicePrivate->characteristicList.insert(characteristicHandle, detailsData);
+ }
+
+ servicePrivate->setState(QLowEnergyService::ServiceDiscovered);
+}
+
+void QLowEnergyControllerPrivateWin32::startAdvertising(const QLowEnergyAdvertisingParameters &, const QLowEnergyAdvertisingData &, const QLowEnergyAdvertisingData &)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWin32::stopAdvertising()
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWin32::requestConnectionUpdate(const QLowEnergyConnectionParameters &)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWin32::readCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+ if (!(charDetails.properties & QLowEnergyCharacteristic::Read)) {
+ // if this succeeds the device has a bug, char is advertised as
+ // non-readable. We try to be permissive and let the remote
+ // device answer to the read attempt
+ qCWarning(QT_BT_WINDOWS) << "Reading non-readable char" << charHandle;
+ }
+
+ ReadCharData data;
+ data.systemErrorCode = NO_ERROR;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicReadError);
+ return;
+ }
+
+ data.gattCharacteristic = recoverNativeLeGattCharacteristic(
+ service->startHandle, charHandle, charDetails);
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::ReadChar;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::writeCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QByteArray &newValue,
+ QLowEnergyService::WriteMode mode)
+{
+ Q_ASSERT(!service.isNull());
+
+ if (!service->characteristicList.contains(charHandle)) {
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+ WriteCharData data;
+ data.systemErrorCode = NO_ERROR;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+
+ data.gattCharacteristic = recoverNativeLeGattCharacteristic(
+ service->startHandle, charHandle, charDetails);
+
+ data.flags = (mode == QLowEnergyService::WriteWithResponse)
+ ? BLUETOOTH_GATT_FLAG_NONE
+ : BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE;
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::WriteChar;
+ data.newValue = newValue;
+ data.mode = mode;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job)
+{
+ switch (job.operation) {
+ case ThreadWorkerJob::WriteChar:
+ {
+ const WriteCharData data = job.data.value<WriteCharData>();
+ const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattCharacteristic.AttributeHandle);
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+
+ if (data.systemErrorCode != NO_ERROR) {
+ const QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle];
+ qCWarning(QT_BT_WINDOWS) << "Unable to set value for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+ updateValueOfCharacteristic(charHandle, data.newValue, false);
+
+ if (data.mode == QLowEnergyService::WriteWithResponse) {
+ const QLowEnergyCharacteristic ch = characteristicForHandle(charHandle);
+ emit service->characteristicWritten(ch, data.newValue);
+ }
+ }
+ break;
+ case ThreadWorkerJob::ReadChar:
+ {
+ const ReadCharData data = job.data.value<ReadCharData>();
+ const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattCharacteristic.AttributeHandle);
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+
+ if (data.systemErrorCode != NO_ERROR) {
+ const QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle];
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicReadError);
+ return;
+ }
+
+ updateValueOfCharacteristic(charHandle, data.value, false);
+
+ const QLowEnergyCharacteristic ch(service, charHandle);
+ emit service->characteristicRead(ch, data.value);
+ }
+ break;
+ case ThreadWorkerJob::WriteDescr:
+ {
+ WriteDescData data = job.data.value<WriteDescData>();
+ const QLowEnergyHandle descriptorHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.AttributeHandle);
+ const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.CharacteristicHandle);
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle];
+ const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle];
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to set value for descriptor"
+ << dscrDetails.uuid.toString()
+ << "for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+
+ if (data.gattDescriptor.DescriptorType == ClientCharacteristicConfiguration) {
+
+ QDataStream in(data.newValue);
+ quint8 u;
+ in >> u;
+
+ if (u & ClientCharacteristicConfigurationValue::UseNotifications
+ || u & ClientCharacteristicConfigurationValue::UseIndications) {
+ if (!charDetails.hValueChangeEvent) {
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic = recoverNativeLeGattCharacteristic(
+ service->startHandle, charHandle, charDetails);
+
+ // note: if the service handle is closed the event registration is no longer valid.
+ charDetails.hValueChangeEvent = registerEvent(
+ data.hService, gattCharacteristic, this, &data.systemErrorCode);
+ }
+ } else {
+ if (charDetails.hValueChangeEvent) {
+ unregisterEvent(charDetails.hValueChangeEvent, &data.systemErrorCode);
+ charDetails.hValueChangeEvent = nullptr;
+ }
+ }
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to subscribe events for descriptor"
+ << dscrDetails.uuid.toString()
+ << "for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+ }
+
+ updateValueOfDescriptor(charHandle, descriptorHandle, data.newValue, false);
+
+ const QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle);
+ emit service->descriptorWritten(dscr, data.newValue);
+ }
+ break;
+ case ThreadWorkerJob::ReadDescr:
+ {
+ ReadDescData data = job.data.value<ReadDescData>();
+ const QLowEnergyHandle descriptorHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.AttributeHandle);
+ const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.CharacteristicHandle);
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle];
+ const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle];
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for descriptor"
+ << dscrDetails.uuid.toString()
+ << "for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorReadError);
+ return;
+ }
+
+ updateValueOfDescriptor(charHandle, descriptorHandle, data.value, false);
+
+ QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle);
+ emit service->descriptorRead(dscr, data.value);
+ }
+ break;
+ }
+
+ QMetaObject::invokeMethod(threadWorker, "runPendingJob", Qt::QueuedConnection);
+}
+
+void QLowEnergyControllerPrivateWin32::readDescriptor(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+ if (!charDetails.descriptorList.contains(descriptorHandle))
+ return;
+
+ ReadDescData data;
+ data.systemErrorCode = NO_ERROR;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorReadError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::DescData &dscrDetails
+ = charDetails.descriptorList[descriptorHandle];
+
+ data.gattDescriptor = recoverNativeLeGattDescriptor(
+ service->startHandle, charHandle, descriptorHandle, dscrDetails);
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::ReadDescr;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::writeDescriptor(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+ if (!charDetails.descriptorList.contains(descriptorHandle))
+ return;
+
+ WriteDescData data;
+ data.systemErrorCode = NO_ERROR;
+ data.newValue = newValue;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::DescData &dscrDetails
+ = charDetails.descriptorList[descriptorHandle];
+
+ data.gattDescriptor = recoverNativeLeGattDescriptor(
+ service->startHandle, charHandle, descriptorHandle, dscrDetails);
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::WriteDescr;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::addToGenericAttributeList(const QLowEnergyServiceData &, QLowEnergyHandle)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void ThreadWorker::putJob(const ThreadWorkerJob &job)
+{
+ m_jobs.append(job);
+ if (m_jobs.count() == 1)
+ runPendingJob();
+}
+
+void ThreadWorker::runPendingJob()
+{
+ if (!m_jobs.count())
+ return;
+
+ ThreadWorkerJob job = m_jobs.first();
+
+ switch (job.operation) {
+ case ThreadWorkerJob::WriteChar:
+ {
+ WriteCharData data = job.data.value<WriteCharData>();
+ setGattCharacteristicValue(data.hService, &data.gattCharacteristic,
+ data.newValue, data.flags, &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ break;
+ case ThreadWorkerJob::ReadChar:
+ {
+ ReadCharData data = job.data.value<ReadCharData>();
+ data.value = getGattCharacteristicValue(
+ data.hService, &data.gattCharacteristic, &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ break;
+ case ThreadWorkerJob::WriteDescr:
+ {
+ WriteDescData data = job.data.value<WriteDescData>();
+ setGattDescriptorValue(data.hService, &data.gattDescriptor,
+ data.newValue, &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ break;
+ case ThreadWorkerJob::ReadDescr:
+ {
+ ReadDescData data = job.data.value<ReadDescData>();
+ data.value = getGattDescriptorValue(
+ data.hService,
+ const_cast<PBTH_LE_GATT_DESCRIPTOR>(&data.gattDescriptor),
+ &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ break;
+ }
+
+ m_jobs.removeFirst();
+ emit jobFinished(job);
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_win_p.h b/src/bluetooth/qlowenergycontroller_win_p.h
new file mode 100644
index 00000000..f8e3b10a
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_win_p.h
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOWENERGYCONTROLLERPRIVATE_WIN32_P_H
+#define QLOWENERGYCONTROLLERPRIVATE_WIN32_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include <QtCore/QQueue>
+#include <QtCore/QVector>
+#include <QtBluetooth/qbluetooth.h>
+#include <QtBluetooth/qlowenergycharacteristic.h>
+#include "qlowenergycontroller.h"
+#include "qlowenergycontrollerbase_p.h"
+
+#include <windows/qwinlowenergybluetooth_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QThread;
+class QLowEnergyControllerPrivateWin32;
+
+class ThreadWorkerJob
+{
+public:
+ enum Operation { WriteChar, ReadChar, WriteDescr, ReadDescr };
+ Operation operation;
+ QVariant data;
+};
+
+struct WriteCharData
+{
+ QByteArray newValue;
+ QLowEnergyService::WriteMode mode;
+ HANDLE hService;
+ DWORD flags;
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic;
+ int systemErrorCode;
+};
+
+struct ReadCharData
+{
+ QByteArray value;
+ HANDLE hService;
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic;
+ int systemErrorCode;
+};
+
+struct WriteDescData
+{
+ QByteArray newValue;
+ HANDLE hService;
+ BTH_LE_GATT_DESCRIPTOR gattDescriptor;
+ int systemErrorCode;
+};
+
+struct ReadDescData
+{
+ QByteArray value;
+ HANDLE hService;
+ BTH_LE_GATT_DESCRIPTOR gattDescriptor;
+ int systemErrorCode;
+};
+
+class ThreadWorker : public QObject
+{
+ Q_OBJECT
+public:
+ Q_INVOKABLE void putJob(const ThreadWorkerJob &job);
+ Q_INVOKABLE void runPendingJob();
+signals:
+ void jobFinished(const ThreadWorkerJob &job);
+private:
+ QVector<ThreadWorkerJob> m_jobs;
+};
+
+class QLowEnergyServiceData;
+
+extern void registerQLowEnergyControllerMetaType();
+
+class QLowEnergyControllerPrivateWin32 : public QLowEnergyControllerPrivate
+{
+ Q_OBJECT
+public:
+ QLowEnergyControllerPrivateWin32();
+ ~QLowEnergyControllerPrivateWin32();
+
+ void init() override;
+
+ void connectToDevice() override;
+ void disconnectFromDevice() override;
+
+ void discoverServices() override;
+ void discoverServiceDetails(const QBluetoothUuid &service) override;
+
+ void startAdvertising(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData) override;
+ void stopAdvertising() override;
+
+ void requestConnectionUpdate(const QLowEnergyConnectionParameters &params) override;
+
+ // read data
+ void readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle) override;
+ void readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle) override;
+
+ // write data
+ void writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QByteArray &newValue, QLowEnergyService::WriteMode mode) override;
+ void writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue) override;
+
+ void addToGenericAttributeList(const QLowEnergyServiceData &service,
+ QLowEnergyHandle startHandle) override;
+public slots:
+ void jobFinished(const ThreadWorkerJob &job);
+protected:
+ void customEvent(QEvent *e);
+private:
+ QThread *thread = nullptr;
+ ThreadWorker *threadWorker = nullptr;
+ QString deviceSystemPath;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(ThreadWorkerJob)
+Q_DECLARE_METATYPE(WriteCharData)
+Q_DECLARE_METATYPE(ReadCharData)
+Q_DECLARE_METATYPE(WriteDescData)
+Q_DECLARE_METATYPE(ReadDescData)
+
+#endif // QLOWENERGYCONTROLLERPRIVATE_WIN32__P_H
diff --git a/src/bluetooth/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 62ca5fd3..18bb53c0 100644
--- a/src/bluetooth/qlowenergydescriptor.h
+++ b/src/bluetooth/qlowenergydescriptor.h
@@ -83,7 +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
diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h
index fb4163a0..226af145 100644
--- a/src/bluetooth/qlowenergyserviceprivate_p.h
+++ b/src/bluetooth/qlowenergyserviceprivate_p.h
@@ -60,6 +60,9 @@
#if defined(QT_ANDROID_BLUETOOTH)
#include <QtAndroidExtras/QAndroidJniObject>
#endif
+#if defined(QT_WIN_BLUETOOTH)
+#include <qt_windows.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -83,6 +86,9 @@ public:
QLowEnergyCharacteristic::PropertyTypes properties;
QByteArray value;
QHash<QLowEnergyHandle, DescData> descriptorList;
+#ifdef QT_WIN_BLUETOOTH
+ Qt::HANDLE hValueChangeEvent;
+#endif
};
enum GattAttributeTypes {
@@ -128,6 +134,9 @@ public:
// reference to the BluetoothGattService object
QAndroidJniObject androidService;
#endif
+#if defined(QT_WIN_BLUETOOTH)
+ Qt::HANDLE hService = nullptr;
+#endif
};
diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h
new file mode 100644
index 00000000..39b88a5f
--- /dev/null
+++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINLOWENERGYBLUETOOTH_P_H
+#define QWINLOWENERGYBLUETOOTH_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qlibrary.h>
+
+#include <qt_windows.h>
+
+#define WIN32_FROM_HRESULT(hr) \
+ (SUCCEEDED(hr) ? ERROR_SUCCESS : \
+ (HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : (hr)))
+
+#define BLUETOOTH_GATT_FLAG_NONE 0x00000000
+#define BLUETOOTH_GATT_FLAG_CONNECTION_ENCRYPTED 0x00000001
+#define BLUETOOTH_GATT_FLAG_CONNECTION_AUTHENTICATED 0x00000002
+#define BLUETOOTH_GATT_FLAG_FORCE_READ_FROM_DEVICE 0x00000004
+#define BLUETOOTH_GATT_FLAG_FORCE_READ_FROM_CACHE 0x00000008
+#define BLUETOOTH_GATT_FLAG_SIGNED_WRITE 0x00000010
+#define BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE 0x00000020
+#define BLUETOOTH_GATT_FLAG_RETURN_ALL 0x00000040
+
+typedef enum _BTH_LE_GATT_DESCRIPTOR_TYPE {
+ CharacteristicExtendedProperties,
+ CharacteristicUserDescription,
+ ClientCharacteristicConfiguration,
+ ServerCharacteristicConfiguration,
+ CharacteristicFormat,
+ CharacteristicAggregateFormat,
+ CustomDescriptor
+} BTH_LE_GATT_DESCRIPTOR_TYPE, *PBTH_LE_GATT_DESCRIPTOR_TYPE;
+
+typedef enum _BTH_LE_GATT_EVENT_TYPE {
+ CharacteristicValueChangedEvent
+} BTH_LE_GATT_EVENT_TYPE;
+
+typedef struct _BTH_LE_UUID {
+ BOOLEAN IsShortUuid;
+ union {
+ USHORT ShortUuid;
+ GUID LongUuid;
+ } Value;
+} BTH_LE_UUID, *PBTH_LE_UUID;
+
+typedef struct _BTH_LE_GATT_SERVICE {
+ BTH_LE_UUID ServiceUuid;
+ USHORT AttributeHandle;
+} BTH_LE_GATT_SERVICE, *PBTH_LE_GATT_SERVICE;
+
+typedef struct _BTH_LE_GATT_CHARACTERISTIC {
+ USHORT ServiceHandle;
+ BTH_LE_UUID CharacteristicUuid;
+ USHORT AttributeHandle;
+ USHORT CharacteristicValueHandle;
+ BOOLEAN IsBroadcastable;
+ BOOLEAN IsReadable;
+ BOOLEAN IsWritable;
+ BOOLEAN IsWritableWithoutResponse;
+ BOOLEAN IsSignedWritable;
+ BOOLEAN IsNotifiable;
+ BOOLEAN IsIndicatable;
+ BOOLEAN HasExtendedProperties;
+} BTH_LE_GATT_CHARACTERISTIC, *PBTH_LE_GATT_CHARACTERISTIC;
+
+typedef struct _BTH_LE_GATT_CHARACTERISTIC_VALUE {
+ ULONG DataSize;
+ UCHAR Data[1];
+} BTH_LE_GATT_CHARACTERISTIC_VALUE, *PBTH_LE_GATT_CHARACTERISTIC_VALUE;
+
+typedef struct _BTH_LE_GATT_DESCRIPTOR {
+ USHORT ServiceHandle;
+ USHORT CharacteristicHandle;
+ BTH_LE_GATT_DESCRIPTOR_TYPE DescriptorType;
+ BTH_LE_UUID DescriptorUuid;
+ USHORT AttributeHandle;
+} BTH_LE_GATT_DESCRIPTOR, *PBTH_LE_GATT_DESCRIPTOR;
+
+typedef struct _BTH_LE_GATT_DESCRIPTOR_VALUE {
+ BTH_LE_GATT_DESCRIPTOR_TYPE DescriptorType;
+ BTH_LE_UUID DescriptorUuid;
+ union {
+ struct {
+ BOOLEAN IsReliableWriteEnabled;
+ BOOLEAN IsAuxiliariesWritable;
+ } CharacteristicExtendedProperties;
+ struct {
+ BOOLEAN IsSubscribeToNotification;
+ BOOLEAN IsSubscribeToIndication;
+ } ClientCharacteristicConfiguration;
+ struct {
+ BOOLEAN IsBroadcast;
+ } ServerCharacteristicConfiguration;
+ struct {
+ UCHAR Format;
+ UCHAR Exponent;
+ BTH_LE_UUID Unit;
+ UCHAR NameSpace;
+ BTH_LE_UUID Description;
+ } CharacteristicFormat;
+ };
+ ULONG DataSize;
+ UCHAR Data[1];
+} BTH_LE_GATT_DESCRIPTOR_VALUE, *PBTH_LE_GATT_DESCRIPTOR_VALUE;
+
+typedef struct _BLUETOOTH_GATT_VALUE_CHANGED_EVENT {
+ USHORT ChangedAttributeHandle;
+ size_t CharacteristicValueDataSize;
+ PBTH_LE_GATT_CHARACTERISTIC_VALUE CharacteristicValue;
+} BLUETOOTH_GATT_VALUE_CHANGED_EVENT, *PBLUETOOTH_GATT_VALUE_CHANGED_EVENT;
+
+typedef struct _BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION {
+ USHORT NumCharacteristics;
+ BTH_LE_GATT_CHARACTERISTIC Characteristics[1];
+} BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION, *PBLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION;
+
+typedef VOID (CALLBACK *PFNBLUETOOTH_GATT_EVENT_CALLBACK)(
+ BTH_LE_GATT_EVENT_TYPE EventType,
+ PVOID EventOutParameter,
+ PVOID Context
+ );
+
+typedef ULONG64 BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, *PBTH_LE_GATT_RELIABLE_WRITE_CONTEXT;
+
+#define DEFINEFUNC(ret, func, ...) \
+ typedef ret (WINAPI *fp_##func)(__VA_ARGS__); \
+ static fp_##func func;
+
+#define RESOLVEFUNC(func) \
+ func = (fp_##func)resolveFunction(library, #func); \
+ if (!func) \
+ return false;
+
+DEFINEFUNC(HRESULT, BluetoothGATTGetServices, HANDLE, USHORT, PBTH_LE_GATT_SERVICE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetIncludedServices, HANDLE, PBTH_LE_GATT_SERVICE, USHORT, PBTH_LE_GATT_SERVICE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetCharacteristics, HANDLE, PBTH_LE_GATT_SERVICE, USHORT, PBTH_LE_GATT_CHARACTERISTIC, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetDescriptors, HANDLE, PBTH_LE_GATT_CHARACTERISTIC, USHORT, PBTH_LE_GATT_DESCRIPTOR, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetCharacteristicValue, HANDLE, PBTH_LE_GATT_CHARACTERISTIC, ULONG, PBTH_LE_GATT_CHARACTERISTIC_VALUE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetDescriptorValue, HANDLE, PBTH_LE_GATT_DESCRIPTOR, ULONG, PBTH_LE_GATT_DESCRIPTOR_VALUE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTBeginReliableWrite, HANDLE, PBTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTEndReliableWrite, HANDLE, BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTAbortReliableWrite, HANDLE, BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTSetCharacteristicValue, HANDLE, PBTH_LE_GATT_CHARACTERISTIC, PBTH_LE_GATT_CHARACTERISTIC_VALUE, BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTSetDescriptorValue, HANDLE, PBTH_LE_GATT_DESCRIPTOR, PBTH_LE_GATT_DESCRIPTOR_VALUE, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTRegisterEvent, HANDLE, BTH_LE_GATT_EVENT_TYPE, PVOID, PFNBLUETOOTH_GATT_EVENT_CALLBACK, PVOID, PHANDLE, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTUnregisterEvent, HANDLE, ULONG)
+
+static inline QFunctionPointer resolveFunction(QLibrary *library, const char *func)
+{
+ QFunctionPointer symbolFunctionPointer = library->resolve(func);
+ if (!symbolFunctionPointer)
+ qWarning("Cannot resolve '%s' in '%s'.", func, qPrintable(library->fileName()));
+ return symbolFunctionPointer;
+}
+
+static inline bool resolveFunctions(QLibrary *library)
+{
+ if (!library->isLoaded()) {
+ library->setFileName(QStringLiteral("bluetoothapis"));
+ if (!library->load()) {
+ qWarning("Unable to load '%s' library.", qPrintable(library->fileName()));
+ return false;
+ }
+ }
+
+ RESOLVEFUNC(BluetoothGATTGetServices)
+ RESOLVEFUNC(BluetoothGATTGetIncludedServices)
+ RESOLVEFUNC(BluetoothGATTGetCharacteristics)
+ RESOLVEFUNC(BluetoothGATTGetDescriptors)
+ RESOLVEFUNC(BluetoothGATTGetCharacteristicValue)
+ RESOLVEFUNC(BluetoothGATTGetDescriptorValue)
+ RESOLVEFUNC(BluetoothGATTBeginReliableWrite)
+ RESOLVEFUNC(BluetoothGATTEndReliableWrite)
+ RESOLVEFUNC(BluetoothGATTAbortReliableWrite)
+ RESOLVEFUNC(BluetoothGATTSetCharacteristicValue)
+ RESOLVEFUNC(BluetoothGATTSetDescriptorValue)
+ RESOLVEFUNC(BluetoothGATTRegisterEvent)
+ RESOLVEFUNC(BluetoothGATTUnregisterEvent)
+
+ return true;
+}
+
+#endif // QWINLOWENERGYBLUETOOTH_P_H
diff --git a/src/bluetooth/windows/windows.pri b/src/bluetooth/windows/windows.pri
new file mode 100644
index 00000000..bf35eaa4
--- /dev/null
+++ b/src/bluetooth/windows/windows.pri
@@ -0,0 +1,2 @@
+PRIVATE_HEADERS += \
+ windows/qwinlowenergybluetooth_p.h
diff --git a/src/imports/bluetooth/plugins.qmltypes b/src/imports/bluetooth/plugins.qmltypes
index 9318fe93..2060b594 100644
--- a/src/imports/bluetooth/plugins.qmltypes
+++ b/src/imports/bluetooth/plugins.qmltypes
@@ -4,11 +4,285 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtBluetooth 5.13'
+// 'qmlplugindump -nonrelocatable QtBluetooth 5.14'
Module {
dependencies: ["QtQuick 2.0"]
Component {
+ name: "QAbstractItemModel"
+ prototype: "QObject"
+ Enum {
+ name: "LayoutChangeHint"
+ values: {
+ "NoLayoutChangeHint": 0,
+ "VerticalSortHint": 1,
+ "HorizontalSortHint": 2
+ }
+ }
+ Enum {
+ name: "CheckIndexOption"
+ values: {
+ "NoOption": 0,
+ "IndexIsValid": 1,
+ "DoNotUseParent": 2,
+ "ParentIsInvalid": 4
+ }
+ }
+ Signal {
+ name: "dataChanged"
+ Parameter { name: "topLeft"; type: "QModelIndex" }
+ Parameter { name: "bottomRight"; type: "QModelIndex" }
+ Parameter { name: "roles"; type: "QVector<int>" }
+ }
+ Signal {
+ name: "dataChanged"
+ Parameter { name: "topLeft"; type: "QModelIndex" }
+ Parameter { name: "bottomRight"; type: "QModelIndex" }
+ }
+ Signal {
+ name: "headerDataChanged"
+ Parameter { name: "orientation"; type: "Qt::Orientation" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "layoutChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
+ }
+ Signal {
+ name: "layoutChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ }
+ Signal { name: "layoutChanged" }
+ Signal {
+ name: "layoutAboutToBeChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
+ }
+ Signal {
+ name: "layoutAboutToBeChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ }
+ Signal { name: "layoutAboutToBeChanged" }
+ Signal {
+ name: "rowsAboutToBeInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "rowsInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "rowsAboutToBeRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "rowsRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsAboutToBeInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsAboutToBeRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal { name: "modelAboutToBeReset" }
+ Signal { name: "modelReset" }
+ Signal {
+ name: "rowsAboutToBeMoved"
+ Parameter { name: "sourceParent"; type: "QModelIndex" }
+ Parameter { name: "sourceStart"; type: "int" }
+ Parameter { name: "sourceEnd"; type: "int" }
+ Parameter { name: "destinationParent"; type: "QModelIndex" }
+ Parameter { name: "destinationRow"; type: "int" }
+ }
+ Signal {
+ name: "rowsMoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "start"; type: "int" }
+ Parameter { name: "end"; type: "int" }
+ Parameter { name: "destination"; type: "QModelIndex" }
+ Parameter { name: "row"; type: "int" }
+ }
+ Signal {
+ name: "columnsAboutToBeMoved"
+ Parameter { name: "sourceParent"; type: "QModelIndex" }
+ Parameter { name: "sourceStart"; type: "int" }
+ Parameter { name: "sourceEnd"; type: "int" }
+ Parameter { name: "destinationParent"; type: "QModelIndex" }
+ Parameter { name: "destinationColumn"; type: "int" }
+ }
+ Signal {
+ name: "columnsMoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "start"; type: "int" }
+ Parameter { name: "end"; type: "int" }
+ Parameter { name: "destination"; type: "QModelIndex" }
+ Parameter { name: "column"; type: "int" }
+ }
+ Method { name: "submit"; type: "bool" }
+ Method { name: "revert" }
+ Method {
+ name: "hasIndex"
+ type: "bool"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "hasIndex"
+ type: "bool"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ }
+ Method {
+ name: "index"
+ type: "QModelIndex"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "index"
+ type: "QModelIndex"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ }
+ Method {
+ name: "parent"
+ type: "QModelIndex"
+ Parameter { name: "child"; type: "QModelIndex" }
+ }
+ Method {
+ name: "sibling"
+ type: "QModelIndex"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ Parameter { name: "idx"; type: "QModelIndex" }
+ }
+ Method {
+ name: "rowCount"
+ type: "int"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method { name: "rowCount"; type: "int" }
+ Method {
+ name: "columnCount"
+ type: "int"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method { name: "columnCount"; type: "int" }
+ Method {
+ name: "hasChildren"
+ type: "bool"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method { name: "hasChildren"; type: "bool" }
+ Method {
+ name: "data"
+ type: "QVariant"
+ Parameter { name: "index"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ }
+ Method {
+ name: "data"
+ type: "QVariant"
+ Parameter { name: "index"; type: "QModelIndex" }
+ }
+ Method {
+ name: "setData"
+ type: "bool"
+ Parameter { name: "index"; type: "QModelIndex" }
+ Parameter { name: "value"; type: "QVariant" }
+ Parameter { name: "role"; type: "int" }
+ }
+ Method {
+ name: "setData"
+ type: "bool"
+ Parameter { name: "index"; type: "QModelIndex" }
+ Parameter { name: "value"; type: "QVariant" }
+ }
+ Method {
+ name: "headerData"
+ type: "QVariant"
+ Parameter { name: "section"; type: "int" }
+ Parameter { name: "orientation"; type: "Qt::Orientation" }
+ Parameter { name: "role"; type: "int" }
+ }
+ Method {
+ name: "headerData"
+ type: "QVariant"
+ Parameter { name: "section"; type: "int" }
+ Parameter { name: "orientation"; type: "Qt::Orientation" }
+ }
+ Method {
+ name: "fetchMore"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "canFetchMore"
+ type: "bool"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "flags"
+ type: "Qt::ItemFlags"
+ Parameter { name: "index"; type: "QModelIndex" }
+ }
+ Method {
+ name: "match"
+ type: "QModelIndexList"
+ Parameter { name: "start"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ Parameter { name: "value"; type: "QVariant" }
+ Parameter { name: "hits"; type: "int" }
+ Parameter { name: "flags"; type: "Qt::MatchFlags" }
+ }
+ Method {
+ name: "match"
+ type: "QModelIndexList"
+ Parameter { name: "start"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ Parameter { name: "value"; type: "QVariant" }
+ Parameter { name: "hits"; type: "int" }
+ }
+ Method {
+ name: "match"
+ type: "QModelIndexList"
+ Parameter { name: "start"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ Parameter { name: "value"; type: "QVariant" }
+ }
+ }
+ Component { name: "QAbstractListModel"; prototype: "QAbstractItemModel" }
+ Component {
name: "QDeclarativeBluetoothDiscoveryModel"
prototype: "QAbstractListModel"
exports: [
diff --git a/src/imports/nfc/plugins.qmltypes b/src/imports/nfc/plugins.qmltypes
index d99cac23..2151ad7d 100644
--- a/src/imports/nfc/plugins.qmltypes
+++ b/src/imports/nfc/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtNfc 5.13'
+// 'qmlplugindump -nonrelocatable QtNfc 5.14'
Module {
dependencies: ["QtQuick 2.0"]
diff --git a/src/nfc/qnearfieldtagtype2.cpp b/src/nfc/qnearfieldtagtype2.cpp
index 24ff8280..492dc5e3 100644
--- a/src/nfc/qnearfieldtagtype2.cpp
+++ b/src/nfc/qnearfieldtagtype2.cpp
@@ -307,9 +307,7 @@ void QNearFieldTagType2::timerEvent(QTimerEvent *event)
killTimer(event->timerId());
- QMutableMapIterator<QNearFieldTarget::RequestId, SectorSelectState> i(d->m_pendingSectorSelectCommands);
- while (i.hasNext()) {
- i.next();
+ for (auto i = d->m_pendingSectorSelectCommands.begin(), end = d->m_pendingSectorSelectCommands.end(); i != end; ++i) {
SectorSelectState &state = i.value();
@@ -318,8 +316,7 @@ void QNearFieldTagType2::timerEvent(QTimerEvent *event)
setResponseForRequest(i.key(), true);
- i.remove();
-
+ d->m_pendingSectorSelectCommands.erase(i);
break;
}
}
diff --git a/src/nfc/qnearfieldtarget.cpp b/src/nfc/qnearfieldtarget.cpp
index e9a6fa11..7d83db78 100644
--- a/src/nfc/qnearfieldtarget.cpp
+++ b/src/nfc/qnearfieldtarget.cpp
@@ -47,7 +47,7 @@
#include <QtCore/QDebug>
-#include <QTime>
+#include <QElapsedTimer>
#include <QCoreApplication>
QT_BEGIN_NAMESPACE
@@ -462,7 +462,7 @@ bool QNearFieldTarget::waitForRequestCompleted(const RequestId &id, int msecs)
{
Q_D(QNearFieldTarget);
- QTime timer;
+ QElapsedTimer timer;
timer.start();
do {
@@ -497,13 +497,12 @@ void QNearFieldTarget::setResponseForRequest(const QNearFieldTarget::RequestId &
{
Q_D(QNearFieldTarget);
- QMutableMapIterator<RequestId, QVariant> i(d->m_decodedResponses);
- while (i.hasNext()) {
- i.next();
-
+ for (auto i = d->m_decodedResponses.begin(), end = d->m_decodedResponses.end(); i != end; /* erasing */) {
// no more external references
if (i.key().refCount() == 1)
- i.remove();
+ i = d->m_decodedResponses.erase(i);
+ else
+ ++i;
}
d->m_decodedResponses.insert(id, response);
diff --git a/src/nfc/qqmlndefrecord.cpp b/src/nfc/qqmlndefrecord.cpp
index bc3667fe..5a96bec8 100644
--- a/src/nfc/qqmlndefrecord.cpp
+++ b/src/nfc/qqmlndefrecord.cpp
@@ -215,10 +215,9 @@ QQmlNdefRecord *qNewDeclarativeNdefRecordForNdefRecord(const QNdefRecord &record
{
const QString urn = urnForRecordType(record.typeNameFormat(), record.type());
- QMapIterator<QString, const QMetaObject *> i(*registeredNdefRecordTypes());
- while (i.hasNext()) {
- i.next();
+ const auto *rt = registeredNdefRecordTypes();
+ for (auto i = rt->cbegin(), end = rt->cend(); i != end; ++i) {
QRegularExpression rx(QRegularExpression::anchoredPattern(i.key()));
if (!rx.match(urn).hasMatch())
continue;
diff --git a/tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp b/tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp
index 6636f0cd..45b429db 100644
--- a/tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp
+++ b/tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp
@@ -64,6 +64,8 @@ private slots:
void tst_cached();
void tst_flags();
+
+ void tst_manufacturerData();
};
tst_QBluetoothDeviceInfo::tst_QBluetoothDeviceInfo()
@@ -447,21 +449,15 @@ void tst_QBluetoothDeviceInfo::tst_serviceUuids()
QBluetoothDeviceInfo deviceInfo;
QBluetoothDeviceInfo copyInfo = deviceInfo;
- QList<QBluetoothUuid> servicesList;
+ QVector<QBluetoothUuid> servicesList;
servicesList.append(QBluetoothUuid::L2cap);
servicesList.append(QBluetoothUuid::Rfcomm);
QVERIFY(servicesList.count() > 0);
- deviceInfo.setServiceUuids(servicesList.toVector());
+ deviceInfo.setServiceUuids(servicesList);
QVERIFY(deviceInfo.serviceUuids().count() > 0);
deviceInfo.setServiceUuids(QVector<QBluetoothUuid>());
QCOMPARE(deviceInfo.serviceUuids().count(), 0);
-
- deviceInfo.setServiceUuids(servicesList, QBluetoothDeviceInfo::DataComplete);
- QVERIFY(deviceInfo.serviceUuids().count() > 0);
- QVERIFY(deviceInfo != copyInfo);
-
- QVERIFY(deviceInfo.serviceUuidsCompleteness() == QBluetoothDeviceInfo::DataComplete);
}
void tst_QBluetoothDeviceInfo::tst_cached()
@@ -520,6 +516,38 @@ void tst_QBluetoothDeviceInfo::tst_flags()
QVERIFY(serviceResult.testFlag(QBluetoothDeviceInfo::CapturingService));
}
+void tst_QBluetoothDeviceInfo::tst_manufacturerData()
+{
+ const int manufacturerAVM = 0x1F;
+
+ QBluetoothDeviceInfo info;
+ QVERIFY(info.manufacturerIds().isEmpty());
+ QVERIFY(info.manufacturerData(manufacturerAVM).isNull());
+
+ QVERIFY(info.setManufacturerData(manufacturerAVM, QByteArray::fromHex("ABCD")));
+ QVERIFY(!info.setManufacturerData(manufacturerAVM, QByteArray::fromHex("ABCD")));
+ QCOMPARE(info.manufacturerData(manufacturerAVM), QByteArray::fromHex("ABCD"));
+ auto temp = info.manufacturerData();
+ QCOMPARE(temp.keys().count(), 1);
+ QCOMPARE(temp.values().count(), 1);
+ QCOMPARE(temp.values(), QList<QByteArray>() << QByteArray::fromHex("ABCD"));
+
+ QVERIFY(info.setManufacturerData(manufacturerAVM, QByteArray::fromHex("CDEF")));
+ QVERIFY(!info.setManufacturerData(manufacturerAVM, QByteArray::fromHex("ABCD")));
+ QVERIFY(!info.setManufacturerData(manufacturerAVM, QByteArray::fromHex("CDEF")));
+
+ temp = info.manufacturerData();
+ QCOMPARE(temp.keys().count(), 2);
+ QCOMPARE(temp.values().count(), 2);
+ auto list = temp.values();
+
+ QCOMPARE(QSet<QByteArray> (list.begin(), list.end()),
+ QSet<QByteArray>() << QByteArray::fromHex("ABCD") << QByteArray::fromHex("CDEF"));
+
+ // return latest entry
+ QCOMPARE(info.manufacturerData(manufacturerAVM), QByteArray::fromHex("CDEF"));
+}
+
QTEST_MAIN(tst_QBluetoothDeviceInfo)
#include "tst_qbluetoothdeviceinfo.moc"
diff --git a/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp b/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp
index ea1abef2..af7f0354 100644
--- a/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp
+++ b/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp
@@ -110,6 +110,9 @@ void tst_QBluetoothLocalDevice::tst_powerOn()
#ifdef Q_OS_OSX
QSKIP("Not possible on OS X");
#endif
+#ifdef Q_OS_WIN
+ QSKIP("Not possible on Windows");
+#endif
QBluetoothLocalDevice localDevice;
@@ -135,6 +138,9 @@ void tst_QBluetoothLocalDevice::tst_powerOff()
#ifdef Q_OS_OSX
QSKIP("Not possible on OS X");
#endif
+#ifdef Q_OS_WIN
+ QSKIP("Not possible on Windows");
+#endif
if (!QBluetoothLocalDevice::allDevices().count())
QSKIP("Skipping test due to missing Bluetooth device");
@@ -183,6 +189,9 @@ void tst_QBluetoothLocalDevice::tst_hostModes()
#ifdef Q_OS_OSX
QSKIP("Not possible on OS X");
#endif
+#ifdef Q_OS_WIN
+ QSKIP("Not possible on Windows");
+#endif
QFETCH(QBluetoothLocalDevice::HostMode, hostModeExpected);
QFETCH(bool, expectSignal);
@@ -340,6 +349,10 @@ void tst_QBluetoothLocalDevice::tst_pairDevice_data()
void tst_QBluetoothLocalDevice::tst_pairDevice()
{
+#ifdef Q_OS_WIN
+ QSKIP("Programmatic pairing not supported on Windows");
+#endif
+
QFETCH(QBluetoothAddress, deviceAddress);
QFETCH(QBluetoothLocalDevice::Pairing, pairingExpected);
QFETCH(int, pairingWaitTime);
diff --git a/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp b/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp
index 10c4bd3b..f89802d2 100644
--- a/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp
+++ b/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp
@@ -184,7 +184,7 @@ void tst_QBluetoothServiceInfo::tst_assignment_data()
bool l2cpSupported = true;
//some platforms don't support L2CP
-#ifdef QT_ANDROID_BLUETOOTH
+#if defined(QT_ANDROID_BLUETOOTH) || defined(Q_OS_WIN)
l2cpSupported = false;
#endif
QTest::newRow("assignment_data_l2cp")
diff --git a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp
index 8ffc0480..ab393210 100644
--- a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp
+++ b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp
@@ -451,14 +451,18 @@ void tst_QLowEnergyController::tst_concurrentDiscovery()
// 2. new controller to same device fails
{
+#ifdef Q_OS_DARWIN
+ QLowEnergyController control2(remoteDeviceInfo);
+#else
QLowEnergyController control2(remoteDevice);
+#endif
control2.connectToDevice();
{
QTRY_IMPL(control2.state() != QLowEnergyController::ConnectingState,
30000);
}
-#if defined(Q_OS_ANDROID) || QT_CONFIG(winrt_bt)
+#if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) || QT_CONFIG(winrt_bt)
QCOMPARE(control.state(), QLowEnergyController::ConnectedState);
QCOMPARE(control2.state(), QLowEnergyController::ConnectedState);
control2.disconnectFromDevice();
diff --git a/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp b/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp
index 4e82aacd..5ebb0b1d 100644
--- a/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp
+++ b/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp
@@ -140,13 +140,18 @@ void tst_QLowEnergyDescriptor::initTestCase()
const QList<QLowEnergyCharacteristic> chars = leService->characteristics();
for (const QLowEnergyCharacteristic &ch : chars) {
- if (!ch.descriptors().isEmpty()) {
- globalService = leService;
- globalControl = controller;
- qWarning() << "Found service with descriptor" << remoteDeviceInfo.address()
- << globalService->serviceName() << globalService->serviceUuid();
- break;
+ const QList<QLowEnergyDescriptor> descriptors = ch.descriptors();
+ for (const QLowEnergyDescriptor &d : descriptors) {
+ if (!d.value().isEmpty()) {
+ globalService = leService;
+ globalControl = controller;
+ qWarning() << "Found service with descriptor" << remoteDeviceInfo.address()
+ << globalService->serviceName() << globalService->serviceUuid();
+ break;
+ }
}
+ if (globalControl)
+ break;
}
if (globalControl)
@@ -238,11 +243,19 @@ void tst_QLowEnergyDescriptor::tst_assignCompare()
QCOMPARE(target.uuid(), QBluetoothUuid());
QCOMPARE(target.value(), QByteArray());
+ int index = -1;
QList<QLowEnergyDescriptor> targets;
const QList<QLowEnergyCharacteristic> chars = globalService->characteristics();
for (const QLowEnergyCharacteristic &ch : chars) {
if (!ch.descriptors().isEmpty()) {
targets = ch.descriptors();
+ for (int i = 0; i < targets.size(); ++i) {
+ // try to get a descriptor we can read
+ if (targets[i].type() == QBluetoothUuid::CharacteristicUserDescription) {
+ index = i;
+ break;
+ }
+ }
break;
}
}
@@ -250,8 +263,10 @@ void tst_QLowEnergyDescriptor::tst_assignCompare()
if (targets.isEmpty())
QSKIP("No descriptor found despite prior indication.");
+ QVERIFY(index != -1);
+
// test assignment operator
- target = targets.first();
+ target = targets[index];
QVERIFY(target.isValid());
QVERIFY(target.type() != QBluetoothUuid::UnknownDescriptorType);
QVERIFY(!target.name().isEmpty());
@@ -259,26 +274,26 @@ void tst_QLowEnergyDescriptor::tst_assignCompare()
QVERIFY(!target.uuid().isNull());
QVERIFY(!target.value().isEmpty());
- QVERIFY(target == targets.first());
- QVERIFY(targets.first() == target);
- QVERIFY(!(target != targets.first()));
- QVERIFY(!(targets.first() != target));
+ QVERIFY(target == targets[index]);
+ QVERIFY(targets[index] == target);
+ QVERIFY(!(target != targets[index]));
+ QVERIFY(!(targets[index] != target));
- QCOMPARE(target.isValid(), targets.first().isValid());
- QCOMPARE(target.type(), targets.first().type());
- QCOMPARE(target.name(), targets.first().name());
- QCOMPARE(target.handle(), targets.first().handle());
- QCOMPARE(target.uuid(), targets.first().uuid());
- QCOMPARE(target.value(), targets.first().value());
+ QCOMPARE(target.isValid(), targets[index].isValid());
+ QCOMPARE(target.type(), targets[index].type());
+ QCOMPARE(target.name(), targets[index].name());
+ QCOMPARE(target.handle(), targets[index].handle());
+ QCOMPARE(target.uuid(), targets[index].uuid());
+ QCOMPARE(target.value(), targets[index].value());
// test copy constructor
QLowEnergyDescriptor copyConstructed(target);
- QCOMPARE(copyConstructed.isValid(), targets.first().isValid());
- QCOMPARE(copyConstructed.type(), targets.first().type());
- QCOMPARE(copyConstructed.name(), targets.first().name());
- QCOMPARE(copyConstructed.handle(), targets.first().handle());
- QCOMPARE(copyConstructed.uuid(), targets.first().uuid());
- QCOMPARE(copyConstructed.value(), targets.first().value());
+ QCOMPARE(copyConstructed.isValid(), targets[index].isValid());
+ QCOMPARE(copyConstructed.type(), targets[index].type());
+ QCOMPARE(copyConstructed.name(), targets[index].name());
+ QCOMPARE(copyConstructed.handle(), targets[index].handle());
+ QCOMPARE(copyConstructed.uuid(), targets[index].uuid());
+ QCOMPARE(copyConstructed.value(), targets[index].value());
QVERIFY(copyConstructed == target);
QVERIFY(target == copyConstructed);
@@ -300,18 +315,18 @@ void tst_QLowEnergyDescriptor::tst_assignCompare()
QVERIFY(!(invalid != target));
QVERIFY(!(target != invalid));
- QVERIFY(!(targets.first() == target));
- QVERIFY(!(target == targets.first()));
- QVERIFY(targets.first() != target);
- QVERIFY(target != targets.first());
+ QVERIFY(!(targets[index] == target));
+ QVERIFY(!(target == targets[index]));
+ QVERIFY(targets[index] != target);
+ QVERIFY(target != targets[index]);
if (targets.count() >= 2) {
- QLowEnergyDescriptor second = targets[1];
+ QLowEnergyDescriptor second = targets[(index+1)%2];
// at least two descriptors
- QVERIFY(!(targets.first() == second));
- QVERIFY(!(second == targets.first()));
- QVERIFY(targets.first() != second);
- QVERIFY(second != targets.first());
+ QVERIFY(!(targets[index] == second));
+ QVERIFY(!(second == targets[index]));
+ QVERIFY(targets[index] != second);
+ QVERIFY(second != targets[index]);
}
}