diff options
52 files changed, 1215 insertions, 1885 deletions
diff --git a/.qmake.conf b/.qmake.conf index f8cda0e7..1581612e 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,5 @@ load(qt_build_config) -MODULE_VERSION = 5.13.0 +DEFINES += QT_NO_JAVA_STYLE_ITERATORS + +MODULE_VERSION = 5.14.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..d860457e 100644 --- a/examples/bluetooth/lowenergyscanner/device.cpp +++ b/examples/bluetooth/lowenergyscanner/device.cpp @@ -152,12 +152,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/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java index 6c548f84..9f10c32a 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); 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..974d8e8a 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -162,42 +162,34 @@ 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 + qlowenergycontroller_darwin_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 } 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 +199,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 { diff --git a/src/bluetooth/osx/btdelegates.cpp b/src/bluetooth/osx/btdelegates.cpp new file mode 100644 index 00000000..531ca1df --- /dev/null +++ b/src/bluetooth/osx/btdelegates.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "btdelegates_p.h" + +#if defined(Q_OS_MACOS) + +QT_BEGIN_NAMESPACE + +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/btraii.mm b/src/bluetooth/osx/btraii.mm new file mode 100644 index 00000000..a1bf2a8d --- /dev/null +++ b/src/bluetooth/osx/btraii.mm @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "btraii_p.h" + +#include <qdebug.h> + +#include <Foundation/Foundation.h> + +#include <utility> + +QT_BEGIN_NAMESPACE + +namespace DarwinBluetooth { + +StrongReference::StrongReference(void *object, RetainPolicy policy) + : objCInstance(object) +{ + if (policy == RetainPolicy::doInitialRetain) + objCInstance = [getAs<NSObject>() retain]; +} + +StrongReference::StrongReference(const StrongReference &other) +{ + objCInstance = [other.getAs<NSObject>() retain]; +} + +StrongReference::StrongReference(StrongReference &&other) +{ + std::swap(objCInstance, other.objCInstance); +} + +StrongReference::~StrongReference() +{ + [getAs<NSObject>() release]; +} + +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; +} + +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..89276310 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) { 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/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/qbluetoothdevicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp index 443be14d..2f0524d4 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp @@ -363,7 +363,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevices( discoveredDevices.append(info); qCDebug(QT_BT_ANDROID) << "Device found: " << info.name() << info.address().toString() - << "isLeScanResult:" << isLeResult; + << "isLeScanResult:" << isLeResult + << "Manufacturer data size:" << info.manufacturerData().size(); emit q->deviceDiscovered(info); } diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm index f62ca0dd..d9883d28 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm @@ -37,11 +37,14 @@ ** ****************************************************************************/ +#include "qbluetoothdevicediscoveryagent_p.h" #include "qbluetoothdevicediscoveryagent.h" + #include "osx/osxbtledeviceinquiry_p.h" +#ifdef Q_OS_MACOS #include "osx/osxbtdeviceinquiry_p.h" -#include "qbluetoothlocaldevice.h" #include "osx/osxbtsdpinquiry_p.h" +#endif // Q_OS_MACOS #include "qbluetoothdeviceinfo.h" #include "osx/osxbtnotifier_p.h" #include "osx/osxbtutility_p.h" @@ -51,13 +54,14 @@ #include "qbluetoothaddress.h" #include "osx/uistrings_p.h" #include "qbluetoothuuid.h" +#include "osx/btraii_p.h" #include <QtCore/qloggingcategory.h> #include <QtCore/qscopedpointer.h> +#include <QtCore/qvector.h> #include <QtCore/qglobal.h> #include <QtCore/qstring.h> #include <QtCore/qdebug.h> -#include <QtCore/qlist.h> #include <Foundation/Foundation.h> @@ -75,108 +79,42 @@ void registerQDeviceDiscoveryMetaType() initDone = true; } } +#ifdef Q_OS_MACOS +using InquiryObjC = QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry); +#endif // Q_OS_MACOS -}//namespace - -class QBluetoothDeviceDiscoveryAgentPrivate : public QObject, - public OSXBluetooth::DeviceInquiryDelegate -{ - friend class QBluetoothDeviceDiscoveryAgent; -public: - template<class T> - using ObjCScopedPointer = OSXBluetooth::ObjCScopedPointer<T>; - using LEDeviceInquiryObjC = QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry); - - QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress & address, - QBluetoothDeviceDiscoveryAgent *q); - - ~QBluetoothDeviceDiscoveryAgentPrivate() override; - - bool isValid() const; - bool isActive() const; - - void start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods); - void startClassic(); - void startLE(); - void stop(); - -private: - enum AgentState { - NonActive, - ClassicScan, - LEScan - }; - - // DeviceInquiryDelegate: - void inquiryFinished(IOBluetoothDeviceInquiry *inq) override; - void error(IOBluetoothDeviceInquiry *inq, IOReturn error) override; - void deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device) override; - - void LEinquiryFinished(); - void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error); - void LEnotSupported(); - - // Check if it's a really new device/updated info and emit - // q_ptr->deviceDiscovered. - void deviceFound(const QBluetoothDeviceInfo &newDeviceInfo); - - void setError(IOReturn error, const QString &text = QString()); - void setError(QBluetoothDeviceDiscoveryAgent::Error, - const QString &text = QString()); - - QBluetoothDeviceDiscoveryAgent *q_ptr; - AgentState agentState; - - QBluetoothAddress adapterAddress; +using LEInquiryObjC = QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry); - bool startPending; - bool stopPending; - - QBluetoothDeviceDiscoveryAgent::Error lastError; - QString errorString; - - QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType; - - using DeviceInquiry = ObjCScopedPointer<DeviceInquiryObjC>; - DeviceInquiry inquiry; - - using LEDeviceInquiry = ObjCScopedPointer<LEDeviceInquiryObjC>; - LEDeviceInquiry inquiryLE; - - using HostController = ObjCScopedPointer<IOBluetoothHostController>; - HostController hostController; - - using DevicesList = QList<QBluetoothDeviceInfo>; - DevicesList discoveredDevices; - - int lowEnergySearchTimeout; - QBluetoothDeviceDiscoveryAgent::DiscoveryMethods requestedMethods; -}; +} //namespace QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &adapter, QBluetoothDeviceDiscoveryAgent *q) : - q_ptr(q), + inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), + lastError(QBluetoothDeviceDiscoveryAgent::NoError), agentState(NonActive), adapterAddress(adapter), startPending(false), stopPending(false), - lastError(QBluetoothDeviceDiscoveryAgent::NoError), - inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), lowEnergySearchTimeout(OSXBluetooth::defaultLEScanTimeoutMS), - requestedMethods(QBluetoothDeviceDiscoveryAgent::ClassicMethod - | QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) +#ifdef Q_OS_MACOS + requestedMethods(QBluetoothDeviceDiscoveryAgent::ClassicMethod | QBluetoothDeviceDiscoveryAgent::LowEnergyMethod), +#else + requestedMethods(QBluetoothDeviceDiscoveryAgent::ClassicMethod), +#endif // Q_OS_MACOS + q_ptr(q) { registerQDeviceDiscoveryMetaType(); Q_ASSERT_X(q != nullptr, Q_FUNC_INFO, "invalid q_ptr (null)"); - HostController controller([[IOBluetoothHostController defaultController] retain]); - if (!controller || [controller powerState] != kBluetoothHCIPowerStateON) { +#ifdef Q_OS_MACOS + IOBluetoothHostController *hostController = [IOBluetoothHostController defaultController]; + if (!hostController || [hostController powerState] != kBluetoothHCIPowerStateON) { qCCritical(QT_BT_OSX) << "no default host controller or adapter is off"; return; } - - hostController.reset(controller.take()); + controller.reset(hostController, DarwinBluetooth::RetainPolicy::doInitialRetain); +#endif } QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() @@ -185,7 +123,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() // We want the LE scan to stop as soon as possible. if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { // Local variable to be retained ... - LEDeviceInquiryObjC *inq = inquiryLE.data(); + LEInquiryObjC *inq = inquiryLE.getAs<LEInquiryObjC>(); dispatch_sync(leQueue, ^{ [inq stop]; }); @@ -193,11 +131,6 @@ QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() } } -bool QBluetoothDeviceDiscoveryAgentPrivate::isValid() const -{ - return hostController && [hostController powerState] == kBluetoothHCIPowerStateON; -} - bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const { if (startPending) @@ -211,12 +144,19 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods) { - Q_ASSERT(isValid()); Q_ASSERT(!isActive()); Q_ASSERT(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError); Q_ASSERT(methods & (QBluetoothDeviceDiscoveryAgent::ClassicMethod | QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)); +#ifdef Q_OS_MACOS + if (!controller) { + setError(QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError); + emit q_ptr->error(lastError); + return; + } +#endif // Q_OS_MACOS + requestedMethods = methods; if (stopPending) { @@ -230,16 +170,18 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent agentState = NonActive; discoveredDevices.clear(); setError(QBluetoothDeviceDiscoveryAgent::NoError); - +#ifdef Q_OS_MACOS if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod) return startClassic(); +#endif // Q_OS_MACOS startLE(); } +#ifdef Q_OS_MACOS + void QBluetoothDeviceDiscoveryAgentPrivate::startClassic() { - Q_ASSERT(isValid()); Q_ASSERT(!isActive()); Q_ASSERT(lastError == QBluetoothDeviceDiscoveryAgent::NoError); Q_ASSERT(requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod); @@ -249,7 +191,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startClassic() if (!inquiry) { // The first Classic scan for this DDA. - inquiry.reset([[DeviceInquiryObjC alloc]initWithDelegate:this]); + inquiry.reset([[InquiryObjC alloc] initWithDelegate:this], + DarwinBluetooth::RetainPolicy::noInitialRetain); + if (!inquiry) { qCCritical(QT_BT_OSX) << "failed to initialize an Classic device inquiry"; setError(QBluetoothDeviceDiscoveryAgent::UnknownError, @@ -261,7 +205,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startClassic() agentState = ClassicScan; - const IOReturn res = [inquiry start]; + const IOReturn res = [inquiry.getAs<InquiryObjC>() start]; if (res != kIOReturnSuccess) { setError(res, QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED)); agentState = NonActive; @@ -269,9 +213,10 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startClassic() } } +#endif // Q_OS_MACOS + void QBluetoothDeviceDiscoveryAgentPrivate::startLE() { - Q_ASSERT(isValid()); Q_ASSERT(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError); Q_ASSERT(requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); @@ -291,7 +236,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE() this, DeviceMemFunPtr(&QBluetoothDeviceDiscoveryAgentPrivate::deviceFound)); // Check queue and create scanner: - inquiryLE.reset([[LEDeviceInquiryObjC alloc] initWithNotifier:notifier.data()]); + inquiryLE.reset([[LEInquiryObjC alloc] initWithNotifier:notifier.data()], + DarwinBluetooth::RetainPolicy::noInitialRetain); if (inquiryLE) notifier.take(); // Whatever happens next, inquiryLE is already the owner ... @@ -307,7 +253,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE() // Now start in on LE queue: agentState = LEScan; // We need the local variable so that it's retained ... - LEDeviceInquiryObjC *inq = inquiryLE.data(); + LEInquiryObjC *inq = inquiryLE.getAs<LEInquiryObjC>(); dispatch_async(leQueue, ^{ [inq startWithTimeout:lowEnergySearchTimeout]; }); @@ -315,7 +261,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE() void QBluetoothDeviceDiscoveryAgentPrivate::stop() { - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "called on invalid device discovery agent"); Q_ASSERT_X(isActive(), Q_FUNC_INFO, "called whithout active inquiry"); Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError, Q_FUNC_INFO, "called with invalid bluetooth adapter"); @@ -328,8 +273,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop() setError(QBluetoothDeviceDiscoveryAgent::NoError); +#ifdef Q_OS_MACOS if (agentState == ClassicScan) { - const IOReturn res = [inquiry stop]; + const IOReturn res = [inquiry.getAs<InquiryObjC>() stop]; if (res != kIOReturnSuccess) { qCWarning(QT_BT_OSX) << "failed to stop"; startPending = prevStart; @@ -338,10 +284,14 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop() emit q_ptr->error(lastError); } } else { +#else + { + Q_UNUSED(prevStart) +#endif // Q_OS_MACOS dispatch_queue_t leQueue(qt_LE_queue()); Q_ASSERT(leQueue); // We need the local variable so that it's retained ... - LEDeviceInquiryObjC *inq = inquiryLE.data(); + LEInquiryObjC *inq = inquiryLE.getAs<LEInquiryObjC>(); dispatch_sync(leQueue, ^{ [inq stop]; }); @@ -351,12 +301,10 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop() } } -void QBluetoothDeviceDiscoveryAgentPrivate::inquiryFinished(IOBluetoothDeviceInquiry *inq) -{ - Q_UNUSED(inq) - - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid device discovery agent"); //We can never be here. +#ifdef Q_OS_MACOS +void QBluetoothDeviceDiscoveryAgentPrivate::inquiryFinished() +{ // The subsequent start(LE) function (if any) // will (re)set the correct state. agentState = NonActive; @@ -381,12 +329,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::inquiryFinished(IOBluetoothDeviceInq } } -void QBluetoothDeviceDiscoveryAgentPrivate::error(IOBluetoothDeviceInquiry *inq, IOReturn error) +void QBluetoothDeviceDiscoveryAgentPrivate::error(IOReturn error) { - Q_UNUSED(inq) - - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid device discovery agent"); - startPending = false; stopPending = false; @@ -395,12 +339,11 @@ void QBluetoothDeviceDiscoveryAgentPrivate::error(IOBluetoothDeviceInquiry *inq, emit q_ptr->error(lastError); } -void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device) +void QBluetoothDeviceDiscoveryAgentPrivate::classicDeviceFound(void *obj) { - Q_UNUSED(inq) - - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid device discovery agent"); + auto device = static_cast<IOBluetoothDevice *>(obj); Q_ASSERT_X(device, Q_FUNC_INFO, "invalid IOBluetoothDevice (nil)"); + Q_ASSERT_X(agentState == ClassicScan, Q_FUNC_INFO, "invalid agent state (expected classic scan)"); @@ -417,7 +360,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(IOBluetoothDeviceInquiry if (device.name) deviceName = QString::fromNSString(device.name); - const qint32 classOfDevice(device.classOfDevice); + const auto classOfDevice = qint32(device.classOfDevice); QBluetoothDeviceInfo deviceInfo(deviceAddress, deviceName, classOfDevice); deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration); @@ -439,6 +382,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::setError(IOReturn error, const QStri setError(QBluetoothDeviceDiscoveryAgent::UnknownError, text); } +#endif // Q_OS_MACOS + void QBluetoothDeviceDiscoveryAgentPrivate::setError(QBluetoothDeviceDiscoveryAgent::Error error, const QString &text) { lastError = error; @@ -459,14 +404,14 @@ void QBluetoothDeviceDiscoveryAgentPrivate::setError(QBluetoothDeviceDiscoveryAg case QBluetoothDeviceDiscoveryAgent::InputOutputError: errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_IO); break; + case QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError: + errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_NOTSUPPORTED); + break; case QBluetoothDeviceDiscoveryAgent::UnknownError: default: errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_UNKNOWN_ERROR); } } - - if (lastError != QBluetoothDeviceDiscoveryAgent::NoError) - qCDebug(QT_BT_OSX) << "error set:"<<errorString; } void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error) @@ -487,6 +432,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported() { qCDebug(QT_BT_OSX) << "no Bluetooth LE support"; +#ifdef Q_OS_MACOS if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod) { // Having both Classic | LE means this is not an error. LEinquiryFinished(); @@ -497,6 +443,13 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported() // as UnsupportedDiscoveryMethod. LEinquiryError(QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod); } +#else + inquiryLE.reset(); + startPending = false; + stopPending = false; + setError(QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError); + emit q_ptr->error(lastError); +#endif } void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() @@ -522,8 +475,12 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(const QBluetoothDeviceIn // Core Bluetooth does not allow us to access addresses, we have to use uuid instead. // This uuid has nothing to do with uuids in Bluetooth in general (it's generated by // Apple's framework using some algorithm), but it's a 128-bit uuid after all. - const bool isLE = newDeviceInfo.coreConfigurations() == QBluetoothDeviceInfo::LowEnergyCoreConfiguration; - + const bool isLE = +#ifdef Q_OS_MACOS + newDeviceInfo.coreConfigurations() == QBluetoothDeviceInfo::LowEnergyCoreConfiguration; +#else + true; +#endif // Q_OS_MACOS for (int i = 0, e = discoveredDevices.size(); i < e; ++i) { if (isLE) { if (discoveredDevices[i].deviceUuid() == newDeviceInfo.deviceUuid()) { @@ -564,6 +521,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(const QBluetoothDeviceIn return; } } else { +#ifdef Q_OS_MACOS if (discoveredDevices[i].address() == newDeviceInfo.address()) { if (discoveredDevices[i] == newDeviceInfo) return; @@ -572,6 +530,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(const QBluetoothDeviceIn emit q_ptr->deviceDiscovered(newDeviceInfo); return; } +#else + Q_UNREACHABLE(); +#endif // Q_OS_MACOS } } @@ -579,125 +540,13 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(const QBluetoothDeviceIn emit q_ptr->deviceDiscovered(newDeviceInfo); } -QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(QObject *parent) : - QObject(parent), - d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(QBluetoothAddress(), this)) -{ -} - -QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent( - const QBluetoothAddress &deviceAdapter, QObject *parent) : - QObject(parent), - d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(deviceAdapter, this)) -{ - if (!deviceAdapter.isNull()) { - const QList<QBluetoothHostInfo> localDevices = QBluetoothLocalDevice::allDevices(); - for (const QBluetoothHostInfo &hostInfo : localDevices) { - if (hostInfo.address() == deviceAdapter) - return; - } - d_ptr->setError(InvalidBluetoothAdapterError); - } -} - -QBluetoothDeviceDiscoveryAgent::~QBluetoothDeviceDiscoveryAgent() -{ - delete d_ptr; -} - -QBluetoothDeviceDiscoveryAgent::InquiryType QBluetoothDeviceDiscoveryAgent::inquiryType() const -{ - return d_ptr->inquiryType; -} - -void QBluetoothDeviceDiscoveryAgent::setInquiryType(InquiryType type) -{ - d_ptr->inquiryType = type; -} - -QList<QBluetoothDeviceInfo> QBluetoothDeviceDiscoveryAgent::discoveredDevices() const -{ - return d_ptr->discoveredDevices; -} - QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() { +#ifdef Q_OS_MACOS return ClassicMethod | LowEnergyMethod; -} - -void QBluetoothDeviceDiscoveryAgent::start() -{ - start(supportedDiscoveryMethods()); -} - -void QBluetoothDeviceDiscoveryAgent::start(DiscoveryMethods methods) -{ - if (methods == NoMethod) - return; - - if ((supportedDiscoveryMethods() & methods) != methods) { - d_ptr->lastError = UnsupportedDiscoveryMethod; - d_ptr->errorString = tr("One or more device discovery methods " - "are not supported on this platform"); - emit error(d_ptr->lastError); - return; - } - - if (d_ptr->lastError != InvalidBluetoothAdapterError) { - if (d_ptr->isValid()) { - if (!isActive()) - d_ptr->start(methods); - } else { - // We previously failed to initialize d_ptr correctly: - // either some memory allocation problem or - // no BT adapter found. - d_ptr->setError(InvalidBluetoothAdapterError); - emit error(InvalidBluetoothAdapterError); - } - } -} - -void QBluetoothDeviceDiscoveryAgent::stop() -{ - if (d_ptr->isValid()) { - if (isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) - d_ptr->stop(); - } -} - -bool QBluetoothDeviceDiscoveryAgent::isActive() const -{ - if (d_ptr->isValid()) - return d_ptr->isActive(); - - return false; -} - -QBluetoothDeviceDiscoveryAgent::Error QBluetoothDeviceDiscoveryAgent::error() const -{ - return d_ptr->lastError; -} - -QString QBluetoothDeviceDiscoveryAgent::errorString() const -{ - return d_ptr->errorString; -} - -void QBluetoothDeviceDiscoveryAgent::setLowEnergyDiscoveryTimeout(int timeout) -{ - // cannot deliberately turn it off - if (timeout < 0) { - qCDebug(QT_BT_OSX) << "The Bluetooth Low Energy device discovery timeout cannot be negative."; - return; - } - - d_ptr->lowEnergySearchTimeout = timeout; - return; -} - -int QBluetoothDeviceDiscoveryAgent::lowEnergyDiscoveryTimeout() const -{ - return d_ptr->lowEnergySearchTimeout; +#else + return LowEnergyMethod; +#endif // Q_OS_MACOS } QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm deleted file mode 100644 index 059f244d..00000000 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm +++ /dev/null @@ -1,458 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtBluetooth module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qbluetoothdevicediscoveryagent.h" -#include "osx/osxbtledeviceinquiry_p.h" -#include "qbluetoothlocaldevice.h" -#include "qbluetoothdeviceinfo.h" -#include "osx/osxbtnotifier_p.h" -#include "osx/osxbtutility_p.h" -#include "osx/uistrings_p.h" -#include "qbluetoothuuid.h" - -#include <QtCore/qloggingcategory.h> -#include <QtCore/qobject.h> -#include <QtCore/qglobal.h> -#include <QtCore/qstring.h> -#include <QtCore/qdebug.h> -#include <QtCore/qlist.h> - -#include <CoreBluetooth/CoreBluetooth.h> - -QT_BEGIN_NAMESPACE - -namespace -{ - -void registerQDeviceDiscoveryMetaType() -{ - static bool initDone = false; - if (!initDone) { - qRegisterMetaType<QBluetoothDeviceInfo>(); - qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::Error>(); - initDone = true; - } -} - -}//namespace - -class QBluetoothDeviceDiscoveryAgentPrivate : public QObject -{ - friend class QBluetoothDeviceDiscoveryAgent; - -public: - QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &address, - QBluetoothDeviceDiscoveryAgent *q); - virtual ~QBluetoothDeviceDiscoveryAgentPrivate(); - - bool isActive() const; - - void start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods m); - void stop(); - -private: - using LEDeviceInquiryObjC = QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry); - - void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error); - void LEnotSupported(); - void LEdeviceFound(const QBluetoothDeviceInfo &info); - void LEinquiryFinished(); - - void setError(QBluetoothDeviceDiscoveryAgent::Error, const QString &text = QString()); - - QBluetoothDeviceDiscoveryAgent *q_ptr; - - QBluetoothDeviceDiscoveryAgent::Error lastError; - QString errorString; - - QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType; - - using LEDeviceInquiry = OSXBluetooth::ObjCScopedPointer<LEDeviceInquiryObjC>; - LEDeviceInquiry inquiryLE; - - using DevicesList = QList<QBluetoothDeviceInfo>; - DevicesList discoveredDevices; - - bool startPending; - bool stopPending; - - int lowEnergySearchTimeout; -}; - -QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &adapter, - QBluetoothDeviceDiscoveryAgent *q) : - q_ptr(q), - lastError(QBluetoothDeviceDiscoveryAgent::NoError), - inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), - startPending(false), - stopPending(false), - lowEnergySearchTimeout(OSXBluetooth::defaultLEScanTimeoutMS) -{ - Q_UNUSED(adapter); - - registerQDeviceDiscoveryMetaType(); - Q_ASSERT_X(q != nullptr, Q_FUNC_INFO, "invalid q_ptr (null)"); -} - -QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() -{ - if (inquiryLE) { - // We want the LE scan to stop as soon as possible. - if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { - // Local variable to be retained ... - LEDeviceInquiryObjC *inq = inquiryLE.data(); - dispatch_sync(leQueue, ^{ - [inq stop]; - }); - } - } -} - -bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const -{ - if (startPending) - return true; - if (stopPending) - return false; - - return inquiryLE; -} - -void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods /*methods*/) -{ - Q_ASSERT_X(!isActive(), Q_FUNC_INFO, "called on active device discovery agent"); - - if (stopPending) { - startPending = true; - return; - } - - using namespace OSXBluetooth; - - QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier); - // Connections: - using ErrMemFunPtr = void (LECBManagerNotifier::*)(QBluetoothDeviceDiscoveryAgent::Error); - notifier->connect(notifier.data(), ErrMemFunPtr(&LECBManagerNotifier::CBManagerError), - this, &QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError); - notifier->connect(notifier.data(), &LECBManagerNotifier::LEnotSupported, - this, &QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported); - notifier->connect(notifier.data(), &LECBManagerNotifier::discoveryFinished, - this, &QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished); - notifier->connect(notifier.data(), &LECBManagerNotifier::deviceDiscovered, - this, &QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound); - - inquiryLE.reset([[LEDeviceInquiryObjC alloc] initWithNotifier:notifier.data()]); - if (inquiryLE) - notifier.take(); // Whatever happens next, inquiryLE is already the owner ... - - dispatch_queue_t leQueue(qt_LE_queue()); - if (!leQueue || !inquiryLE) { - setError(QBluetoothDeviceDiscoveryAgent::UnknownError, - QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED)); - emit q_ptr->error(lastError); - return; - } - - discoveredDevices.clear(); - setError(QBluetoothDeviceDiscoveryAgent::NoError); - - // Create a local variable - to have a strong referece in a block. - LEDeviceInquiryObjC *inq = inquiryLE.data(); - dispatch_async(leQueue, ^{ - [inq startWithTimeout:lowEnergySearchTimeout]; - }); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::stop() -{ - Q_ASSERT_X(isActive(), Q_FUNC_INFO, "called whithout active inquiry"); - - startPending = false; - stopPending = true; - - dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - Q_ASSERT(leQueue); - - setError(QBluetoothDeviceDiscoveryAgent::NoError); - - // Create a local variable - to have a strong referece in a block. - LEDeviceInquiryObjC *inq = inquiryLE.data(); - dispatch_sync(leQueue, ^{ - [inq stop]; - }); - // We consider LE scan to be stopped immediately and - // do not care about this LEDeviceInquiry object anymore. - LEinquiryFinished(); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error) -{ - // At the moment the only error reported by osxbtledeviceinquiry - // can be 'powered off' error, it happens - // after the LE scan started (so we have LE support and this is - // a real PoweredOffError). - Q_ASSERT_X(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError, - Q_FUNC_INFO, "unexpected error"); - - inquiryLE.reset(); - - startPending = false; - stopPending = false; - setError(error); - emit q_ptr->error(lastError); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported() -{ - inquiryLE.reset(); - - startPending = false; - stopPending = false; - setError(QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError); - emit q_ptr->error(lastError); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(const QBluetoothDeviceInfo &newDeviceInfo) -{ - // Update, append or discard. - for (int i = 0, e = discoveredDevices.size(); i < e; ++i) { - if (discoveredDevices[i].deviceUuid() == newDeviceInfo.deviceUuid()) { - QBluetoothDeviceInfo::Fields updatedFields = QBluetoothDeviceInfo::Field::None; - if (discoveredDevices[i].rssi() != newDeviceInfo.rssi()) { - qCDebug(QT_BT_OSX) << "Updating RSSI for" << newDeviceInfo.address() - << newDeviceInfo.rssi(); - discoveredDevices[i].setRssi(newDeviceInfo.rssi()); - updatedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI); - } - - if (discoveredDevices[i].manufacturerData() != newDeviceInfo.manufacturerData()) { - qCDebug(QT_BT_OSX) << "Updating manufacturer data for" << newDeviceInfo.address(); - const QVector<quint16> keys = newDeviceInfo.manufacturerIds(); - for (auto key: keys) - discoveredDevices[i].setManufacturerData(key, newDeviceInfo.manufacturerData(key)); - updatedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData); - } - - if (lowEnergySearchTimeout > 0) { - if (discoveredDevices[i] != newDeviceInfo) { - discoveredDevices.replace(i, newDeviceInfo); - emit q_ptr->deviceDiscovered(newDeviceInfo); - } else { - if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None)) - emit q_ptr->deviceUpdated(discoveredDevices[i], updatedFields); - } - - return; - } - - discoveredDevices.replace(i, newDeviceInfo); - emit q_ptr->deviceDiscovered(newDeviceInfo); - - if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None)) - emit q_ptr->deviceUpdated(discoveredDevices[i], updatedFields); - - return; - } - } - - discoveredDevices.append(newDeviceInfo); - emit q_ptr->deviceDiscovered(newDeviceInfo); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() -{ - inquiryLE.reset(); - - if (stopPending && !startPending) { - stopPending = false; - emit q_ptr->canceled(); - } else if (startPending) { - startPending = false; - stopPending = false; - // always the same method for start() on iOS - // classic search not supported - start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); - } else { - emit q_ptr->finished(); - } -} - -void QBluetoothDeviceDiscoveryAgentPrivate::setError(QBluetoothDeviceDiscoveryAgent::Error error, - const QString &text) -{ - lastError = error; - - if (text.length() > 0) { - errorString = text; - } else { - switch (lastError) { - case QBluetoothDeviceDiscoveryAgent::NoError: - errorString = QString(); - break; - case QBluetoothDeviceDiscoveryAgent::PoweredOffError: - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_POWERED_OFF); - break; - case QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError: - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_INVALID_ADAPTER); - break; - case QBluetoothDeviceDiscoveryAgent::InputOutputError: - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_IO); - break; - case QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError: - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_NOTSUPPORTED); - break; - case QBluetoothDeviceDiscoveryAgent::UnknownError: - default: - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_UNKNOWN_ERROR); - } - } -} - -QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(QObject *parent) : - QObject(parent), - d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(QBluetoothAddress(), this)) -{ -} - -QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent( - const QBluetoothAddress &deviceAdapter, QObject *parent) : - QObject(parent), - d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(deviceAdapter, this)) -{ - if (!deviceAdapter.isNull()) { - qCWarning(QT_BT_OSX) << "local device address is " - "not available, provided address is ignored"; - d_ptr->setError(InvalidBluetoothAdapterError); - } -} - -QBluetoothDeviceDiscoveryAgent::~QBluetoothDeviceDiscoveryAgent() -{ - delete d_ptr; -} - -QBluetoothDeviceDiscoveryAgent::InquiryType QBluetoothDeviceDiscoveryAgent::inquiryType() const -{ - return d_ptr->inquiryType; -} - -void QBluetoothDeviceDiscoveryAgent::setInquiryType(QBluetoothDeviceDiscoveryAgent::InquiryType type) -{ - d_ptr->inquiryType = type; -} - -QList<QBluetoothDeviceInfo> QBluetoothDeviceDiscoveryAgent::discoveredDevices() const -{ - return d_ptr->discoveredDevices; -} - -QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() -{ - return LowEnergyMethod; -} - -void QBluetoothDeviceDiscoveryAgent::start() -{ - if (d_ptr->lastError != InvalidBluetoothAdapterError) { - if (!isActive()) - d_ptr->start(supportedDiscoveryMethods()); - else - qCDebug(QT_BT_OSX) << "already started"; - } -} - -void QBluetoothDeviceDiscoveryAgent::start(DiscoveryMethods methods) -{ - if (methods == NoMethod) - return; - - DiscoveryMethods supported = - QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods(); - - if (!((supported & methods) == methods)) { - d_ptr->lastError = UnsupportedDiscoveryMethod; - d_ptr->errorString = QBluetoothDeviceDiscoveryAgent::tr("One or more device discovery methods " - "are not supported on this platform"); - emit error(d_ptr->lastError); - return; - } - - if (!isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) - d_ptr->start(methods); -} - -void QBluetoothDeviceDiscoveryAgent::stop() -{ - if (isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) - d_ptr->stop(); -} - -bool QBluetoothDeviceDiscoveryAgent::isActive() const -{ - return d_ptr->isActive(); -} - -QBluetoothDeviceDiscoveryAgent::Error QBluetoothDeviceDiscoveryAgent::error() const -{ - return d_ptr->lastError; -} - -QString QBluetoothDeviceDiscoveryAgent::errorString() const -{ - return d_ptr->errorString; -} - -int QBluetoothDeviceDiscoveryAgent::lowEnergyDiscoveryTimeout() const -{ - return d_ptr->lowEnergySearchTimeout; -} - -void QBluetoothDeviceDiscoveryAgent::setLowEnergyDiscoveryTimeout(int timeout) -{ - // cannot deliberately turn it off - if (timeout < 0) { - qCDebug(QT_BT_OSX) << "The Bluetooth Low Energy device discovery timeout cannot be negative."; - return; - } - - d_ptr->lowEnergySearchTimeout = timeout; - return; -} - -QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index 3e76c13d..a59c6a94 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -58,6 +58,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> @@ -93,11 +98,14 @@ class QWinRTBluetoothDeviceDiscoveryWorker; #endif class QBluetoothDeviceDiscoveryAgentPrivate -#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) +#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_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) @@ -180,6 +188,57 @@ private: QTimer *leScanTimer; #endif +#ifdef Q_OS_DARWIN + + void startLE(); + +#ifdef Q_OS_MACOS + + void startClassic(); + + // Classic (IOBluetooth) inquiry delegate's methods: + void inquiryFinished() override; + void error(IOReturn error) override; + void classicDeviceFound(void *device) override; + // Classic (IOBluetooth) errors: + void setError(IOReturn error, const QString &text = QString()); + +#endif // Q_OS_MACOS + + // LE scan delegates (CoreBluetooth, all Darwin OSes): + void LEinquiryFinished(); + void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error); + void LEnotSupported(); + + // LE errors: + void setError(QBluetoothDeviceDiscoveryAgent::Error, + const QString &text = QString()); + + // Both LE and Classic devices go there: + void deviceFound(const QBluetoothDeviceInfo &newDeviceInfo); + + enum AgentState { + NonActive, + ClassicScan, // macOS (IOBluetooth) only + LEScan + } agentState; + + QBluetoothAddress adapterAddress; + + bool startPending; + bool stopPending; + +#ifdef Q_OS_MACOS + + DarwinBluetooth::ScopedPointer controller; + DarwinBluetooth::ScopedPointer inquiry; + +#endif // Q_OS_MACOS + + DarwinBluetooth::ScopedPointer inquiryLE; + +#endif // Q_OS_DARWIN + int lowEnergySearchTimeout; QBluetoothDeviceDiscoveryAgent::DiscoveryMethods requestedMethods; QBluetoothDeviceDiscoveryAgent *q_ptr; diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp index 177b8082..9d306053 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp @@ -758,13 +758,13 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IB boolean isPaired; hr = pairing->get_IsPaired(&isPaired); Q_ASSERT_SUCCEEDED(hr); - QList<QBluetoothUuid> uuids; + QVector<QBluetoothUuid> uuids; const LEAdvertisingInfo adInfo = m_foundLEDevicesMap.value(address); const qint16 rssi = adInfo.rssi; // Use the services obtained from the advertisement data if the device is not paired if (!isPaired) { - uuids = adInfo.services.toList(); + uuids = adInfo.services; } else { IVectorView <GenericAttributeProfile::GattDeviceService *> *deviceServices; hr = device->get_GattServices(&deviceServices); @@ -790,7 +790,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)); diff --git a/src/bluetooth/qlowenergycharacteristic.h b/src/bluetooth/qlowenergycharacteristic.h index 9b27d621..d5d783c2 100644 --- a/src/bluetooth/qlowenergycharacteristic.h +++ b/src/bluetooth/qlowenergycharacteristic.h @@ -101,7 +101,7 @@ protected: friend class QLowEnergyControllerPrivateBluez; friend class QLowEnergyControllerPrivateBluezDBus; friend class QLowEnergyControllerPrivateCommon; - friend class QLowEnergyControllerPrivateOSX; + 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..3cf0d920 100644 --- a/src/bluetooth/qlowenergycontroller.cpp +++ b/src/bluetooth/qlowenergycontroller.cpp @@ -60,6 +60,8 @@ #if QT_CONFIG(winrt_btle_no_pairing) #include "qlowenergycontroller_winrt_new_p.h" #endif +#elif defined(Q_OS_DARWIN) +#include "qlowenergycontroller_darwin_p.h" #else #include "qlowenergycontroller_p.h" #endif @@ -321,6 +323,9 @@ static QLowEnergyControllerPrivate *privateController(QLowEnergyController::Role qCDebug(QT_BT_WINRT) << "Using pre 15063 low energy controller"; return new QLowEnergyControllerPrivateWinRT(); #endif +#elif defined(Q_OS_DARWIN) + Q_UNUSED(role) + return new QLowEnergyControllerPrivateDarwin(); #else Q_UNUSED(role); return new QLowEnergyControllerPrivateCommon(); @@ -344,6 +349,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 +381,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 +415,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 +443,29 @@ QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDevice return new QLowEnergyController(remoteDevice, parent); } +/*! + Returns a new instance of this class with \a parent. + + The \a remoteDevice must contain the address of the remote Bluetooth Low + Energy device to which this object should attempt to connect later on. + + The connection is established via \a localDevice. If \a localDevice is invalid, + the local default device is automatically selected. If \a localDevice specifies + a local device that is not a local Bluetooth adapter, \l error() is set to + \l InvalidBluetoothAdapterError once \l connectToDevice() is called. + + Note that specifying the local device to be used for the connection is only + possible when using BlueZ. All other platforms do not support this feature. + + \since 5.13 + */ +QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothAddress &remoteDevice, + const QBluetoothAddress &localDevice, + QObject *parent) +{ + return new QLowEnergyController(remoteDevice, localDevice, parent); +} + /*! Returns a new object of this class that is in the \l PeripheralRole and has the @@ -511,7 +545,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..5498d4a5 100644 --- a/src/bluetooth/qlowenergycontroller.h +++ b/src/bluetooth/qlowenergycontroller.h @@ -93,13 +93,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 f019d9c2..4e5f3430 100644 --- a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp +++ b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp @@ -268,9 +268,13 @@ void QLowEnergyControllerPrivateBluezDBus::connectToDeviceHelper() const QVariantMap &ifaceValues = jt.value(); if (iface == QStringLiteral("org.bluez.Device1")) { - if (remoteDevice.toString() == ifaceValues.value(QStringLiteral("Address")).toString()) { - devicePath = it.key().path(); - break; + if (remoteDevice.toString() == ifaceValues.value(QStringLiteral("Address")).toString()) + { + const QVariant adapterForCurrentDevice = ifaceValues.value(QStringLiteral("Adapter")); + if (qvariant_cast<QDBusObjectPath>(adapterForCurrentDevice).path() == hostAdapterPath) { + devicePath = it.key().path(); + break; + } } } } diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_darwin.mm index 8bcdc22e..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()]); + centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()], + DarwinBluetooth::RetainPolicy::noInitialRetain); if (!centralManager) { - qCWarning(QT_BT_OSX) << "failed to initialize central manager"; + qCWarning(QT_BT_OSX) << "failed to initialize a central manager"; return; } } - if (!connectSlots(notifier.data())) { + if (!connectSlots(notifier.data())) qCWarning(QT_BT_OSX) << "failed to connect to notifier's signal(s)"; - } + // Ownership was taken by central manager. notifier.take(); } -QLowEnergyControllerPrivateOSX::~QLowEnergyControllerPrivateOSX() +void QLowEnergyControllerPrivateDarwin::connectToDevice() { - if (const auto leQueue = OSXBluetooth::qt_LE_queue()) { - if (role == QLowEnergyController::CentralRole) { - const auto manager = centralManager.data(); - dispatch_sync(leQueue, ^{ - [manager detach]; + Q_ASSERT_X(state == QLowEnergyController::UnconnectedState, + Q_FUNC_INFO, "invalid state"); + + if (!isValid()) { + // init() had failed for was never called. + return _q_CBManagerError(QLowEnergyController::UnknownError); + } + + if (deviceUuid.isNull()) { + // Wrong constructor was used or invalid UUID was provided. + return _q_CBManagerError(QLowEnergyController::UnknownRemoteDeviceError); + } + + // The logic enforcing the role is in the public class. + Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, + Q_FUNC_INFO, "invalid role (peripheral)"); + + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + if (!leQueue) { + qCWarning(QT_BT_OSX) << "no LE queue found"; + setErrorDescription(QLowEnergyController::UnknownError); + return; + } + + setErrorDescription(QLowEnergyController::NoError); + setState(QLowEnergyController::ConnectingState); + + const QBluetoothUuid deviceUuidCopy(deviceUuid); + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_async(leQueue, ^{ + [manager connectToDevice:deviceUuidCopy]; + }); +} + +void QLowEnergyControllerPrivateDarwin::disconnectFromDevice() +{ + if (role == QLowEnergyController::PeripheralRole) { + // CoreBluetooth API intentionally does not provide any way of closing + // a connection. All we can do here is to stop the advertisement. + stopAdvertising(); + return; + } + + if (isValid()) { + const auto oldState = state; + + if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { + setState(QLowEnergyController::ClosingState); + invalidateServices(); + + auto manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_async(leQueue, ^{ + [manager disconnectFromDevice]; }); + + if (oldState == QLowEnergyController::ConnectingState) { + // With a pending connect attempt there is no + // guarantee we'll ever have didDisconnect callback, + // set the state here and now to make sure we still + // can connect. + setState(QLowEnergyController::UnconnectedState); + } } else { -#ifndef Q_OS_TVOS - const auto manager = peripheralManager.data(); - dispatch_sync(leQueue, ^{ - [manager detach]; - }); -#endif + qCCritical(QT_BT_OSX) << "qt LE queue is nil, " + "can not dispatch 'disconnect'"; } } } -bool QLowEnergyControllerPrivateOSX::isValid() const +void QLowEnergyControllerPrivateDarwin::discoverServices() +{ + Q_ASSERT_X(state != QLowEnergyController::UnconnectedState, + Q_FUNC_INFO, "not connected to peripheral"); + Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, + Q_FUNC_INFO, "invalid role (peripheral)"); + + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + Q_ASSERT_X(leQueue, Q_FUNC_INFO, "LE queue not found"); + + setState(QLowEnergyController::DiscoveringState); + + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_async(leQueue, ^{ + [manager discoverServices]; + }); +} + +void QLowEnergyControllerPrivateDarwin::discoverServiceDetails(const QBluetoothUuid &serviceUuid) +{ + if (state != QLowEnergyController::DiscoveredState) { + qCWarning(QT_BT_OSX) << "can not discover service details in the current state, " + "QLowEnergyController::DiscoveredState is expected"; + return; + } + + if (!serviceList.contains(serviceUuid)) { + qCWarning(QT_BT_OSX) << "unknown service: " << serviceUuid; + return; + } + + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + Q_ASSERT(leQueue); + + ServicePrivate qtService(serviceList.value(serviceUuid)); + qtService->setState(QLowEnergyService::DiscoveringServices); + // Copy objects ... + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); + const QBluetoothUuid serviceUuidCopy(serviceUuid); + dispatch_async(leQueue, ^{ + [manager discoverServiceDetails:serviceUuidCopy]; + }); +} + +void QLowEnergyControllerPrivateDarwin::requestConnectionUpdate(const QLowEnergyConnectionParameters ¶ms) +{ + Q_UNUSED(params); + // TODO: implement this, if possible. + qCWarning(QT_BT_OSX) << "Connection update not implemented on your platform"; +} + +void QLowEnergyControllerPrivateDarwin::addToGenericAttributeList(const QLowEnergyServiceData &service, + QLowEnergyHandle startHandle) +{ + Q_UNUSED(service); + Q_UNUSED(startHandle); + // TODO: check why I don't need this (apparently it is used in addServiceHelper + // of the base class). +} + +QLowEnergyService * QLowEnergyControllerPrivateDarwin::addServiceHelper(const QLowEnergyServiceData &service) { + // Three checks below should be removed, they are done in the q_ptr's class. #ifdef Q_OS_TVOS - return centralManager; + Q_UNUSED(service); + qCDebug(QT_BT_OSX, "peripheral role is not supported on tvOS"); #else - return centralManager || peripheralManager; -#endif + if (role != QLowEnergyController::PeripheralRole) { + qCWarning(QT_BT_OSX) << "not in peripheral role"; + return nullptr; + } + + if (state != QLowEnergyController::UnconnectedState) { + qCWarning(QT_BT_OSX) << "invalid state"; + return nullptr; + } + + if (!service.isValid()) { + qCWarning(QT_BT_OSX) << "invalid service"; + return nullptr; + } + + for (auto includedService : service.includedServices()) + includedService->d_ptr->type |= QLowEnergyService::IncludedService; + + const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); + Q_ASSERT(manager); + if (const auto servicePrivate = [manager addService:service]) { + servicePrivate->setController(this); + servicePrivate->state = QLowEnergyService::LocalService; + localServices.insert(servicePrivate->uuid, servicePrivate); + return new QLowEnergyService(servicePrivate); + } +#endif // Q_OS_TVOS + return nullptr; } -void QLowEnergyControllerPrivateOSX::_q_connected() +void QLowEnergyControllerPrivateDarwin::_q_connected() { - controllerState = QLowEnergyController::ConnectedState; - - emit q_ptr->stateChanged(QLowEnergyController::ConnectedState); + setState(QLowEnergyController::ConnectedState); emit q_ptr->connected(); } -void QLowEnergyControllerPrivateOSX::_q_disconnected() +void QLowEnergyControllerPrivateDarwin::_q_disconnected() { - controllerState = QLowEnergyController::UnconnectedState; - if (role == QLowEnergyController::CentralRole) invalidateServices(); - emit q_ptr->stateChanged(QLowEnergyController::UnconnectedState); + setState(QLowEnergyController::UnconnectedState); emit q_ptr->disconnected(); } -void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() +void QLowEnergyControllerPrivateDarwin::_q_serviceDiscoveryFinished() { - Q_ASSERT_X(controllerState == QLowEnergyController::DiscoveringState, + Q_ASSERT_X(state == QLowEnergyController::DiscoveringState, Q_FUNC_INFO, "invalid state"); using namespace OSXBluetooth; QT_BT_MAC_AUTORELEASEPOOL; - NSArray *const services = [centralManager.data() peripheral].services; + NSArray *const services = [centralManager.getAs<ObjCCentralManager>() peripheral].services; // Now we have to traverse the discovered services tree. // Essentially it's an iterative version of more complicated code from the // OSXBTCentralManager's code. @@ -249,13 +403,13 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() const ServicePrivate newService(qt_createLEService(this, cbService, false)); if (!newService.data()) continue; - if (discoveredServices.contains(newService->uuid)) { + if (serviceList.contains(newService->uuid)) { // It's a bit stupid we first created it ... qCDebug(QT_BT_OSX) << "discovered service with a duplicated UUID" << newService->uuid; continue; } - discoveredServices.insert(newService->uuid, newService); + serviceList.insert(newService->uuid, newService); discoveredCBServices.insert(newService->uuid, cbService); } @@ -273,8 +427,8 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() } const QBluetoothUuid uuid(qt_uuid(s.UUID)); - if (discoveredServices.contains(uuid) && discoveredCBServices.value(uuid) == s) { - ServicePrivate qtService(discoveredServices.value(uuid)); + if (serviceList.contains(uuid) && discoveredCBServices.value(uuid) == s) { + ServicePrivate qtService(serviceList.value(uuid)); // Add included UUIDs: qtService->includedServices.append(qt_servicesUuids(s.includedServices)); }// Else - we ignored this CBService object. @@ -286,15 +440,15 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() for (NSUInteger i = 0, e = [toVisitNext count]; i < e; ++i) { CBService *const s = [toVisitNext objectAtIndex:i]; const QBluetoothUuid uuid(qt_uuid(s.UUID)); - if (discoveredServices.contains(uuid)) { + if (serviceList.contains(uuid)) { if (discoveredCBServices.value(uuid) == s) { - ServicePrivate qtService(discoveredServices.value(uuid)); + ServicePrivate qtService(serviceList.value(uuid)); qtService->type |= QLowEnergyService::IncludedService; } // Else this is the duplicate we ignored already. } else { // Oh, we do not even have it yet??? ServicePrivate newService(qt_createLEService(this, s, true)); - discoveredServices.insert(newService->uuid, newService); + serviceList.insert(newService->uuid, newService); discoveredCBServices.insert(newService->uuid, s); } } @@ -306,31 +460,26 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() qCDebug(QT_BT_OSX) << "no services found"; } - for (ServiceMap::const_iterator it = discoveredServices.constBegin(); it != discoveredServices.constEnd(); ++it) { - const QBluetoothUuid &uuid = it.key(); - QMetaObject::invokeMethod(q_ptr, "serviceDiscovered", Qt::QueuedConnection, - Q_ARG(QBluetoothUuid, uuid)); - } + for (ServiceMap::const_iterator it = serviceList.constBegin(); it != serviceList.constEnd(); ++it) + emit q_ptr->serviceDiscovered(it.key()); - controllerState = QLowEnergyController::DiscoveredState; - QMetaObject::invokeMethod(q_ptr, "stateChanged", Qt::QueuedConnection, - Q_ARG(QLowEnergyController::ControllerState, controllerState)); - QMetaObject::invokeMethod(q_ptr, "discoveryFinished", Qt::QueuedConnection); + setState(QLowEnergyController::DiscoveredState); + emit q_ptr->discoveryFinished(); } -void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service) +void QLowEnergyControllerPrivateDarwin::_q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service) { QT_BT_MAC_AUTORELEASEPOOL; Q_ASSERT(service); - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid)) { qCDebug(QT_BT_OSX) << "unknown service uuid:" << service->uuid; return; } - ServicePrivate qtService(discoveredServices.value(service->uuid)); + ServicePrivate qtService(serviceList.value(service->uuid)); // Assert on handles? qtService->startHandle = service->startHandle; qtService->endHandle = service->endHandle; @@ -339,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 ¶ms, - const QLowEnergyAdvertisingData &advertisingData, - const QLowEnergyAdvertisingData &scanResponseData) +void QLowEnergyControllerPrivateDarwin::startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, + 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 ¶ms) -{ - 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 ¶ms) override; + void addToGenericAttributeList(const QLowEnergyServiceData &service, + QLowEnergyHandle startHandle) override; + + void startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, + 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/qlowenergycontrollerbase.cpp b/src/bluetooth/qlowenergycontrollerbase.cpp index 86108648..de72808e 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()) 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..84f48fbc 100644 --- a/src/bluetooth/qlowenergydescriptor.h +++ b/src/bluetooth/qlowenergydescriptor.h @@ -83,7 +83,7 @@ protected: friend class QLowEnergyControllerPrivateBluez; friend class QLowEnergyControllerPrivateBluezDBus; friend class QLowEnergyControllerPrivateCommon; - friend class QLowEnergyControllerPrivateOSX; + 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/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..d8c80291 100644 --- a/tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp +++ b/tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp @@ -447,21 +447,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() 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(); |