diff options
Diffstat (limited to 'src/bluetooth')
50 files changed, 1645 insertions, 3449 deletions
diff --git a/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp b/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp index 99245af3..f1f50516 100644 --- a/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp +++ b/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp @@ -516,7 +516,7 @@ QBluetoothDeviceInfo DeviceDiscoveryBroadcastReceiver::retrieveDeviceInfo(JNIEnv const char *scanRecordBuffer = reinterpret_cast<const char *>(elems); const int scanRecordLength = env->GetArrayLength(scanRecord); - QList<QBluetoothUuid> serviceUuids; + QVector<QBluetoothUuid> serviceUuids; int i = 0; // Spec 4.2, Vol 3, Part C, Chapter 11 @@ -567,7 +567,7 @@ QBluetoothDeviceInfo DeviceDiscoveryBroadcastReceiver::retrieveDeviceInfo(JNIEnv serviceUuids.append(foundService); } - info.setServiceUuids(serviceUuids, QBluetoothDeviceInfo::DataIncomplete); + info.setServiceUuids(serviceUuids); env->ReleaseByteArrayElements(scanRecord, elems, JNI_ABORT); } diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index b104a902..4e7088aa 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -162,42 +162,27 @@ qtConfig(bluez) { include(osx/osxbt.pri) OBJECTIVE_SOURCES += \ qbluetoothlocaldevice_osx.mm \ - qbluetoothdevicediscoveryagent_osx.mm \ + qbluetoothdevicediscoveryagent_darwin.mm \ qbluetoothserviceinfo_osx.mm \ qbluetoothservicediscoveryagent_osx.mm \ qbluetoothsocket_osx.mm \ qbluetoothserver_osx.mm \ qbluetoothtransferreply_osx.mm \ - qlowenergycontroller_osx.mm \ - qlowenergyservice_osx.mm + qlowenergycontroller_darwin.mm PRIVATE_HEADERS += qbluetoothsocket_osx_p.h \ - qbluetoothserver_osx_p.h \ qbluetoothtransferreply_osx_p.h \ - qbluetoothtransferreply_osx_p.h \ - qlowenergycontroller_osx_p.h - - SOURCES -= qbluetoothdevicediscoveryagent.cpp - SOURCES -= qbluetoothserviceinfo.cpp - SOURCES -= qbluetoothservicediscoveryagent.cpp - SOURCES -= qbluetoothsocket.cpp - SOURCES -= qbluetoothsocketbase.cpp - SOURCES -= qbluetoothserver.cpp - SOURCES -= qlowenergyservice_p.cpp - SOURCES -= qlowenergyservice.cpp - SOURCES -= qlowenergycontroller.cpp - SOURCES -= qlowenergycontrollerbase.cpp + qlowenergycontroller_darwin_p.h } else:ios|tvos { DEFINES += QT_IOS_BLUETOOTH LIBS_PRIVATE += -framework Foundation -framework CoreBluetooth OBJECTIVE_SOURCES += \ - qbluetoothdevicediscoveryagent_ios.mm \ - qlowenergycontroller_osx.mm \ - qlowenergyservice_osx.mm + qbluetoothdevicediscoveryagent_darwin.mm \ + qlowenergycontroller_darwin.mm PRIVATE_HEADERS += \ - qlowenergycontroller_osx_p.h \ + qlowenergycontroller_darwin_p.h \ qbluetoothsocket_dummy_p.h include(osx/osxbt.pri) @@ -207,11 +192,6 @@ qtConfig(bluez) { qbluetoothservicediscoveryagent_p.cpp \ qbluetoothsocket_dummy.cpp \ qbluetoothserver_p.cpp - - SOURCES -= qbluetoothdevicediscoveryagent.cpp - SOURCES -= qlowenergyservice.cpp - SOURCES -= qlowenergycontroller.cpp - SOURCES -= qlowenergycontrollerbase.cpp } else: qtConfig(winrt_bt) { DEFINES += QT_WINRT_BLUETOOTH !winrt { diff --git a/src/bluetooth/osx/osxbtchanneldelegate.mm b/src/bluetooth/osx/btdelegates.cpp index 822e9d4e..531ca1df 100644 --- a/src/bluetooth/osx/osxbtchanneldelegate.mm +++ b/src/bluetooth/osx/btdelegates.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -37,16 +37,40 @@ ** ****************************************************************************/ -#include "osxbtchanneldelegate_p.h" +#include "btdelegates_p.h" + +#if defined(Q_OS_MACOS) QT_BEGIN_NAMESPACE -namespace OSXBluetooth { +namespace DarwinBluetooth { + +DeviceInquiryDelegate::~DeviceInquiryDelegate() +{ +} + +PairingDelegate::~PairingDelegate() +{ +} + +SDPInquiryDelegate::~SDPInquiryDelegate() +{ +} ChannelDelegate::~ChannelDelegate() { } +ConnectionMonitor::~ConnectionMonitor() +{ +} + +SocketListener::~SocketListener() +{ } +} // namespace DarwinBluetooth + QT_END_NAMESPACE + +#endif diff --git a/src/bluetooth/osx/btdelegates_p.h b/src/bluetooth/osx/btdelegates_p.h new file mode 100644 index 00000000..11fbcc28 --- /dev/null +++ b/src/bluetooth/osx/btdelegates_p.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BTDELEGATES_P_H +#define BTDELEGATES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qbluetoothdevicediscoveryagent.h" +#include "qlowenergycontroller.h" +#include "qbluetooth.h" + +#include <QtCore/qsharedpointer.h> +#include <QtCore/qglobal.h> + +#if defined(Q_OS_MACOS) + +#include <IOKit/IOReturn.h> + +#include <cstdint> + +QT_BEGIN_NAMESPACE + +class QLowEnergyServicePrivate; +class QBluetoothAddress; +class QByteArray; + +namespace DarwinBluetooth { + +class DeviceInquiryDelegate +{ +public: + virtual ~DeviceInquiryDelegate(); + + virtual void inquiryFinished() = 0; + virtual void error(IOReturn error) = 0; + virtual void classicDeviceFound(void *ioBluetoothDevice) = 0; +}; + +class PairingDelegate +{ +public: + using BluetoothNumericValue = uint32_t; + using BluetoothPasskey = BluetoothNumericValue; + + virtual ~PairingDelegate(); + + virtual void connecting(void *pair) = 0; + virtual void requestPIN(void *pair) = 0; + virtual void requestUserConfirmation(void *pair, + BluetoothNumericValue) = 0; + virtual void passkeyNotification(void *pair, + BluetoothPasskey passkey) = 0; + virtual void error(void *pair, IOReturn errorCode) = 0; + virtual void pairingFinished(void *pair) = 0; +}; + +class SDPInquiryDelegate { +public: + virtual ~SDPInquiryDelegate(); + + virtual void SDPInquiryFinished(void *ioBluetoothDevice) = 0; + virtual void SDPInquiryError(void *ioBluetoothDevice, IOReturn errorCode) = 0; +}; + +// L2CAP and RFCOMM. +class ChannelDelegate +{ +public: + virtual ~ChannelDelegate(); + + virtual void setChannelError(IOReturn errorCode) = 0; + virtual void channelOpenComplete() = 0; + virtual void channelClosed() = 0; + + virtual void readChannelData(void *data, std::size_t size) = 0; + virtual void writeComplete() = 0; +}; + +class ConnectionMonitor { +public: + virtual ~ConnectionMonitor(); + + virtual void deviceConnected(const QBluetoothAddress &address) = 0; + virtual void deviceDisconnected(const QBluetoothAddress &address) = 0; +}; + +class SocketListener +{ +public: + virtual ~SocketListener(); + + virtual void openNotifyRFCOMM(void *rfcommChannel) = 0; + virtual void openNotifyL2CAP(void *l2capChannel) = 0; +}; + + +} // namespace DarwinBluetooth + +QT_END_NAMESPACE + +#endif // Q_OS_MACOS + +#endif // DARWINBTDELEGATES_P_H diff --git a/src/bluetooth/osx/osxbtchanneldelegate_p.h b/src/bluetooth/osx/btraii.mm index 1102e935..a1bf2a8d 100644 --- a/src/bluetooth/osx/osxbtchanneldelegate_p.h +++ b/src/bluetooth/osx/btraii.mm @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -37,43 +37,74 @@ ** ****************************************************************************/ -#ifndef OSXBTCHANNELDELEGATE_P_H -#define OSXBTCHANNELDELEGATE_P_H +#include "btraii_p.h" -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// +#include <qdebug.h> -#include <QtCore/qglobal.h> +#include <Foundation/Foundation.h> -#include <IOKit/IOReturn.h> +#include <utility> QT_BEGIN_NAMESPACE -namespace OSXBluetooth { +namespace DarwinBluetooth { -class ChannelDelegate +StrongReference::StrongReference(void *object, RetainPolicy policy) + : objCInstance(object) { -public: - virtual ~ChannelDelegate(); + if (policy == RetainPolicy::doInitialRetain) + objCInstance = [getAs<NSObject>() retain]; +} - virtual void setChannelError(IOReturn errorCode) = 0; - virtual void channelOpenComplete() = 0; - virtual void channelClosed() = 0; +StrongReference::StrongReference(const StrongReference &other) +{ + objCInstance = [other.getAs<NSObject>() retain]; +} - virtual void readChannelData(void *data, std::size_t size) = 0; - virtual void writeComplete() = 0; -}; +StrongReference::StrongReference(StrongReference &&other) +{ + std::swap(objCInstance, other.objCInstance); +} +StrongReference::~StrongReference() +{ + [getAs<NSObject>() release]; } -QT_END_NAMESPACE +StrongReference &StrongReference::operator = (const StrongReference &other) noexcept +{ + if (this != &other) { + [getAs<NSObject>() release]; + objCInstance = [other.getAs<NSObject>() retain]; + } + + return *this; +} + +StrongReference &StrongReference::operator = (StrongReference &&other) noexcept +{ + swap(other); + return *this; +} + +void StrongReference::reset() +{ + [getAs<NSObject>() release]; + objCInstance = nullptr; +} -#endif +void StrongReference::reset(void *obj, RetainPolicy policy) +{ + [getAs<NSObject>() release]; + objCInstance = obj; + + if (policy == RetainPolicy::doInitialRetain) { + auto newInstance = static_cast<NSObject *>(obj); + Q_ASSERT(newInstance); + objCInstance = [newInstance retain]; + } +} + +} // namespace DarwinBluetooth + +QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothserver_osx_p.h b/src/bluetooth/osx/btraii_p.h index 3116ca02..6053d63b 100644 --- a/src/bluetooth/qbluetoothserver_osx_p.h +++ b/src/bluetooth/osx/btraii_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QBLUETOOTHSERVER_OSX_P_H -#define QBLUETOOTHSERVER_OSX_P_H +#ifndef BTRAII_P_H +#define BTRAII_P_H // // W A R N I N G @@ -51,77 +51,86 @@ // We mean it. // -#ifdef QT_OSX_BLUETOOTH - -#include "osx/osxbtsocketlistener_p.h" -#include "qbluetoothserviceinfo.h" -#include "osx/osxbtutility_p.h" -#include "qbluetoothserver.h" - #include <QtCore/qglobal.h> -#include <QtCore/qlist.h> + +#include <utility> QT_BEGIN_NAMESPACE -class QMutex; +namespace DarwinBluetooth { -class QBluetoothServerPrivate : public OSXBluetooth::SocketListener +enum class RetainPolicy { - friend class QBluetoothServer; - friend class QBluetoothServiceInfoPrivate; + noInitialRetain, + doInitialRetain +}; +// The class StrongReference and its descendant ScopedGuard +// are RAII classes dealing with raw pointers to NSObject class +// and its descendants (and thus hiding Objective-C's retain/ +// release semantics). The header itself is meant to be included +// into *.cpp files so it's a pure C++ code without any Objective-C +// syntax. Thus it's a bit clunky - the type information is 'erased' +// and has to be enforced by the code using these smart pointers. +// That's because these types are Objective-C classes - thus require +// Objective-C compiler to work. Member-function template 'getAs' is +// a convenience shortcut giving the desired pointer type in +// Objective-C++ files (*.mm). + +// TODO: on top of these classes I can build ObjCStrongReference (it's +// now inside osxbtutils_p.h, a template class that does have type +// information needed but works only in Objective-C++ environment. +class StrongReference +{ public: - QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol type, QBluetoothServer *q); - ~QBluetoothServerPrivate(); - - void _q_newConnection(); -private: - bool startListener(quint16 realPort); - void stopListener(); - - // SocketListener (delegate): - void openNotify(IOBluetoothRFCOMMChannel *channel) override; - void openNotify(IOBluetoothL2CAPChannel *channel) override; + StrongReference() = default; + StrongReference(void *object, RetainPolicy policy); + StrongReference(const StrongReference &other); + StrongReference(StrongReference &&other); - QBluetoothServiceInfo::Protocol serverType; - QBluetoothServer *q_ptr; - QBluetoothServer::Error lastError; + ~StrongReference(); - // Either a "temporary" channelID/PSM assigned by QBluetoothServer::listen, - // or a real channelID/PSM returned by IOBluetooth after we've registered - // a service. - quint16 port; + StrongReference &operator = (const StrongReference &other) noexcept; + StrongReference &operator = (StrongReference &&other) noexcept; - typedef OSXBluetooth::ObjCScopedPointer<ObjCListener> Listener; - Listener listener; + void swap(StrongReference &other) noexcept + { + std::swap(objCInstance, other.objCInstance); + } - int maxPendingConnections; + void reset(); + void reset(void *newInstance, RetainPolicy policy); - // These static functions below - // deal with differences between bluetooth sockets - // (bluez and QtBluetooth's API) and IOBluetooth, where it's not possible - // to have a real PSM/channelID _before_ a service is registered, - // the solution - "fake" ports. - // These functions require external locking - using channelMapMutex. - static QMutex &channelMapMutex(); + template<class ObjCType> + ObjCType *getAs() const + { + return static_cast<ObjCType *>(objCInstance); + } - static bool channelIsBusy(quint16 channelID); - static quint16 findFreeChannel(); + operator bool() const + { + return !!objCInstance; + } - static bool psmIsBusy(quint16 psm); - static quint16 findFreePSM(); - - static void registerServer(QBluetoothServerPrivate *server, quint16 port); - static QBluetoothServerPrivate *registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol); - static void unregisterServer(QBluetoothServerPrivate *server); +private: + void *objCInstance = nullptr; +}; - typedef OSXBluetooth::ObjCStrongReference<NSObject> PendingConnection; - QList<PendingConnection> pendingConnections; +class ScopedPointer final : public StrongReference +{ +public: + ScopedPointer() = default; + ScopedPointer(void *instance, RetainPolicy policy) + : StrongReference(instance, policy) + { + } +private: + Q_DISABLE_COPY_MOVE(ScopedPointer) }; -QT_END_NAMESPACE +} // namespace DarwinBluetooth -#endif //QT_OSX_BLUETOOTH +QT_END_NAMESPACE -#endif +#endif // BTRAII_P_H diff --git a/src/bluetooth/osx/osxbt.pri b/src/bluetooth/osx/osxbt.pri index b7ac0535..8f6ea0d1 100644 --- a/src/bluetooth/osx/osxbt.pri +++ b/src/bluetooth/osx/osxbt.pri @@ -1,8 +1,15 @@ -SOURCES += osx/uistrings.cpp osx/osxbtnotifier.cpp +SOURCES += osx/uistrings.cpp \ + osx/osxbtnotifier.cpp \ + osx/btdelegates.cpp + PRIVATE_HEADERS += osx/uistrings_p.h \ - osx/osxbtgcdtimer_p.h + osx/osxbtgcdtimer_p.h \ + osx/btraii_p.h \ + osx/btdelegates_p.h + -OBJECTIVE_SOURCES += osx/osxbtgcdtimer.mm +OBJECTIVE_SOURCES += osx/osxbtgcdtimer.mm \ + osx/btraii.mm #QMAKE_CXXFLAGS_WARN_ON += -Wno-nullability-completeness CONFIG(osx) { @@ -13,7 +20,6 @@ CONFIG(osx) { osx/osxbtsdpinquiry_p.h \ osx/osxbtrfcommchannel_p.h \ osx/osxbtl2capchannel_p.h \ - osx/osxbtchanneldelegate_p.h \ osx/osxbtservicerecord_p.h \ osx/osxbtsocketlistener_p.h \ osx/osxbtobexsession_p.h \ @@ -30,7 +36,6 @@ CONFIG(osx) { osx/osxbtsdpinquiry.mm \ osx/osxbtrfcommchannel.mm \ osx/osxbtl2capchannel.mm \ - osx/osxbtchanneldelegate.mm \ osx/osxbtservicerecord.mm \ osx/osxbtsocketlistener.mm \ osx/osxbtobexsession.mm \ diff --git a/src/bluetooth/osx/osxbtcentralmanager.mm b/src/bluetooth/osx/osxbtcentralmanager.mm index 6b742684..b9a1ae0f 100644 --- a/src/bluetooth/osx/osxbtcentralmanager.mm +++ b/src/bluetooth/osx/osxbtcentralmanager.mm @@ -39,6 +39,7 @@ #include "qlowenergyserviceprivate_p.h" #include "qlowenergycharacteristic.h" +#include "qlowenergycontroller.h" #include "osxbtcentralmanager_p.h" #include "osxbtnotifier_p.h" @@ -132,6 +133,7 @@ QT_USE_NAMESPACE - (CBDescriptor *)descriptor:(const QBluetoothUuid &)dUuid forCharacteristic:(CBCharacteristic *)ch; - (bool)cacheWriteValue:(const QByteArray &)value for:(NSObject *)obj; +- (void)handleReadWriteError:(NSError *)error; - (void)reset; @end @@ -1202,6 +1204,21 @@ QT_USE_NAMESPACE // TODO: also serviceToVisit/VisitNext and visitedServices ? } +- (void)handleReadWriteError:(NSError *)error +{ + Q_ASSERT(notifier); + + switch (error.code) { + case 0x05: // GATT_INSUFFICIENT_AUTHORIZATION + case 0x0F: // GATT_INSUFFICIENT_ENCRYPTION + emit notifier->CBManagerError(QLowEnergyController::AuthorizationError); + [self detach]; + break; + default: + break; + } +} + // CBCentralManagerDelegate (the real one). - (void)centralManagerDidUpdateState:(CBCentralManager *)central @@ -1540,6 +1557,7 @@ QT_USE_NAMESPACE currentReadHandle = 0; requestPending = false; emit notifier->CBManagerError(qtUuid, QLowEnergyService::CharacteristicReadError); + [self handleReadWriteError:error]; [self performNextRequest]; } return; @@ -1658,6 +1676,7 @@ QT_USE_NAMESPACE currentReadHandle = 0; requestPending = false; emit notifier->CBManagerError(qtUuid, QLowEnergyService::DescriptorReadError); + [self handleReadWriteError:error]; [self performNextRequest]; } return; @@ -1747,6 +1766,7 @@ QT_USE_NAMESPACE NSLog(@"%s failed with error %@", Q_FUNC_INFO, error); emit notifier->CBManagerError(qt_uuid(characteristic.service.UUID), QLowEnergyService::CharacteristicWriteError); + [self handleReadWriteError:error]; } else { const QLowEnergyHandle cHandle = charMap.key(characteristic); emit notifier->characteristicWritten(cHandle, valueToReport); @@ -1781,6 +1801,7 @@ QT_USE_NAMESPACE NSLog(@"%s failed with error %@", Q_FUNC_INFO, error); emit notifier->CBManagerError(qt_uuid(descriptor.characteristic.service.UUID), QLowEnergyService::DescriptorWriteError); + [self handleReadWriteError:error]; } else { const QLowEnergyHandle dHandle = descMap.key(descriptor); Q_ASSERT_X(dHandle, Q_FUNC_INFO, "descriptor not found in the descriptors map"); diff --git a/src/bluetooth/osx/osxbtdeviceinquiry.mm b/src/bluetooth/osx/osxbtdeviceinquiry.mm index 57cd73e1..3a77c1f7 100644 --- a/src/bluetooth/osx/osxbtdeviceinquiry.mm +++ b/src/bluetooth/osx/osxbtdeviceinquiry.mm @@ -43,30 +43,16 @@ #include <QtCore/qloggingcategory.h> #include <QtCore/qdebug.h> -QT_BEGIN_NAMESPACE - -namespace OSXBluetooth { - -DeviceInquiryDelegate::~DeviceInquiryDelegate() -{ -} - -} - - -QT_END_NAMESPACE - QT_USE_NAMESPACE - @implementation QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry) { IOBluetoothDeviceInquiry *m_inquiry; bool m_active; - QT_PREPEND_NAMESPACE(OSXBluetooth::DeviceInquiryDelegate) *m_delegate;//C++ "delegate" + DarwinBluetooth::DeviceInquiryDelegate *m_delegate;//C++ "delegate" } -- (id)initWithDelegate:(OSXBluetooth::DeviceInquiryDelegate *)delegate +- (id)initWithDelegate:(DarwinBluetooth::DeviceInquiryDelegate *)delegate { if (self = [super init]) { Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid device inquiry delegate (null)"); @@ -158,9 +144,9 @@ QT_USE_NAMESPACE // QtBluetooth has not too many error codes, 'UnknownError' is not really // useful, report the actual error code here: qCWarning(QT_BT_OSX) << "IOKit error code: " << error; - m_delegate->error(sender, error); + m_delegate->error(error); } else { - m_delegate->inquiryFinished(sender); + m_delegate->inquiryFinished(); } } @@ -171,7 +157,7 @@ QT_USE_NAMESPACE return; Q_ASSERT_X(m_delegate, Q_FUNC_INFO, "invalid device inquiry delegate (null)"); - m_delegate->deviceFound(sender, device); + m_delegate->classicDeviceFound(device); } - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry *)sender diff --git a/src/bluetooth/osx/osxbtdeviceinquiry_p.h b/src/bluetooth/osx/osxbtdeviceinquiry_p.h index 0fec2db2..86ed3fdf 100644 --- a/src/bluetooth/osx/osxbtdeviceinquiry_p.h +++ b/src/bluetooth/osx/osxbtdeviceinquiry_p.h @@ -52,36 +52,16 @@ // #include "osxbluetooth_p.h" +#include "btdelegates_p.h" #include <QtCore/qglobal.h> #include <Foundation/Foundation.h> #include <IOKit/IOReturn.h> -@class QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry); - -QT_BEGIN_NAMESPACE - -namespace OSXBluetooth { - -class DeviceInquiryDelegate { -public: - typedef QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry) DeviceInquiryObjC; - - virtual ~DeviceInquiryDelegate(); - - virtual void inquiryFinished(IOBluetoothDeviceInquiry *inq) = 0; - virtual void error(IOBluetoothDeviceInquiry *inq, IOReturn error) = 0; - virtual void deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device) = 0; -}; - -} - -QT_END_NAMESPACE - @interface QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry) : NSObject<IOBluetoothDeviceInquiryDelegate> -- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth::DeviceInquiryDelegate) *)delegate; +- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth::DeviceInquiryDelegate) *)delegate; - (void)dealloc; - (bool)isActive; diff --git a/src/bluetooth/osx/osxbtl2capchannel.mm b/src/bluetooth/osx/osxbtl2capchannel.mm index dc8468a0..03e3a982 100644 --- a/src/bluetooth/osx/osxbtl2capchannel.mm +++ b/src/bluetooth/osx/osxbtl2capchannel.mm @@ -37,10 +37,10 @@ ** ****************************************************************************/ -#include "osxbtchanneldelegate_p.h" #include "osxbtl2capchannel_p.h" #include "qbluetoothaddress.h" #include "osxbtutility_p.h" +#include "btdelegates_p.h" #include <QtCore/qloggingcategory.h> #include <QtCore/qdebug.h> @@ -49,13 +49,13 @@ QT_USE_NAMESPACE @implementation QT_MANGLE_NAMESPACE(OSXBTL2CAPChannel) { - QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *delegate; + QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *delegate; IOBluetoothDevice *device; IOBluetoothL2CAPChannel *channel; bool connected; } -- (id)initWithDelegate:(OSXBluetooth::ChannelDelegate *)aDelegate +- (id)initWithDelegate:(DarwinBluetooth::ChannelDelegate *)aDelegate { Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)"); @@ -69,7 +69,7 @@ QT_USE_NAMESPACE return self; } -- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth::ChannelDelegate) *)aDelegate +- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth::ChannelDelegate) *)aDelegate channel:(IOBluetoothL2CAPChannel *)aChannel { // This type of channel does not require connect, it's created with diff --git a/src/bluetooth/osx/osxbtl2capchannel_p.h b/src/bluetooth/osx/osxbtl2capchannel_p.h index 512087b4..42eec8e7 100644 --- a/src/bluetooth/osx/osxbtl2capchannel_p.h +++ b/src/bluetooth/osx/osxbtl2capchannel_p.h @@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE class QBluetoothAddress; -namespace OSXBluetooth { +namespace DarwinBluetooth { class ChannelDelegate; @@ -73,8 +73,8 @@ QT_END_NAMESPACE @interface QT_MANGLE_NAMESPACE(OSXBTL2CAPChannel) : NSObject<IOBluetoothL2CAPChannelDelegate> -- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *)aDelegate; -- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *)aDelegate +- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *)aDelegate; +- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *)aDelegate channel:(IOBluetoothL2CAPChannel *)aChannel; - (void)dealloc; diff --git a/src/bluetooth/osx/osxbtnotifier_p.h b/src/bluetooth/osx/osxbtnotifier_p.h index dca6c268..397214d0 100644 --- a/src/bluetooth/osx/osxbtnotifier_p.h +++ b/src/bluetooth/osx/osxbtnotifier_p.h @@ -96,7 +96,6 @@ Q_SIGNALS: void CBManagerError(QLowEnergyController::Error error); void CBManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyController::Error error); void CBManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyService::ServiceError error); - }; } diff --git a/src/bluetooth/osx/osxbtrfcommchannel.mm b/src/bluetooth/osx/osxbtrfcommchannel.mm index 00b67ee0..d2d3e2f8 100644 --- a/src/bluetooth/osx/osxbtrfcommchannel.mm +++ b/src/bluetooth/osx/osxbtrfcommchannel.mm @@ -37,22 +37,22 @@ ** ****************************************************************************/ -#include "osxbtchanneldelegate_p.h" #include "osxbtrfcommchannel_p.h" #include "qbluetoothaddress.h" #include "osxbtutility_p.h" +#include "btdelegates_p.h" QT_USE_NAMESPACE @implementation QT_MANGLE_NAMESPACE(OSXBTRFCOMMChannel) { - QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *delegate; + QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *delegate; IOBluetoothDevice *device; IOBluetoothRFCOMMChannel *channel; bool connected; } -- (id)initWithDelegate:(OSXBluetooth::ChannelDelegate *)aDelegate +- (id)initWithDelegate:(DarwinBluetooth::ChannelDelegate *)aDelegate { Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)"); @@ -66,7 +66,7 @@ QT_USE_NAMESPACE return self; } -- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth::ChannelDelegate) *)aDelegate +- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth::ChannelDelegate) *)aDelegate channel:(IOBluetoothRFCOMMChannel *)aChannel { // This type of channel does not require connect, it's created with diff --git a/src/bluetooth/osx/osxbtrfcommchannel_p.h b/src/bluetooth/osx/osxbtrfcommchannel_p.h index 775999ed..44416cce 100644 --- a/src/bluetooth/osx/osxbtrfcommchannel_p.h +++ b/src/bluetooth/osx/osxbtrfcommchannel_p.h @@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE class QBluetoothAddress; -namespace OSXBluetooth { +namespace DarwinBluetooth { class ChannelDelegate; @@ -73,8 +73,8 @@ QT_END_NAMESPACE @interface QT_MANGLE_NAMESPACE(OSXBTRFCOMMChannel) : NSObject<IOBluetoothRFCOMMChannelDelegate> -- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *)aDelegate; -- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::ChannelDelegate *)aDelegate +- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *)aDelegate; +- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *)aDelegate channel:(IOBluetoothRFCOMMChannel *)aChannel; - (void)dealloc; diff --git a/src/bluetooth/osx/osxbtsdpinquiry.mm b/src/bluetooth/osx/osxbtsdpinquiry.mm index a7bdc2c4..a2b02b1a 100644 --- a/src/bluetooth/osx/osxbtsdpinquiry.mm +++ b/src/bluetooth/osx/osxbtsdpinquiry.mm @@ -41,6 +41,7 @@ #include "osxbtsdpinquiry_p.h" #include "qbluetoothuuid.h" #include "osxbtutility_p.h" +#include "btdelegates_p.h" #include <QtCore/qvariant.h> #include <QtCore/qstring.h> @@ -49,10 +50,6 @@ QT_BEGIN_NAMESPACE namespace OSXBluetooth { -SDPInquiryDelegate::~SDPInquiryDelegate() -{ -} - namespace { QBluetoothUuid sdp_element_to_uuid(IOBluetoothSDPDataElement *element) @@ -213,12 +210,12 @@ using namespace OSXBluetooth; @implementation QT_MANGLE_NAMESPACE(OSXBTSDPInquiry) { - QT_PREPEND_NAMESPACE(OSXBluetooth::SDPInquiryDelegate) *delegate; + QT_PREPEND_NAMESPACE(DarwinBluetooth::SDPInquiryDelegate) *delegate; IOBluetoothDevice *device; bool isActive; } -- (id)initWithDelegate:(SDPInquiryDelegate *)aDelegate +- (id)initWithDelegate:(DarwinBluetooth::SDPInquiryDelegate *)aDelegate { Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)"); diff --git a/src/bluetooth/osx/osxbtsdpinquiry_p.h b/src/bluetooth/osx/osxbtsdpinquiry_p.h index dd38a28b..e2658670 100644 --- a/src/bluetooth/osx/osxbtsdpinquiry_p.h +++ b/src/bluetooth/osx/osxbtsdpinquiry_p.h @@ -68,17 +68,13 @@ QT_BEGIN_NAMESPACE class QBluetoothServiceInfo; class QVariant; -namespace OSXBluetooth { +namespace DarwinBluetooth { -class SDPInquiryDelegate { -public: - typedef QT_MANGLE_NAMESPACE(OSXBTSDPInquiry) ObjCServiceInquiry; +class SDPInquiryDelegate; - virtual ~SDPInquiryDelegate(); +} - virtual void SDPInquiryFinished(IOBluetoothDevice *device) = 0; - virtual void SDPInquiryError(IOBluetoothDevice *device, IOReturn errorCode) = 0; -}; +namespace OSXBluetooth { void extract_service_record(IOBluetoothSDPServiceRecord *record, QBluetoothServiceInfo &serviceInfo); QVariant extract_attribute_value(IOBluetoothSDPDataElement *dataElement); @@ -90,7 +86,7 @@ QT_END_NAMESPACE @interface QT_MANGLE_NAMESPACE(OSXBTSDPInquiry) : NSObject -- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth::SDPInquiryDelegate) *)aDelegate; +- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth::SDPInquiryDelegate) *)aDelegate; - (void)dealloc; - (IOReturn)performSDPQueryWithDevice:(const QBluetoothAddress &)address; diff --git a/src/bluetooth/osx/osxbtsocketlistener.mm b/src/bluetooth/osx/osxbtsocketlistener.mm index 517b7f2d..10526b0f 100644 --- a/src/bluetooth/osx/osxbtsocketlistener.mm +++ b/src/bluetooth/osx/osxbtsocketlistener.mm @@ -39,31 +39,20 @@ #include "osxbtsocketlistener_p.h" #include "osxbtutility_p.h" +#include "btdelegates_p.h" #include <QtCore/qdebug.h> -QT_BEGIN_NAMESPACE - -namespace OSXBluetooth { - -SocketListener::~SocketListener() -{ -} - -} - -QT_END_NAMESPACE - QT_USE_NAMESPACE @implementation QT_MANGLE_NAMESPACE(OSXBTSocketListener) { IOBluetoothUserNotification *connectionNotification; - QT_PREPEND_NAMESPACE(OSXBluetooth::SocketListener) *delegate; + QT_PREPEND_NAMESPACE(DarwinBluetooth::SocketListener) *delegate; quint16 port; } -- (id)initWithListener:(OSXBluetooth::SocketListener *)aDelegate +- (id)initWithListener:(DarwinBluetooth::SocketListener *)aDelegate { Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)"); if (self = [super init]) { @@ -119,7 +108,7 @@ QT_USE_NAMESPACE Q_UNUSED(notification) Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); - delegate->openNotify(newChannel); + delegate->openNotifyRFCOMM(newChannel); } - (void)l2capOpenNotification:(IOBluetoothUserNotification *)notification @@ -128,7 +117,7 @@ QT_USE_NAMESPACE Q_UNUSED(notification) Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); - delegate->openNotify(newChannel); + delegate->openNotifyL2CAP(newChannel); } - (quint16)port diff --git a/src/bluetooth/osx/osxbtsocketlistener_p.h b/src/bluetooth/osx/osxbtsocketlistener_p.h index cac0b7c4..3bbce24e 100644 --- a/src/bluetooth/osx/osxbtsocketlistener_p.h +++ b/src/bluetooth/osx/osxbtsocketlistener_p.h @@ -57,22 +57,15 @@ #include <Foundation/Foundation.h> +// TODO: use the special macros we have to create an +// alias for a mangled name. @class QT_MANGLE_NAMESPACE(OSXBTSocketListener); QT_BEGIN_NAMESPACE -namespace OSXBluetooth { +namespace DarwinBluetooth { -class SocketListener -{ -public: - typedef QT_MANGLE_NAMESPACE(OSXBTSocketListener) ObjCListener; - - virtual ~SocketListener(); - - virtual void openNotify(IOBluetoothRFCOMMChannel *channel) = 0; - virtual void openNotify(IOBluetoothL2CAPChannel *channel) = 0; -}; +class SocketListener; } @@ -83,7 +76,7 @@ QT_END_NAMESPACE @interface QT_MANGLE_NAMESPACE(OSXBTSocketListener) : NSObject -- (id)initWithListener:(QT_PREPEND_NAMESPACE(OSXBluetooth::SocketListener) *)aDelegate; +- (id)initWithListener:(QT_PREPEND_NAMESPACE(DarwinBluetooth::SocketListener) *)aDelegate; - (void)dealloc; - (bool)listenRFCOMMConnectionsWithChannelID:(BluetoothRFCOMMChannelID)channelID; diff --git a/src/bluetooth/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 71ebc88e..00000000 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm +++ /dev/null @@ -1,458 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtBluetooth module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qbluetoothdevicediscoveryagent.h" -#include "osx/osxbtledeviceinquiry_p.h" -#include "qbluetoothlocaldevice.h" -#include "qbluetoothdeviceinfo.h" -#include "osx/osxbtnotifier_p.h" -#include "osx/osxbtutility_p.h" -#include "osx/uistrings_p.h" -#include "qbluetoothuuid.h" - -#include <QtCore/qloggingcategory.h> -#include <QtCore/qobject.h> -#include <QtCore/qglobal.h> -#include <QtCore/qstring.h> -#include <QtCore/qdebug.h> -#include <QtCore/qlist.h> - -#include <CoreBluetooth/CoreBluetooth.h> - -QT_BEGIN_NAMESPACE - -namespace -{ - -void registerQDeviceDiscoveryMetaType() -{ - static bool initDone = false; - if (!initDone) { - qRegisterMetaType<QBluetoothDeviceInfo>(); - qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::Error>(); - initDone = true; - } -} - -}//namespace - -class QBluetoothDeviceDiscoveryAgentPrivate : public QObject -{ - friend class QBluetoothDeviceDiscoveryAgent; - -public: - QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &address, - QBluetoothDeviceDiscoveryAgent *q); - virtual ~QBluetoothDeviceDiscoveryAgentPrivate(); - - bool isActive() const; - - void start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods m); - void stop(); - -private: - using LEDeviceInquiryObjC = QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry); - - void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error); - void LEnotSupported(); - void LEdeviceFound(const QBluetoothDeviceInfo &info); - void LEinquiryFinished(); - - void setError(QBluetoothDeviceDiscoveryAgent::Error, const QString &text = QString()); - - QBluetoothDeviceDiscoveryAgent *q_ptr; - - QBluetoothDeviceDiscoveryAgent::Error lastError; - QString errorString; - - QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType; - - using LEDeviceInquiry = OSXBluetooth::ObjCScopedPointer<LEDeviceInquiryObjC>; - LEDeviceInquiry inquiryLE; - - using DevicesList = QList<QBluetoothDeviceInfo>; - DevicesList discoveredDevices; - - bool startPending; - bool stopPending; - - int lowEnergySearchTimeout; -}; - -QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &adapter, - QBluetoothDeviceDiscoveryAgent *q) : - q_ptr(q), - lastError(QBluetoothDeviceDiscoveryAgent::NoError), - inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), - startPending(false), - stopPending(false), - lowEnergySearchTimeout(OSXBluetooth::defaultLEScanTimeoutMS) -{ - Q_UNUSED(adapter); - - registerQDeviceDiscoveryMetaType(); - Q_ASSERT_X(q != nullptr, Q_FUNC_INFO, "invalid q_ptr (null)"); -} - -QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() -{ - if (inquiryLE.data()) { - // We want the LE scan to stop as soon as possible. - if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { - // Local variable to be retained ... - LEDeviceInquiryObjC *inq = inquiryLE.data(); - dispatch_sync(leQueue, ^{ - [inq stop]; - }); - } - } -} - -bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const -{ - if (startPending) - return true; - if (stopPending) - return false; - - return !!inquiryLE.data(); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods /*methods*/) -{ - Q_ASSERT_X(!isActive(), Q_FUNC_INFO, "called on active device discovery agent"); - - if (stopPending) { - startPending = true; - return; - } - - using namespace OSXBluetooth; - - QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier); - // Connections: - using ErrMemFunPtr = void (LECBManagerNotifier::*)(QBluetoothDeviceDiscoveryAgent::Error); - notifier->connect(notifier.data(), ErrMemFunPtr(&LECBManagerNotifier::CBManagerError), - this, &QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError); - notifier->connect(notifier.data(), &LECBManagerNotifier::LEnotSupported, - this, &QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported); - notifier->connect(notifier.data(), &LECBManagerNotifier::discoveryFinished, - this, &QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished); - notifier->connect(notifier.data(), &LECBManagerNotifier::deviceDiscovered, - this, &QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound); - - inquiryLE.reset([[LEDeviceInquiryObjC alloc] initWithNotifier:notifier.data()]); - if (inquiryLE.data()) - notifier.take(); // Whatever happens next, inquiryLE is already the owner ... - - dispatch_queue_t leQueue(qt_LE_queue()); - if (!leQueue || !inquiryLE) { - setError(QBluetoothDeviceDiscoveryAgent::UnknownError, - QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED)); - emit q_ptr->error(lastError); - return; - } - - discoveredDevices.clear(); - setError(QBluetoothDeviceDiscoveryAgent::NoError); - - // Create a local variable - to have a strong referece in a block. - LEDeviceInquiryObjC *inq = inquiryLE.data(); - dispatch_async(leQueue, ^{ - [inq startWithTimeout:lowEnergySearchTimeout]; - }); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::stop() -{ - Q_ASSERT_X(isActive(), Q_FUNC_INFO, "called whithout active inquiry"); - - startPending = false; - stopPending = true; - - dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - Q_ASSERT(leQueue); - - setError(QBluetoothDeviceDiscoveryAgent::NoError); - - // Create a local variable - to have a strong referece in a block. - LEDeviceInquiryObjC *inq = inquiryLE.data(); - dispatch_sync(leQueue, ^{ - [inq stop]; - }); - // We consider LE scan to be stopped immediately and - // do not care about this LEDeviceInquiry object anymore. - LEinquiryFinished(); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error) -{ - // At the moment the only error reported by osxbtledeviceinquiry - // can be 'powered off' error, it happens - // after the LE scan started (so we have LE support and this is - // a real PoweredOffError). - Q_ASSERT_X(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError, - Q_FUNC_INFO, "unexpected error"); - - inquiryLE.reset(); - - startPending = false; - stopPending = false; - setError(error); - emit q_ptr->error(lastError); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported() -{ - inquiryLE.reset(); - - startPending = false; - stopPending = false; - setError(QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError); - emit q_ptr->error(lastError); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(const QBluetoothDeviceInfo &newDeviceInfo) -{ - // Update, append or discard. - for (int i = 0, e = discoveredDevices.size(); i < e; ++i) { - if (discoveredDevices[i].deviceUuid() == newDeviceInfo.deviceUuid()) { - QBluetoothDeviceInfo::Fields updatedFields = QBluetoothDeviceInfo::Field::None; - if (discoveredDevices[i].rssi() != newDeviceInfo.rssi()) { - qCDebug(QT_BT_OSX) << "Updating RSSI for" << newDeviceInfo.address() - << newDeviceInfo.rssi(); - discoveredDevices[i].setRssi(newDeviceInfo.rssi()); - updatedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI); - } - - if (discoveredDevices[i].manufacturerData() != newDeviceInfo.manufacturerData()) { - qCDebug(QT_BT_OSX) << "Updating manufacturer data for" << newDeviceInfo.address(); - const QVector<quint16> keys = newDeviceInfo.manufacturerIds(); - for (auto key: keys) - discoveredDevices[i].setManufacturerData(key, newDeviceInfo.manufacturerData(key)); - updatedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData); - } - - if (lowEnergySearchTimeout > 0) { - if (discoveredDevices[i] != newDeviceInfo) { - discoveredDevices.replace(i, newDeviceInfo); - emit q_ptr->deviceDiscovered(newDeviceInfo); - } else { - if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None)) - emit q_ptr->deviceUpdated(discoveredDevices[i], updatedFields); - } - - return; - } - - discoveredDevices.replace(i, newDeviceInfo); - emit q_ptr->deviceDiscovered(newDeviceInfo); - - if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None)) - emit q_ptr->deviceUpdated(discoveredDevices[i], updatedFields); - - return; - } - } - - discoveredDevices.append(newDeviceInfo); - emit q_ptr->deviceDiscovered(newDeviceInfo); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() -{ - inquiryLE.reset(); - - if (stopPending && !startPending) { - stopPending = false; - emit q_ptr->canceled(); - } else if (startPending) { - startPending = false; - stopPending = false; - // always the same method for start() on iOS - // classic search not supported - start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); - } else { - emit q_ptr->finished(); - } -} - -void QBluetoothDeviceDiscoveryAgentPrivate::setError(QBluetoothDeviceDiscoveryAgent::Error error, - const QString &text) -{ - lastError = error; - - if (text.length() > 0) { - errorString = text; - } else { - switch (lastError) { - case QBluetoothDeviceDiscoveryAgent::NoError: - errorString = QString(); - break; - case QBluetoothDeviceDiscoveryAgent::PoweredOffError: - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_POWERED_OFF); - break; - case QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError: - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_INVALID_ADAPTER); - break; - case QBluetoothDeviceDiscoveryAgent::InputOutputError: - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_IO); - break; - case QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError: - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_NOTSUPPORTED); - break; - case QBluetoothDeviceDiscoveryAgent::UnknownError: - default: - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_UNKNOWN_ERROR); - } - } -} - -QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(QObject *parent) : - QObject(parent), - d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(QBluetoothAddress(), this)) -{ -} - -QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent( - const QBluetoothAddress &deviceAdapter, QObject *parent) : - QObject(parent), - d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(deviceAdapter, this)) -{ - if (!deviceAdapter.isNull()) { - qCWarning(QT_BT_OSX) << "local device address is " - "not available, provided address is ignored"; - d_ptr->setError(InvalidBluetoothAdapterError); - } -} - -QBluetoothDeviceDiscoveryAgent::~QBluetoothDeviceDiscoveryAgent() -{ - delete d_ptr; -} - -QBluetoothDeviceDiscoveryAgent::InquiryType QBluetoothDeviceDiscoveryAgent::inquiryType() const -{ - return d_ptr->inquiryType; -} - -void QBluetoothDeviceDiscoveryAgent::setInquiryType(QBluetoothDeviceDiscoveryAgent::InquiryType type) -{ - d_ptr->inquiryType = type; -} - -QList<QBluetoothDeviceInfo> QBluetoothDeviceDiscoveryAgent::discoveredDevices() const -{ - return d_ptr->discoveredDevices; -} - -QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() -{ - return LowEnergyMethod; -} - -void QBluetoothDeviceDiscoveryAgent::start() -{ - if (d_ptr->lastError != InvalidBluetoothAdapterError) { - if (!isActive()) - d_ptr->start(supportedDiscoveryMethods()); - else - qCDebug(QT_BT_OSX) << "already started"; - } -} - -void QBluetoothDeviceDiscoveryAgent::start(DiscoveryMethods methods) -{ - if (methods == NoMethod) - return; - - DiscoveryMethods supported = - QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods(); - - if (!((supported & methods) == methods)) { - d_ptr->lastError = UnsupportedDiscoveryMethod; - d_ptr->errorString = QBluetoothDeviceDiscoveryAgent::tr("One or more device discovery methods " - "are not supported on this platform"); - emit error(d_ptr->lastError); - return; - } - - if (!isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) - d_ptr->start(methods); -} - -void QBluetoothDeviceDiscoveryAgent::stop() -{ - if (isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) - d_ptr->stop(); -} - -bool QBluetoothDeviceDiscoveryAgent::isActive() const -{ - return d_ptr->isActive(); -} - -QBluetoothDeviceDiscoveryAgent::Error QBluetoothDeviceDiscoveryAgent::error() const -{ - return d_ptr->lastError; -} - -QString QBluetoothDeviceDiscoveryAgent::errorString() const -{ - return d_ptr->errorString; -} - -int QBluetoothDeviceDiscoveryAgent::lowEnergyDiscoveryTimeout() const -{ - return d_ptr->lowEnergySearchTimeout; -} - -void QBluetoothDeviceDiscoveryAgent::setLowEnergyDiscoveryTimeout(int timeout) -{ - // cannot deliberately turn it off - if (timeout < 0) { - qCDebug(QT_BT_OSX) << "The Bluetooth Low Energy device discovery timeout cannot be negative."; - return; - } - - d_ptr->lowEnergySearchTimeout = timeout; - return; -} - -QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index 3e76c13d..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/qbluetoothserver.cpp b/src/bluetooth/qbluetoothserver.cpp index 6991518f..14b0796d 100644 --- a/src/bluetooth/qbluetoothserver.cpp +++ b/src/bluetooth/qbluetoothserver.cpp @@ -266,7 +266,7 @@ bool QBluetoothServer::isListening() const { Q_D(const QBluetoothServer); -#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) +#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(QT_OSX_BLUETOOTH) return d->isListening(); #endif diff --git a/src/bluetooth/qbluetoothserver_osx.mm b/src/bluetooth/qbluetoothserver_osx.mm index eefaf4da..9674331d 100644 --- a/src/bluetooth/qbluetoothserver_osx.mm +++ b/src/bluetooth/qbluetoothserver_osx.mm @@ -38,7 +38,7 @@ ****************************************************************************/ #include "osx/osxbtsocketlistener_p.h" -#include "qbluetoothserver_osx_p.h" +#include "qbluetoothserver_p.h" // The order is important: a workround for // a private header included by private header @@ -58,7 +58,6 @@ #include <QtCore/qglobal.h> #include <QtCore/qmutex.h> -// Import, since Obj-C headers do not have inclusion guards. #include <Foundation/Foundation.h> #include <limits> @@ -67,7 +66,9 @@ QT_BEGIN_NAMESPACE namespace { -typedef QBluetoothServiceInfo QSInfo; +using DarwinBluetooth::RetainPolicy; +using ServiceInfo = QBluetoothServiceInfo; +using ObjCListener = QT_MANGLE_NAMESPACE(OSXBTSocketListener); QMap<quint16, QBluetoothServerPrivate *> &busyPSMs() { @@ -86,79 +87,88 @@ typedef QMap<quint16, QBluetoothServerPrivate *>::iterator ServerMapIterator; } -QBluetoothServerPrivate::QBluetoothServerPrivate(QSInfo::Protocol type, QBluetoothServer *q) - : serverType(type), - q_ptr(q), - lastError(QBluetoothServer::NoError), - port(0), - maxPendingConnections(1) +QBluetoothServerPrivate::QBluetoothServerPrivate(ServiceInfo::Protocol type) + : socket(nullptr), + maxPendingConnections(1), + securityFlags(QBluetooth::NoSecurity), + serverType(type), + q_ptr(nullptr), + m_lastError(QBluetoothServer::NoError), + port(0) { - Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); - if (serverType == QSInfo::UnknownProtocol) + if (serverType == ServiceInfo::UnknownProtocol) qCWarning(QT_BT_OSX) << "unknown protocol"; } QBluetoothServerPrivate::~QBluetoothServerPrivate() { - // Actually, not good, but lock must be acquired. - // TODO: test this. const QMutexLocker lock(&channelMapMutex()); unregisterServer(this); } -void QBluetoothServerPrivate::_q_newConnection() -{ - // Noop, we have openNotify for this. -} - bool QBluetoothServerPrivate::startListener(quint16 realPort) { Q_ASSERT_X(realPort, Q_FUNC_INFO, "invalid port"); - if (serverType == QSInfo::UnknownProtocol) { + if (serverType == ServiceInfo::UnknownProtocol) { qCWarning(QT_BT_OSX) << "invalid protocol"; return false; } - if (!listener) - listener.reset([[ObjCListener alloc] initWithListener:this]); + if (!listener) { + listener.reset([[ObjCListener alloc] initWithListener:this], + RetainPolicy::noInitialRetain); + } bool result = false; - if (serverType == QSInfo::RfcommProtocol) - result = [listener listenRFCOMMConnectionsWithChannelID:realPort]; + if (serverType == ServiceInfo::RfcommProtocol) + result = [listener.getAs<ObjCListener>() listenRFCOMMConnectionsWithChannelID:realPort]; else - result = [listener listenL2CAPConnectionsWithPSM:realPort]; + result = [listener.getAs<ObjCListener>() listenL2CAPConnectionsWithPSM:realPort]; if (!result) - listener.reset(nil); + listener.reset(); return result; } +bool QBluetoothServerPrivate::isListening() const +{ + if (serverType == ServiceInfo::UnknownProtocol) + return false; + + const QMutexLocker lock(&QBluetoothServerPrivate::channelMapMutex()); + return QBluetoothServerPrivate::registeredServer(q_ptr->serverPort(), serverType); +} + void QBluetoothServerPrivate::stopListener() { - listener.reset(nil); + listener.reset(); } -void QBluetoothServerPrivate::openNotify(IOBluetoothRFCOMMChannel *channel) +void QBluetoothServerPrivate::openNotifyRFCOMM(void *generic) { + auto channel = static_cast<IOBluetoothRFCOMMChannel *>(generic); + Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)"); Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)"); Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); - PendingConnection newConnection(channel, true); + PendingConnection newConnection(channel, RetainPolicy::doInitialRetain); pendingConnections.append(newConnection); emit q_ptr->newConnection(); } -void QBluetoothServerPrivate::openNotify(IOBluetoothL2CAPChannel *channel) +void QBluetoothServerPrivate::openNotifyL2CAP(void *generic) { + auto channel = static_cast<IOBluetoothL2CAPChannel *>(generic); + Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)"); Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)"); Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); - PendingConnection newConnection(channel, true); + PendingConnection newConnection(channel, RetainPolicy::doInitialRetain); pendingConnections.append(newConnection); emit q_ptr->newConnection(); @@ -209,11 +219,11 @@ void QBluetoothServerPrivate::registerServer(QBluetoothServerPrivate *server, qu // External lock is required + port must be free. Q_ASSERT_X(server, Q_FUNC_INFO, "invalid server (null)"); - const QSInfo::Protocol type = server->serverType; - if (type == QSInfo::RfcommProtocol) { + const ServiceInfo::Protocol type = server->serverType; + if (type == ServiceInfo::RfcommProtocol) { Q_ASSERT_X(!channelIsBusy(port), Q_FUNC_INFO, "port is busy"); busyChannels()[port] = server; - } else if (type == QSInfo::L2capProtocol) { + } else if (type == ServiceInfo::L2capProtocol) { Q_ASSERT_X(!psmIsBusy(port), Q_FUNC_INFO, "port is busy"); busyPSMs()[port] = server; } else { @@ -225,11 +235,11 @@ void QBluetoothServerPrivate::registerServer(QBluetoothServerPrivate *server, qu QBluetoothServerPrivate *QBluetoothServerPrivate::registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol) { // Eternal lock is required. - if (protocol == QSInfo::RfcommProtocol) { + if (protocol == ServiceInfo::RfcommProtocol) { ServerMapIterator it = busyChannels().find(port); if (it != busyChannels().end()) return it.value(); - } else if (protocol == QSInfo::L2capProtocol) { + } else if (protocol == ServiceInfo::L2capProtocol) { ServerMapIterator it = busyPSMs().find(port); if (it != busyPSMs().end()) return it.value(); @@ -243,17 +253,17 @@ QBluetoothServerPrivate *QBluetoothServerPrivate::registeredServer(quint16 port, void QBluetoothServerPrivate::unregisterServer(QBluetoothServerPrivate *server) { // External lock is required. - const QSInfo::Protocol type = server->serverType; + const ServiceInfo::Protocol type = server->serverType; const quint16 port = server->port; - if (type == QSInfo::RfcommProtocol) { + if (type == ServiceInfo::RfcommProtocol) { ServerMapIterator it = busyChannels().find(port); if (it != busyChannels().end()) { busyChannels().erase(it); } else { qCWarning(QT_BT_OSX) << "server is not registered"; } - } else if (type == QSInfo::L2capProtocol) { + } else if (type == ServiceInfo::L2capProtocol) { ServerMapIterator it = busyPSMs().find(port); if (it != busyPSMs().end()) { busyPSMs().erase(it); @@ -265,21 +275,9 @@ void QBluetoothServerPrivate::unregisterServer(QBluetoothServerPrivate *server) } } - -QBluetoothServer::QBluetoothServer(QSInfo::Protocol serverType, QObject *parent) - : QObject(parent), - d_ptr(new QBluetoothServerPrivate(serverType, this)) -{ -} - -QBluetoothServer::~QBluetoothServer() -{ - delete d_ptr; -} - void QBluetoothServer::close() { - d_ptr->listener.reset(nil); + d_ptr->listener.reset(); // Needs a lock :( const QMutexLocker lock(&d_ptr->channelMapMutex()); @@ -289,8 +287,6 @@ void QBluetoothServer::close() bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port) { - typedef QBluetoothServerPrivate::ObjCListener ObjCListener; - OSXBluetooth::qt_test_iobluetooth_runloop(); if (d_ptr->listener) { @@ -303,7 +299,7 @@ bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port) qCWarning(QT_BT_OSX) << "device does not support Bluetooth or" << address.toString() << "is not a valid local adapter"; - d_ptr->lastError = UnknownError; + d_ptr->m_lastError = UnknownError; emit error(UnknownError); return false; } @@ -311,53 +307,53 @@ bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port) const QBluetoothLocalDevice::HostMode hostMode = device.hostMode(); if (hostMode == QBluetoothLocalDevice::HostPoweredOff) { qCWarning(QT_BT_OSX) << "Bluetooth device is powered off"; - d_ptr->lastError = PoweredOffError; + d_ptr->m_lastError = PoweredOffError; emit error(PoweredOffError); return false; } - const QSInfo::Protocol type = d_ptr->serverType; + const ServiceInfo::Protocol type = d_ptr->serverType; - if (type == QSInfo::UnknownProtocol) { + if (type == ServiceInfo::UnknownProtocol) { qCWarning(QT_BT_OSX) << "invalid protocol"; - d_ptr->lastError = UnsupportedProtocolError; - emit error(d_ptr->lastError); + d_ptr->m_lastError = UnsupportedProtocolError; + emit error(d_ptr->m_lastError); return false; } - d_ptr->lastError = QBluetoothServer::NoError; + d_ptr->m_lastError = QBluetoothServer::NoError; // Now we have to register a (fake) port, doing a proper (?) lock. const QMutexLocker lock(&d_ptr->channelMapMutex()); if (port) { - if (type == QSInfo::RfcommProtocol) { + if (type == ServiceInfo::RfcommProtocol) { if (d_ptr->channelIsBusy(port)) { qCWarning(QT_BT_OSX) << "server port:" << port << "already registered"; - d_ptr->lastError = ServiceAlreadyRegisteredError; + d_ptr->m_lastError = ServiceAlreadyRegisteredError; } } else { if (d_ptr->psmIsBusy(port)) { qCWarning(QT_BT_OSX) << "server port:" << port << "already registered"; - d_ptr->lastError = ServiceAlreadyRegisteredError; + d_ptr->m_lastError = ServiceAlreadyRegisteredError; } } } else { - type == QSInfo::RfcommProtocol ? port = d_ptr->findFreeChannel() + type == ServiceInfo::RfcommProtocol ? port = d_ptr->findFreeChannel() : port = d_ptr->findFreePSM(); } - if (d_ptr->lastError != QBluetoothServer::NoError) { - emit error(d_ptr->lastError); + if (d_ptr->m_lastError != QBluetoothServer::NoError) { + emit error(d_ptr->m_lastError); return false; } if (!port) { qCWarning(QT_BT_OSX) << "all ports are busy"; - d_ptr->lastError = ServiceAlreadyRegisteredError; - emit error(d_ptr->lastError); + d_ptr->m_lastError = ServiceAlreadyRegisteredError; + emit error(d_ptr->m_lastError); return false; } @@ -365,82 +361,17 @@ bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port) // (provided after a service was registered). d_ptr->port = port; d_ptr->registerServer(d_ptr, port); - d_ptr->listener.reset([[ObjCListener alloc] initWithListener:d_ptr]); + d_ptr->listener.reset([[ObjCListener alloc] initWithListener:d_ptr], + RetainPolicy::noInitialRetain); return true; } -QBluetoothServiceInfo QBluetoothServer::listen(const QBluetoothUuid &uuid, const QString &serviceName) -{ - if (!listen()) - return QBluetoothServiceInfo(); - - QBluetoothServiceInfo serviceInfo; - serviceInfo.setAttribute(QSInfo::ServiceName, serviceName); - QBluetoothServiceInfo::Sequence publicBrowse; - publicBrowse << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup)); - serviceInfo.setAttribute(QSInfo::BrowseGroupList, publicBrowse); - - QSInfo::Sequence profileSequence; - QSInfo::Sequence classId; - classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); - classId << QVariant::fromValue(quint16(0x100)); - profileSequence.append(QVariant::fromValue(classId)); - serviceInfo.setAttribute(QSInfo::BluetoothProfileDescriptorList, profileSequence); - - classId.clear(); - classId << QVariant::fromValue(uuid); - classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); - serviceInfo.setAttribute(QSInfo::ServiceClassIds, classId); - serviceInfo.setServiceUuid(uuid); - - QSInfo::Sequence protocolDescriptorList; - QSInfo::Sequence protocol; - protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap)); - if (d_ptr->serverType == QSInfo::L2capProtocol) - protocol << QVariant::fromValue(serverPort()); - protocolDescriptorList.append(QVariant::fromValue(protocol)); - protocol.clear(); - - if (d_ptr->serverType == QBluetoothServiceInfo::RfcommProtocol) { - protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm)) - << QVariant::fromValue(quint8(serverPort())); - protocolDescriptorList.append(QVariant::fromValue(protocol)); - } - - serviceInfo.setAttribute(QSInfo::ProtocolDescriptorList, - protocolDescriptorList); - - - // It's now up to a service info to acquire a real PSM/channel ID - // (provided by IOBluetooth) and start a listener. - if (!serviceInfo.registerService()) - return QBluetoothServiceInfo(); - - return serviceInfo; -} - -bool QBluetoothServer::isListening() const -{ - if (d_ptr->serverType == QSInfo::UnknownProtocol) - return false; - - const QMutexLocker lock(&QBluetoothServerPrivate::channelMapMutex()); - return QBluetoothServerPrivate::registeredServer(serverPort(), d_ptr->serverType); -} - void QBluetoothServer::setMaxPendingConnections(int numConnections) { - // That's a 'fake' limit, it affects nothing. d_ptr->maxPendingConnections = numConnections; } -int QBluetoothServer::maxPendingConnections() const -{ - // That's a 'fake' limit, it affects nothing. - return d_ptr->maxPendingConnections; -} - bool QBluetoothServer::hasPendingConnections() const { return d_ptr->pendingConnections.size(); @@ -457,11 +388,11 @@ QBluetoothSocket *QBluetoothServer::nextPendingConnection() // Remove it even if we have some errors below. d_ptr->pendingConnections.pop_front(); - if (d_ptr->serverType == QSInfo::RfcommProtocol) { - if (!newSocket->d_ptr->setChannel(static_cast<IOBluetoothRFCOMMChannel *>(channel))) + if (d_ptr->serverType == ServiceInfo::RfcommProtocol) { + if (!static_cast<QBluetoothSocketPrivate *>(newSocket->d_ptr)->setRFCOMChannel(channel.getAs<IOBluetoothRFCOMMChannel>())) return nullptr; } else { - if (!newSocket->d_ptr->setChannel(static_cast<IOBluetoothL2CAPChannel *>(channel))) + if (!static_cast<QBluetoothSocketPrivate *>(newSocket->d_ptr)->setL2CAPChannel(channel.getAs<IOBluetoothL2CAPChannel>())) return nullptr; } @@ -481,25 +412,13 @@ quint16 QBluetoothServer::serverPort() const void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security) { Q_UNUSED(security) - // Not implemented (yet?) + Q_UNIMPLEMENTED(); } QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const { - // Not implemented (yet?) + Q_UNIMPLEMENTED(); return QBluetooth::NoSecurity; } -QSInfo::Protocol QBluetoothServer::serverType() const -{ - return d_ptr->serverType; -} - -QBluetoothServer::Error QBluetoothServer::error() const -{ - return d_ptr->lastError; -} - -#include "moc_qbluetoothserver.cpp" - QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothserver_p.h b/src/bluetooth/qbluetoothserver_p.h index d78eee5f..76349de6 100644 --- a/src/bluetooth/qbluetoothserver_p.h +++ b/src/bluetooth/qbluetoothserver_p.h @@ -77,6 +77,15 @@ class ServerAcceptanceThread; #include <windows.networking.sockets.h> #endif +#ifdef QT_OSX_BLUETOOTH + +#include "osx/btdelegates_p.h" +#include "osx/btraii_p.h" + +#include <QtCore/qvector.h> + +#endif // QT_OSX_BLUETOOTH + QT_BEGIN_NAMESPACE class QBluetoothAddress; @@ -84,9 +93,10 @@ class QBluetoothSocket; class QBluetoothServer; -#ifndef QT_OSX_BLUETOOTH - class QBluetoothServerPrivate +#ifdef QT_OSX_BLUETOOTH + : public DarwinBluetooth::SocketListener +#endif { Q_DECLARE_PUBLIC(QBluetoothServer) @@ -140,9 +150,53 @@ public: bool initiateActiveListening(const QString &serviceName); bool deactivateActiveListening(); #endif -}; -#endif //QT_OSX_BLUETOOTH +#ifdef QT_OSX_BLUETOOTH + +public: + + friend class QBluetoothServer; + friend class QBluetoothServiceInfoPrivate; + +private: + bool startListener(quint16 realPort); + void stopListener(); + bool isListening() const; + + // SocketListener (delegate): + void openNotifyRFCOMM(void *channel) override; + void openNotifyL2CAP(void *channel) override; + + // Either a "temporary" channelID/PSM assigned by QBluetoothServer::listen, + // or a real channelID/PSM returned by IOBluetooth after we've registered + // a service. + quint16 port; + + DarwinBluetooth::StrongReference listener; + + // These static functions below + // deal with differences between bluetooth sockets + // (bluez and QtBluetooth's API) and IOBluetooth, where it's not possible + // to have a real PSM/channelID _before_ a service is registered, + // the solution - "fake" ports. + // These functions require external locking - using channelMapMutex. + static QMutex &channelMapMutex(); + + static bool channelIsBusy(quint16 channelID); + static quint16 findFreeChannel(); + + static bool psmIsBusy(quint16 psm); + static quint16 findFreePSM(); + + static void registerServer(QBluetoothServerPrivate *server, quint16 port); + static QBluetoothServerPrivate *registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol); + static void unregisterServer(QBluetoothServerPrivate *server); + + using PendingConnection = DarwinBluetooth::StrongReference; + QVector<PendingConnection> pendingConnections; + +#endif // QT_OSX_BLUETOOTH +}; QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.cpp b/src/bluetooth/qbluetoothservicediscoveryagent.cpp index 9750b2aa..e76c2311 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent.cpp @@ -308,6 +308,13 @@ QBluetoothAddress QBluetoothServiceDiscoveryAgent::remoteAddress() const return QBluetoothAddress(); } +namespace OSXBluetooth { + +void qt_test_iobluetooth_runloop(); + +} + + /*! Starts service discovery. \a mode specifies the type of service discovery to perform. @@ -318,6 +325,10 @@ QBluetoothAddress QBluetoothServiceDiscoveryAgent::remoteAddress() const void QBluetoothServiceDiscoveryAgent::start(DiscoveryMode mode) { Q_D(QBluetoothServiceDiscoveryAgent); +#ifdef QT_OSX_BLUETOOTH + // Make sure we are on the right thread/have a run loop: + OSXBluetooth::qt_test_iobluetooth_runloop(); +#endif if (d->discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::Inactive && d->error != InvalidBluetoothAdapterError) { diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm index bd9cc7f3..d8decae1 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm +++ b/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm @@ -37,6 +37,7 @@ ** ****************************************************************************/ +#include "qbluetoothservicediscoveryagent_p.h" #include "qbluetoothservicediscoveryagent.h" #include "qbluetoothdevicediscoveryagent.h" #include "qbluetoothlocaldevice.h" @@ -57,132 +58,41 @@ QT_BEGIN_NAMESPACE -class QBluetoothServiceDiscoveryAgentPrivate : public QObject, public OSXBluetooth::SDPInquiryDelegate -{ - friend class QBluetoothServiceDiscoveryAgent; -public: - enum DiscoveryState { - Inactive, - DeviceDiscovery, - ServiceDiscovery, - }; - - QBluetoothServiceDiscoveryAgentPrivate(QBluetoothServiceDiscoveryAgent *qp, - const QBluetoothAddress &localAddress); - - void startDeviceDiscovery(); - void stopDeviceDiscovery(); - - void startServiceDiscovery(); - void stopServiceDiscovery(); - - DiscoveryState discoveryState(); - void setDiscoveryMode(QBluetoothServiceDiscoveryAgent::DiscoveryMode m); - QBluetoothServiceDiscoveryAgent::DiscoveryMode DiscoveryMode(); - - void _q_deviceDiscovered(const QBluetoothDeviceInfo &info); - void _q_deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error); - void _q_deviceDiscoveryFinished(); - -private: - // SDPInquiryDelegate: - void SDPInquiryFinished(IOBluetoothDevice *device) override; - void SDPInquiryError(IOBluetoothDevice *device, IOReturn errorCode) override; - - void performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress); - void setupDeviceDiscoveryAgent(); - bool isDuplicatedService(const QBluetoothServiceInfo &serviceInfo) const; - void serviceDiscoveryFinished(); - - bool serviceHasMathingUuid(const QBluetoothServiceInfo &serviceInfo) const; - - QBluetoothServiceDiscoveryAgent *q_ptr; +namespace { - QBluetoothServiceDiscoveryAgent::Error error; - QString errorString; +using DarwinBluetooth::RetainPolicy; +using ObjCServiceInquiry = QT_MANGLE_NAMESPACE(OSXBTSDPInquiry); - QList<QBluetoothDeviceInfo> discoveredDevices; - QList<QBluetoothServiceInfo> discoveredServices; - QList<QBluetoothUuid> uuidFilter; - - bool singleDevice; - QBluetoothAddress deviceAddress; - QBluetoothAddress localAdapterAddress; - - DiscoveryState state; - QBluetoothServiceDiscoveryAgent::DiscoveryMode discoveryMode; - - QScopedPointer<QBluetoothDeviceDiscoveryAgent> deviceDiscoveryAgent; - OSXBluetooth::ObjCScopedPointer<ObjCServiceInquiry> serviceInquiry; -}; +} QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &localAddress) : - q_ptr(qp), + error(QBluetoothServiceDiscoveryAgent::NoError), - singleDevice(false), - localAdapterAddress(localAddress), + m_deviceAdapterAddress(localAddress), state(Inactive), - discoveryMode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery) -{ - serviceInquiry.reset([[ObjCServiceInquiry alloc] initWithDelegate:this]); -} + mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), + singleDevice(false), + q_ptr(qp) -void QBluetoothServiceDiscoveryAgentPrivate::startDeviceDiscovery() { - Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); - Q_ASSERT_X(state == Inactive, Q_FUNC_INFO, "invalid state"); - Q_ASSERT_X(error != QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError, - Q_FUNC_INFO, "invalid bluetooth adapter"); - - Q_ASSERT_X(deviceDiscoveryAgent.isNull(), "startDeviceDiscovery()", - "discovery agent already exists"); - - state = DeviceDiscovery; - - setupDeviceDiscoveryAgent(); - deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::ClassicMethod); + Q_ASSERT(q_ptr); + serviceInquiry.reset([[ObjCServiceInquiry alloc] initWithDelegate:this], RetainPolicy::noInitialRetain); } -void QBluetoothServiceDiscoveryAgentPrivate::stopDeviceDiscovery() +QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate() { - Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); - Q_ASSERT_X(!deviceDiscoveryAgent.isNull(), Q_FUNC_INFO, - "invalid device discovery agent (null)"); - Q_ASSERT_X(state == DeviceDiscovery, Q_FUNC_INFO, "invalid state"); - - deviceDiscoveryAgent->stop(); - deviceDiscoveryAgent.reset(nullptr); - state = Inactive; - - emit q_ptr->canceled(); } -void QBluetoothServiceDiscoveryAgentPrivate::startServiceDiscovery() +void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &deviceAddress) { - // Any of 'Inactive'/'DeviceDiscovery'/'ServiceDiscovery' states - // are possible. - - Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); - Q_ASSERT_X(error != QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError, - Q_FUNC_INFO, "invalid bluetooth adapter"); - - if (discoveredDevices.isEmpty()) { - state = Inactive; - emit q_ptr->finished(); - return; - } - QT_BT_MAC_AUTORELEASEPOOL; - state = ServiceDiscovery; - const QBluetoothAddress &address(discoveredDevices.at(0).address()); - - if (address.isNull()) { + if (deviceAddress.isNull()) { // This can happen: LE scan works with CoreBluetooth, but CBPeripherals // do not expose hardware addresses. // Pop the current QBluetoothDeviceInfo and decide what to do next. - return serviceDiscoveryFinished(); + return _q_serviceDiscoveryFinished(); } // Autoreleased object. @@ -195,17 +105,18 @@ void QBluetoothServiceDiscoveryAgentPrivate::startServiceDiscovery() emit q_ptr->error(error); } - return serviceDiscoveryFinished(); + return _q_serviceDiscoveryFinished(); } if (DiscoveryMode() == QBluetoothServiceDiscoveryAgent::MinimalDiscovery) { - performMinimalServiceDiscovery(address); + performMinimalServiceDiscovery(deviceAddress); } else { IOReturn result = kIOReturnSuccess; + auto nativeInquiry = serviceInquiry.getAs<ObjCServiceInquiry>(); if (uuidFilter.size()) - result = [serviceInquiry performSDPQueryWithDevice:address filters:uuidFilter]; + result = [nativeInquiry performSDPQueryWithDevice:deviceAddress filters:uuidFilter]; else - result = [serviceInquiry performSDPQueryWithDevice:address]; + result = [nativeInquiry performSDPQueryWithDevice:deviceAddress]; if (result != kIOReturnSuccess) { // Failed immediately to perform an SDP inquiry on IOBluetoothDevice: @@ -214,87 +125,21 @@ void QBluetoothServiceDiscoveryAgentPrivate::startServiceDiscovery() } } -void QBluetoothServiceDiscoveryAgentPrivate::stopServiceDiscovery() +void QBluetoothServiceDiscoveryAgentPrivate::stop() { - Q_ASSERT_X(state != Inactive, Q_FUNC_INFO, "invalid state"); Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); discoveredDevices.clear(); - state = Inactive; // "Stops" immediately. - [serviceInquiry stopSDPQuery]; + [serviceInquiry.getAs<ObjCServiceInquiry>() stopSDPQuery]; emit q_ptr->canceled(); } -QBluetoothServiceDiscoveryAgentPrivate::DiscoveryState - QBluetoothServiceDiscoveryAgentPrivate::discoveryState() -{ - return state; -} - -void QBluetoothServiceDiscoveryAgentPrivate::setDiscoveryMode( - QBluetoothServiceDiscoveryAgent::DiscoveryMode m) -{ - discoveryMode = m; - -} - -QBluetoothServiceDiscoveryAgent::DiscoveryMode - QBluetoothServiceDiscoveryAgentPrivate::DiscoveryMode() -{ - return discoveryMode; -} - -void QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscovered(const QBluetoothDeviceInfo &info) -{ - // Look for duplicates, and cached entries - for (int i = 0; i < discoveredDevices.count(); i++) { - if (discoveredDevices.at(i).address() == info.address()) { - discoveredDevices.removeAt(i); - break; - } - } - - discoveredDevices.prepend(info); -} - -void QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error) -{ - Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); - - error = QBluetoothServiceDiscoveryAgent::UnknownError; - errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_UNKNOWN_ERROR); - - deviceDiscoveryAgent->stop(); - deviceDiscoveryAgent.reset(nullptr); - - state = QBluetoothServiceDiscoveryAgentPrivate::Inactive; - emit q_ptr->error(error); - emit q_ptr->finished(); -} - -void QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryFinished() -{ - Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); - - if (deviceDiscoveryAgent->error() != QBluetoothDeviceDiscoveryAgent::NoError) { - //Forward the device discovery error - error = static_cast<QBluetoothServiceDiscoveryAgent::Error>(deviceDiscoveryAgent->error()); - errorString = deviceDiscoveryAgent->errorString(); - deviceDiscoveryAgent.reset(nullptr); - state = Inactive; - emit q_ptr->error(error); - emit q_ptr->finished(); - } else { - deviceDiscoveryAgent.reset(nullptr); - startServiceDiscovery(); - } -} - -void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryFinished(IOBluetoothDevice *device) +void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryFinished(void *generic) { + auto device = static_cast<IOBluetoothDevice *>(generic); Q_ASSERT_X(device, Q_FUNC_INFO, "invalid IOBluetoothDevice (nil)"); if (state == Inactive) @@ -323,10 +168,10 @@ void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryFinished(IOBluetoothDevic } } - serviceDiscoveryFinished(); + _q_serviceDiscoveryFinished(); } -void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryError(IOBluetoothDevice *device, IOReturn errorCode) +void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryError(void *device, IOReturn errorCode) { Q_UNUSED(device) @@ -340,7 +185,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryError(IOBluetoothDevice * emit q_ptr->error(error); } - serviceDiscoveryFinished(); + _q_serviceDiscoveryFinished(); } void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress) @@ -371,7 +216,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(cons if (!serviceInfo.isValid()) continue; - if (!uuidFilter.isEmpty() && !serviceHasMathingUuid(serviceInfo)) + if (!uuidFilter.isEmpty() && !serviceHasMatchingUuid(serviceInfo)) continue; if (!isDuplicatedService(serviceInfo)) { @@ -381,52 +226,10 @@ void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(cons } } - serviceDiscoveryFinished(); -} - -void QBluetoothServiceDiscoveryAgentPrivate::setupDeviceDiscoveryAgent() -{ - Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); - Q_ASSERT_X(deviceDiscoveryAgent.isNull() || !deviceDiscoveryAgent->isActive(), - Q_FUNC_INFO, "device discovery agent is active"); - - deviceDiscoveryAgent.reset(new QBluetoothDeviceDiscoveryAgent(localAdapterAddress, q_ptr)); - - QObject::connect(deviceDiscoveryAgent.data(), &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, - this, &QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscovered); - QObject::connect(deviceDiscoveryAgent.data(), &QBluetoothDeviceDiscoveryAgent::finished, - this, &QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryFinished); - QObject::connect(deviceDiscoveryAgent.data(), - QOverload<QBluetoothDeviceDiscoveryAgent::Error>::of(&QBluetoothDeviceDiscoveryAgent::error), - this, - &QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryError); -} - -bool QBluetoothServiceDiscoveryAgentPrivate::isDuplicatedService(const QBluetoothServiceInfo &serviceInfo) const -{ - //check the service is not already part of our known list - for (int j = 0; j < discoveredServices.count(); j++) { - const QBluetoothServiceInfo &info = discoveredServices.at(j); - if (info.device() == serviceInfo.device() - && info.serviceClassUuids() == serviceInfo.serviceClassUuids() - && info.serviceUuid() == serviceInfo.serviceUuid()) { - return true; - } - } - - return false; -} - -void QBluetoothServiceDiscoveryAgentPrivate::serviceDiscoveryFinished() -{ - if (!discoveredDevices.isEmpty()) - discoveredDevices.removeFirst(); - - if (state == ServiceDiscovery) - startServiceDiscovery(); + _q_serviceDiscoveryFinished(); } -bool QBluetoothServiceDiscoveryAgentPrivate::serviceHasMathingUuid(const QBluetoothServiceInfo &serviceInfo) const +bool QBluetoothServiceDiscoveryAgentPrivate::serviceHasMatchingUuid(const QBluetoothServiceInfo &serviceInfo) const { for (const auto &requestedUuid : uuidFilter) { if (serviceInfo.serviceUuid() == requestedUuid) @@ -437,161 +240,4 @@ bool QBluetoothServiceDiscoveryAgentPrivate::serviceHasMathingUuid(const QBlueto return false; } -QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(QObject *parent) -: QObject(parent), - d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(this, QBluetoothAddress())) -{ -} - -QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(const QBluetoothAddress &deviceAdapter, QObject *parent) -: QObject(parent), - d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(this, deviceAdapter)) -{ - if (!deviceAdapter.isNull()) { - const QList<QBluetoothHostInfo> localDevices = QBluetoothLocalDevice::allDevices(); - for (const QBluetoothHostInfo &hostInfo : localDevices) { - if (hostInfo.address() == deviceAdapter) - return; - } - d_ptr->error = InvalidBluetoothAdapterError; - d_ptr->errorString = QCoreApplication::translate(SERVICE_DISCOVERY, SD_INVALID_ADDRESS); - } -} - -QBluetoothServiceDiscoveryAgent::~QBluetoothServiceDiscoveryAgent() -{ - delete d_ptr; -} - -QList<QBluetoothServiceInfo> QBluetoothServiceDiscoveryAgent::discoveredServices() const -{ - return d_ptr->discoveredServices; -} - -/* - Sets the UUID filter to \a uuids. Only services matching the UUIDs in \a uuids will be - returned. - - An empty UUID list is equivalent to a list containing only QBluetoothUuid::PublicBrowseGroup. - - \sa uuidFilter() -*/ -void QBluetoothServiceDiscoveryAgent::setUuidFilter(const QList<QBluetoothUuid> &uuids) -{ - d_ptr->uuidFilter = uuids; -} - -/* - This is an overloaded member function, provided for convenience. - - Sets the UUID filter to a list containing the single element \a uuid. - - \sa uuidFilter() -*/ -void QBluetoothServiceDiscoveryAgent::setUuidFilter(const QBluetoothUuid &uuid) -{ - d_ptr->uuidFilter.clear(); - d_ptr->uuidFilter.append(uuid); -} - -/* - Returns the UUID filter. - - \sa setUuidFilter() -*/ -QList<QBluetoothUuid> QBluetoothServiceDiscoveryAgent::uuidFilter() const -{ - return d_ptr->uuidFilter; -} - -/* - Sets the remote device address to \a address. If \a address is default constructed, - services will be discovered on all contactable Bluetooth devices. A new remote - address can only be set while there is no service discovery in progress; otherwise - this function returns false. - - \sa remoteAddress() -*/ -bool QBluetoothServiceDiscoveryAgent::setRemoteAddress(const QBluetoothAddress &address) -{ - if (isActive()) - return false; - - if (!address.isNull()) - d_ptr->singleDevice = true; - - d_ptr->deviceAddress = address; - return true; -} - -QBluetoothAddress QBluetoothServiceDiscoveryAgent::remoteAddress() const -{ - if (d_ptr->singleDevice) - return d_ptr->deviceAddress; - - return QBluetoothAddress(); -} - -void QBluetoothServiceDiscoveryAgent::start(DiscoveryMode mode) -{ - OSXBluetooth::qt_test_iobluetooth_runloop(); - - if (d_ptr->discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::Inactive - && d_ptr->error != InvalidBluetoothAdapterError) - { - d_ptr->setDiscoveryMode(mode); - if (d_ptr->deviceAddress.isNull()) { - d_ptr->startDeviceDiscovery(); - } else { - d_ptr->discoveredDevices.append(QBluetoothDeviceInfo(d_ptr->deviceAddress, QString(), 0)); - d_ptr->startServiceDiscovery(); - } - } -} - -void QBluetoothServiceDiscoveryAgent::stop() -{ - if (d_ptr->error == InvalidBluetoothAdapterError || !isActive()) - return; - - switch (d_ptr->discoveryState()) { - case QBluetoothServiceDiscoveryAgentPrivate::DeviceDiscovery: - d_ptr->stopDeviceDiscovery(); - break; - case QBluetoothServiceDiscoveryAgentPrivate::ServiceDiscovery: - d_ptr->stopServiceDiscovery(); - default:; - } - - d_ptr->discoveredDevices.clear(); -} - -void QBluetoothServiceDiscoveryAgent::clear() -{ - // Don't clear the list while the search is ongoing - if (isActive()) - return; - - d_ptr->discoveredDevices.clear(); - d_ptr->discoveredServices.clear(); - d_ptr->uuidFilter.clear(); -} - -bool QBluetoothServiceDiscoveryAgent::isActive() const -{ - return d_ptr->state != QBluetoothServiceDiscoveryAgentPrivate::Inactive; -} - -QBluetoothServiceDiscoveryAgent::Error QBluetoothServiceDiscoveryAgent::error() const -{ - return d_ptr->error; -} - -QString QBluetoothServiceDiscoveryAgent::errorString() const -{ - return d_ptr->errorString; -} - -#include "moc_qbluetoothservicediscoveryagent.cpp" - QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h index dbf8b1d4..c17221be 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h @@ -76,6 +76,11 @@ QT_END_NAMESPACE #include <QtCore/QPointer> #endif +#ifdef QT_OSX_BLUETOOTH +#include "osx/btdelegates_p.h" +#include "osx/btraii_p.h" +#endif + QT_BEGIN_NAMESPACE class QBluetoothDeviceDiscoveryAgent; @@ -95,6 +100,9 @@ class QBluetoothServiceDiscoveryAgentPrivate : public QObject { Q_OBJECT +#elif defined(QT_OSX_BLUETOOTH) + : public QObject, public DarwinBluetooth::SDPInquiryDelegate +{ #else { #endif @@ -211,6 +219,19 @@ private: QPointer<QWinRTBluetoothServiceDiscoveryWorker> worker; #endif +#ifdef QT_OSX_BLUETOOTH + // SDPInquiryDelegate: + void SDPInquiryFinished(void *device) override; + void SDPInquiryError(void *device, IOReturn errorCode) override; + + void performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress); + //void serviceDiscoveryFinished(); + + bool serviceHasMatchingUuid(const QBluetoothServiceInfo &serviceInfo) const; + + DarwinBluetooth::ScopedPointer serviceInquiry; +#endif // QT_OSX_BLUETOOTH + protected: QBluetoothServiceDiscoveryAgent *q_ptr; }; diff --git a/src/bluetooth/qbluetoothserviceinfo.cpp b/src/bluetooth/qbluetoothserviceinfo.cpp index 7c3780ec..23a78c81 100644 --- a/src/bluetooth/qbluetoothserviceinfo.cpp +++ b/src/bluetooth/qbluetoothserviceinfo.cpp @@ -180,7 +180,12 @@ bool QBluetoothServiceInfo::isRegistered() const bool QBluetoothServiceInfo::registerService(const QBluetoothAddress &localAdapter) { +#ifdef QT_OSX_BLUETOOTH + Q_UNUSED(localAdapter) + return d_ptr->registerService(*this); +#else return d_ptr->registerService(localAdapter); +#endif } /*! diff --git a/src/bluetooth/qbluetoothserviceinfo_osx.mm b/src/bluetooth/qbluetoothserviceinfo_osx.mm index 34de4695..41e4e8b7 100644 --- a/src/bluetooth/qbluetoothserviceinfo_osx.mm +++ b/src/bluetooth/qbluetoothserviceinfo_osx.mm @@ -38,9 +38,10 @@ ****************************************************************************/ #include "osx/osxbtservicerecord_p.h" -#include "qbluetoothserver_osx_p.h" +#include "qbluetoothserviceinfo_p.h" #include "qbluetoothserviceinfo.h" #include "qbluetoothdeviceinfo.h" +#include "qbluetoothserver_p.h" #include "osx/osxbtutility_p.h" #include "osx/osxbluetooth_p.h" @@ -55,85 +56,116 @@ QT_BEGIN_NAMESPACE -class QBluetoothServiceInfoPrivate +namespace { + +using DarwinBluetooth::RetainPolicy; +using ServiceInfo = QBluetoothServiceInfo; + +// Alas, since there is no d_ptr<->q_ptr link (which is not that bad in itself), +// I need these getters duplicated here: +ServiceInfo::Protocol socket_protocol(const QBluetoothServiceInfoPrivate &privateInfo) { -public: + ServiceInfo::Sequence parameters = privateInfo.protocolDescriptor(QBluetoothUuid::Rfcomm); + if (!parameters.isEmpty()) + return ServiceInfo::RfcommProtocol; - typedef QBluetoothServiceInfo QSInfo; + parameters = privateInfo.protocolDescriptor(QBluetoothUuid::L2cap); + if (!parameters.isEmpty()) + return ServiceInfo::L2capProtocol; - bool registerService(const OSXBluetooth::ObjCStrongReference<NSMutableDictionary> &serviceDict); - bool isRegistered() const; - bool unregisterService(); + return ServiceInfo::UnknownProtocol; +} - QBluetoothDeviceInfo deviceInfo; - QMap<quint16, QVariant> attributes; +int channel_or_psm(const QBluetoothServiceInfoPrivate &privateInfo, QBluetoothUuid::ProtocolUuid uuid) +{ + const auto parameters = privateInfo.protocolDescriptor(uuid); + if (parameters.isEmpty()) + return -1; + else if (parameters.count() == 1) + return 0; - QBluetoothServiceInfo::Sequence protocolDescriptor(QBluetoothUuid::ProtocolUuid protocol) const; - QBluetoothServiceInfo::Protocol socketProtocol() const; - int protocolServiceMultiplexer() const; - int serverChannel() const; + return parameters.at(1).toInt(); +} -private: +} // unnamed namespace - bool registered = false; +QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate() +{ +} - typedef OSXBluetooth::ObjCScopedPointer<IOBluetoothSDPServiceRecord> SDPRecord; - SDPRecord serviceRecord; - BluetoothSDPServiceRecordHandle serviceRecordHandle = 0; -}; +QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate() +{ +} -bool QBluetoothServiceInfoPrivate::registerService(const OSXBluetooth::ObjCStrongReference<NSMutableDictionary> &serviceDict) +bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAddress) +{ + Q_UNUSED(localAddress); + return false; +} + +bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothServiceInfo &info) { using namespace OSXBluetooth; - Q_ASSERT(serviceDict); + if (isRegistered()) + return false; + + using namespace OSXBluetooth; + + ObjCStrongReference<NSMutableDictionary> serviceDict(iobluetooth_service_dictionary(info)); + if (!serviceDict) { + qCWarning(QT_BT_OSX) << "failed to create a service dictionary"; + return false; + } + Q_ASSERT(!registered); Q_ASSERT_X(!serviceRecord, Q_FUNC_INFO, "not registered, but serviceRecord is not nil"); SDPRecord newRecord; - newRecord.reset([[IOBluetoothSDPServiceRecord - publishedServiceRecordWithDictionary:serviceDict] retain]); + newRecord.reset([IOBluetoothSDPServiceRecord + publishedServiceRecordWithDictionary:serviceDict], RetainPolicy::doInitialRetain); if (!newRecord) { qCWarning(QT_BT_OSX) << "failed to register a service record"; return false; } BluetoothSDPServiceRecordHandle newRecordHandle = 0; - if ([newRecord getServiceRecordHandle:&newRecordHandle] != kIOReturnSuccess) { + auto *ioSDPRecord = newRecord.getAs<IOBluetoothSDPServiceRecord>(); + if ([ioSDPRecord getServiceRecordHandle:&newRecordHandle] != kIOReturnSuccess) { qCWarning(QT_BT_OSX) << "failed to register a service record"; - [newRecord removeServiceRecord]; + [ioSDPRecord removeServiceRecord]; return false; } - const QSInfo::Protocol type = socketProtocol(); + const ServiceInfo::Protocol type = info.socketProtocol(); quint16 realPort = 0; QBluetoothServerPrivate *server = nullptr; bool configured = false; if (type == QBluetoothServiceInfo::L2capProtocol) { BluetoothL2CAPPSM psm = 0; - server = QBluetoothServerPrivate::registeredServer(protocolServiceMultiplexer(), type); - if ([newRecord getL2CAPPSM:&psm] == kIOReturnSuccess) { + server = QBluetoothServerPrivate::registeredServer(info.protocolServiceMultiplexer(), type); + if ([ioSDPRecord getL2CAPPSM:&psm] == kIOReturnSuccess) { configured = true; realPort = psm; } } else if (type == QBluetoothServiceInfo::RfcommProtocol) { BluetoothRFCOMMChannelID channelID = 0; - server = QBluetoothServerPrivate::registeredServer(serverChannel(), type); - if ([newRecord getRFCOMMChannelID:&channelID] == kIOReturnSuccess) { + server = QBluetoothServerPrivate::registeredServer(info.serverChannel(), type); + if ([ioSDPRecord getRFCOMMChannelID:&channelID] == kIOReturnSuccess) { configured = true; realPort = channelID; } } if (!configured) { - [newRecord removeServiceRecord]; + [ioSDPRecord removeServiceRecord]; qCWarning(QT_BT_OSX) << "failed to register a service record"; return false; } registered = true; - serviceRecord.reset(newRecord.take()); + serviceRecord.swap(newRecord); serviceRecordHandle = newRecordHandle; if (server) @@ -154,17 +186,18 @@ bool QBluetoothServiceInfoPrivate::unregisterService() Q_ASSERT_X(serviceRecord, Q_FUNC_INFO, "service registered, but serviceRecord is nil"); - [serviceRecord removeServiceRecord]; - serviceRecord.reset(nil); + auto *nativeRecord = serviceRecord.getAs<IOBluetoothSDPServiceRecord>(); + [nativeRecord removeServiceRecord]; + serviceRecord.reset(); - const QSInfo::Protocol type = socketProtocol(); + const ServiceInfo::Protocol type = socket_protocol(*this); QBluetoothServerPrivate *server = nullptr; const QMutexLocker lock(&QBluetoothServerPrivate::channelMapMutex()); - if (type == QSInfo::RfcommProtocol) - server = QBluetoothServerPrivate::registeredServer(serverChannel(), type); - else if (type == QSInfo::L2capProtocol) - server = QBluetoothServerPrivate::registeredServer(protocolServiceMultiplexer(), type); + if (type == ServiceInfo::RfcommProtocol) + server = QBluetoothServerPrivate::registeredServer(channel_or_psm(*this, QBluetoothUuid::Rfcomm), type); + else if (type == ServiceInfo::L2capProtocol) + server = QBluetoothServerPrivate::registeredServer(channel_or_psm(*this, QBluetoothUuid::L2cap), type); if (server) server->stopListener(); @@ -175,281 +208,4 @@ bool QBluetoothServiceInfoPrivate::unregisterService() return true; } -bool QBluetoothServiceInfo::isRegistered() const -{ - return d_ptr->isRegistered(); -} - -bool QBluetoothServiceInfo::registerService(const QBluetoothAddress &localAdapter) -{ - Q_UNUSED(localAdapter); - if (isRegistered()) - return false; - - using namespace OSXBluetooth; - - ObjCStrongReference<NSMutableDictionary> serviceDict(iobluetooth_service_dictionary(*this)); - if (!serviceDict) { - qCWarning(QT_BT_OSX) << "failed to create a service dictionary"; - return false; - } - - return d_ptr->registerService(serviceDict); -} - -bool QBluetoothServiceInfo::unregisterService() -{ - return d_ptr->unregisterService(); -} - -QBluetoothServiceInfo::QBluetoothServiceInfo() - : d_ptr(new QBluetoothServiceInfoPrivate) -{ -} - -QBluetoothServiceInfo::QBluetoothServiceInfo(const QBluetoothServiceInfo &other) - : d_ptr(other.d_ptr) -{ -} - -QBluetoothServiceInfo::~QBluetoothServiceInfo() -{ -} - -bool QBluetoothServiceInfo::isValid() const -{ - return !d_ptr->attributes.isEmpty(); -} - -bool QBluetoothServiceInfo::isComplete() const -{ - return d_ptr->attributes.contains(ProtocolDescriptorList); -} - -QBluetoothDeviceInfo QBluetoothServiceInfo::device() const -{ - return d_ptr->deviceInfo; -} - -void QBluetoothServiceInfo::setDevice(const QBluetoothDeviceInfo &device) -{ - d_ptr->deviceInfo = device; -} - -void QBluetoothServiceInfo::setAttribute(quint16 attributeId, const QVariant &value) -{ - d_ptr->attributes[attributeId] = value; -} - -QVariant QBluetoothServiceInfo::attribute(quint16 attributeId) const -{ - return d_ptr->attributes.value(attributeId); -} - -QList<quint16> QBluetoothServiceInfo::attributes() const -{ - return d_ptr->attributes.keys(); -} - -bool QBluetoothServiceInfo::contains(quint16 attributeId) const -{ - return d_ptr->attributes.contains(attributeId); -} - -void QBluetoothServiceInfo::removeAttribute(quint16 attributeId) -{ - d_ptr->attributes.remove(attributeId); -} - -QBluetoothServiceInfo::Protocol QBluetoothServiceInfo::socketProtocol() const -{ - return d_ptr->socketProtocol(); -} - -int QBluetoothServiceInfo::protocolServiceMultiplexer() const -{ - return d_ptr->protocolServiceMultiplexer(); -} - -int QBluetoothServiceInfo::serverChannel() const -{ - return d_ptr->serverChannel(); -} - -QBluetoothServiceInfo::Sequence QBluetoothServiceInfo::protocolDescriptor(QBluetoothUuid::ProtocolUuid protocol) const -{ - return d_ptr->protocolDescriptor(protocol); -} - -QList<QBluetoothUuid> QBluetoothServiceInfo::serviceClassUuids() const -{ - QList<QBluetoothUuid> results; - - const QVariant var = attribute(QBluetoothServiceInfo::ServiceClassIds); - if (!var.isValid()) - return results; - - const QBluetoothServiceInfo::Sequence seq = var.value<QBluetoothServiceInfo::Sequence>(); - for (int i = 0; i < seq.count(); i++) - results.append(seq.at(i).value<QBluetoothUuid>()); - - return results; -} - -QBluetoothServiceInfo &QBluetoothServiceInfo::operator=(const QBluetoothServiceInfo &other) -{ - if (this != &other) - d_ptr = other.d_ptr; - - return *this; -} - -static void dumpAttributeVariant(QDebug dbg, const QVariant &var, const QString& indent) -{ - switch (int(var.type())) { - case QMetaType::Void: - dbg << QString::asprintf("%sEmpty\n", indent.toUtf8().constData()); - break; - case QMetaType::UChar: - dbg << QString::asprintf("%suchar %u\n", indent.toUtf8().constData(), var.toUInt()); - break; - case QMetaType::UShort: - dbg << QString::asprintf("%sushort %u\n", indent.toUtf8().constData(), var.toUInt()); - break; - case QMetaType::UInt: - dbg << QString::asprintf("%suint %u\n", indent.toUtf8().constData(), var.toUInt()); - break; - case QMetaType::Char: - dbg << QString::asprintf("%schar %d\n", indent.toUtf8().constData(), var.toInt()); - break; - case QMetaType::Short: - dbg << QString::asprintf("%sshort %d\n", indent.toUtf8().constData(), var.toInt()); - break; - case QMetaType::Int: - dbg << QString::asprintf("%sint %d\n", indent.toUtf8().constData(), var.toInt()); - break; - case QMetaType::QString: - dbg << QString::asprintf("%sstring %s\n", indent.toUtf8().constData(), - var.toString().toUtf8().constData()); - break; - case QMetaType::QByteArray: - dbg << QString::asprintf("%sbytearray %s\n", indent.toUtf8().constData(), - var.toByteArray().toHex().constData()); - break; - case QMetaType::Bool: - dbg << QString::asprintf("%sbool %d\n", indent.toUtf8().constData(), var.toBool()); - break; - case QMetaType::QUrl: - dbg << QString::asprintf("%surl %s\n", indent.toUtf8().constData(), - var.toUrl().toString().toUtf8().constData()); - break; - case QVariant::UserType: - if (var.userType() == qMetaTypeId<QBluetoothUuid>()) { - QBluetoothUuid uuid = var.value<QBluetoothUuid>(); - switch (uuid.minimumSize()) { - case 0: - dbg << QString::asprintf("%suuid NULL\n", indent.toUtf8().constData()); - break; - case 2: - dbg << QString::asprintf("%suuid2 %04x\n", indent.toUtf8().constData(), - uuid.toUInt16()); - break; - case 4: - dbg << QString::asprintf("%suuid %08x\n", indent.toUtf8().constData(), - uuid.toUInt32()); - break; - case 16: - dbg << QString::asprintf("%suuid %s\n", - indent.toUtf8().constData(), - QByteArray(reinterpret_cast<const char *>(uuid.toUInt128().data), 16).toHex().constData()); - break; - default: - dbg << QString::asprintf("%suuid ???\n", indent.toUtf8().constData()); - } - } else if (var.userType() == qMetaTypeId<QBluetoothServiceInfo::Sequence>()) { - dbg << QString::asprintf("%sSequence\n", indent.toUtf8().constData()); - const QBluetoothServiceInfo::Sequence *sequence = static_cast<const QBluetoothServiceInfo::Sequence *>(var.data()); - for (const QVariant &v : *sequence) - dumpAttributeVariant(dbg, v, indent + QLatin1Char('\t')); - } else if (var.userType() == qMetaTypeId<QBluetoothServiceInfo::Alternative>()) { - dbg << QString::asprintf("%sAlternative\n", indent.toUtf8().constData()); - const QBluetoothServiceInfo::Alternative *alternative = static_cast<const QBluetoothServiceInfo::Alternative *>(var.data()); - for (const QVariant &v : *alternative) - dumpAttributeVariant(dbg, v, indent + QLatin1Char('\t')); - } - break; - default: - dbg << QString::asprintf("%sunknown variant type %d\n", indent.toUtf8().constData(), - var.userType()); - } -} - -QDebug operator << (QDebug dbg, const QBluetoothServiceInfo &info) -{ - QDebugStateSaver saver(dbg); - dbg.noquote() << "\n"; - const QList<quint16> attributes = info.attributes(); - for (quint16 id : attributes) { - dumpAttributeVariant(dbg, info.attribute(id), QString::fromLatin1("(%1)\t").arg(id)); - } - return dbg; -} - -QBluetoothServiceInfo::Sequence QBluetoothServiceInfoPrivate::protocolDescriptor(QBluetoothUuid::ProtocolUuid protocol) const -{ - if (!attributes.contains(QBluetoothServiceInfo::ProtocolDescriptorList)) - return QBluetoothServiceInfo::Sequence(); - - const QBluetoothServiceInfo::Sequence sequence - = attributes.value(QBluetoothServiceInfo::ProtocolDescriptorList).value<QBluetoothServiceInfo::Sequence>(); - for (const QVariant &v : sequence) { - QBluetoothServiceInfo::Sequence parameters = v.value<QBluetoothServiceInfo::Sequence>(); - if (parameters.empty()) - continue; - if (parameters.at(0).userType() == qMetaTypeId<QBluetoothUuid>()) { - if (parameters.at(0).value<QBluetoothUuid>() == protocol) - return parameters; - } - } - - return QBluetoothServiceInfo::Sequence(); -} - -QBluetoothServiceInfo::Protocol QBluetoothServiceInfoPrivate::socketProtocol() const -{ - QBluetoothServiceInfo::Sequence parameters = protocolDescriptor(QBluetoothUuid::Rfcomm); - if (!parameters.isEmpty()) - return QBluetoothServiceInfo::RfcommProtocol; - - parameters = protocolDescriptor(QBluetoothUuid::L2cap); - if (!parameters.isEmpty()) - return QBluetoothServiceInfo::L2capProtocol; - - return QBluetoothServiceInfo::UnknownProtocol; -} - - -int QBluetoothServiceInfoPrivate::protocolServiceMultiplexer() const -{ - const QBluetoothServiceInfo::Sequence parameters = protocolDescriptor(QBluetoothUuid::L2cap); - if (parameters.isEmpty()) - return -1; - else if (parameters.count() == 1) - return 0; - - return parameters.at(1).toUInt(); -} - - -int QBluetoothServiceInfoPrivate::serverChannel() const -{ - const QBluetoothServiceInfo::Sequence parameters = protocolDescriptor(QBluetoothUuid::Rfcomm); - if (parameters.isEmpty()) - return -1; - else if (parameters.count() == 1) - return 0; - - return parameters.at(1).toUInt(); -} - QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothserviceinfo_p.h b/src/bluetooth/qbluetoothserviceinfo_p.h index e4e835e4..a0515b8c 100644 --- a/src/bluetooth/qbluetoothserviceinfo_p.h +++ b/src/bluetooth/qbluetoothserviceinfo_p.h @@ -59,6 +59,10 @@ #include <QMap> #include <QVariant> +#ifdef Q_OS_MACOS +#include "osx/btraii_p.h" +#endif + class OrgBluezServiceInterface; class OrgBluezProfileManager1Interface; @@ -82,7 +86,6 @@ QT_BEGIN_NAMESPACE class QBluetoothServiceInfo; -#ifndef QT_OSX_BLUETOOTH class QBluetoothServiceInfoPrivate : public QObject @@ -120,11 +123,20 @@ private: bool writeSdpAttributes(); #endif - mutable bool registered; -}; +#if QT_OSX_BLUETOOTH +public: + bool registerService(const QBluetoothServiceInfo &info); -#endif +private: + + using SDPRecord = DarwinBluetooth::ScopedPointer; + SDPRecord serviceRecord; + quint32 serviceRecordHandle = 0; +#endif // QT_OSX_BLUETOOTH + + mutable bool registered = false; +}; QT_END_NAMESPACE -#endif +#endif // QBLUETOOTHSERVICEINFO_P_H diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp index db7c8be4..b1f75741 100644 --- a/src/bluetooth/qbluetoothsocket.cpp +++ b/src/bluetooth/qbluetoothsocket.cpp @@ -47,6 +47,8 @@ #include "qbluetoothsocket_android_p.h" #elif defined(QT_WINRT_BLUETOOTH) #include "qbluetoothsocket_winrt_p.h" +#elif defined(QT_OSX_BLUETOOTH) +#include "qbluetoothsocket_osx_p.h" #else #include "qbluetoothsocket_dummy_p.h" #endif @@ -267,6 +269,8 @@ static QBluetoothSocketBasePrivate *createSocketPrivate() return new QBluetoothSocketPrivateAndroid(); #elif defined(QT_WINRT_BLUETOOTH) return new QBluetoothSocketPrivateWinRT(); +#elif defined(QT_OSX_BLUETOOTH) + return new QBluetoothSocketPrivate(); #else return new QBluetoothSocketPrivateDummy(); #endif @@ -513,6 +517,9 @@ QString QBluetoothSocket::errorString() const */ void QBluetoothSocket::setPreferredSecurityFlags(QBluetooth::SecurityFlags flags) { +#ifdef QT_OSX_BLUETOOTH + return; // not supported on macOS. +#endif Q_D(QBluetoothSocketBase); if (d->secFlags != flags) d->secFlags = flags; @@ -534,8 +541,13 @@ void QBluetoothSocket::setPreferredSecurityFlags(QBluetooth::SecurityFlags flags */ QBluetooth::SecurityFlags QBluetoothSocket::preferredSecurityFlags() const { +#if QT_OSX_BLUETOOTH + // not supported on macOS - platform always uses encryption + return QBluetooth::Secure; +#else Q_D(const QBluetoothSocketBase); return d->secFlags; +#endif // QT_OSX_BLUETOOTH } /*! @@ -559,6 +571,9 @@ void QBluetoothSocket::setSocketState(QBluetoothSocket::SocketState state) emit disconnected(); } if(state == ListeningState){ +#ifdef QT_OSX_BLUETOOTH + qCWarning(QT_BT) << "listening socket is not supported by IOBluetooth"; +#endif // TODO: look at this, is this really correct? // if we're a listening socket we can't handle connects? if (d->readNotifier) { diff --git a/src/bluetooth/qbluetoothsocket.h b/src/bluetooth/qbluetoothsocket.h index d2535544..256a95dc 100644 --- a/src/bluetooth/qbluetoothsocket.h +++ b/src/bluetooth/qbluetoothsocket.h @@ -52,21 +52,14 @@ QT_BEGIN_NAMESPACE -#ifndef QT_OSX_BLUETOOTH + class QBluetoothSocketBasePrivate; -#else -class QBluetoothSocketPrivate; -#endif class Q_BLUETOOTH_EXPORT QBluetoothSocket : public QIODevice { Q_OBJECT -#ifndef QT_OSX_BLUETOOTH - Q_DECLARE_PRIVATE(QBluetoothSocketBase) -#else - Q_DECLARE_PRIVATE(QBluetoothSocket) -#endif + Q_DECLARE_PRIVATE(QBluetoothSocketBase) friend class QBluetoothServer; friend class QBluetoothServerPrivate; @@ -187,11 +180,8 @@ protected: QBluetoothServiceInfo::Protocol socketType, QObject *parent = nullptr); #endif -#ifndef QT_OSX_BLUETOOTH + QBluetoothSocketBasePrivate *d_ptr; -#else - QBluetoothSocketPrivate *d_ptr; -#endif private: friend class QLowEnergyControllerPrivateBluez; diff --git a/src/bluetooth/qbluetoothsocket_osx.mm b/src/bluetooth/qbluetoothsocket_osx.mm index 7f630146..f74c14f8 100644 --- a/src/bluetooth/qbluetoothsocket_osx.mm +++ b/src/bluetooth/qbluetoothsocket_osx.mm @@ -43,6 +43,9 @@ // dependencies problem. #include "qbluetoothsocketbase_p.h" #include "qbluetoothsocket_osx_p.h" + +#include "osx/osxbtrfcommchannel_p.h" +#include "osx/osxbtl2capchannel_p.h" #include "qbluetoothlocaldevice.h" #include "qbluetoothdeviceinfo.h" #include "osx/osxbtutility_p.h" @@ -57,30 +60,294 @@ QT_BEGIN_NAMESPACE +namespace { + +using DarwinBluetooth::RetainPolicy; +using ObjCL2CAPChannel = QT_MANGLE_NAMESPACE(OSXBTL2CAPChannel); +using ObjCRFCOMMChannel = QT_MANGLE_NAMESPACE(OSXBTRFCOMMChannel); + +} // unnamed namespace + QBluetoothSocketPrivate::QBluetoothSocketPrivate() - : writeChunk(std::numeric_limits<UInt16>::max()), - openMode(QIODevice::NotOpen), // That's what is set in public class' ctors. - state(QBluetoothSocket::UnconnectedState), - socketType(QBluetoothServiceInfo::UnknownProtocol), - socketError(QBluetoothSocket::NoSocketError), - isConnecting(false) + : writeChunk(std::numeric_limits<UInt16>::max()) { q_ptr = nullptr; } QBluetoothSocketPrivate::~QBluetoothSocketPrivate() { - // "Empty" dtor to make a shared pointer happy (parametrized with - // incomplete type in the header file). +} + +bool QBluetoothSocketPrivate::ensureNativeSocket(QBluetoothServiceInfo::Protocol type) +{ + // For now - very simplistic, we don't call it in this file, public class + // only calls it in a ctor, setting the protocol RFCOMM (in case of Android) + // or, indeed, doing, socket-related initialization in BlueZ backend. + Q_ASSERT(socketType == QBluetoothServiceInfo::UnknownProtocol); + socketType = type; + return type != QBluetoothServiceInfo::UnknownProtocol; +} + +QString QBluetoothSocketPrivate::localName() const +{ + const QBluetoothLocalDevice device; + return device.name(); +} + +QBluetoothAddress QBluetoothSocketPrivate::localAddress() const +{ + const QBluetoothLocalDevice device; + return device.address(); +} + +quint16 QBluetoothSocketPrivate::localPort() const +{ + return 0; +} + +QString QBluetoothSocketPrivate::peerName() const +{ + QT_BT_MAC_AUTORELEASEPOOL; + + NSString *nsName = nil; + if (socketType == QBluetoothServiceInfo::RfcommProtocol) { + if (rfcommChannel) + nsName = [rfcommChannel.getAs<ObjCRFCOMMChannel>() peerName]; + } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { + if (l2capChannel) + nsName = [l2capChannel.getAs<ObjCL2CAPChannel>() peerName]; + } + + if (nsName) + return QString::fromNSString(nsName); + + return QString(); +} + +QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const +{ + BluetoothDeviceAddress addr = {}; + if (socketType == QBluetoothServiceInfo::RfcommProtocol) { + if (rfcommChannel) + addr = [rfcommChannel.getAs<ObjCRFCOMMChannel>() peerAddress]; + } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { + if (l2capChannel) + addr = [l2capChannel.getAs<ObjCL2CAPChannel>() peerAddress]; + } + + return OSXBluetooth::qt_address(&addr); +} + +quint16 QBluetoothSocketPrivate::peerPort() const +{ + if (socketType == QBluetoothServiceInfo::RfcommProtocol) { + if (rfcommChannel) + return [rfcommChannel.getAs<ObjCRFCOMMChannel>() getChannelID]; + } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { + if (l2capChannel) + return [l2capChannel.getAs<ObjCL2CAPChannel>() getPSM]; + } + + return 0; +} + +void QBluetoothSocketPrivate::abort() +{ + // Can never be called while we're in connectToService: + Q_ASSERT_X(!isConnecting, Q_FUNC_INFO, "internal inconsistency - " + "still in connectToService()"); + + if (socketType == QBluetoothServiceInfo::RfcommProtocol) + rfcommChannel.reset(); + else if (socketType == QBluetoothServiceInfo::L2capProtocol) + l2capChannel.reset(); + + Q_ASSERT(q_ptr); + + q_ptr->setSocketState(QBluetoothSocket::UnconnectedState); + emit q_ptr->readChannelFinished(); + emit q_ptr->disconnected(); + +} + +void QBluetoothSocketPrivate::close() +{ + // Can never be called while we're in connectToService: + Q_ASSERT_X(!isConnecting, Q_FUNC_INFO, "internal inconsistency - " + "still in connectToService()"); + + if (!txBuffer.size()) + abort(); +} + + +qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize) +{ + Q_ASSERT_X(data, Q_FUNC_INFO, "invalid data (null)"); + Q_ASSERT_X(maxSize > 0, Q_FUNC_INFO, "invalid data size"); + + if (state != QBluetoothSocket::ConnectedState) { + errorString = QCoreApplication::translate(SOCKET, SOC_NOWRITE); + q_ptr->setSocketError(QBluetoothSocket::OperationError); + return -1; + } + + // We do not have a real socket API under the hood, + // IOBluetoothL2CAPChannel is buffered (writeAsync). + + if (!txBuffer.size()) + QMetaObject::invokeMethod(this, "_q_writeNotify", Qt::QueuedConnection); + + char *dst = txBuffer.reserve(int(maxSize)); + std::copy(data, data + maxSize, dst); + + return maxSize; +} + +qint64 QBluetoothSocketPrivate::readData(char *data, qint64 maxSize) +{ + if (!data) + return 0; + + if (state != QBluetoothSocket::ConnectedState) { + errorString = QCoreApplication::translate(SOCKET, SOC_NOREAD); + q_ptr->setSocketError(QBluetoothSocket::OperationError); + return -1; + } + + if (!buffer.isEmpty()) + return buffer.read(data, int(maxSize)); + + return 0; +} + +qint64 QBluetoothSocketPrivate::bytesAvailable() const +{ + return buffer.size(); +} + +bool QBluetoothSocketPrivate::canReadLine() const +{ + return buffer.canReadLine(); +} + +qint64 QBluetoothSocketPrivate::bytesToWrite() const +{ + return txBuffer.size(); +} + +bool QBluetoothSocketPrivate::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, + QBluetoothSocket::SocketState socketState, QIODevice::OpenMode openMode) +{ + Q_UNUSED(socketDescriptor) + Q_UNUSED(socketType) + Q_UNUSED(socketState) + Q_UNUSED(openMode) + + qCWarning(QT_BT_OSX) << "setting a socket descriptor is not supported by IOBluetooth"; + // Noop on macOS. + return true; +} + +void QBluetoothSocketPrivate::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, + QIODevice::OpenMode openMode) +{ + Q_UNUSED(address) + Q_UNUSED(port) + Q_UNUSED(openMode) +} + +void QBluetoothSocketPrivate::connectToService(const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode) +{ + Q_ASSERT(q_ptr); + + OSXBluetooth::qt_test_iobluetooth_runloop(); + + if (state!= QBluetoothSocket::UnconnectedState && state != QBluetoothSocket::ServiceLookupState) { + qCWarning(QT_BT_OSX) << "called on a busy socket"; + errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS); + q_ptr->setSocketError(QBluetoothSocket::OperationError); + return; + } + + // Report this problem early, potentially avoid device discovery: + if (service.socketProtocol() == QBluetoothServiceInfo::UnknownProtocol) { + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type"; + errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR); + q_ptr->setSocketError(QBluetoothSocket::UnsupportedProtocolError); + return; + } + + socketType = service.socketProtocol(); + + if (service.protocolServiceMultiplexer() > 0) { + connectToService(service.device().address(), + quint16(service.protocolServiceMultiplexer()), + openMode); + } else if (service.serverChannel() > 0) { + connectToService(service.device().address(), + quint16(service.serverChannel()), + openMode); + } else { + // Try service discovery. + if (service.serviceUuid().isNull()) { + qCWarning(QT_BT_OSX) << "No port, no PSM, and no " + "UUID provided, unable to connect"; + return; + } + + q_ptr->doDeviceDiscovery(service, openMode); + } +} + +void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, + QIODevice::OpenMode openMode) +{ + Q_ASSERT(q_ptr); + + OSXBluetooth::qt_test_iobluetooth_runloop(); + + // Report this problem early, avoid device discovery: + if (socketType == QBluetoothServiceInfo::UnknownProtocol) { + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type"; + errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR); + q_ptr->setSocketError(QBluetoothSocket::UnsupportedProtocolError); + return; + } + + if (state != QBluetoothSocket::UnconnectedState) { + qCWarning(QT_BT_OSX) << "called on a busy socket"; + errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS); + q_ptr->setSocketError(QBluetoothSocket::OperationError); + return; + } + + QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice); + QBluetoothServiceInfo service; + service.setDevice(device); + service.setServiceUuid(uuid); + q_ptr->doDeviceDiscovery(service, openMode); } void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode mode) { - Q_ASSERT_X(state == QBluetoothSocket::ServiceLookupState - || state == QBluetoothSocket::UnconnectedState, + Q_ASSERT(q_ptr); + + OSXBluetooth::qt_test_iobluetooth_runloop(); + + if (socketType == QBluetoothServiceInfo::UnknownProtocol) { + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type"; + errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR); + q_ptr->setSocketError(QBluetoothSocket::UnsupportedProtocolError); + return; + } + + Q_ASSERT_X(state == QBluetoothSocket::ServiceLookupState || state == QBluetoothSocket::UnconnectedState, Q_FUNC_INFO, "invalid state"); + q_ptr->setOpenMode(mode); + socketError = QBluetoothSocket::NoSocketError; errorString.clear(); buffer.clear(); @@ -100,15 +367,15 @@ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, openMode = mode; if (socketType == QBluetoothServiceInfo::RfcommProtocol) { - rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this]); + rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this], RetainPolicy::noInitialRetain); if (rfcommChannel) - status = [rfcommChannel connectAsyncToDevice:address withChannelID:port]; + status = [rfcommChannel.getAs<ObjCRFCOMMChannel>() connectAsyncToDevice:address withChannelID:port]; else status = kIOReturnNoMemory; } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { - l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this]); + l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this], RetainPolicy::noInitialRetain); if (l2capChannel) - status = [l2capChannel connectAsyncToDevice:address withPSM:port]; + status = [l2capChannel.getAs<ObjCL2CAPChannel>() connectAsyncToDevice:address withPSM:port]; else status = kIOReturnNoMemory; } @@ -148,84 +415,6 @@ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, } } -void QBluetoothSocketPrivate::close() -{ - // Can never be called while we're in connectToService: - Q_ASSERT_X(!isConnecting, Q_FUNC_INFO, "internal inconsistency - " - "still in connectToService()"); - - if (!txBuffer.size()) - abort(); -} - -void QBluetoothSocketPrivate::abort() -{ - // Can never be called while we're in connectToService: - Q_ASSERT_X(!isConnecting, Q_FUNC_INFO, "internal inconsistency - " - "still in connectToService()"); - - if (socketType == QBluetoothServiceInfo::RfcommProtocol) - rfcommChannel.reset(nil); - else if (socketType == QBluetoothServiceInfo::L2capProtocol) - l2capChannel.reset(nil); -} - -quint64 QBluetoothSocketPrivate::bytesAvailable() const -{ - return buffer.size(); -} - -QString QBluetoothSocketPrivate::peerName() const -{ - QT_BT_MAC_AUTORELEASEPOOL; - - NSString *nsName = nil; - if (socketType == QBluetoothServiceInfo::RfcommProtocol) { - if (rfcommChannel) - nsName = [rfcommChannel peerName]; - } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { - if (l2capChannel) - nsName = [l2capChannel peerName]; - } - - if (nsName) - return QString::fromNSString(nsName); - - return QString(); -} - -QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const -{ - BluetoothDeviceAddress addr = {}; - if (socketType == QBluetoothServiceInfo::RfcommProtocol) { - if (rfcommChannel) - addr = [rfcommChannel peerAddress]; - } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { - if (l2capChannel) - addr = [l2capChannel peerAddress]; - } - - return OSXBluetooth::qt_address(&addr); -} - -quint16 QBluetoothSocketPrivate::peerPort() const -{ - if (socketType == QBluetoothServiceInfo::RfcommProtocol) { - if (rfcommChannel) - return [rfcommChannel getChannelID]; - } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { - if (l2capChannel) - return [l2capChannel getPSM]; - } - - return 0; -} - -void QBluetoothSocketPrivate::_q_readNotify() -{ - // Noop. -} - void QBluetoothSocketPrivate::_q_writeNotify() { Q_ASSERT_X(socketType == QBluetoothServiceInfo::L2capProtocol @@ -238,14 +427,14 @@ void QBluetoothSocketPrivate::_q_writeNotify() if (txBuffer.size()) { const bool isL2CAP = socketType == QBluetoothServiceInfo::L2capProtocol; writeChunk.resize(isL2CAP ? std::numeric_limits<UInt16>::max() : - [rfcommChannel getMTU]); + [rfcommChannel.getAs<ObjCRFCOMMChannel>() getMTU]); const int size = txBuffer.read(writeChunk.data(), writeChunk.size()); IOReturn status = kIOReturnError; if (!isL2CAP) - status = [rfcommChannel writeAsync:writeChunk.data() length:UInt16(size)]; + status = [rfcommChannel.getAs<ObjCRFCOMMChannel>() writeAsync:writeChunk.data() length:UInt16(size)]; else - status = [l2capChannel writeAsync:writeChunk.data() length:UInt16(size)]; + status = [l2capChannel.getAs<ObjCL2CAPChannel>() writeAsync:writeChunk.data() length:UInt16(size)]; if (status != kIOReturnSuccess) { errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR); @@ -260,21 +449,22 @@ void QBluetoothSocketPrivate::_q_writeNotify() close(); } -bool QBluetoothSocketPrivate::setChannel(IOBluetoothRFCOMMChannel *channel) +bool QBluetoothSocketPrivate::setRFCOMChannel(void *generic) { // A special case "constructor": on OS X we do not have a real listening socket, // instead a bluetooth server "listens" for channel open notifications and // creates (if asked by a user later) a "socket" object // for this connection. This function initializes // a "socket" from such an external channel (reported by a notification). - + auto channel = static_cast<IOBluetoothRFCOMMChannel *>(generic); // It must be a newborn socket! Q_ASSERT_X(socketError == QBluetoothSocket::NoSocketError && state == QBluetoothSocket::UnconnectedState && !rfcommChannel && !l2capChannel, Q_FUNC_INFO, "unexpected socket state"); openMode = QIODevice::ReadWrite; - rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this channel:channel]); + rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this channel:channel], + RetainPolicy::noInitialRetain); if (rfcommChannel) {// We do not handle errors, up to an external user. q_ptr->setOpenMode(QIODevice::ReadWrite); state = QBluetoothSocket::ConnectedState; @@ -284,13 +474,14 @@ bool QBluetoothSocketPrivate::setChannel(IOBluetoothRFCOMMChannel *channel) return rfcommChannel; } -bool QBluetoothSocketPrivate::setChannel(IOBluetoothL2CAPChannel *channel) +bool QBluetoothSocketPrivate::setL2CAPChannel(void *generic) { // A special case "constructor": on OS X we do not have a real listening socket, // instead a bluetooth server "listens" for channel open notifications and // creates (if asked by a user later) a "socket" object // for this connection. This function initializes // a "socket" from such an external channel (reported by a notification). + auto channel = static_cast<IOBluetoothL2CAPChannel *>(generic); // It must be a newborn socket! Q_ASSERT_X(socketError == QBluetoothSocket::NoSocketError @@ -298,7 +489,7 @@ bool QBluetoothSocketPrivate::setChannel(IOBluetoothL2CAPChannel *channel) Q_FUNC_INFO, "unexpected socket state"); openMode = QIODevice::ReadWrite; - l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this channel:channel]); + l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this channel:channel], RetainPolicy::noInitialRetain); if (l2capChannel) {// We do not handle errors, up to an external user. q_ptr->setOpenMode(QIODevice::ReadWrite); state = QBluetoothSocket::ConnectedState; @@ -308,7 +499,6 @@ bool QBluetoothSocketPrivate::setChannel(IOBluetoothL2CAPChannel *channel) return l2capChannel; } - void QBluetoothSocketPrivate::setChannelError(IOReturn errorCode) { Q_UNUSED(errorCode) @@ -365,7 +555,7 @@ void QBluetoothSocketPrivate::readChannelData(void *data, std::size_t size) Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); const char *src = static_cast<char *>(data); - char *dst = buffer.reserve(size); + char *dst = buffer.reserve(int(size)); std::copy(src, src + size, dst); if (!isConnecting) { @@ -379,449 +569,4 @@ void QBluetoothSocketPrivate::writeComplete() _q_writeNotify(); } -qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize) -{ - Q_ASSERT_X(data, Q_FUNC_INFO, "invalid data (null)"); - Q_ASSERT_X(maxSize > 0, Q_FUNC_INFO, "invalid data size"); - - if (state != QBluetoothSocket::ConnectedState) { - errorString = QCoreApplication::translate(SOCKET, SOC_NOWRITE); - q_ptr->setSocketError(QBluetoothSocket::OperationError); - return -1; - } - - // We do not have a real socket API under the hood, - // IOBluetoothL2CAPChannel buffered (writeAsync). - - if (!txBuffer.size()) - QMetaObject::invokeMethod(this, "_q_writeNotify", Qt::QueuedConnection); - - char *dst = txBuffer.reserve(maxSize); - std::copy(data, data + maxSize, dst); - - return maxSize; -} - -QBluetoothSocket::QBluetoothSocket(QBluetoothServiceInfo::Protocol socketType, QObject *parent) - : QIODevice(parent), - d_ptr(new QBluetoothSocketPrivate) -{ - d_ptr->q_ptr = this; - d_ptr->socketType = socketType; - - setOpenMode(NotOpen); -} - -QBluetoothSocket::QBluetoothSocket(QObject *parent) - : QIODevice(parent), - d_ptr(new QBluetoothSocketPrivate) -{ - d_ptr->q_ptr = this; - setOpenMode(NotOpen); -} - -QBluetoothSocket::~QBluetoothSocket() -{ - delete d_ptr; -} - -bool QBluetoothSocket::isSequential() const -{ - return true; -} - -qint64 QBluetoothSocket::bytesAvailable() const -{ - return QIODevice::bytesAvailable() + d_ptr->bytesAvailable(); -} - -qint64 QBluetoothSocket::bytesToWrite() const -{ - return d_ptr->txBuffer.size(); -} - -void QBluetoothSocket::connectToService(const QBluetoothServiceInfo &service, OpenMode openMode) -{ - OSXBluetooth::qt_test_iobluetooth_runloop(); - - if (state() != UnconnectedState && state() != ServiceLookupState) { - qCWarning(QT_BT_OSX) << "called on a busy socket"; - d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS); - setSocketError(OperationError); - return; - } - - // Report this problem early, potentially avoid device discovery: - if (service.socketProtocol() == QBluetoothServiceInfo::UnknownProtocol) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type"; - d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR); - setSocketError(QBluetoothSocket::UnsupportedProtocolError); - return; - } - - d_ptr->socketType = service.socketProtocol(); - - if (service.protocolServiceMultiplexer() > 0) { - d_ptr->connectToService(service.device().address(), - service.protocolServiceMultiplexer(), - openMode); - } else if (service.serverChannel() > 0) { - d_ptr->connectToService(service.device().address(), - service.serverChannel(), openMode); - } else { - // Try service discovery. - if (service.serviceUuid().isNull()) { - qCWarning(QT_BT_OSX) << "No port, no PSM, and no " - "UUID provided, unable to connect"; - return; - } - - doDeviceDiscovery(service, openMode); - } -} - -void QBluetoothSocket::connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, - OpenMode openMode) -{ - OSXBluetooth::qt_test_iobluetooth_runloop(); - - // Report this problem early, avoid device discovery: - if (socketType() == QBluetoothServiceInfo::UnknownProtocol) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type"; - d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR); - setSocketError(QBluetoothSocket::UnsupportedProtocolError); - return; - } - - if (state() != QBluetoothSocket::UnconnectedState) { - qCWarning(QT_BT_OSX) << "called on a busy socket"; - d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS); - setSocketError(QBluetoothSocket::OperationError); - return; - } - - QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice); - QBluetoothServiceInfo service; - service.setDevice(device); - service.setServiceUuid(uuid); - doDeviceDiscovery(service, openMode); -} - -void QBluetoothSocket::connectToService(const QBluetoothAddress &address, quint16 port, - OpenMode openMode) -{ - OSXBluetooth::qt_test_iobluetooth_runloop(); - - if (socketType() == QBluetoothServiceInfo::UnknownProtocol) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type"; - d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR); - setSocketError(QBluetoothSocket::UnsupportedProtocolError); - return; - } - - if (state() != QBluetoothSocket::UnconnectedState) { - qCWarning(QT_BT_OSX) << "called on a busy socket"; - d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS); - setSocketError(OperationError); - return; - } - - setOpenMode(openMode); - d_ptr->connectToService(address, port, openMode); -} - -QBluetoothServiceInfo::Protocol QBluetoothSocket::socketType() const -{ - return d_ptr->socketType; -} - -QBluetoothSocket::SocketState QBluetoothSocket::state() const -{ - return d_ptr->state; -} - -QBluetoothSocket::SocketError QBluetoothSocket::error() const -{ - return d_ptr->socketError; -} - -QString QBluetoothSocket::errorString() const -{ - return d_ptr->errorString; -} - -void QBluetoothSocket::setSocketState(QBluetoothSocket::SocketState state) -{ - const SocketState oldState = d_ptr->state; - d_ptr->state = state; - if (oldState != d_ptr->state) - emit stateChanged(state); - - if (state == ListeningState) { - // We can register for L2CAP/RFCOMM open notifications, - // that's different from 'listen' and is implemented - // in QBluetoothServer. - qCWarning(QT_BT_OSX) << "listening sockets are not supported"; - } -} - -bool QBluetoothSocket::canReadLine() const -{ - return d_ptr->buffer.canReadLine() || QIODevice::canReadLine(); -} - -void QBluetoothSocket::setSocketError(QBluetoothSocket::SocketError socketError) -{ - d_ptr->socketError = socketError; - emit error(socketError); -} - -void QBluetoothSocket::doDeviceDiscovery(const QBluetoothServiceInfo &service, OpenMode openMode) -{ - OSXBluetooth::qt_test_iobluetooth_runloop(); - - setSocketState(ServiceLookupState); - - if (d_ptr->discoveryAgent) - d_ptr->discoveryAgent->stop(); - - d_ptr->discoveryAgent.reset(new QBluetoothServiceDiscoveryAgent(this)); - d_ptr->discoveryAgent->setRemoteAddress(service.device().address()); - - connect(d_ptr->discoveryAgent.data(), SIGNAL(serviceDiscovered(QBluetoothServiceInfo)), - this, SLOT(serviceDiscovered(QBluetoothServiceInfo))); - connect(d_ptr->discoveryAgent.data(), SIGNAL(finished()), - this, SLOT(discoveryFinished())); - - d_ptr->openMode = openMode; - - if (!service.serviceUuid().isNull()) - d_ptr->discoveryAgent->setUuidFilter(service.serviceUuid()); - - if (!service.serviceClassUuids().isEmpty()) - d_ptr->discoveryAgent->setUuidFilter(service.serviceClassUuids()); - - Q_ASSERT_X(!d_ptr->discoveryAgent->uuidFilter().isEmpty(), Q_FUNC_INFO, - "invalid service info"); - - d_ptr->discoveryAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery); -} - -void QBluetoothSocket::serviceDiscovered(const QBluetoothServiceInfo &service) -{ - if (service.protocolServiceMultiplexer() != 0 || service.serverChannel() != 0) { - d_ptr->discoveryAgent->stop(); - connectToService(service, d_ptr->openMode); - } -} - -void QBluetoothSocket::discoveryFinished() -{ - d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_SERVICE_NOT_FOUND); - setSocketState(UnconnectedState); - setSocketError(ServiceNotFoundError); -} - -void QBluetoothSocket::abort() -{ - if (state() == UnconnectedState) - return; - - setOpenMode(NotOpen); - - if (state() == ServiceLookupState && d_ptr->discoveryAgent) { - d_ptr->discoveryAgent->disconnect(); - d_ptr->discoveryAgent->stop(); - d_ptr->discoveryAgent.reset(); - } - - setSocketState(QBluetoothSocket::ClosingState); - d_ptr->abort(); - - setSocketState(QBluetoothSocket::UnconnectedState); - emit readChannelFinished(); - emit disconnected(); -} - -void QBluetoothSocket::disconnectFromService() -{ - close(); -} - -QString QBluetoothSocket::localName() const -{ - const QBluetoothLocalDevice device; - return device.name(); -} - -QBluetoothAddress QBluetoothSocket::localAddress() const -{ - const QBluetoothLocalDevice device; - return device.address(); -} - -quint16 QBluetoothSocket::localPort() const -{ - return 0; -} - -QString QBluetoothSocket::peerName() const -{ - return d_ptr->peerName(); -} - -QBluetoothAddress QBluetoothSocket::peerAddress() const -{ - return d_ptr->peerAddress(); -} - -quint16 QBluetoothSocket::peerPort() const -{ - return d_ptr->peerPort(); -} - -qint64 QBluetoothSocket::writeData(const char *data, qint64 maxSize) -{ - if (!data || maxSize <= 0) { - d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_INVAL_DATASIZE); - setSocketError(QBluetoothSocket::OperationError); - return -1; - } - - return d_ptr->writeData(data, maxSize); -} - -qint64 QBluetoothSocketPrivate::readData(char *data, qint64 maxSize) -{ - if (state != QBluetoothSocket::ConnectedState) { - errorString = QCoreApplication::translate(SOCKET, SOC_NOREAD); - q_ptr->setSocketError(QBluetoothSocket::OperationError); - return -1; - } - - if (!buffer.isEmpty()) - return buffer.read(data, maxSize); - - return 0; -} - -qint64 QBluetoothSocket::readData(char *data, qint64 maxSize) -{ - return d_ptr->readData(data, maxSize); -} - -void QBluetoothSocket::close() -{ - if (state() == UnconnectedState) - return; - - setOpenMode(NotOpen); - - if (state() == ServiceLookupState && d_ptr->discoveryAgent) { - d_ptr->discoveryAgent->disconnect(); - d_ptr->discoveryAgent->stop(); - d_ptr->discoveryAgent.reset(); - } - - setSocketState(ClosingState); - - d_ptr->close(); - - setSocketState(UnconnectedState); - emit readChannelFinished(); - emit disconnected(); -} - -bool QBluetoothSocket::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, - SocketState socketState, OpenMode openMode) -{ - Q_UNUSED(socketDescriptor) - Q_UNUSED(socketType) - Q_UNUSED(socketState) - Q_UNUSED(openMode) - - // Noop on OS X. - return true; -} - -int QBluetoothSocket::socketDescriptor() const -{ - return -1; -} - -/* not supported on OS X */ -void QBluetoothSocket::setPreferredSecurityFlags(QBluetooth::SecurityFlags flags) -{ - Q_UNUSED(flags) -} - -/* not supported on OS X - platform always uses encryption */ -QBluetooth::SecurityFlags QBluetoothSocket::preferredSecurityFlags() const -{ - return QBluetooth::Secure; -} - -#ifndef QT_NO_DEBUG_STREAM - -QDebug operator<<(QDebug debug, QBluetoothSocket::SocketError error) -{ - switch (error) { - case QBluetoothSocket::UnknownSocketError: - debug << "QBluetoothSocket::UnknownSocketError"; - break; - case QBluetoothSocket::HostNotFoundError: - debug << "QBluetoothSocket::HostNotFoundError"; - break; - case QBluetoothSocket::RemoteHostClosedError: - debug << "QBluetoothSocket::RemoteHostClosedError"; - break; - case QBluetoothSocket::ServiceNotFoundError: - debug << "QBluetoothSocket::ServiceNotFoundError"; - break; - case QBluetoothSocket::NetworkError: - debug << "QBluetoothSocket::NetworkError"; - break; - case QBluetoothSocket::UnsupportedProtocolError: - debug << "QBluetoothSocket::UnsupportedProtocolError"; - break; - default: - debug << "QBluetoothSocket::SocketError(" << (int)error << ")"; - } - return debug; -} - -QDebug operator<<(QDebug debug, QBluetoothSocket::SocketState state) -{ - switch (state) { - case QBluetoothSocket::UnconnectedState: - debug << "QBluetoothSocket::UnconnectedState"; - break; - case QBluetoothSocket::ConnectingState: - debug << "QBluetoothSocket::ConnectingState"; - break; - case QBluetoothSocket::ConnectedState: - debug << "QBluetoothSocket::ConnectedState"; - break; - case QBluetoothSocket::BoundState: - debug << "QBluetoothSocket::BoundState"; - break; - case QBluetoothSocket::ClosingState: - debug << "QBluetoothSocket::ClosingState"; - break; - case QBluetoothSocket::ListeningState: - debug << "QBluetoothSocket::ListeningState"; - break; - case QBluetoothSocket::ServiceLookupState: - debug << "QBluetoothSocket::ServiceLookupState"; - break; - default: - debug << "QBluetoothSocket::SocketState(" << (int)state << ")"; - } - return debug; -} - -#endif // QT_NO_DEBUG_STREAM - -#include "moc_qbluetoothsocket.cpp" - QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothsocket_osx_p.h b/src/bluetooth/qbluetoothsocket_osx_p.h index dcc684b8..1291878c 100644 --- a/src/bluetooth/qbluetoothsocket_osx_p.h +++ b/src/bluetooth/qbluetoothsocket_osx_p.h @@ -53,12 +53,11 @@ #ifdef QT_OSX_BLUETOOTH -#include "osx/osxbtchanneldelegate_p.h" -#include "osx/osxbtrfcommchannel_p.h" -#include "osx/osxbtl2capchannel_p.h" +#include "qbluetoothsocketbase_p.h" #include "qbluetoothserviceinfo.h" -#include "osx/osxbtutility_p.h" +#include "osx/btdelegates_p.h" #include "qbluetoothsocket.h" +#include "osx/btraii_p.h" #ifndef QPRIVATELINEARBUFFER_BUFFERSIZE #define QPRIVATELINEARBUFFER_BUFFERSIZE Q_INT64_C(16384) @@ -74,14 +73,11 @@ #include <QtCore/qstring.h> #include <QtCore/qvector.h> -@class IOBluetoothRFCOMMChannel; -@class IOBluetoothL2CAPChannel; - QT_BEGIN_NAMESPACE class QBluetoothAddress; -class QBluetoothSocketPrivate : public QBluetoothSocketBasePrivate, public OSXBluetooth::ChannelDelegate +class QBluetoothSocketPrivate : public QBluetoothSocketBasePrivate, public DarwinBluetooth::ChannelDelegate { friend class QBluetoothSocket; friend class QBluetoothServer; @@ -90,25 +86,47 @@ public: QBluetoothSocketPrivate(); ~QBluetoothSocketPrivate(); - void connectToService(const QBluetoothAddress &address, quint16 port, - QIODevice::OpenMode openMode); + // + bool ensureNativeSocket(QBluetoothServiceInfo::Protocol type) override; + + QString localName() const override; + QBluetoothAddress localAddress() const override; + quint16 localPort() const override; + + QString peerName() const override; + QBluetoothAddress peerAddress() const override; + quint16 peerPort() const override; - void close(); - void abort(); + void abort() override; + void close() override; - quint64 bytesAvailable() const; + qint64 writeData(const char *data, qint64 maxSize) override; + qint64 readData(char *data, qint64 maxSize) override; - QString peerName() const; - QBluetoothAddress peerAddress() const; - quint16 peerPort() const; + qint64 bytesAvailable() const override; + bool canReadLine() const override; + qint64 bytesToWrite() const override; - void _q_readNotify(); - void _q_writeNotify() override; + bool setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, + QBluetoothSocket::SocketState socketState, + QBluetoothSocket::OpenMode openMode) override; + + void connectToServiceHelper(const QBluetoothAddress &address, quint16 port, + QIODevice::OpenMode openMode) override; + void connectToService(const QBluetoothServiceInfo &service, + QIODevice::OpenMode openMode) override; + void connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, + QIODevice::OpenMode openMode) override; + + void connectToService(const QBluetoothAddress &address, quint16 port, + QIODevice::OpenMode openMode) override; + + void _q_writeNotify(); private: // Create a socket from an external source (without connectToService). - bool setChannel(IOBluetoothRFCOMMChannel *channel); - bool setChannel(IOBluetoothL2CAPChannel *channel); + bool setRFCOMChannel(void *channel); + bool setL2CAPChannel(void *channel); // L2CAP and RFCOMM delegate void setChannelError(IOReturn errorCode) override; @@ -117,33 +135,15 @@ private: void readChannelData(void *data, std::size_t size) override; void writeComplete() override; - qint64 writeData(const char *data, qint64 maxSize); - qint64 readData(char *data, qint64 maxSize); - - QScopedPointer<QBluetoothServiceDiscoveryAgent> discoveryAgent; - - QPrivateLinearBuffer buffer; - QPrivateLinearBuffer txBuffer; QVector<char> writeChunk; - // Probably, not needed. - QIODevice::OpenMode openMode; - - QBluetoothSocket::SocketState state; - QBluetoothServiceInfo::Protocol socketType; - - QBluetoothSocket::SocketError socketError; - QString errorString; - - typedef QT_MANGLE_NAMESPACE(OSXBTL2CAPChannel) ObjCL2CAPChannel; - typedef OSXBluetooth::ObjCScopedPointer<ObjCL2CAPChannel> L2CAPChannel; + using L2CAPChannel = DarwinBluetooth::ScopedPointer; L2CAPChannel l2capChannel; - typedef QT_MANGLE_NAMESPACE(OSXBTRFCOMMChannel) ObjCRFCOMMChannel; - typedef OSXBluetooth::ObjCScopedPointer<ObjCRFCOMMChannel> RFCOMMChannel; + using RFCOMMChannel = L2CAPChannel; RFCOMMChannel rfcommChannel; // A trick to deal with channel open too fast (synchronously). - bool isConnecting; + bool isConnecting = false; }; QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothsocketbase_p.h b/src/bluetooth/qbluetoothsocketbase_p.h index 410dcbbd..d1894e96 100644 --- a/src/bluetooth/qbluetoothsocketbase_p.h +++ b/src/bluetooth/qbluetoothsocketbase_p.h @@ -89,7 +89,6 @@ QT_FORWARD_DECLARE_CLASS(QBluetoothServiceDiscoveryAgent) QT_BEGIN_NAMESPACE -#ifndef QT_OSX_BLUETOOTH class QBluetoothSocketBasePrivate : public QObject { Q_OBJECT @@ -198,26 +197,6 @@ static inline quint64 convertAddress(const quint8 (&from)[6], quint64 *to = null return result; } -#else // QT_OSX_BLUETOOTH - -// QBluetoothSocketPrivate on macOS can not contain -// Q_OBJECT (moc does not parse Objective-C syntax). -// But QBluetoothSocket still requires QMetaObject::invokeMethod -// to work. Here's the trick: -class QBluetoothSocketBasePrivate : public QObject -{ -// The most important part of it: - Q_OBJECT -public slots: - virtual void _q_writeNotify() = 0; - -protected: - Q_DECLARE_PUBLIC(QBluetoothSocket) - QBluetoothSocket *q_ptr; -}; - -#endif // QT_OSX_BLUETOOTH - QT_END_NAMESPACE #endif // QBLUETOOTHSOCKETBASE_P_H diff --git a/src/bluetooth/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..d6af6999 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 @@ -157,6 +159,9 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) This value was introduced by Qt 5.7. \value RemoteHostClosedError The remote device closed the connection. This value was introduced by Qt 5.10. + \value AuthorizationError The local Bluetooth device closed the connection due to + insufficient authorization. + This value was introduced by Qt 5.14. */ /*! @@ -321,6 +326,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 +352,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 +384,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 +418,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 +446,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 +548,7 @@ QBluetoothAddress QLowEnergyController::remoteAddress() const */ QBluetoothUuid QLowEnergyController::remoteDeviceUuid() const { - return QBluetoothUuid(); + return d_ptr->deviceUuid; } /*! diff --git a/src/bluetooth/qlowenergycontroller.h b/src/bluetooth/qlowenergycontroller.h index 9fe46fe5..37e7b82d 100644 --- a/src/bluetooth/qlowenergycontroller.h +++ b/src/bluetooth/qlowenergycontroller.h @@ -66,7 +66,8 @@ public: InvalidBluetoothAdapterError, ConnectionError, AdvertisingError, - RemoteHostClosedError + RemoteHostClosedError, + AuthorizationError }; Q_ENUM(Error) @@ -93,13 +94,16 @@ public: explicit QLowEnergyController(const QBluetoothAddress &remoteDevice, QObject *parent = nullptr); // TODO Qt 6 remove ctor explicit QLowEnergyController(const QBluetoothDeviceInfo &remoteDevice, - QObject *parent = nullptr); + QObject *parent = nullptr); // TODO Qt 6 make private explicit QLowEnergyController(const QBluetoothAddress &remoteDevice, const QBluetoothAddress &localDevice, QObject *parent = nullptr); // TODO Qt 6 remove ctor static QLowEnergyController *createCentral(const QBluetoothDeviceInfo &remoteDevice, QObject *parent = nullptr); + static QLowEnergyController *createCentral(const QBluetoothAddress &remoteDevice, + const QBluetoothAddress &localDevice, + QObject *parent = nullptr); static QLowEnergyController *createPeripheral(QObject *parent = nullptr); // TODO: Allow to set connection timeout (disconnect when no data has been exchanged for n seconds). diff --git a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp index 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 9aaee855..253956e2 100644 --- a/src/bluetooth/qlowenergycontroller_osx.mm +++ b/src/bluetooth/qlowenergycontroller_darwin.mm @@ -38,13 +38,17 @@ ** ****************************************************************************/ -#include "osx/osxbtnotifier_p.h" #include "osx/osxbtutility_p.h" #include "osx/uistrings_p.h" +#ifndef Q_OS_TVOS +#include "osx/osxbtperipheralmanager_p.h" +#endif // Q_OS_TVOS +#include "qlowenergycontroller_darwin_p.h" #include "qlowenergyserviceprivate_p.h" -#include "qlowenergycontroller_osx_p.h" +#include "osx/osxbtcentralmanager_p.h" + #include "qlowenergyservicedata.h" #include "qbluetoothlocaldevice.h" #include "qbluetoothdeviceinfo.h" @@ -58,30 +62,14 @@ #include <QtCore/qstring.h> #include <QtCore/qlist.h> -#define OSX_D_PTR QLowEnergyControllerPrivateOSX *osx_d_ptr = static_cast<QLowEnergyControllerPrivateOSX *>(d_ptr) - QT_BEGIN_NAMESPACE namespace { -static void registerQLowEnergyControllerMetaType() -{ - static bool initDone = false; - if (!initDone) { - qRegisterMetaType<QLowEnergyController::ControllerState>(); - qRegisterMetaType<QLowEnergyController::Error>(); - qRegisterMetaType<QLowEnergyHandle>("QLowEnergyHandle"); - qRegisterMetaType<QSharedPointer<QLowEnergyServicePrivate> >(); - qRegisterMetaType<QLowEnergyCharacteristic>(); - qRegisterMetaType<QLowEnergyDescriptor>(); - initDone = true; - } -} - typedef QSharedPointer<QLowEnergyServicePrivate> ServicePrivate; // Convenience function, can return a smart pointer that 'isNull'. -ServicePrivate qt_createLEService(QLowEnergyControllerPrivateOSX *controller, CBService *cbService, bool included) +ServicePrivate qt_createLEService(QLowEnergyControllerPrivateDarwin *controller, CBService *cbService, bool included) { Q_ASSERT_X(controller, Q_FUNC_INFO, "invalid controller (null)"); Q_ASSERT_X(cbService, Q_FUNC_INFO, "invalid service (nil)"); @@ -131,110 +119,276 @@ UUIDList qt_servicesUuids(NSArray *services) return uuids; } -} +} // unnamed namespace + +#ifndef Q_OS_TVOS +using ObjCPeripheralManager = QT_MANGLE_NAMESPACE(OSXBTPeripheralManager); +#endif // Q_OS_TVOS + +using ObjCCentralManager = QT_MANGLE_NAMESPACE(OSXBTCentralManager); -QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyController::Role r, - QLowEnergyController *q, - const QBluetoothDeviceInfo &deviceInfo) - : q_ptr(q), - deviceUuid(deviceInfo.deviceUuid()), - deviceName(deviceInfo.name()), - lastError(QLowEnergyController::NoError), - controllerState(QLowEnergyController::UnconnectedState), - addressType(QLowEnergyController::PublicAddress) +QLowEnergyControllerPrivateDarwin::QLowEnergyControllerPrivateDarwin() { + void registerQLowEnergyControllerMetaType(); registerQLowEnergyControllerMetaType(); + qRegisterMetaType<QLowEnergyHandle>("QLowEnergyHandle"); + qRegisterMetaType<QSharedPointer<QLowEnergyServicePrivate>>(); +} - Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)"); +QLowEnergyControllerPrivateDarwin::~QLowEnergyControllerPrivateDarwin() +{ + if (const auto leQueue = OSXBluetooth::qt_LE_queue()) { + if (role == QLowEnergyController::CentralRole) { + const auto manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_sync(leQueue, ^{ + [manager detach]; + }); + } else { +#ifndef Q_OS_TVOS + const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); + dispatch_sync(leQueue, ^{ + [manager detach]; + }); +#endif + } + } +} - using OSXBluetooth::LECBManagerNotifier; +bool QLowEnergyControllerPrivateDarwin::isValid() const +{ +#ifdef Q_OS_TVOS + return centralManager; +#else + return centralManager || peripheralManager; +#endif +} - role = r; +void QLowEnergyControllerPrivateDarwin::init() +{ + using OSXBluetooth::LECBManagerNotifier; QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier); if (role == QLowEnergyController::PeripheralRole) { #ifndef Q_OS_TVOS - peripheralManager.reset([[ObjCPeripheralManager alloc] initWith:notifier.data()]); + peripheralManager.reset([[ObjCPeripheralManager alloc] initWith:notifier.data()], + DarwinBluetooth::RetainPolicy::noInitialRetain); if (!peripheralManager) { - qCWarning(QT_BT_OSX) << "failed to initialize peripheral manager"; + qCWarning(QT_BT_OSX) << "failed to create a peripheral manager"; return; } #else - qCWarning(QT_BT_OSX) << "peripheral role is not supported on your platform"; + qCWarning(QT_BT_OSX) << "the peripheral role is not supported on your platform"; return; -#endif +#endif // Q_OS_TVOS } else { - centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()]); - if (!centralManager.data()) { - qCWarning(QT_BT_OSX) << "failed to initialize central manager"; + centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()], + DarwinBluetooth::RetainPolicy::noInitialRetain); + if (!centralManager) { + qCWarning(QT_BT_OSX) << "failed to initialize a central manager"; return; } } - if (!connectSlots(notifier.data())) { + if (!connectSlots(notifier.data())) qCWarning(QT_BT_OSX) << "failed to connect to notifier's signal(s)"; - } + // Ownership was taken by central manager. notifier.take(); } -QLowEnergyControllerPrivateOSX::~QLowEnergyControllerPrivateOSX() +void QLowEnergyControllerPrivateDarwin::connectToDevice() { - if (const auto leQueue = OSXBluetooth::qt_LE_queue()) { - if (role == QLowEnergyController::CentralRole) { - const auto manager = centralManager.data(); - dispatch_sync(leQueue, ^{ - [manager detach]; + Q_ASSERT_X(state == QLowEnergyController::UnconnectedState, + Q_FUNC_INFO, "invalid state"); + + if (!isValid()) { + // init() had failed for was never called. + return _q_CBManagerError(QLowEnergyController::UnknownError); + } + + if (deviceUuid.isNull()) { + // Wrong constructor was used or invalid UUID was provided. + return _q_CBManagerError(QLowEnergyController::UnknownRemoteDeviceError); + } + + // The logic enforcing the role is in the public class. + Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, + Q_FUNC_INFO, "invalid role (peripheral)"); + + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + if (!leQueue) { + qCWarning(QT_BT_OSX) << "no LE queue found"; + setErrorDescription(QLowEnergyController::UnknownError); + return; + } + + setErrorDescription(QLowEnergyController::NoError); + setState(QLowEnergyController::ConnectingState); + + const QBluetoothUuid deviceUuidCopy(deviceUuid); + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_async(leQueue, ^{ + [manager connectToDevice:deviceUuidCopy]; + }); +} + +void QLowEnergyControllerPrivateDarwin::disconnectFromDevice() +{ + if (role == QLowEnergyController::PeripheralRole) { + // CoreBluetooth API intentionally does not provide any way of closing + // a connection. All we can do here is to stop the advertisement. + stopAdvertising(); + return; + } + + if (isValid()) { + const auto oldState = state; + + if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { + setState(QLowEnergyController::ClosingState); + invalidateServices(); + + auto manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_async(leQueue, ^{ + [manager disconnectFromDevice]; }); + + if (oldState == QLowEnergyController::ConnectingState) { + // With a pending connect attempt there is no + // guarantee we'll ever have didDisconnect callback, + // set the state here and now to make sure we still + // can connect. + setState(QLowEnergyController::UnconnectedState); + } } else { -#ifndef Q_OS_TVOS - const auto manager = peripheralManager.data(); - dispatch_sync(leQueue, ^{ - [manager detach]; - }); -#endif + qCCritical(QT_BT_OSX) << "qt LE queue is nil, " + "can not dispatch 'disconnect'"; } } } -bool QLowEnergyControllerPrivateOSX::isValid() const +void QLowEnergyControllerPrivateDarwin::discoverServices() +{ + Q_ASSERT_X(state != QLowEnergyController::UnconnectedState, + Q_FUNC_INFO, "not connected to peripheral"); + Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, + Q_FUNC_INFO, "invalid role (peripheral)"); + + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + Q_ASSERT_X(leQueue, Q_FUNC_INFO, "LE queue not found"); + + setState(QLowEnergyController::DiscoveringState); + + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_async(leQueue, ^{ + [manager discoverServices]; + }); +} + +void QLowEnergyControllerPrivateDarwin::discoverServiceDetails(const QBluetoothUuid &serviceUuid) +{ + if (state != QLowEnergyController::DiscoveredState) { + qCWarning(QT_BT_OSX) << "can not discover service details in the current state, " + "QLowEnergyController::DiscoveredState is expected"; + return; + } + + if (!serviceList.contains(serviceUuid)) { + qCWarning(QT_BT_OSX) << "unknown service: " << serviceUuid; + return; + } + + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + Q_ASSERT(leQueue); + + ServicePrivate qtService(serviceList.value(serviceUuid)); + qtService->setState(QLowEnergyService::DiscoveringServices); + // Copy objects ... + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); + const QBluetoothUuid serviceUuidCopy(serviceUuid); + dispatch_async(leQueue, ^{ + [manager discoverServiceDetails:serviceUuidCopy]; + }); +} + +void QLowEnergyControllerPrivateDarwin::requestConnectionUpdate(const QLowEnergyConnectionParameters ¶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.data(); + Q_UNUSED(service); + qCDebug(QT_BT_OSX, "peripheral role is not supported on tvOS"); #else - return centralManager.data() || peripheralManager.data(); -#endif + if (role != QLowEnergyController::PeripheralRole) { + qCWarning(QT_BT_OSX) << "not in peripheral role"; + return nullptr; + } + + if (state != QLowEnergyController::UnconnectedState) { + qCWarning(QT_BT_OSX) << "invalid state"; + return nullptr; + } + + if (!service.isValid()) { + qCWarning(QT_BT_OSX) << "invalid service"; + return nullptr; + } + + for (auto includedService : service.includedServices()) + includedService->d_ptr->type |= QLowEnergyService::IncludedService; + + const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); + Q_ASSERT(manager); + if (const auto servicePrivate = [manager addService:service]) { + servicePrivate->setController(this); + servicePrivate->state = QLowEnergyService::LocalService; + localServices.insert(servicePrivate->uuid, servicePrivate); + return new QLowEnergyService(servicePrivate); + } +#endif // Q_OS_TVOS + return nullptr; } -void QLowEnergyControllerPrivateOSX::_q_connected() +void QLowEnergyControllerPrivateDarwin::_q_connected() { - controllerState = QLowEnergyController::ConnectedState; - - emit q_ptr->stateChanged(QLowEnergyController::ConnectedState); + setState(QLowEnergyController::ConnectedState); emit q_ptr->connected(); } -void QLowEnergyControllerPrivateOSX::_q_disconnected() +void QLowEnergyControllerPrivateDarwin::_q_disconnected() { - controllerState = QLowEnergyController::UnconnectedState; - if (role == QLowEnergyController::CentralRole) invalidateServices(); - emit q_ptr->stateChanged(QLowEnergyController::UnconnectedState); + setState(QLowEnergyController::UnconnectedState); emit q_ptr->disconnected(); } -void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() +void QLowEnergyControllerPrivateDarwin::_q_serviceDiscoveryFinished() { - Q_ASSERT_X(controllerState == QLowEnergyController::DiscoveringState, + Q_ASSERT_X(state == QLowEnergyController::DiscoveringState, Q_FUNC_INFO, "invalid state"); using namespace OSXBluetooth; QT_BT_MAC_AUTORELEASEPOOL; - NSArray *const services = [centralManager.data() peripheral].services; + NSArray *const services = [centralManager.getAs<ObjCCentralManager>() peripheral].services; // Now we have to traverse the discovered services tree. // Essentially it's an iterative version of more complicated code from the // OSXBTCentralManager's code. @@ -249,13 +403,13 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() const ServicePrivate newService(qt_createLEService(this, cbService, false)); if (!newService.data()) continue; - if (discoveredServices.contains(newService->uuid)) { + if (serviceList.contains(newService->uuid)) { // It's a bit stupid we first created it ... qCDebug(QT_BT_OSX) << "discovered service with a duplicated UUID" << newService->uuid; continue; } - discoveredServices.insert(newService->uuid, newService); + serviceList.insert(newService->uuid, newService); discoveredCBServices.insert(newService->uuid, cbService); } @@ -273,8 +427,8 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() } const QBluetoothUuid uuid(qt_uuid(s.UUID)); - if (discoveredServices.contains(uuid) && discoveredCBServices.value(uuid) == s) { - ServicePrivate qtService(discoveredServices.value(uuid)); + if (serviceList.contains(uuid) && discoveredCBServices.value(uuid) == s) { + ServicePrivate qtService(serviceList.value(uuid)); // Add included UUIDs: qtService->includedServices.append(qt_servicesUuids(s.includedServices)); }// Else - we ignored this CBService object. @@ -286,15 +440,15 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() for (NSUInteger i = 0, e = [toVisitNext count]; i < e; ++i) { CBService *const s = [toVisitNext objectAtIndex:i]; const QBluetoothUuid uuid(qt_uuid(s.UUID)); - if (discoveredServices.contains(uuid)) { + if (serviceList.contains(uuid)) { if (discoveredCBServices.value(uuid) == s) { - ServicePrivate qtService(discoveredServices.value(uuid)); + ServicePrivate qtService(serviceList.value(uuid)); qtService->type |= QLowEnergyService::IncludedService; } // Else this is the duplicate we ignored already. } else { // Oh, we do not even have it yet??? ServicePrivate newService(qt_createLEService(this, s, true)); - discoveredServices.insert(newService->uuid, newService); + serviceList.insert(newService->uuid, newService); discoveredCBServices.insert(newService->uuid, s); } } @@ -306,31 +460,26 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() qCDebug(QT_BT_OSX) << "no services found"; } - for (ServiceMap::const_iterator it = discoveredServices.constBegin(); it != discoveredServices.constEnd(); ++it) { - const QBluetoothUuid &uuid = it.key(); - QMetaObject::invokeMethod(q_ptr, "serviceDiscovered", Qt::QueuedConnection, - Q_ARG(QBluetoothUuid, uuid)); - } + for (ServiceMap::const_iterator it = serviceList.constBegin(); it != serviceList.constEnd(); ++it) + emit q_ptr->serviceDiscovered(it.key()); - controllerState = QLowEnergyController::DiscoveredState; - QMetaObject::invokeMethod(q_ptr, "stateChanged", Qt::QueuedConnection, - Q_ARG(QLowEnergyController::ControllerState, controllerState)); - QMetaObject::invokeMethod(q_ptr, "discoveryFinished", Qt::QueuedConnection); + setState(QLowEnergyController::DiscoveredState); + emit q_ptr->discoveryFinished(); } -void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service) +void QLowEnergyControllerPrivateDarwin::_q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service) { QT_BT_MAC_AUTORELEASEPOOL; Q_ASSERT(service); - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid)) { qCDebug(QT_BT_OSX) << "unknown service uuid:" << service->uuid; return; } - ServicePrivate qtService(discoveredServices.value(service->uuid)); + ServicePrivate qtService(serviceList.value(service->uuid)); // Assert on handles? qtService->startHandle = service->startHandle; qtService->endHandle = service->endHandle; @@ -339,23 +488,23 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedP qtService->setState(QLowEnergyService::ServiceDiscovered); } -void QLowEnergyControllerPrivateOSX::_q_servicesWereModified() +void QLowEnergyControllerPrivateDarwin::_q_servicesWereModified() { - if (!(controllerState == QLowEnergyController::DiscoveringState - || controllerState == QLowEnergyController::DiscoveredState)) { + if (!(state == QLowEnergyController::DiscoveringState + || state == QLowEnergyController::DiscoveredState)) { qCWarning(QT_BT_OSX) << "services were modified while controller is not in Discovered/Discovering state"; return; } - if (controllerState == QLowEnergyController::DiscoveredState) + if (state == QLowEnergyController::DiscoveredState) invalidateServices(); - controllerState = QLowEnergyController::ConnectedState; + setState(QLowEnergyController::ConnectedState); q_ptr->discoverServices(); } -void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle charHandle, - const QByteArray &value) +void QLowEnergyControllerPrivateDarwin::_q_characteristicRead(QLowEnergyHandle charHandle, + const QByteArray &value) { Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)"); @@ -375,8 +524,8 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle char emit service->characteristicRead(characteristic, value); } -void QLowEnergyControllerPrivateOSX::_q_characteristicWritten(QLowEnergyHandle charHandle, - const QByteArray &value) +void QLowEnergyControllerPrivateDarwin::_q_characteristicWritten(QLowEnergyHandle charHandle, + const QByteArray &value) { Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)"); @@ -399,8 +548,8 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicWritten(QLowEnergyHandle c emit service->characteristicWritten(characteristic, value); } -void QLowEnergyControllerPrivateOSX::_q_characteristicUpdated(QLowEnergyHandle charHandle, - const QByteArray &value) +void QLowEnergyControllerPrivateDarwin::_q_characteristicUpdated(QLowEnergyHandle charHandle, + const QByteArray &value) { // TODO: write/update notifications are quite similar (except asserts/warnings messages // and different signals emitted). Merge them into one function? @@ -428,8 +577,8 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicUpdated(QLowEnergyHandle c emit service->characteristicChanged(characteristic, value); } -void QLowEnergyControllerPrivateOSX::_q_descriptorRead(QLowEnergyHandle dHandle, - const QByteArray &value) +void QLowEnergyControllerPrivateDarwin::_q_descriptorRead(QLowEnergyHandle dHandle, + const QByteArray &value) { Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)"); @@ -444,8 +593,8 @@ void QLowEnergyControllerPrivateOSX::_q_descriptorRead(QLowEnergyHandle dHandle, emit service->descriptorRead(qtDescriptor, value); } -void QLowEnergyControllerPrivateOSX::_q_descriptorWritten(QLowEnergyHandle dHandle, - const QByteArray &value) +void QLowEnergyControllerPrivateDarwin::_q_descriptorWritten(QLowEnergyHandle dHandle, + const QByteArray &value) { Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)"); @@ -461,8 +610,8 @@ void QLowEnergyControllerPrivateOSX::_q_descriptorWritten(QLowEnergyHandle dHand emit service->descriptorWritten(qtDescriptor, value); } -void QLowEnergyControllerPrivateOSX::_q_notificationEnabled(QLowEnergyHandle charHandle, - bool enabled) +void QLowEnergyControllerPrivateDarwin::_q_notificationEnabled(QLowEnergyHandle charHandle, + bool enabled) { // CoreBluetooth in peripheral role does not allow mutable descriptors, // in central we can only call setNotification:enabled/disabled. @@ -504,7 +653,7 @@ void QLowEnergyControllerPrivateOSX::_q_notificationEnabled(QLowEnergyHandle cha } } -void QLowEnergyControllerPrivateOSX::_q_LEnotSupported() +void QLowEnergyControllerPrivateDarwin::_q_LEnotSupported() { // Report as an error. But this should not be possible // actually: before connecting to any device, we have @@ -512,32 +661,30 @@ void QLowEnergyControllerPrivateOSX::_q_LEnotSupported() // be supported. } -void QLowEnergyControllerPrivateOSX::_q_CBManagerError(QLowEnergyController::Error errorCode) +void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(QLowEnergyController::Error errorCode) { - // Errors reported during connect and general errors. - - setErrorDescription(errorCode); - emit q_ptr->error(lastError); - - if (controllerState == QLowEnergyController::ConnectingState) { - controllerState = QLowEnergyController::UnconnectedState; - emit q_ptr->stateChanged(controllerState); - } else if (controllerState == QLowEnergyController::DiscoveringState) { - controllerState = QLowEnergyController::ConnectedState; - emit q_ptr->stateChanged(controllerState); - } // In any other case we stay in Discovered, it's - // a service/characteristic - related error. + // This function handles errors reported while connecting to a remote device + // and also other errors in general. + setError(errorCode); + + if (state == QLowEnergyController::ConnectingState) + setState(QLowEnergyController::UnconnectedState); + else if (state == QLowEnergyController::DiscoveringState) + setState(QLowEnergyController::ConnectedState); + + // In any other case we stay in Discovered, it's + // a service/characteristic - related error. } -void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &serviceUuid, - QLowEnergyController::Error errorCode) +void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(const QBluetoothUuid &serviceUuid, + QLowEnergyController::Error errorCode) { // Errors reported while discovering service details etc. Q_UNUSED(errorCode) // TODO: setError? // We failed to discover any characteristics/descriptors. - if (discoveredServices.contains(serviceUuid)) { - ServicePrivate qtService(discoveredServices.value(serviceUuid)); + if (serviceList.contains(serviceUuid)) { + ServicePrivate qtService(serviceList.value(serviceUuid)); qtService->setState(QLowEnergyService::InvalidService); } else { qCDebug(QT_BT_OSX) << "error reported for unknown service" @@ -545,109 +692,24 @@ void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &ser } } -void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &serviceUuid, - QLowEnergyService::ServiceError errorCode) +void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(const QBluetoothUuid &serviceUuid, + QLowEnergyService::ServiceError errorCode) { - if (!discoveredServices.contains(serviceUuid)) { + if (!serviceList.contains(serviceUuid)) { qCDebug(QT_BT_OSX) << "unknown service uuid:" << serviceUuid; return; } - ServicePrivate service(discoveredServices.value(serviceUuid)); + ServicePrivate service(serviceList.value(serviceUuid)); service->setError(errorCode); } -void QLowEnergyControllerPrivateOSX::connectToDevice() -{ - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller"); - Q_ASSERT_X(controllerState == QLowEnergyController::UnconnectedState, - Q_FUNC_INFO, "invalid state"); - Q_ASSERT_X(!deviceUuid.isNull(), Q_FUNC_INFO, - "invalid private controller (no device uuid)"); - Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, - Q_FUNC_INFO, "invalid role (peripheral)"); - - dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - setErrorDescription(QLowEnergyController::UnknownError); - return; - } - - setErrorDescription(QLowEnergyController::NoError); - controllerState = QLowEnergyController::ConnectingState; - - const QBluetoothUuid deviceUuidCopy(deviceUuid); - ObjCCentralManager *manager = centralManager.data(); - dispatch_async(leQueue, ^{ - [manager connectToDevice:deviceUuidCopy]; - }); -} - -void QLowEnergyControllerPrivateOSX::discoverServices() -{ - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller"); - Q_ASSERT_X(controllerState != QLowEnergyController::UnconnectedState, - Q_FUNC_INFO, "not connected to peripheral"); - Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, - Q_FUNC_INFO, "invalid role (peripheral)"); - - dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - setErrorDescription(QLowEnergyController::UnknownError); - return; - } - - controllerState = QLowEnergyController::DiscoveringState; - emit q_ptr->stateChanged(QLowEnergyController::DiscoveringState); - - ObjCCentralManager *manager = centralManager.data(); - dispatch_async(leQueue, ^{ - [manager discoverServices]; - }); -} - -void QLowEnergyControllerPrivateOSX::discoverServiceDetails(const QBluetoothUuid &serviceUuid) -{ - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller"); - - if (controllerState != QLowEnergyController::DiscoveredState) { - // This will also exclude peripheral role, since controller - // can never be in discovered state ... - qCWarning(QT_BT_OSX) << "can not discover service details in the current state, " - "QLowEnergyController::DiscoveredState is expected"; - return; - } - - if (!discoveredServices.contains(serviceUuid)) { - qCWarning(QT_BT_OSX) << "unknown service: " << serviceUuid; - return; - } - - dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - return; - } - - ServicePrivate qtService(discoveredServices.value(serviceUuid)); - qtService->setState(QLowEnergyService::DiscoveringServices); - // Copy objects ... - ObjCCentralManager *manager = centralManager.data(); - const QBluetoothUuid serviceUuidCopy(serviceUuid); - dispatch_async(leQueue, ^{ - [manager discoverServiceDetails:serviceUuidCopy]; - }); -} - -void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle charHandle, - const QByteArray &newValue) +void QLowEnergyControllerPrivateDarwin::setNotifyValue(QSharedPointer<QLowEnergyServicePrivate> service, + QLowEnergyHandle charHandle, + const QByteArray &newValue) { Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller"); if (role == QLowEnergyController::PeripheralRole) { qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; @@ -666,7 +728,7 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer return; } - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid)) { qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << "found"; return; } @@ -678,11 +740,9 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - return; - } - ObjCCentralManager *manager = centralManager.data(); + Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); + + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); const QBluetoothUuid serviceUuid(service->uuid); const QByteArray newValueCopy(newValue); dispatch_async(leQueue, ^{ @@ -692,18 +752,17 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer }); } -void QLowEnergyControllerPrivateOSX::readCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle charHandle) +void QLowEnergyControllerPrivateDarwin::readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle) { Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller"); if (role == QLowEnergyController::PeripheralRole) { qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; return; } - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid)) { qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << "found"; return; @@ -716,29 +775,26 @@ void QLowEnergyControllerPrivateOSX::readCharacteristic(QSharedPointer<QLowEnerg } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - return; - } + Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); + // Attention! We have to copy UUID. - ObjCCentralManager *manager = centralManager.data(); + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); const QBluetoothUuid serviceUuid(service->uuid); dispatch_async(leQueue, ^{ [manager readCharacteristic:charHandle onService:serviceUuid]; }); } -void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle charHandle, const QByteArray &newValue, - QLowEnergyService::WriteMode mode) +void QLowEnergyControllerPrivateDarwin::writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, const QByteArray &newValue, + QLowEnergyService::WriteMode mode) { Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller"); // We can work only with services found on a given peripheral // (== created by the given LE controller). - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid) && !localServices.contains(service->uuid)) { qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << " found"; return; @@ -751,15 +807,12 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - return; - } + Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); // Attention! We have to copy objects! const QByteArray newValueCopy(newValue); if (role == QLowEnergyController::CentralRole) { const QBluetoothUuid serviceUuid(service->uuid); - const auto manager = centralManager.data(); + const auto manager = centralManager.getAs<ObjCCentralManager>(); dispatch_async(leQueue, ^{ [manager write:newValueCopy charHandle:charHandle @@ -768,7 +821,7 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner }); } else { #ifndef Q_OS_TVOS - const auto manager = peripheralManager.data(); + const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); dispatch_async(leQueue, ^{ [manager write:newValueCopy charHandle:charHandle]; }); @@ -778,9 +831,9 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner } } -quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHandle charHandle, - const QByteArray &value, - bool appendValue) +quint16 QLowEnergyControllerPrivateDarwin::updateValueOfCharacteristic(QLowEnergyHandle charHandle, + const QByteArray &value, + bool appendValue) { QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); if (!service.isNull()) { @@ -799,18 +852,20 @@ quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHa return 0; } -void QLowEnergyControllerPrivateOSX::readDescriptor(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle descriptorHandle) +void QLowEnergyControllerPrivateDarwin::readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QLowEnergyHandle descriptorHandle) { + Q_UNUSED(charHandle) // Hehe, yes! + Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller"); if (role == QLowEnergyController::PeripheralRole) { qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; return; } - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid)) { qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << "found"; return; @@ -823,19 +878,21 @@ void QLowEnergyControllerPrivateOSX::readDescriptor(QSharedPointer<QLowEnergySer } // Attention! Copy objects! const QBluetoothUuid serviceUuid(service->uuid); - ObjCCentralManager * const manager = centralManager.data(); + ObjCCentralManager * const manager = centralManager.getAs<ObjCCentralManager>(); dispatch_async(leQueue, ^{ [manager readDescriptor:descriptorHandle onService:serviceUuid]; }); } -void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle descriptorHandle, - const QByteArray &newValue) +void QLowEnergyControllerPrivateDarwin::writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QLowEnergyHandle descriptorHandle, + const QByteArray &newValue) { + Q_UNUSED(charHandle) + Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller"); if (role == QLowEnergyController::PeripheralRole) { qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; @@ -845,20 +902,17 @@ void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergySe // We can work only with services found on a given peripheral // (== created by the given LE controller), // otherwise we can not write anything at all. - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid)) { qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << " found"; return; } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - return; - } + Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); // Attention! Copy objects! const QBluetoothUuid serviceUuid(service->uuid); - ObjCCentralManager * const manager = centralManager.data(); + ObjCCentralManager * const manager = centralManager.getAs<ObjCCentralManager>(); const QByteArray newValueCopy(newValue); dispatch_async(leQueue, ^{ [manager write:newValueCopy @@ -867,8 +921,8 @@ void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergySe }); } -quint16 QLowEnergyControllerPrivateOSX::updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descHandle, - const QByteArray &value, bool appendValue) +quint16 QLowEnergyControllerPrivateDarwin::updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descHandle, + const QByteArray &value, bool appendValue) { QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); if (!service.isNull()) { @@ -893,66 +947,15 @@ quint16 QLowEnergyControllerPrivateOSX::updateValueOfDescriptor(QLowEnergyHandle return 0; } -QSharedPointer<QLowEnergyServicePrivate> QLowEnergyControllerPrivateOSX::serviceForHandle(QLowEnergyHandle handle) -{ - const QList<QSharedPointer<QLowEnergyServicePrivate>> services - = discoveredServices.values(); - for (QSharedPointer<QLowEnergyServicePrivate> service : services) { - if (service->startHandle <= handle && handle <= service->endHandle) - return service; - } - - return QSharedPointer<QLowEnergyServicePrivate>(); -} - -QLowEnergyCharacteristic QLowEnergyControllerPrivateOSX::characteristicForHandle(QLowEnergyHandle charHandle) -{ - QSharedPointer<QLowEnergyServicePrivate> service(serviceForHandle(charHandle)); - if (service.isNull()) - return QLowEnergyCharacteristic(); - - if (service->characteristicList.isEmpty()) - return QLowEnergyCharacteristic(); - - // Check whether it is the handle of a characteristic header - if (service->characteristicList.contains(charHandle)) - return QLowEnergyCharacteristic(service, charHandle); - - // Check whether it is the handle of the characteristic value or its descriptors - QList<QLowEnergyHandle> charHandles(service->characteristicList.keys()); - std::sort(charHandles.begin(), charHandles.end()); - - for (int i = charHandles.size() - 1; i >= 0; --i) { - if (charHandles.at(i) > charHandle) - continue; - - return QLowEnergyCharacteristic(service, charHandles.at(i)); - } - - return QLowEnergyCharacteristic(); -} - -QLowEnergyDescriptor QLowEnergyControllerPrivateOSX::descriptorForHandle(QLowEnergyHandle descriptorHandle) -{ - const QLowEnergyCharacteristic ch(characteristicForHandle(descriptorHandle)); - if (!ch.isValid()) - return QLowEnergyDescriptor(); - - const QLowEnergyServicePrivate::CharData charData = ch.d_ptr->characteristicList[ch.attributeHandle()]; - - if (charData.descriptorList.contains(descriptorHandle)) - return QLowEnergyDescriptor(ch.d_ptr, ch.attributeHandle(), descriptorHandle); - - return QLowEnergyDescriptor(); -} - -void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::Error errorCode) +void QLowEnergyControllerPrivateDarwin::setErrorDescription(QLowEnergyController::Error errorCode) { // This function does not emit! + // TODO: well, it is not a reason to duplicate a significant part of + // setError though! - lastError = errorCode; + error = errorCode; - switch (lastError) { + switch (error) { case QLowEnergyController::NoError: errorString.clear(); break; @@ -978,48 +981,36 @@ void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::E } } -void QLowEnergyControllerPrivateOSX::invalidateServices() -{ - const QList<QSharedPointer<QLowEnergyServicePrivate>> services - = discoveredServices.values(); - for (const QSharedPointer<QLowEnergyServicePrivate> service : services) { - service->setController(nullptr); - service->setState(QLowEnergyService::InvalidService); - } - - discoveredServices.clear(); -} - -bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECBManagerNotifier *notifier) +bool QLowEnergyControllerPrivateDarwin::connectSlots(OSXBluetooth::LECBManagerNotifier *notifier) { using OSXBluetooth::LECBManagerNotifier; Q_ASSERT_X(notifier, Q_FUNC_INFO, "invalid notifier object (null)"); bool ok = connect(notifier, &LECBManagerNotifier::connected, - this, &QLowEnergyControllerPrivateOSX::_q_connected); + this, &QLowEnergyControllerPrivateDarwin::_q_connected); ok = ok && connect(notifier, &LECBManagerNotifier::disconnected, - this, &QLowEnergyControllerPrivateOSX::_q_disconnected); + this, &QLowEnergyControllerPrivateDarwin::_q_disconnected); ok = ok && connect(notifier, &LECBManagerNotifier::serviceDiscoveryFinished, - this, &QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished); - ok = ok && connect(notifier, &LECBManagerNotifier::serviceDetailsDiscoveryFinished, - this, &QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished); + this, &QLowEnergyControllerPrivateDarwin::_q_serviceDiscoveryFinished); ok = ok && connect(notifier, &LECBManagerNotifier::servicesWereModified, - this, &QLowEnergyControllerPrivateOSX::_q_servicesWereModified); + this, &QLowEnergyControllerPrivateDarwin::_q_servicesWereModified); + ok = ok && connect(notifier, &LECBManagerNotifier::serviceDetailsDiscoveryFinished, + this, &QLowEnergyControllerPrivateDarwin::_q_serviceDetailsDiscoveryFinished); ok = ok && connect(notifier, &LECBManagerNotifier::characteristicRead, - this, &QLowEnergyControllerPrivateOSX::_q_characteristicRead); + this, &QLowEnergyControllerPrivateDarwin::_q_characteristicRead); ok = ok && connect(notifier, &LECBManagerNotifier::characteristicWritten, - this, &QLowEnergyControllerPrivateOSX::_q_characteristicWritten); + this, &QLowEnergyControllerPrivateDarwin::_q_characteristicWritten); ok = ok && connect(notifier, &LECBManagerNotifier::characteristicUpdated, - this, &QLowEnergyControllerPrivateOSX::_q_characteristicUpdated); + this, &QLowEnergyControllerPrivateDarwin::_q_characteristicUpdated); ok = ok && connect(notifier, &LECBManagerNotifier::descriptorRead, - this, &QLowEnergyControllerPrivateOSX::_q_descriptorRead); + this, &QLowEnergyControllerPrivateDarwin::_q_descriptorRead); ok = ok && connect(notifier, &LECBManagerNotifier::descriptorWritten, - this, &QLowEnergyControllerPrivateOSX::_q_descriptorWritten); + this, &QLowEnergyControllerPrivateDarwin::_q_descriptorWritten); ok = ok && connect(notifier, &LECBManagerNotifier::notificationEnabled, - this, &QLowEnergyControllerPrivateOSX::_q_notificationEnabled); + this, &QLowEnergyControllerPrivateDarwin::_q_notificationEnabled); ok = ok && connect(notifier, &LECBManagerNotifier::LEnotSupported, - this, &QLowEnergyControllerPrivateOSX::_q_LEnotSupported); + this, &QLowEnergyControllerPrivateDarwin::_q_LEnotSupported); ok = ok && connect(notifier, SIGNAL(CBManagerError(QLowEnergyController::Error)), this, SLOT(_q_CBManagerError(QLowEnergyController::Error))); ok = ok && connect(notifier, SIGNAL(CBManagerError(const QBluetoothUuid &, QLowEnergyController::Error)), @@ -1033,253 +1024,9 @@ bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECBManagerNotif return ok; } -QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddress, - QObject *parent) - : QObject(parent), - d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this)) -{ - OSX_D_PTR; - - osx_d_ptr->remoteAddress = remoteAddress; - osx_d_ptr->localAddress = QBluetoothLocalDevice().address(); - - qCWarning(QT_BT_OSX) << "construction with remote address " - "is not supported!"; -} - -QLowEnergyController::QLowEnergyController(const QBluetoothDeviceInfo &remoteDevice, - QObject *parent) - : QObject(parent), - d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this, remoteDevice)) -{ - OSX_D_PTR; - - osx_d_ptr->localAddress = QBluetoothLocalDevice().address(); - // That's the only "real" ctor - with Core Bluetooth we need a _valid_ deviceUuid - // from 'remoteDevice'. -} - -QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddress, - const QBluetoothAddress &localAddress, - QObject *parent) - : QObject(parent), - d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this)) -{ - OSX_D_PTR; - - osx_d_ptr->remoteAddress = remoteAddress; - osx_d_ptr->localAddress = localAddress; - - qCWarning(QT_BT_OSX) << "construction with remote/local " - "addresses is not supported!"; -} - -QLowEnergyController::QLowEnergyController(QObject *parent) - : QObject(parent), - d_ptr(new QLowEnergyControllerPrivateOSX(PeripheralRole, this)) -{ - OSX_D_PTR; - - osx_d_ptr->localAddress = QBluetoothLocalDevice().address(); -} - -QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDeviceInfo &remoteDevice, - QObject *parent) -{ - return new QLowEnergyController(remoteDevice, parent); -} - -QLowEnergyController *QLowEnergyController::createPeripheral(QObject *parent) -{ - return new QLowEnergyController(parent); -} - -QLowEnergyController::~QLowEnergyController() -{ - // Deleting a peripheral will also disconnect. - delete d_ptr; -} - -QLowEnergyController::Role QLowEnergyController::role() const -{ - OSX_D_PTR; - - return osx_d_ptr->role; -} - -QBluetoothAddress QLowEnergyController::localAddress() const -{ - OSX_D_PTR; - - return osx_d_ptr->localAddress; -} - -QBluetoothAddress QLowEnergyController::remoteAddress() const -{ - OSX_D_PTR; - - return osx_d_ptr->remoteAddress; -} - -QBluetoothUuid QLowEnergyController::remoteDeviceUuid() const -{ - OSX_D_PTR; - - return osx_d_ptr->deviceUuid; -} - -QString QLowEnergyController::remoteName() const -{ - OSX_D_PTR; - - return osx_d_ptr->deviceName; -} - -QLowEnergyController::ControllerState QLowEnergyController::state() const -{ - OSX_D_PTR; - - return osx_d_ptr->controllerState; -} - -QLowEnergyController::RemoteAddressType QLowEnergyController::remoteAddressType() const -{ - OSX_D_PTR; - - return osx_d_ptr->addressType; -} - -void QLowEnergyController::setRemoteAddressType(RemoteAddressType type) -{ - Q_UNUSED(type) - - OSX_D_PTR; - - osx_d_ptr->addressType = type; -} - -void QLowEnergyController::connectToDevice() -{ - OSX_D_PTR; - - // A memory allocation problem. - if (!osx_d_ptr->isValid()) - return osx_d_ptr->_q_CBManagerError(UnknownError); - - if (role() == PeripheralRole) { - qCWarning(QT_BT_OSX) << "can not connect in peripheral role"; - return osx_d_ptr->_q_CBManagerError(ConnectionError); - } - - // No QBluetoothDeviceInfo provided during construction. - if (osx_d_ptr->deviceUuid.isNull()) - return osx_d_ptr->_q_CBManagerError(UnknownRemoteDeviceError); - - if (osx_d_ptr->controllerState != UnconnectedState) - return; - - osx_d_ptr->connectToDevice(); -} - -void QLowEnergyController::disconnectFromDevice() -{ - if (state() == UnconnectedState || state() == ClosingState) - return; - - OSX_D_PTR; - - if (role() == PeripheralRole) { - // CoreBluetooth API intentionally does not provide any way of closing - // a connection. All we can do here is to stop the advertisement. - stopAdvertising(); - return; - } - - if (osx_d_ptr->isValid()) { - const ControllerState oldState = osx_d_ptr->controllerState; - - if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { - osx_d_ptr->controllerState = ClosingState; - emit stateChanged(ClosingState); - osx_d_ptr->invalidateServices(); - - QT_MANGLE_NAMESPACE(OSXBTCentralManager) *manager - = osx_d_ptr->centralManager.data(); - dispatch_async(leQueue, ^{ - [manager disconnectFromDevice]; - }); - - if (oldState == ConnectingState) { - // With a pending connect attempt there is no - // guarantee we'll ever have didDisconnect callback, - // set the state here and now to make sure we still - // can connect. - osx_d_ptr->controllerState = UnconnectedState; - emit stateChanged(UnconnectedState); - } - } else { - qCCritical(QT_BT_OSX) << "qt LE queue is nil, " - "can not dispatch 'disconnect'"; - } - } -} - -void QLowEnergyController::discoverServices() -{ - if (role() == PeripheralRole) { - qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; - return; - } - - if (state() != ConnectedState) - return; - - OSX_D_PTR; - - osx_d_ptr->discoverServices(); -} - -QList<QBluetoothUuid> QLowEnergyController::services() const -{ - OSX_D_PTR; - - return osx_d_ptr->discoveredServices.keys(); -} - -QLowEnergyService *QLowEnergyController::createServiceObject(const QBluetoothUuid &serviceUuid, - QObject *parent) -{ - OSX_D_PTR; - - QLowEnergyService *service = nullptr; - - QLowEnergyControllerPrivateOSX::ServiceMap::const_iterator it = osx_d_ptr->discoveredServices.constFind(serviceUuid); - if (it != osx_d_ptr->discoveredServices.constEnd()) { - const QSharedPointer<QLowEnergyServicePrivate> &serviceData = it.value(); - - service = new QLowEnergyService(serviceData, parent); - } - - return service; -} - -QLowEnergyController::Error QLowEnergyController::error() const -{ - OSX_D_PTR; - - return osx_d_ptr->lastError; -} - -QString QLowEnergyController::errorString() const -{ - OSX_D_PTR; - - return osx_d_ptr->errorString; -} - -void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameters ¶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..059bd41b 100644 --- a/src/bluetooth/qlowenergycontrollerbase.cpp +++ b/src/bluetooth/qlowenergycontrollerbase.cpp @@ -61,7 +61,7 @@ QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() bool QLowEnergyControllerPrivate::isValidLocalAdapter() { -#ifdef QT_WINRT_BLUETOOTH +#if defined(QT_WINRT_BLUETOOTH) || defined(Q_OS_DARWIN) return true; #endif if (localAdapter.isNull()) @@ -106,6 +106,9 @@ void QLowEnergyControllerPrivate::setError( case QLowEnergyController::RemoteHostClosedError: errorString = QLowEnergyController::tr("Remote device closed the connection"); break; + case QLowEnergyController::AuthorizationError: + errorString = QLowEnergyController::tr("Failed to authorize on the remote device"); + break; case QLowEnergyController::NoError: return; default: diff --git a/src/bluetooth/qlowenergycontrollerbase_p.h b/src/bluetooth/qlowenergycontrollerbase_p.h index a8d1c676..169ba07b 100644 --- a/src/bluetooth/qlowenergycontrollerbase_p.h +++ b/src/bluetooth/qlowenergycontrollerbase_p.h @@ -51,24 +51,6 @@ // We mean it. // -#if defined(QT_OSX_BLUETOOTH) || defined(QT_IOS_BLUETOOTH) - -#include <QtCore/qglobal.h> -#include <QtCore/qobject.h> - -QT_BEGIN_NAMESPACE - -class QLowEnergyControllerPrivate : public QObject -{ -public: - // This class is required to make shared pointer machinery and - // moc (== Obj-C syntax) happy on both OS X and iOS. -}; - -QT_END_NAMESPACE - -#else - #include <qglobal.h> #include <QtCore/qobject.h> @@ -135,7 +117,6 @@ public: virtual QLowEnergyService *addServiceHelper( const QLowEnergyServiceData &service); - // common backend methods bool isValidLocalAdapter(); void setError(QLowEnergyController::Error newError); @@ -174,6 +155,7 @@ protected: QLowEnergyHandle lastLocalHandle{}; QString remoteName; // device name of the remote + QBluetoothUuid deviceUuid; // quite useless anywhere but Darwin (CoreBluetooth). Q_DECLARE_PUBLIC(QLowEnergyController) QLowEnergyController *q_ptr; @@ -181,6 +163,4 @@ protected: QT_END_NAMESPACE -#endif //defined(QT_OSX_BLUETOOTH) || defined(QT_IOS_BLUETOOTH) - #endif // QLOWENERGYCONTROLLERPRIVATEBASE_P_H diff --git a/src/bluetooth/qlowenergydescriptor.h b/src/bluetooth/qlowenergydescriptor.h index 62ca5fd3..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 |