From 722fde4664b8b3f5f661e37d212c32ba0fb2bbad Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 26 Apr 2019 14:18:26 +0200 Subject: De-duplicate device discovery code (Darwin) - use the shared *._p.h file/class declaration for the private class - use the shared *.cpp with the public class implementation - get rid of *_ios.mm - iOS/tvOS/watchOS specific implementation and use the shared (by macOS, iOS, tvOS, watchOS) *_darwin.mm source. - get rid of somewhat weird 'isValid' in the private class, it is is not taken care of in the public API anyway and today its whole concept/usage looks (quite) buggy to me. I only have to check that the default controller is not nil and has the state 'ON'. Task-number: QTBUG-75348 Change-Id: I5383e4f8df02ac12f069c2f59e252cb8a200800b Reviewed-by: Timur Pocheptsov --- src/bluetooth/bluetooth.pro | 6 +- src/bluetooth/osx/btdelegates.cpp | 4 + src/bluetooth/osx/btdelegates_p.h | 16 +- src/bluetooth/osx/btraii_p.h | 5 + src/bluetooth/osx/osxbt.pri | 21 +- src/bluetooth/osx/osxbtdeviceinquiry.mm | 24 +- src/bluetooth/osx/osxbtdeviceinquiry_p.h | 24 +- .../qbluetoothdevicediscoveryagent_darwin.mm | 552 ++++++++++++++++ .../qbluetoothdevicediscoveryagent_ios.mm | 458 -------------- .../qbluetoothdevicediscoveryagent_osx.mm | 703 --------------------- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 63 +- 11 files changed, 648 insertions(+), 1228 deletions(-) create mode 100644 src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm delete mode 100644 src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm delete mode 100644 src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index 2bcdee12..71c8e6ed 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -162,7 +162,7 @@ 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 \ @@ -177,7 +177,6 @@ qtConfig(bluez) { qbluetoothtransferreply_osx_p.h \ qlowenergycontroller_osx_p.h - SOURCES -= qbluetoothdevicediscoveryagent.cpp SOURCES -= qbluetoothserviceinfo.cpp SOURCES -= qbluetoothservicediscoveryagent.cpp SOURCES -= qbluetoothsocket.cpp @@ -192,7 +191,7 @@ qtConfig(bluez) { LIBS_PRIVATE += -framework Foundation -framework CoreBluetooth OBJECTIVE_SOURCES += \ - qbluetoothdevicediscoveryagent_ios.mm \ + qbluetoothdevicediscoveryagent_darwin.mm \ qlowenergycontroller_osx.mm \ qlowenergyservice_osx.mm @@ -208,7 +207,6 @@ qtConfig(bluez) { qbluetoothsocket_dummy.cpp \ qbluetoothserver_p.cpp - SOURCES -= qbluetoothdevicediscoveryagent.cpp SOURCES -= qlowenergyservice.cpp SOURCES -= qlowenergycontroller.cpp SOURCES -= qlowenergycontrollerbase.cpp diff --git a/src/bluetooth/osx/btdelegates.cpp b/src/bluetooth/osx/btdelegates.cpp index c86516f9..531ca1df 100644 --- a/src/bluetooth/osx/btdelegates.cpp +++ b/src/bluetooth/osx/btdelegates.cpp @@ -39,6 +39,8 @@ #include "btdelegates_p.h" +#if defined(Q_OS_MACOS) + QT_BEGIN_NAMESPACE namespace DarwinBluetooth { @@ -70,3 +72,5 @@ SocketListener::~SocketListener() } // namespace DarwinBluetooth QT_END_NAMESPACE + +#endif diff --git a/src/bluetooth/osx/btdelegates_p.h b/src/bluetooth/osx/btdelegates_p.h index cb29b87e..11fbcc28 100644 --- a/src/bluetooth/osx/btdelegates_p.h +++ b/src/bluetooth/osx/btdelegates_p.h @@ -58,9 +58,9 @@ #include #include -#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) + #include -#endif #include @@ -72,8 +72,6 @@ class QByteArray; namespace DarwinBluetooth { -#if defined(Q_OS_MACOS) - class DeviceInquiryDelegate { public: @@ -81,7 +79,7 @@ public: virtual void inquiryFinished() = 0; virtual void error(IOReturn error) = 0; - virtual void deviceFound(void *ioBluetoothDevice) = 0; + virtual void classicDeviceFound(void *ioBluetoothDevice) = 0; }; class PairingDelegate @@ -141,15 +139,11 @@ public: virtual void openNotifyL2CAP(void *l2capChannel) = 0; }; -#else - -#error "This header is only for macOS (Bluetooth Classic only)" - -#endif // Q_OS_MACOS - } // namespace DarwinBluetooth QT_END_NAMESPACE +#endif // Q_OS_MACOS + #endif // DARWINBTDELEGATES_P_H diff --git a/src/bluetooth/osx/btraii_p.h b/src/bluetooth/osx/btraii_p.h index b64defb6..6053d63b 100644 --- a/src/bluetooth/osx/btraii_p.h +++ b/src/bluetooth/osx/btraii_p.h @@ -107,6 +107,11 @@ public: return static_cast(objCInstance); } + operator bool() const + { + return !!objCInstance; + } + private: void *objCInstance = nullptr; }; diff --git a/src/bluetooth/osx/osxbt.pri b/src/bluetooth/osx/osxbt.pri index ae6111ee..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) { @@ -21,9 +28,7 @@ CONFIG(osx) { osx/osxbluetooth_p.h \ osx/osxbtcentralmanager_p.h \ osx/osxbtnotifier_p.h \ - osx/osxbtperipheralmanager_p.h \ - osx/btraii_p.h \ - osx/btdelegates_p.h + osx/osxbtperipheralmanager_p.h OBJECTIVE_SOURCES += osx/osxbtutility.mm \ osx/osxbtdevicepair.mm \ @@ -38,9 +43,7 @@ CONFIG(osx) { osx/osxbtobexsession.mm \ osx/osxbtledeviceinquiry.mm \ osx/osxbtcentralmanager.mm \ - osx/osxbtperipheralmanager.mm \ - osx/btraii.mm - SOURCES += osx/btdelegates.cpp + osx/osxbtperipheralmanager.mm } else { PRIVATE_HEADERS += osx/osxbtutility_p.h \ osx/osxbtledeviceinquiry_p.h \ 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 #include -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 #include #include -@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 -- (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/qbluetoothdevicediscoveryagent_darwin.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm new file mode 100644 index 00000000..d9883d28 --- /dev/null +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm @@ -0,0 +1,552 @@ +/**************************************************************************** +** +** 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_p.h" +#include "qbluetoothdevicediscoveryagent.h" + +#include "osx/osxbtledeviceinquiry_p.h" +#ifdef Q_OS_MACOS +#include "osx/osxbtdeviceinquiry_p.h" +#include "osx/osxbtsdpinquiry_p.h" +#endif // Q_OS_MACOS +#include "qbluetoothdeviceinfo.h" +#include "osx/osxbtnotifier_p.h" +#include "osx/osxbtutility_p.h" +#include "osx/osxbluetooth_p.h" +#include "osx/uistrings_p.h" +#include "qbluetoothhostinfo.h" +#include "qbluetoothaddress.h" +#include "osx/uistrings_p.h" +#include "qbluetoothuuid.h" +#include "osx/btraii_p.h" + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace +{ + +void registerQDeviceDiscoveryMetaType() +{ + static bool initDone = false; + if (!initDone) { + qRegisterMetaType(); + qRegisterMetaType(); + initDone = true; + } +} +#ifdef Q_OS_MACOS +using InquiryObjC = QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry); +#endif // Q_OS_MACOS + +using LEInquiryObjC = QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry); + +} //namespace + +QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &adapter, + QBluetoothDeviceDiscoveryAgent *q) : + inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), + lastError(QBluetoothDeviceDiscoveryAgent::NoError), + agentState(NonActive), + adapterAddress(adapter), + startPending(false), + stopPending(false), + lowEnergySearchTimeout(OSXBluetooth::defaultLEScanTimeoutMS), +#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)"); + +#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; + } + controller.reset(hostController, DarwinBluetooth::RetainPolicy::doInitialRetain); +#endif +} + +QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() +{ + if (inquiryLE && agentState != NonActive) { + // We want the LE scan to stop as soon as possible. + if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { + // Local variable to be retained ... + LEInquiryObjC *inq = inquiryLE.getAs(); + dispatch_sync(leQueue, ^{ + [inq stop]; + }); + } + } +} + +bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const +{ + if (startPending) + return true; + + if (stopPending) + return false; + + return agentState != NonActive; +} + +void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods) +{ + 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) { + startPending = true; + return; + } + + // This function (re)starts the scan(s) from the scratch; + // starting from Classic if it's in 'methods' (or LE scan if not). + + 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(!isActive()); + Q_ASSERT(lastError == QBluetoothDeviceDiscoveryAgent::NoError); + Q_ASSERT(requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod); + Q_ASSERT(agentState == NonActive); + + OSXBluetooth::qt_test_iobluetooth_runloop(); + + if (!inquiry) { + // The first Classic scan for this DDA. + 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, + QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED)); + emit q_ptr->error(lastError); + return; + } + } + + agentState = ClassicScan; + + const IOReturn res = [inquiry.getAs() start]; + if (res != kIOReturnSuccess) { + setError(res, QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED)); + agentState = NonActive; + emit q_ptr->error(lastError); + } +} + +#endif // Q_OS_MACOS + +void QBluetoothDeviceDiscoveryAgentPrivate::startLE() +{ + Q_ASSERT(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError); + Q_ASSERT(requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); + + using namespace OSXBluetooth; + + QScopedPointer 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); + using DeviceMemFunPtr = void (QBluetoothDeviceDiscoveryAgentPrivate::*)(const QBluetoothDeviceInfo &); + notifier->connect(notifier.data(), &LECBManagerNotifier::deviceDiscovered, + this, DeviceMemFunPtr(&QBluetoothDeviceDiscoveryAgentPrivate::deviceFound)); + + // Check queue and create scanner: + inquiryLE.reset([[LEInquiryObjC alloc] initWithNotifier:notifier.data()], + DarwinBluetooth::RetainPolicy::noInitialRetain); + if (inquiryLE) + notifier.take(); // Whatever happens next, inquiryLE is already the owner ... + + dispatch_queue_t leQueue(qt_LE_queue()); + if (!leQueue || !inquiryLE) { + setError(QBluetoothDeviceDiscoveryAgent::UnknownError, + QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED_LE)); + agentState = NonActive; + emit q_ptr->error(lastError); + return; + } + + // Now start in on LE queue: + agentState = LEScan; + // We need the local variable so that it's retained ... + LEInquiryObjC *inq = inquiryLE.getAs(); + dispatch_async(leQueue, ^{ + [inq startWithTimeout:lowEnergySearchTimeout]; + }); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::stop() +{ + 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"); + + using namespace OSXBluetooth; + + const bool prevStart = startPending; + startPending = false; + stopPending = true; + + setError(QBluetoothDeviceDiscoveryAgent::NoError); + +#ifdef Q_OS_MACOS + if (agentState == ClassicScan) { + const IOReturn res = [inquiry.getAs() stop]; + if (res != kIOReturnSuccess) { + qCWarning(QT_BT_OSX) << "failed to stop"; + startPending = prevStart; + stopPending = false; + setError(res, QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STOPPED)); + 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 ... + LEInquiryObjC *inq = inquiryLE.getAs(); + dispatch_sync(leQueue, ^{ + [inq stop]; + }); + // We consider LE scan to be stopped immediately and + // do not care about this LEDeviceInquiry object anymore. + LEinquiryFinished(); + } +} + +#ifdef Q_OS_MACOS + +void QBluetoothDeviceDiscoveryAgentPrivate::inquiryFinished() +{ + // The subsequent start(LE) function (if any) + // will (re)set the correct state. + agentState = NonActive; + + if (stopPending && !startPending) { + stopPending = false; + emit q_ptr->canceled(); + } else if (startPending) { + startPending = false; + stopPending = false; + start(requestedMethods); + } else { + // We can be here _only_ if a classic scan + // finished in a normal way (not cancelled) + // and requestedMethods includes LowEnergyMethod. + // startLE() will take care of old devices + // not supporting Bluetooth 4.0. + if (requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) + startLE(); + else + emit q_ptr->finished(); + } +} + +void QBluetoothDeviceDiscoveryAgentPrivate::error(IOReturn error) +{ + startPending = false; + stopPending = false; + + setError(error); + + emit q_ptr->error(lastError); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::classicDeviceFound(void *obj) +{ + auto device = static_cast(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)"); + + QT_BT_MAC_AUTORELEASEPOOL; + + // Let's collect some info about this device: + const QBluetoothAddress deviceAddress(OSXBluetooth::qt_address([device getAddress])); + if (deviceAddress.isNull()) { + qCWarning(QT_BT_OSX) << "invalid Bluetooth address"; + return; + } + + QString deviceName; + if (device.name) + deviceName = QString::fromNSString(device.name); + + const auto classOfDevice = qint32(device.classOfDevice); + + QBluetoothDeviceInfo deviceInfo(deviceAddress, deviceName, classOfDevice); + deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration); + deviceInfo.setRssi(device.RSSI); + + const QVector uuids(OSXBluetooth::extract_services_uuids(device)); + deviceInfo.setServiceUuids(uuids); + + deviceFound(deviceInfo); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::setError(IOReturn error, const QString &text) +{ + if (error == kIOReturnSuccess) + setError(QBluetoothDeviceDiscoveryAgent::NoError, text); + else if (error == kIOReturnNoPower) + setError(QBluetoothDeviceDiscoveryAgent::PoweredOffError, text); + else + setError(QBluetoothDeviceDiscoveryAgent::UnknownError, text); +} + +#endif // Q_OS_MACOS + +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); + } + } +} + +void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error) +{ + Q_ASSERT(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError + || error == QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod); + + inquiryLE.reset(); + + startPending = false; + stopPending = false; + agentState = NonActive; + setError(error); + emit q_ptr->error(lastError); +} + +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(); + } else { + // In the past this was never an error, that's why we have + // LEnotSupported as a special method. But now, since + // we can have separate Classic/LE scans, we have to report it + // as UnsupportedDiscoveryMethod. + LEinquiryError(QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod); + } +#else + inquiryLE.reset(); + startPending = false; + stopPending = false; + setError(QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError); + emit q_ptr->error(lastError); +#endif +} + +void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() +{ + // The same logic as in inquiryFinished, but does not start LE scan. + agentState = NonActive; + inquiryLE.reset(); + + if (stopPending && !startPending) { + stopPending = false; + emit q_ptr->canceled(); + } else if (startPending) { + startPending = false; + stopPending = false; + start(requestedMethods); //Start again. + } else { + emit q_ptr->finished(); + } +} + +void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(const QBluetoothDeviceInfo &newDeviceInfo) +{ + // 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 = +#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()) { + 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 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; + } + } else { +#ifdef Q_OS_MACOS + if (discoveredDevices[i].address() == newDeviceInfo.address()) { + if (discoveredDevices[i] == newDeviceInfo) + return; + + discoveredDevices.replace(i, newDeviceInfo); + emit q_ptr->deviceDiscovered(newDeviceInfo); + return; + } +#else + Q_UNREACHABLE(); +#endif // Q_OS_MACOS + } + } + + discoveredDevices.append(newDeviceInfo); + emit q_ptr->deviceDiscovered(newDeviceInfo); +} + +QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() +{ +#ifdef Q_OS_MACOS + return ClassicMethod | LowEnergyMethod; +#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 -#include -#include -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -namespace -{ - -void registerQDeviceDiscoveryMetaType() -{ - static bool initDone = false; - if (!initDone) { - qRegisterMetaType(); - qRegisterMetaType(); - 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; - LEDeviceInquiry inquiryLE; - - using DevicesList = QList; - 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 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 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 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_osx.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm deleted file mode 100644 index f62ca0dd..00000000 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm +++ /dev/null @@ -1,703 +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 "osx/osxbtdeviceinquiry_p.h" -#include "qbluetoothlocaldevice.h" -#include "osx/osxbtsdpinquiry_p.h" -#include "qbluetoothdeviceinfo.h" -#include "osx/osxbtnotifier_p.h" -#include "osx/osxbtutility_p.h" -#include "osx/osxbluetooth_p.h" -#include "osx/uistrings_p.h" -#include "qbluetoothhostinfo.h" -#include "qbluetoothaddress.h" -#include "osx/uistrings_p.h" -#include "qbluetoothuuid.h" - -#include -#include -#include -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -namespace -{ - -void registerQDeviceDiscoveryMetaType() -{ - static bool initDone = false; - if (!initDone) { - qRegisterMetaType(); - qRegisterMetaType(); - initDone = true; - } -} - -}//namespace - -class QBluetoothDeviceDiscoveryAgentPrivate : public QObject, - public OSXBluetooth::DeviceInquiryDelegate -{ - friend class QBluetoothDeviceDiscoveryAgent; -public: - template - using ObjCScopedPointer = OSXBluetooth::ObjCScopedPointer; - 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; - - bool startPending; - bool stopPending; - - QBluetoothDeviceDiscoveryAgent::Error lastError; - QString errorString; - - QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType; - - using DeviceInquiry = ObjCScopedPointer; - DeviceInquiry inquiry; - - using LEDeviceInquiry = ObjCScopedPointer; - LEDeviceInquiry inquiryLE; - - using HostController = ObjCScopedPointer; - HostController hostController; - - using DevicesList = QList; - DevicesList discoveredDevices; - - int lowEnergySearchTimeout; - QBluetoothDeviceDiscoveryAgent::DiscoveryMethods requestedMethods; -}; - -QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &adapter, - QBluetoothDeviceDiscoveryAgent *q) : - q_ptr(q), - agentState(NonActive), - adapterAddress(adapter), - startPending(false), - stopPending(false), - lastError(QBluetoothDeviceDiscoveryAgent::NoError), - inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), - lowEnergySearchTimeout(OSXBluetooth::defaultLEScanTimeoutMS), - requestedMethods(QBluetoothDeviceDiscoveryAgent::ClassicMethod - | QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) -{ - registerQDeviceDiscoveryMetaType(); - - Q_ASSERT_X(q != nullptr, Q_FUNC_INFO, "invalid q_ptr (null)"); - - HostController controller([[IOBluetoothHostController defaultController] retain]); - if (!controller || [controller powerState] != kBluetoothHCIPowerStateON) { - qCCritical(QT_BT_OSX) << "no default host controller or adapter is off"; - return; - } - - hostController.reset(controller.take()); -} - -QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() -{ - if (inquiryLE && agentState != NonActive) { - // We want the LE scan to stop as soon as possible. - if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { - // Local variable to be retained ... - LEDeviceInquiryObjC *inq = inquiryLE.data(); - dispatch_sync(leQueue, ^{ - [inq stop]; - }); - } - } -} - -bool QBluetoothDeviceDiscoveryAgentPrivate::isValid() const -{ - return hostController && [hostController powerState] == kBluetoothHCIPowerStateON; -} - -bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const -{ - if (startPending) - return true; - - if (stopPending) - return false; - - return agentState != NonActive; -} - -void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods) -{ - Q_ASSERT(isValid()); - Q_ASSERT(!isActive()); - Q_ASSERT(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError); - Q_ASSERT(methods & (QBluetoothDeviceDiscoveryAgent::ClassicMethod - | QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)); - - requestedMethods = methods; - - if (stopPending) { - startPending = true; - return; - } - - // This function (re)starts the scan(s) from the scratch; - // starting from Classic if it's in 'methods' (or LE scan if not). - - agentState = NonActive; - discoveredDevices.clear(); - setError(QBluetoothDeviceDiscoveryAgent::NoError); - - if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod) - return startClassic(); - - startLE(); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::startClassic() -{ - Q_ASSERT(isValid()); - Q_ASSERT(!isActive()); - Q_ASSERT(lastError == QBluetoothDeviceDiscoveryAgent::NoError); - Q_ASSERT(requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod); - Q_ASSERT(agentState == NonActive); - - OSXBluetooth::qt_test_iobluetooth_runloop(); - - if (!inquiry) { - // The first Classic scan for this DDA. - inquiry.reset([[DeviceInquiryObjC alloc]initWithDelegate:this]); - if (!inquiry) { - qCCritical(QT_BT_OSX) << "failed to initialize an Classic device inquiry"; - setError(QBluetoothDeviceDiscoveryAgent::UnknownError, - QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED)); - emit q_ptr->error(lastError); - return; - } - } - - agentState = ClassicScan; - - const IOReturn res = [inquiry start]; - if (res != kIOReturnSuccess) { - setError(res, QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED)); - agentState = NonActive; - emit q_ptr->error(lastError); - } -} - -void QBluetoothDeviceDiscoveryAgentPrivate::startLE() -{ - Q_ASSERT(isValid()); - Q_ASSERT(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError); - Q_ASSERT(requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); - - using namespace OSXBluetooth; - - QScopedPointer 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); - using DeviceMemFunPtr = void (QBluetoothDeviceDiscoveryAgentPrivate::*)(const QBluetoothDeviceInfo &); - notifier->connect(notifier.data(), &LECBManagerNotifier::deviceDiscovered, - this, DeviceMemFunPtr(&QBluetoothDeviceDiscoveryAgentPrivate::deviceFound)); - - // Check queue and create scanner: - 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_LE)); - agentState = NonActive; - emit q_ptr->error(lastError); - return; - } - - // Now start in on LE queue: - agentState = LEScan; - // We need the local variable so that it's retained ... - LEDeviceInquiryObjC *inq = inquiryLE.data(); - dispatch_async(leQueue, ^{ - [inq startWithTimeout:lowEnergySearchTimeout]; - }); -} - -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"); - - using namespace OSXBluetooth; - - const bool prevStart = startPending; - startPending = false; - stopPending = true; - - setError(QBluetoothDeviceDiscoveryAgent::NoError); - - if (agentState == ClassicScan) { - const IOReturn res = [inquiry stop]; - if (res != kIOReturnSuccess) { - qCWarning(QT_BT_OSX) << "failed to stop"; - startPending = prevStart; - stopPending = false; - setError(res, QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STOPPED)); - emit q_ptr->error(lastError); - } - } else { - dispatch_queue_t leQueue(qt_LE_queue()); - Q_ASSERT(leQueue); - // We need the local variable so that it's retained ... - 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::inquiryFinished(IOBluetoothDeviceInquiry *inq) -{ - Q_UNUSED(inq) - - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid device discovery agent"); //We can never be here. - - // The subsequent start(LE) function (if any) - // will (re)set the correct state. - agentState = NonActive; - - if (stopPending && !startPending) { - stopPending = false; - emit q_ptr->canceled(); - } else if (startPending) { - startPending = false; - stopPending = false; - start(requestedMethods); - } else { - // We can be here _only_ if a classic scan - // finished in a normal way (not cancelled) - // and requestedMethods includes LowEnergyMethod. - // startLE() will take care of old devices - // not supporting Bluetooth 4.0. - if (requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) - startLE(); - else - emit q_ptr->finished(); - } -} - -void QBluetoothDeviceDiscoveryAgentPrivate::error(IOBluetoothDeviceInquiry *inq, IOReturn error) -{ - Q_UNUSED(inq) - - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid device discovery agent"); - - startPending = false; - stopPending = false; - - setError(error); - - emit q_ptr->error(lastError); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device) -{ - Q_UNUSED(inq) - - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid device discovery agent"); - Q_ASSERT_X(device, Q_FUNC_INFO, "invalid IOBluetoothDevice (nil)"); - Q_ASSERT_X(agentState == ClassicScan, Q_FUNC_INFO, - "invalid agent state (expected classic scan)"); - - QT_BT_MAC_AUTORELEASEPOOL; - - // Let's collect some info about this device: - const QBluetoothAddress deviceAddress(OSXBluetooth::qt_address([device getAddress])); - if (deviceAddress.isNull()) { - qCWarning(QT_BT_OSX) << "invalid Bluetooth address"; - return; - } - - QString deviceName; - if (device.name) - deviceName = QString::fromNSString(device.name); - - const qint32 classOfDevice(device.classOfDevice); - - QBluetoothDeviceInfo deviceInfo(deviceAddress, deviceName, classOfDevice); - deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration); - deviceInfo.setRssi(device.RSSI); - - const QVector uuids(OSXBluetooth::extract_services_uuids(device)); - deviceInfo.setServiceUuids(uuids); - - deviceFound(deviceInfo); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::setError(IOReturn error, const QString &text) -{ - if (error == kIOReturnSuccess) - setError(QBluetoothDeviceDiscoveryAgent::NoError, text); - else if (error == kIOReturnNoPower) - setError(QBluetoothDeviceDiscoveryAgent::PoweredOffError, text); - else - setError(QBluetoothDeviceDiscoveryAgent::UnknownError, text); -} - -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::UnknownError: - default: - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_UNKNOWN_ERROR); - } - } - - if (lastError != QBluetoothDeviceDiscoveryAgent::NoError) - qCDebug(QT_BT_OSX) << "error set:"<error(lastError); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported() -{ - qCDebug(QT_BT_OSX) << "no Bluetooth LE support"; - - if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod) { - // Having both Classic | LE means this is not an error. - LEinquiryFinished(); - } else { - // In the past this was never an error, that's why we have - // LEnotSupported as a special method. But now, since - // we can have separate Classic/LE scans, we have to report it - // as UnsupportedDiscoveryMethod. - LEinquiryError(QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod); - } -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() -{ - // The same logic as in inquiryFinished, but does not start LE scan. - agentState = NonActive; - inquiryLE.reset(); - - if (stopPending && !startPending) { - stopPending = false; - emit q_ptr->canceled(); - } else if (startPending) { - startPending = false; - stopPending = false; - start(requestedMethods); //Start again. - } else { - emit q_ptr->finished(); - } -} - -void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(const QBluetoothDeviceInfo &newDeviceInfo) -{ - // 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; - - for (int i = 0, e = discoveredDevices.size(); i < e; ++i) { - if (isLE) { - 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 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; - } - } else { - if (discoveredDevices[i].address() == newDeviceInfo.address()) { - if (discoveredDevices[i] == newDeviceInfo) - return; - - discoveredDevices.replace(i, newDeviceInfo); - emit q_ptr->deviceDiscovered(newDeviceInfo); - return; - } - } - } - - discoveredDevices.append(newDeviceInfo); - 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 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 QBluetoothDeviceDiscoveryAgent::discoveredDevices() const -{ - return d_ptr->discoveredDevices; -} - -QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() -{ - 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; -} - -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 #endif +#ifdef Q_OS_DARWIN +#include "osx/btdelegates_p.h" +#include "osx/btraii_p.h" +#endif // Q_OS_DARWIN + #include #include @@ -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; -- cgit v1.2.3