diff options
-rw-r--r-- | src/bluetooth/bluetooth.pro | 19 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycharacteristic.h | 2 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller.cpp | 15 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_darwin.mm (renamed from src/bluetooth/qlowenergycontroller_osx.mm) | 1019 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_darwin_p.h (renamed from src/bluetooth/qlowenergycontroller_osx_p.h) | 111 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontrollerbase.cpp | 2 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontrollerbase_p.h | 22 | ||||
-rw-r--r-- | src/bluetooth/qlowenergydescriptor.h | 2 | ||||
-rw-r--r-- | src/bluetooth/qlowenergyservice.cpp | 19 | ||||
-rw-r--r-- | src/bluetooth/qlowenergyservice.h | 1 | ||||
-rw-r--r-- | src/bluetooth/qlowenergyservice_osx.mm | 277 |
11 files changed, 437 insertions, 1052 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index 71c8e6ed..d2b711d9 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -168,35 +168,28 @@ qtConfig(bluez) { qbluetoothsocket_osx.mm \ qbluetoothserver_osx.mm \ qbluetoothtransferreply_osx.mm \ - qlowenergycontroller_osx.mm \ - qlowenergyservice_osx.mm + qlowenergycontroller_darwin.mm PRIVATE_HEADERS += qbluetoothsocket_osx_p.h \ qbluetoothserver_osx_p.h \ qbluetoothtransferreply_osx_p.h \ - qbluetoothtransferreply_osx_p.h \ - qlowenergycontroller_osx_p.h + qlowenergycontroller_darwin_p.h SOURCES -= qbluetoothserviceinfo.cpp SOURCES -= qbluetoothservicediscoveryagent.cpp SOURCES -= qbluetoothsocket.cpp SOURCES -= qbluetoothsocketbase.cpp SOURCES -= qbluetoothserver.cpp - SOURCES -= qlowenergyservice_p.cpp - SOURCES -= qlowenergyservice.cpp - SOURCES -= qlowenergycontroller.cpp - SOURCES -= qlowenergycontrollerbase.cpp } else:ios|tvos { DEFINES += QT_IOS_BLUETOOTH LIBS_PRIVATE += -framework Foundation -framework CoreBluetooth OBJECTIVE_SOURCES += \ qbluetoothdevicediscoveryagent_darwin.mm \ - qlowenergycontroller_osx.mm \ - qlowenergyservice_osx.mm + qlowenergycontroller_darwin.mm PRIVATE_HEADERS += \ - qlowenergycontroller_osx_p.h \ + qlowenergycontroller_darwin_p.h \ qbluetoothsocket_dummy_p.h include(osx/osxbt.pri) @@ -206,10 +199,6 @@ qtConfig(bluez) { qbluetoothservicediscoveryagent_p.cpp \ qbluetoothsocket_dummy.cpp \ qbluetoothserver_p.cpp - - SOURCES -= qlowenergyservice.cpp - SOURCES -= qlowenergycontroller.cpp - SOURCES -= qlowenergycontrollerbase.cpp } else: qtConfig(winrt_bt) { DEFINES += QT_WINRT_BLUETOOTH !winrt { 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 d8aa00d7..3cf0d920 100644 --- a/src/bluetooth/qlowenergycontroller.cpp +++ b/src/bluetooth/qlowenergycontroller.cpp @@ -60,6 +60,8 @@ #if QT_CONFIG(winrt_btle_no_pairing) #include "qlowenergycontroller_winrt_new_p.h" #endif +#elif defined(Q_OS_DARWIN) +#include "qlowenergycontroller_darwin_p.h" #else #include "qlowenergycontroller_p.h" #endif @@ -321,6 +323,9 @@ static QLowEnergyControllerPrivate *privateController(QLowEnergyController::Role qCDebug(QT_BT_WINRT) << "Using pre 15063 low energy controller"; return new QLowEnergyControllerPrivateWinRT(); #endif +#elif defined(Q_OS_DARWIN) + Q_UNUSED(role) + return new QLowEnergyControllerPrivateDarwin(); #else Q_UNUSED(role); return new QLowEnergyControllerPrivateCommon(); @@ -344,6 +349,9 @@ QLowEnergyController::QLowEnergyController( QObject *parent) : QObject(parent) { + // Note: a central created using this ctor is useless + // on Darwin - no way to use addresses when connecting. + d_ptr = privateController(CentralRole); Q_D(QLowEnergyController); @@ -373,11 +381,12 @@ QLowEnergyController::QLowEnergyController( QObject *parent) : QObject(parent) { - d_ptr = privateController(CentralRole); + d_ptr = privateController(CentralRole); Q_D(QLowEnergyController); d->q_ptr = this; d->role = CentralRole; + d->deviceUuid = remoteDeviceInfo.deviceUuid(); d->remoteDevice = remoteDeviceInfo.address(); d->localAdapter = QBluetoothLocalDevice().address(); d->addressType = QLowEnergyController::PublicAddress; @@ -406,6 +415,8 @@ QLowEnergyController::QLowEnergyController( QObject *parent) : QObject(parent) { + // Note: a central create using this ctor is useless on + // Darwin (CoreBluetooth does not work with addresses). d_ptr = privateController(CentralRole); Q_D(QLowEnergyController); @@ -534,7 +545,7 @@ QBluetoothAddress QLowEnergyController::remoteAddress() const */ QBluetoothUuid QLowEnergyController::remoteDeviceUuid() const { - return QBluetoothUuid(); + return d_ptr->deviceUuid; } /*! diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_darwin.mm index 8bcdc22e..253956e2 100644 --- a/src/bluetooth/qlowenergycontroller_osx.mm +++ b/src/bluetooth/qlowenergycontroller_darwin.mm @@ -38,13 +38,17 @@ ** ****************************************************************************/ -#include "osx/osxbtnotifier_p.h" #include "osx/osxbtutility_p.h" #include "osx/uistrings_p.h" +#ifndef Q_OS_TVOS +#include "osx/osxbtperipheralmanager_p.h" +#endif // Q_OS_TVOS +#include "qlowenergycontroller_darwin_p.h" #include "qlowenergyserviceprivate_p.h" -#include "qlowenergycontroller_osx_p.h" +#include "osx/osxbtcentralmanager_p.h" + #include "qlowenergyservicedata.h" #include "qbluetoothlocaldevice.h" #include "qbluetoothdeviceinfo.h" @@ -58,30 +62,14 @@ #include <QtCore/qstring.h> #include <QtCore/qlist.h> -#define OSX_D_PTR QLowEnergyControllerPrivateOSX *osx_d_ptr = static_cast<QLowEnergyControllerPrivateOSX *>(d_ptr) - QT_BEGIN_NAMESPACE namespace { -static void registerQLowEnergyControllerMetaType() -{ - static bool initDone = false; - if (!initDone) { - qRegisterMetaType<QLowEnergyController::ControllerState>(); - qRegisterMetaType<QLowEnergyController::Error>(); - qRegisterMetaType<QLowEnergyHandle>("QLowEnergyHandle"); - qRegisterMetaType<QSharedPointer<QLowEnergyServicePrivate> >(); - qRegisterMetaType<QLowEnergyCharacteristic>(); - qRegisterMetaType<QLowEnergyDescriptor>(); - initDone = true; - } -} - typedef QSharedPointer<QLowEnergyServicePrivate> ServicePrivate; // Convenience function, can return a smart pointer that 'isNull'. -ServicePrivate qt_createLEService(QLowEnergyControllerPrivateOSX *controller, CBService *cbService, bool included) +ServicePrivate qt_createLEService(QLowEnergyControllerPrivateDarwin *controller, CBService *cbService, bool included) { Q_ASSERT_X(controller, Q_FUNC_INFO, "invalid controller (null)"); Q_ASSERT_X(cbService, Q_FUNC_INFO, "invalid service (nil)"); @@ -131,110 +119,276 @@ UUIDList qt_servicesUuids(NSArray *services) return uuids; } -} +} // unnamed namespace + +#ifndef Q_OS_TVOS +using ObjCPeripheralManager = QT_MANGLE_NAMESPACE(OSXBTPeripheralManager); +#endif // Q_OS_TVOS + +using ObjCCentralManager = QT_MANGLE_NAMESPACE(OSXBTCentralManager); -QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyController::Role r, - QLowEnergyController *q, - const QBluetoothDeviceInfo &deviceInfo) - : q_ptr(q), - deviceUuid(deviceInfo.deviceUuid()), - deviceName(deviceInfo.name()), - lastError(QLowEnergyController::NoError), - controllerState(QLowEnergyController::UnconnectedState), - addressType(QLowEnergyController::PublicAddress) +QLowEnergyControllerPrivateDarwin::QLowEnergyControllerPrivateDarwin() { + void registerQLowEnergyControllerMetaType(); registerQLowEnergyControllerMetaType(); + qRegisterMetaType<QLowEnergyHandle>("QLowEnergyHandle"); + qRegisterMetaType<QSharedPointer<QLowEnergyServicePrivate>>(); +} - Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)"); +QLowEnergyControllerPrivateDarwin::~QLowEnergyControllerPrivateDarwin() +{ + if (const auto leQueue = OSXBluetooth::qt_LE_queue()) { + if (role == QLowEnergyController::CentralRole) { + const auto manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_sync(leQueue, ^{ + [manager detach]; + }); + } else { +#ifndef Q_OS_TVOS + const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); + dispatch_sync(leQueue, ^{ + [manager detach]; + }); +#endif + } + } +} - using OSXBluetooth::LECBManagerNotifier; +bool QLowEnergyControllerPrivateDarwin::isValid() const +{ +#ifdef Q_OS_TVOS + return centralManager; +#else + return centralManager || peripheralManager; +#endif +} - role = r; +void QLowEnergyControllerPrivateDarwin::init() +{ + using OSXBluetooth::LECBManagerNotifier; QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier); if (role == QLowEnergyController::PeripheralRole) { #ifndef Q_OS_TVOS - peripheralManager.reset([[ObjCPeripheralManager alloc] initWith:notifier.data()]); + peripheralManager.reset([[ObjCPeripheralManager alloc] initWith:notifier.data()], + DarwinBluetooth::RetainPolicy::noInitialRetain); if (!peripheralManager) { - qCWarning(QT_BT_OSX) << "failed to initialize peripheral manager"; + qCWarning(QT_BT_OSX) << "failed to create a peripheral manager"; return; } #else - qCWarning(QT_BT_OSX) << "peripheral role is not supported on your platform"; + qCWarning(QT_BT_OSX) << "the peripheral role is not supported on your platform"; return; -#endif +#endif // Q_OS_TVOS } else { - centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()]); + centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()], + DarwinBluetooth::RetainPolicy::noInitialRetain); if (!centralManager) { - qCWarning(QT_BT_OSX) << "failed to initialize central manager"; + qCWarning(QT_BT_OSX) << "failed to initialize a central manager"; return; } } - if (!connectSlots(notifier.data())) { + if (!connectSlots(notifier.data())) qCWarning(QT_BT_OSX) << "failed to connect to notifier's signal(s)"; - } + // Ownership was taken by central manager. notifier.take(); } -QLowEnergyControllerPrivateOSX::~QLowEnergyControllerPrivateOSX() +void QLowEnergyControllerPrivateDarwin::connectToDevice() { - if (const auto leQueue = OSXBluetooth::qt_LE_queue()) { - if (role == QLowEnergyController::CentralRole) { - const auto manager = centralManager.data(); - dispatch_sync(leQueue, ^{ - [manager detach]; + Q_ASSERT_X(state == QLowEnergyController::UnconnectedState, + Q_FUNC_INFO, "invalid state"); + + if (!isValid()) { + // init() had failed for was never called. + return _q_CBManagerError(QLowEnergyController::UnknownError); + } + + if (deviceUuid.isNull()) { + // Wrong constructor was used or invalid UUID was provided. + return _q_CBManagerError(QLowEnergyController::UnknownRemoteDeviceError); + } + + // The logic enforcing the role is in the public class. + Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, + Q_FUNC_INFO, "invalid role (peripheral)"); + + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + if (!leQueue) { + qCWarning(QT_BT_OSX) << "no LE queue found"; + setErrorDescription(QLowEnergyController::UnknownError); + return; + } + + setErrorDescription(QLowEnergyController::NoError); + setState(QLowEnergyController::ConnectingState); + + const QBluetoothUuid deviceUuidCopy(deviceUuid); + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_async(leQueue, ^{ + [manager connectToDevice:deviceUuidCopy]; + }); +} + +void QLowEnergyControllerPrivateDarwin::disconnectFromDevice() +{ + if (role == QLowEnergyController::PeripheralRole) { + // CoreBluetooth API intentionally does not provide any way of closing + // a connection. All we can do here is to stop the advertisement. + stopAdvertising(); + return; + } + + if (isValid()) { + const auto oldState = state; + + if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { + setState(QLowEnergyController::ClosingState); + invalidateServices(); + + auto manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_async(leQueue, ^{ + [manager disconnectFromDevice]; }); + + if (oldState == QLowEnergyController::ConnectingState) { + // With a pending connect attempt there is no + // guarantee we'll ever have didDisconnect callback, + // set the state here and now to make sure we still + // can connect. + setState(QLowEnergyController::UnconnectedState); + } } else { -#ifndef Q_OS_TVOS - const auto manager = peripheralManager.data(); - dispatch_sync(leQueue, ^{ - [manager detach]; - }); -#endif + qCCritical(QT_BT_OSX) << "qt LE queue is nil, " + "can not dispatch 'disconnect'"; } } } -bool QLowEnergyControllerPrivateOSX::isValid() const +void QLowEnergyControllerPrivateDarwin::discoverServices() +{ + Q_ASSERT_X(state != QLowEnergyController::UnconnectedState, + Q_FUNC_INFO, "not connected to peripheral"); + Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, + Q_FUNC_INFO, "invalid role (peripheral)"); + + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + Q_ASSERT_X(leQueue, Q_FUNC_INFO, "LE queue not found"); + + setState(QLowEnergyController::DiscoveringState); + + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_async(leQueue, ^{ + [manager discoverServices]; + }); +} + +void QLowEnergyControllerPrivateDarwin::discoverServiceDetails(const QBluetoothUuid &serviceUuid) +{ + if (state != QLowEnergyController::DiscoveredState) { + qCWarning(QT_BT_OSX) << "can not discover service details in the current state, " + "QLowEnergyController::DiscoveredState is expected"; + return; + } + + if (!serviceList.contains(serviceUuid)) { + qCWarning(QT_BT_OSX) << "unknown service: " << serviceUuid; + return; + } + + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + Q_ASSERT(leQueue); + + ServicePrivate qtService(serviceList.value(serviceUuid)); + qtService->setState(QLowEnergyService::DiscoveringServices); + // Copy objects ... + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); + const QBluetoothUuid serviceUuidCopy(serviceUuid); + dispatch_async(leQueue, ^{ + [manager discoverServiceDetails:serviceUuidCopy]; + }); +} + +void QLowEnergyControllerPrivateDarwin::requestConnectionUpdate(const QLowEnergyConnectionParameters ¶ms) +{ + Q_UNUSED(params); + // TODO: implement this, if possible. + qCWarning(QT_BT_OSX) << "Connection update not implemented on your platform"; +} + +void QLowEnergyControllerPrivateDarwin::addToGenericAttributeList(const QLowEnergyServiceData &service, + QLowEnergyHandle startHandle) +{ + Q_UNUSED(service); + Q_UNUSED(startHandle); + // TODO: check why I don't need this (apparently it is used in addServiceHelper + // of the base class). +} + +QLowEnergyService * QLowEnergyControllerPrivateDarwin::addServiceHelper(const QLowEnergyServiceData &service) { + // Three checks below should be removed, they are done in the q_ptr's class. #ifdef Q_OS_TVOS - return centralManager; + Q_UNUSED(service); + qCDebug(QT_BT_OSX, "peripheral role is not supported on tvOS"); #else - return centralManager || peripheralManager; -#endif + if (role != QLowEnergyController::PeripheralRole) { + qCWarning(QT_BT_OSX) << "not in peripheral role"; + return nullptr; + } + + if (state != QLowEnergyController::UnconnectedState) { + qCWarning(QT_BT_OSX) << "invalid state"; + return nullptr; + } + + if (!service.isValid()) { + qCWarning(QT_BT_OSX) << "invalid service"; + return nullptr; + } + + for (auto includedService : service.includedServices()) + includedService->d_ptr->type |= QLowEnergyService::IncludedService; + + const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); + Q_ASSERT(manager); + if (const auto servicePrivate = [manager addService:service]) { + servicePrivate->setController(this); + servicePrivate->state = QLowEnergyService::LocalService; + localServices.insert(servicePrivate->uuid, servicePrivate); + return new QLowEnergyService(servicePrivate); + } +#endif // Q_OS_TVOS + return nullptr; } -void QLowEnergyControllerPrivateOSX::_q_connected() +void QLowEnergyControllerPrivateDarwin::_q_connected() { - controllerState = QLowEnergyController::ConnectedState; - - emit q_ptr->stateChanged(QLowEnergyController::ConnectedState); + setState(QLowEnergyController::ConnectedState); emit q_ptr->connected(); } -void QLowEnergyControllerPrivateOSX::_q_disconnected() +void QLowEnergyControllerPrivateDarwin::_q_disconnected() { - controllerState = QLowEnergyController::UnconnectedState; - if (role == QLowEnergyController::CentralRole) invalidateServices(); - emit q_ptr->stateChanged(QLowEnergyController::UnconnectedState); + setState(QLowEnergyController::UnconnectedState); emit q_ptr->disconnected(); } -void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() +void QLowEnergyControllerPrivateDarwin::_q_serviceDiscoveryFinished() { - Q_ASSERT_X(controllerState == QLowEnergyController::DiscoveringState, + Q_ASSERT_X(state == QLowEnergyController::DiscoveringState, Q_FUNC_INFO, "invalid state"); using namespace OSXBluetooth; QT_BT_MAC_AUTORELEASEPOOL; - NSArray *const services = [centralManager.data() peripheral].services; + NSArray *const services = [centralManager.getAs<ObjCCentralManager>() peripheral].services; // Now we have to traverse the discovered services tree. // Essentially it's an iterative version of more complicated code from the // OSXBTCentralManager's code. @@ -249,13 +403,13 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() const ServicePrivate newService(qt_createLEService(this, cbService, false)); if (!newService.data()) continue; - if (discoveredServices.contains(newService->uuid)) { + if (serviceList.contains(newService->uuid)) { // It's a bit stupid we first created it ... qCDebug(QT_BT_OSX) << "discovered service with a duplicated UUID" << newService->uuid; continue; } - discoveredServices.insert(newService->uuid, newService); + serviceList.insert(newService->uuid, newService); discoveredCBServices.insert(newService->uuid, cbService); } @@ -273,8 +427,8 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() } const QBluetoothUuid uuid(qt_uuid(s.UUID)); - if (discoveredServices.contains(uuid) && discoveredCBServices.value(uuid) == s) { - ServicePrivate qtService(discoveredServices.value(uuid)); + if (serviceList.contains(uuid) && discoveredCBServices.value(uuid) == s) { + ServicePrivate qtService(serviceList.value(uuid)); // Add included UUIDs: qtService->includedServices.append(qt_servicesUuids(s.includedServices)); }// Else - we ignored this CBService object. @@ -286,15 +440,15 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() for (NSUInteger i = 0, e = [toVisitNext count]; i < e; ++i) { CBService *const s = [toVisitNext objectAtIndex:i]; const QBluetoothUuid uuid(qt_uuid(s.UUID)); - if (discoveredServices.contains(uuid)) { + if (serviceList.contains(uuid)) { if (discoveredCBServices.value(uuid) == s) { - ServicePrivate qtService(discoveredServices.value(uuid)); + ServicePrivate qtService(serviceList.value(uuid)); qtService->type |= QLowEnergyService::IncludedService; } // Else this is the duplicate we ignored already. } else { // Oh, we do not even have it yet??? ServicePrivate newService(qt_createLEService(this, s, true)); - discoveredServices.insert(newService->uuid, newService); + serviceList.insert(newService->uuid, newService); discoveredCBServices.insert(newService->uuid, s); } } @@ -306,31 +460,26 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() qCDebug(QT_BT_OSX) << "no services found"; } - for (ServiceMap::const_iterator it = discoveredServices.constBegin(); it != discoveredServices.constEnd(); ++it) { - const QBluetoothUuid &uuid = it.key(); - QMetaObject::invokeMethod(q_ptr, "serviceDiscovered", Qt::QueuedConnection, - Q_ARG(QBluetoothUuid, uuid)); - } + for (ServiceMap::const_iterator it = serviceList.constBegin(); it != serviceList.constEnd(); ++it) + emit q_ptr->serviceDiscovered(it.key()); - controllerState = QLowEnergyController::DiscoveredState; - QMetaObject::invokeMethod(q_ptr, "stateChanged", Qt::QueuedConnection, - Q_ARG(QLowEnergyController::ControllerState, controllerState)); - QMetaObject::invokeMethod(q_ptr, "discoveryFinished", Qt::QueuedConnection); + setState(QLowEnergyController::DiscoveredState); + emit q_ptr->discoveryFinished(); } -void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service) +void QLowEnergyControllerPrivateDarwin::_q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service) { QT_BT_MAC_AUTORELEASEPOOL; Q_ASSERT(service); - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid)) { qCDebug(QT_BT_OSX) << "unknown service uuid:" << service->uuid; return; } - ServicePrivate qtService(discoveredServices.value(service->uuid)); + ServicePrivate qtService(serviceList.value(service->uuid)); // Assert on handles? qtService->startHandle = service->startHandle; qtService->endHandle = service->endHandle; @@ -339,23 +488,23 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedP qtService->setState(QLowEnergyService::ServiceDiscovered); } -void QLowEnergyControllerPrivateOSX::_q_servicesWereModified() +void QLowEnergyControllerPrivateDarwin::_q_servicesWereModified() { - if (!(controllerState == QLowEnergyController::DiscoveringState - || controllerState == QLowEnergyController::DiscoveredState)) { + if (!(state == QLowEnergyController::DiscoveringState + || state == QLowEnergyController::DiscoveredState)) { qCWarning(QT_BT_OSX) << "services were modified while controller is not in Discovered/Discovering state"; return; } - if (controllerState == QLowEnergyController::DiscoveredState) + if (state == QLowEnergyController::DiscoveredState) invalidateServices(); - controllerState = QLowEnergyController::ConnectedState; + setState(QLowEnergyController::ConnectedState); q_ptr->discoverServices(); } -void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle charHandle, - const QByteArray &value) +void QLowEnergyControllerPrivateDarwin::_q_characteristicRead(QLowEnergyHandle charHandle, + const QByteArray &value) { Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)"); @@ -375,8 +524,8 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle char emit service->characteristicRead(characteristic, value); } -void QLowEnergyControllerPrivateOSX::_q_characteristicWritten(QLowEnergyHandle charHandle, - const QByteArray &value) +void QLowEnergyControllerPrivateDarwin::_q_characteristicWritten(QLowEnergyHandle charHandle, + const QByteArray &value) { Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)"); @@ -399,8 +548,8 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicWritten(QLowEnergyHandle c emit service->characteristicWritten(characteristic, value); } -void QLowEnergyControllerPrivateOSX::_q_characteristicUpdated(QLowEnergyHandle charHandle, - const QByteArray &value) +void QLowEnergyControllerPrivateDarwin::_q_characteristicUpdated(QLowEnergyHandle charHandle, + const QByteArray &value) { // TODO: write/update notifications are quite similar (except asserts/warnings messages // and different signals emitted). Merge them into one function? @@ -428,8 +577,8 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicUpdated(QLowEnergyHandle c emit service->characteristicChanged(characteristic, value); } -void QLowEnergyControllerPrivateOSX::_q_descriptorRead(QLowEnergyHandle dHandle, - const QByteArray &value) +void QLowEnergyControllerPrivateDarwin::_q_descriptorRead(QLowEnergyHandle dHandle, + const QByteArray &value) { Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)"); @@ -444,8 +593,8 @@ void QLowEnergyControllerPrivateOSX::_q_descriptorRead(QLowEnergyHandle dHandle, emit service->descriptorRead(qtDescriptor, value); } -void QLowEnergyControllerPrivateOSX::_q_descriptorWritten(QLowEnergyHandle dHandle, - const QByteArray &value) +void QLowEnergyControllerPrivateDarwin::_q_descriptorWritten(QLowEnergyHandle dHandle, + const QByteArray &value) { Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)"); @@ -461,8 +610,8 @@ void QLowEnergyControllerPrivateOSX::_q_descriptorWritten(QLowEnergyHandle dHand emit service->descriptorWritten(qtDescriptor, value); } -void QLowEnergyControllerPrivateOSX::_q_notificationEnabled(QLowEnergyHandle charHandle, - bool enabled) +void QLowEnergyControllerPrivateDarwin::_q_notificationEnabled(QLowEnergyHandle charHandle, + bool enabled) { // CoreBluetooth in peripheral role does not allow mutable descriptors, // in central we can only call setNotification:enabled/disabled. @@ -504,7 +653,7 @@ void QLowEnergyControllerPrivateOSX::_q_notificationEnabled(QLowEnergyHandle cha } } -void QLowEnergyControllerPrivateOSX::_q_LEnotSupported() +void QLowEnergyControllerPrivateDarwin::_q_LEnotSupported() { // Report as an error. But this should not be possible // actually: before connecting to any device, we have @@ -512,32 +661,30 @@ void QLowEnergyControllerPrivateOSX::_q_LEnotSupported() // be supported. } -void QLowEnergyControllerPrivateOSX::_q_CBManagerError(QLowEnergyController::Error errorCode) +void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(QLowEnergyController::Error errorCode) { - // Errors reported during connect and general errors. - - setErrorDescription(errorCode); - emit q_ptr->error(lastError); - - if (controllerState == QLowEnergyController::ConnectingState) { - controllerState = QLowEnergyController::UnconnectedState; - emit q_ptr->stateChanged(controllerState); - } else if (controllerState == QLowEnergyController::DiscoveringState) { - controllerState = QLowEnergyController::ConnectedState; - emit q_ptr->stateChanged(controllerState); - } // In any other case we stay in Discovered, it's - // a service/characteristic - related error. + // This function handles errors reported while connecting to a remote device + // and also other errors in general. + setError(errorCode); + + if (state == QLowEnergyController::ConnectingState) + setState(QLowEnergyController::UnconnectedState); + else if (state == QLowEnergyController::DiscoveringState) + setState(QLowEnergyController::ConnectedState); + + // In any other case we stay in Discovered, it's + // a service/characteristic - related error. } -void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &serviceUuid, - QLowEnergyController::Error errorCode) +void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(const QBluetoothUuid &serviceUuid, + QLowEnergyController::Error errorCode) { // Errors reported while discovering service details etc. Q_UNUSED(errorCode) // TODO: setError? // We failed to discover any characteristics/descriptors. - if (discoveredServices.contains(serviceUuid)) { - ServicePrivate qtService(discoveredServices.value(serviceUuid)); + if (serviceList.contains(serviceUuid)) { + ServicePrivate qtService(serviceList.value(serviceUuid)); qtService->setState(QLowEnergyService::InvalidService); } else { qCDebug(QT_BT_OSX) << "error reported for unknown service" @@ -545,109 +692,24 @@ void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &ser } } -void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &serviceUuid, - QLowEnergyService::ServiceError errorCode) +void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(const QBluetoothUuid &serviceUuid, + QLowEnergyService::ServiceError errorCode) { - if (!discoveredServices.contains(serviceUuid)) { + if (!serviceList.contains(serviceUuid)) { qCDebug(QT_BT_OSX) << "unknown service uuid:" << serviceUuid; return; } - ServicePrivate service(discoveredServices.value(serviceUuid)); + ServicePrivate service(serviceList.value(serviceUuid)); service->setError(errorCode); } -void QLowEnergyControllerPrivateOSX::connectToDevice() -{ - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller"); - Q_ASSERT_X(controllerState == QLowEnergyController::UnconnectedState, - Q_FUNC_INFO, "invalid state"); - Q_ASSERT_X(!deviceUuid.isNull(), Q_FUNC_INFO, - "invalid private controller (no device uuid)"); - Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, - Q_FUNC_INFO, "invalid role (peripheral)"); - - dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - setErrorDescription(QLowEnergyController::UnknownError); - return; - } - - setErrorDescription(QLowEnergyController::NoError); - controllerState = QLowEnergyController::ConnectingState; - - const QBluetoothUuid deviceUuidCopy(deviceUuid); - ObjCCentralManager *manager = centralManager.data(); - dispatch_async(leQueue, ^{ - [manager connectToDevice:deviceUuidCopy]; - }); -} - -void QLowEnergyControllerPrivateOSX::discoverServices() -{ - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller"); - Q_ASSERT_X(controllerState != QLowEnergyController::UnconnectedState, - Q_FUNC_INFO, "not connected to peripheral"); - Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, - Q_FUNC_INFO, "invalid role (peripheral)"); - - dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - setErrorDescription(QLowEnergyController::UnknownError); - return; - } - - controllerState = QLowEnergyController::DiscoveringState; - emit q_ptr->stateChanged(QLowEnergyController::DiscoveringState); - - ObjCCentralManager *manager = centralManager.data(); - dispatch_async(leQueue, ^{ - [manager discoverServices]; - }); -} - -void QLowEnergyControllerPrivateOSX::discoverServiceDetails(const QBluetoothUuid &serviceUuid) -{ - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller"); - - if (controllerState != QLowEnergyController::DiscoveredState) { - // This will also exclude peripheral role, since controller - // can never be in discovered state ... - qCWarning(QT_BT_OSX) << "can not discover service details in the current state, " - "QLowEnergyController::DiscoveredState is expected"; - return; - } - - if (!discoveredServices.contains(serviceUuid)) { - qCWarning(QT_BT_OSX) << "unknown service: " << serviceUuid; - return; - } - - dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - return; - } - - ServicePrivate qtService(discoveredServices.value(serviceUuid)); - qtService->setState(QLowEnergyService::DiscoveringServices); - // Copy objects ... - ObjCCentralManager *manager = centralManager.data(); - const QBluetoothUuid serviceUuidCopy(serviceUuid); - dispatch_async(leQueue, ^{ - [manager discoverServiceDetails:serviceUuidCopy]; - }); -} - -void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle charHandle, - const QByteArray &newValue) +void QLowEnergyControllerPrivateDarwin::setNotifyValue(QSharedPointer<QLowEnergyServicePrivate> service, + QLowEnergyHandle charHandle, + const QByteArray &newValue) { Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller"); if (role == QLowEnergyController::PeripheralRole) { qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; @@ -666,7 +728,7 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer return; } - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid)) { qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << "found"; return; } @@ -678,11 +740,9 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - return; - } - ObjCCentralManager *manager = centralManager.data(); + Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); + + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); const QBluetoothUuid serviceUuid(service->uuid); const QByteArray newValueCopy(newValue); dispatch_async(leQueue, ^{ @@ -692,18 +752,17 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer }); } -void QLowEnergyControllerPrivateOSX::readCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle charHandle) +void QLowEnergyControllerPrivateDarwin::readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle) { Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller"); if (role == QLowEnergyController::PeripheralRole) { qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; return; } - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid)) { qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << "found"; return; @@ -716,29 +775,26 @@ void QLowEnergyControllerPrivateOSX::readCharacteristic(QSharedPointer<QLowEnerg } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - return; - } + Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); + // Attention! We have to copy UUID. - ObjCCentralManager *manager = centralManager.data(); + ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); const QBluetoothUuid serviceUuid(service->uuid); dispatch_async(leQueue, ^{ [manager readCharacteristic:charHandle onService:serviceUuid]; }); } -void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle charHandle, const QByteArray &newValue, - QLowEnergyService::WriteMode mode) +void QLowEnergyControllerPrivateDarwin::writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, const QByteArray &newValue, + QLowEnergyService::WriteMode mode) { Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller"); // We can work only with services found on a given peripheral // (== created by the given LE controller). - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid) && !localServices.contains(service->uuid)) { qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << " found"; return; @@ -751,15 +807,12 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - return; - } + Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); // Attention! We have to copy objects! const QByteArray newValueCopy(newValue); if (role == QLowEnergyController::CentralRole) { const QBluetoothUuid serviceUuid(service->uuid); - const auto manager = centralManager.data(); + const auto manager = centralManager.getAs<ObjCCentralManager>(); dispatch_async(leQueue, ^{ [manager write:newValueCopy charHandle:charHandle @@ -768,7 +821,7 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner }); } else { #ifndef Q_OS_TVOS - const auto manager = peripheralManager.data(); + const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); dispatch_async(leQueue, ^{ [manager write:newValueCopy charHandle:charHandle]; }); @@ -778,9 +831,9 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner } } -quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHandle charHandle, - const QByteArray &value, - bool appendValue) +quint16 QLowEnergyControllerPrivateDarwin::updateValueOfCharacteristic(QLowEnergyHandle charHandle, + const QByteArray &value, + bool appendValue) { QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); if (!service.isNull()) { @@ -799,18 +852,20 @@ quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHa return 0; } -void QLowEnergyControllerPrivateOSX::readDescriptor(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle descriptorHandle) +void QLowEnergyControllerPrivateDarwin::readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QLowEnergyHandle descriptorHandle) { + Q_UNUSED(charHandle) // Hehe, yes! + Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller"); if (role == QLowEnergyController::PeripheralRole) { qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; return; } - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid)) { qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << "found"; return; @@ -823,19 +878,21 @@ void QLowEnergyControllerPrivateOSX::readDescriptor(QSharedPointer<QLowEnergySer } // Attention! Copy objects! const QBluetoothUuid serviceUuid(service->uuid); - ObjCCentralManager * const manager = centralManager.data(); + ObjCCentralManager * const manager = centralManager.getAs<ObjCCentralManager>(); dispatch_async(leQueue, ^{ [manager readDescriptor:descriptorHandle onService:serviceUuid]; }); } -void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle descriptorHandle, - const QByteArray &newValue) +void QLowEnergyControllerPrivateDarwin::writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QLowEnergyHandle descriptorHandle, + const QByteArray &newValue) { + Q_UNUSED(charHandle) + Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller"); if (role == QLowEnergyController::PeripheralRole) { qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; @@ -845,20 +902,17 @@ void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergySe // We can work only with services found on a given peripheral // (== created by the given LE controller), // otherwise we can not write anything at all. - if (!discoveredServices.contains(service->uuid)) { + if (!serviceList.contains(service->uuid)) { qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << " found"; return; } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - if (!leQueue) { - qCWarning(QT_BT_OSX) << "no LE queue found"; - return; - } + Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); // Attention! Copy objects! const QBluetoothUuid serviceUuid(service->uuid); - ObjCCentralManager * const manager = centralManager.data(); + ObjCCentralManager * const manager = centralManager.getAs<ObjCCentralManager>(); const QByteArray newValueCopy(newValue); dispatch_async(leQueue, ^{ [manager write:newValueCopy @@ -867,8 +921,8 @@ void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergySe }); } -quint16 QLowEnergyControllerPrivateOSX::updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descHandle, - const QByteArray &value, bool appendValue) +quint16 QLowEnergyControllerPrivateDarwin::updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descHandle, + const QByteArray &value, bool appendValue) { QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); if (!service.isNull()) { @@ -893,66 +947,15 @@ quint16 QLowEnergyControllerPrivateOSX::updateValueOfDescriptor(QLowEnergyHandle return 0; } -QSharedPointer<QLowEnergyServicePrivate> QLowEnergyControllerPrivateOSX::serviceForHandle(QLowEnergyHandle handle) -{ - const QList<QSharedPointer<QLowEnergyServicePrivate>> services - = discoveredServices.values(); - for (QSharedPointer<QLowEnergyServicePrivate> service : services) { - if (service->startHandle <= handle && handle <= service->endHandle) - return service; - } - - return QSharedPointer<QLowEnergyServicePrivate>(); -} - -QLowEnergyCharacteristic QLowEnergyControllerPrivateOSX::characteristicForHandle(QLowEnergyHandle charHandle) -{ - QSharedPointer<QLowEnergyServicePrivate> service(serviceForHandle(charHandle)); - if (service.isNull()) - return QLowEnergyCharacteristic(); - - if (service->characteristicList.isEmpty()) - return QLowEnergyCharacteristic(); - - // Check whether it is the handle of a characteristic header - if (service->characteristicList.contains(charHandle)) - return QLowEnergyCharacteristic(service, charHandle); - - // Check whether it is the handle of the characteristic value or its descriptors - QList<QLowEnergyHandle> charHandles(service->characteristicList.keys()); - std::sort(charHandles.begin(), charHandles.end()); - - for (int i = charHandles.size() - 1; i >= 0; --i) { - if (charHandles.at(i) > charHandle) - continue; - - return QLowEnergyCharacteristic(service, charHandles.at(i)); - } - - return QLowEnergyCharacteristic(); -} - -QLowEnergyDescriptor QLowEnergyControllerPrivateOSX::descriptorForHandle(QLowEnergyHandle descriptorHandle) -{ - const QLowEnergyCharacteristic ch(characteristicForHandle(descriptorHandle)); - if (!ch.isValid()) - return QLowEnergyDescriptor(); - - const QLowEnergyServicePrivate::CharData charData = ch.d_ptr->characteristicList[ch.attributeHandle()]; - - if (charData.descriptorList.contains(descriptorHandle)) - return QLowEnergyDescriptor(ch.d_ptr, ch.attributeHandle(), descriptorHandle); - - return QLowEnergyDescriptor(); -} - -void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::Error errorCode) +void QLowEnergyControllerPrivateDarwin::setErrorDescription(QLowEnergyController::Error errorCode) { // This function does not emit! + // TODO: well, it is not a reason to duplicate a significant part of + // setError though! - lastError = errorCode; + error = errorCode; - switch (lastError) { + switch (error) { case QLowEnergyController::NoError: errorString.clear(); break; @@ -978,48 +981,36 @@ void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::E } } -void QLowEnergyControllerPrivateOSX::invalidateServices() -{ - const QList<QSharedPointer<QLowEnergyServicePrivate>> services - = discoveredServices.values(); - for (const QSharedPointer<QLowEnergyServicePrivate> service : services) { - service->setController(nullptr); - service->setState(QLowEnergyService::InvalidService); - } - - discoveredServices.clear(); -} - -bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECBManagerNotifier *notifier) +bool QLowEnergyControllerPrivateDarwin::connectSlots(OSXBluetooth::LECBManagerNotifier *notifier) { using OSXBluetooth::LECBManagerNotifier; Q_ASSERT_X(notifier, Q_FUNC_INFO, "invalid notifier object (null)"); bool ok = connect(notifier, &LECBManagerNotifier::connected, - this, &QLowEnergyControllerPrivateOSX::_q_connected); + this, &QLowEnergyControllerPrivateDarwin::_q_connected); ok = ok && connect(notifier, &LECBManagerNotifier::disconnected, - this, &QLowEnergyControllerPrivateOSX::_q_disconnected); + this, &QLowEnergyControllerPrivateDarwin::_q_disconnected); ok = ok && connect(notifier, &LECBManagerNotifier::serviceDiscoveryFinished, - this, &QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished); - ok = ok && connect(notifier, &LECBManagerNotifier::serviceDetailsDiscoveryFinished, - this, &QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished); + this, &QLowEnergyControllerPrivateDarwin::_q_serviceDiscoveryFinished); ok = ok && connect(notifier, &LECBManagerNotifier::servicesWereModified, - this, &QLowEnergyControllerPrivateOSX::_q_servicesWereModified); + this, &QLowEnergyControllerPrivateDarwin::_q_servicesWereModified); + ok = ok && connect(notifier, &LECBManagerNotifier::serviceDetailsDiscoveryFinished, + this, &QLowEnergyControllerPrivateDarwin::_q_serviceDetailsDiscoveryFinished); ok = ok && connect(notifier, &LECBManagerNotifier::characteristicRead, - this, &QLowEnergyControllerPrivateOSX::_q_characteristicRead); + this, &QLowEnergyControllerPrivateDarwin::_q_characteristicRead); ok = ok && connect(notifier, &LECBManagerNotifier::characteristicWritten, - this, &QLowEnergyControllerPrivateOSX::_q_characteristicWritten); + this, &QLowEnergyControllerPrivateDarwin::_q_characteristicWritten); ok = ok && connect(notifier, &LECBManagerNotifier::characteristicUpdated, - this, &QLowEnergyControllerPrivateOSX::_q_characteristicUpdated); + this, &QLowEnergyControllerPrivateDarwin::_q_characteristicUpdated); ok = ok && connect(notifier, &LECBManagerNotifier::descriptorRead, - this, &QLowEnergyControllerPrivateOSX::_q_descriptorRead); + this, &QLowEnergyControllerPrivateDarwin::_q_descriptorRead); ok = ok && connect(notifier, &LECBManagerNotifier::descriptorWritten, - this, &QLowEnergyControllerPrivateOSX::_q_descriptorWritten); + this, &QLowEnergyControllerPrivateDarwin::_q_descriptorWritten); ok = ok && connect(notifier, &LECBManagerNotifier::notificationEnabled, - this, &QLowEnergyControllerPrivateOSX::_q_notificationEnabled); + this, &QLowEnergyControllerPrivateDarwin::_q_notificationEnabled); ok = ok && connect(notifier, &LECBManagerNotifier::LEnotSupported, - this, &QLowEnergyControllerPrivateOSX::_q_LEnotSupported); + this, &QLowEnergyControllerPrivateDarwin::_q_LEnotSupported); ok = ok && connect(notifier, SIGNAL(CBManagerError(QLowEnergyController::Error)), this, SLOT(_q_CBManagerError(QLowEnergyController::Error))); ok = ok && connect(notifier, SIGNAL(CBManagerError(const QBluetoothUuid &, QLowEnergyController::Error)), @@ -1033,253 +1024,9 @@ bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECBManagerNotif return ok; } -QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddress, - QObject *parent) - : QObject(parent), - d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this)) -{ - OSX_D_PTR; - - osx_d_ptr->remoteAddress = remoteAddress; - osx_d_ptr->localAddress = QBluetoothLocalDevice().address(); - - qCWarning(QT_BT_OSX) << "construction with remote address " - "is not supported!"; -} - -QLowEnergyController::QLowEnergyController(const QBluetoothDeviceInfo &remoteDevice, - QObject *parent) - : QObject(parent), - d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this, remoteDevice)) -{ - OSX_D_PTR; - - osx_d_ptr->localAddress = QBluetoothLocalDevice().address(); - // That's the only "real" ctor - with Core Bluetooth we need a _valid_ deviceUuid - // from 'remoteDevice'. -} - -QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddress, - const QBluetoothAddress &localAddress, - QObject *parent) - : QObject(parent), - d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this)) -{ - OSX_D_PTR; - - osx_d_ptr->remoteAddress = remoteAddress; - osx_d_ptr->localAddress = localAddress; - - qCWarning(QT_BT_OSX) << "construction with remote/local " - "addresses is not supported!"; -} - -QLowEnergyController::QLowEnergyController(QObject *parent) - : QObject(parent), - d_ptr(new QLowEnergyControllerPrivateOSX(PeripheralRole, this)) -{ - OSX_D_PTR; - - osx_d_ptr->localAddress = QBluetoothLocalDevice().address(); -} - -QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDeviceInfo &remoteDevice, - QObject *parent) -{ - return new QLowEnergyController(remoteDevice, parent); -} - -QLowEnergyController *QLowEnergyController::createPeripheral(QObject *parent) -{ - return new QLowEnergyController(parent); -} - -QLowEnergyController::~QLowEnergyController() -{ - // Deleting a peripheral will also disconnect. - delete d_ptr; -} - -QLowEnergyController::Role QLowEnergyController::role() const -{ - OSX_D_PTR; - - return osx_d_ptr->role; -} - -QBluetoothAddress QLowEnergyController::localAddress() const -{ - OSX_D_PTR; - - return osx_d_ptr->localAddress; -} - -QBluetoothAddress QLowEnergyController::remoteAddress() const -{ - OSX_D_PTR; - - return osx_d_ptr->remoteAddress; -} - -QBluetoothUuid QLowEnergyController::remoteDeviceUuid() const -{ - OSX_D_PTR; - - return osx_d_ptr->deviceUuid; -} - -QString QLowEnergyController::remoteName() const -{ - OSX_D_PTR; - - return osx_d_ptr->deviceName; -} - -QLowEnergyController::ControllerState QLowEnergyController::state() const -{ - OSX_D_PTR; - - return osx_d_ptr->controllerState; -} - -QLowEnergyController::RemoteAddressType QLowEnergyController::remoteAddressType() const -{ - OSX_D_PTR; - - return osx_d_ptr->addressType; -} - -void QLowEnergyController::setRemoteAddressType(RemoteAddressType type) -{ - Q_UNUSED(type) - - OSX_D_PTR; - - osx_d_ptr->addressType = type; -} - -void QLowEnergyController::connectToDevice() -{ - OSX_D_PTR; - - // A memory allocation problem. - if (!osx_d_ptr->isValid()) - return osx_d_ptr->_q_CBManagerError(UnknownError); - - if (role() == PeripheralRole) { - qCWarning(QT_BT_OSX) << "can not connect in peripheral role"; - return osx_d_ptr->_q_CBManagerError(ConnectionError); - } - - // No QBluetoothDeviceInfo provided during construction. - if (osx_d_ptr->deviceUuid.isNull()) - return osx_d_ptr->_q_CBManagerError(UnknownRemoteDeviceError); - - if (osx_d_ptr->controllerState != UnconnectedState) - return; - - osx_d_ptr->connectToDevice(); -} - -void QLowEnergyController::disconnectFromDevice() -{ - if (state() == UnconnectedState || state() == ClosingState) - return; - - OSX_D_PTR; - - if (role() == PeripheralRole) { - // CoreBluetooth API intentionally does not provide any way of closing - // a connection. All we can do here is to stop the advertisement. - stopAdvertising(); - return; - } - - if (osx_d_ptr->isValid()) { - const ControllerState oldState = osx_d_ptr->controllerState; - - if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { - osx_d_ptr->controllerState = ClosingState; - emit stateChanged(ClosingState); - osx_d_ptr->invalidateServices(); - - QT_MANGLE_NAMESPACE(OSXBTCentralManager) *manager - = osx_d_ptr->centralManager.data(); - dispatch_async(leQueue, ^{ - [manager disconnectFromDevice]; - }); - - if (oldState == ConnectingState) { - // With a pending connect attempt there is no - // guarantee we'll ever have didDisconnect callback, - // set the state here and now to make sure we still - // can connect. - osx_d_ptr->controllerState = UnconnectedState; - emit stateChanged(UnconnectedState); - } - } else { - qCCritical(QT_BT_OSX) << "qt LE queue is nil, " - "can not dispatch 'disconnect'"; - } - } -} - -void QLowEnergyController::discoverServices() -{ - if (role() == PeripheralRole) { - qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; - return; - } - - if (state() != ConnectedState) - return; - - OSX_D_PTR; - - osx_d_ptr->discoverServices(); -} - -QList<QBluetoothUuid> QLowEnergyController::services() const -{ - OSX_D_PTR; - - return osx_d_ptr->discoveredServices.keys(); -} - -QLowEnergyService *QLowEnergyController::createServiceObject(const QBluetoothUuid &serviceUuid, - QObject *parent) -{ - OSX_D_PTR; - - QLowEnergyService *service = nullptr; - - QLowEnergyControllerPrivateOSX::ServiceMap::const_iterator it = osx_d_ptr->discoveredServices.constFind(serviceUuid); - if (it != osx_d_ptr->discoveredServices.constEnd()) { - const QSharedPointer<QLowEnergyServicePrivate> &serviceData = it.value(); - - service = new QLowEnergyService(serviceData, parent); - } - - return service; -} - -QLowEnergyController::Error QLowEnergyController::error() const -{ - OSX_D_PTR; - - return osx_d_ptr->lastError; -} - -QString QLowEnergyController::errorString() const -{ - OSX_D_PTR; - - return osx_d_ptr->errorString; -} - -void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, - const QLowEnergyAdvertisingData &advertisingData, - const QLowEnergyAdvertisingData &scanResponseData) +void QLowEnergyControllerPrivateDarwin::startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, + const QLowEnergyAdvertisingData &advertisingData, + const QLowEnergyAdvertisingData &scanResponseData) { #ifdef Q_OS_TVOS Q_UNUSED(params) @@ -1287,123 +1034,65 @@ void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameter Q_UNUSED(scanResponseData) qCWarning(QT_BT_OSX) << "advertising is not supported on your platform"; #else - OSX_D_PTR; - if (!osx_d_ptr->isValid()) - return osx_d_ptr->_q_CBManagerError(UnknownError); + if (!isValid()) + return _q_CBManagerError(QLowEnergyController::UnknownError); - if (role() != PeripheralRole) { - qCWarning(QT_BT_OSX) << "invalid role"; + if (role != QLowEnergyController::PeripheralRole) { + qCWarning(QT_BT_OSX) << "controller is not a peripheral, cannot start advertising"; return; } - if (state() != UnconnectedState) { - qCWarning(QT_BT_OSX) << "invalid state" << state(); + if (state != QLowEnergyController::UnconnectedState) { + qCWarning(QT_BT_OSX) << "invalid state" << state; return; } auto leQueue(OSXBluetooth::qt_LE_queue()); if (!leQueue) { qCWarning(QT_BT_OSX) << "no LE queue found"; - osx_d_ptr->setErrorDescription(QLowEnergyController::UnknownError); + setErrorDescription(QLowEnergyController::UnknownError); return; } - [osx_d_ptr->peripheralManager setParameters:params - data:advertisingData - scanResponse:scanResponseData]; + const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); + [manager setParameters:params data:advertisingData scanResponse:scanResponseData]; - osx_d_ptr->controllerState = AdvertisingState; - emit stateChanged(AdvertisingState); + setState(QLowEnergyController::AdvertisingState); - const auto manager = osx_d_ptr->peripheralManager.data(); dispatch_async(leQueue, ^{ [manager startAdvertising]; }); #endif } -void QLowEnergyController::stopAdvertising() +void QLowEnergyControllerPrivateDarwin::stopAdvertising() { #ifdef Q_OS_TVOS qCWarning(QT_BT_OSX) << "advertising is not supported on your platform"; #else - OSX_D_PTR; - - if (!osx_d_ptr->isValid()) - return osx_d_ptr->_q_CBManagerError(UnknownError); + if (!isValid()) + return _q_CBManagerError(QLowEnergyController::UnknownError); - if (state() != AdvertisingState) { - qCDebug(QT_BT_OSX) << "cannot stop advertising, called in state" << state(); + if (state != QLowEnergyController::AdvertisingState) { + qCDebug(QT_BT_OSX) << "cannot stop advertising, called in state" << state; return; } if (const auto leQueue = OSXBluetooth::qt_LE_queue()) { - const auto manager = osx_d_ptr->peripheralManager.data(); + const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); dispatch_sync(leQueue, ^{ [manager stopAdvertising]; }); - osx_d_ptr->controllerState = UnconnectedState; - emit stateChanged(UnconnectedState); + setState(QLowEnergyController::UnconnectedState); } else { qCWarning(QT_BT_OSX) << "no LE queue found"; - osx_d_ptr->setErrorDescription(QLowEnergyController::UnknownError); + setErrorDescription(QLowEnergyController::UnknownError); return; } #endif } -QLowEnergyService *QLowEnergyController::addService(const QLowEnergyServiceData &data, - QObject *parent) -{ -#ifdef Q_OS_TVOS - Q_UNUSED(data) - Q_UNUSED(parent) - qCWarning(QT_BT_OSX) << "peripheral role is not supported on your platform"; -#else - OSX_D_PTR; - - if (!osx_d_ptr->isValid()) { - osx_d_ptr->_q_CBManagerError(UnknownError); - return nullptr; - } - - if (role() != PeripheralRole) { - qCWarning(QT_BT_OSX) << "not in peripheral role"; - return nullptr; - } - - if (state() != UnconnectedState) { - qCWarning(QT_BT_OSX) << "invalid state"; - return nullptr; - } - - if (!data.isValid()) { - qCWarning(QT_BT_OSX) << "invalid service"; - return nullptr; - } - - for (auto includedService : data.includedServices()) - includedService->d_ptr->type |= QLowEnergyService::IncludedService; - - if (const auto servicePrivate = [osx_d_ptr->peripheralManager addService:data]) { - servicePrivate->setController(osx_d_ptr); - servicePrivate->state = QLowEnergyService::LocalService; - osx_d_ptr->discoveredServices.insert(servicePrivate->uuid, servicePrivate); - return new QLowEnergyService(servicePrivate, parent); - } -#endif - - return nullptr; -} - -void QLowEnergyController::requestConnectionUpdate(const QLowEnergyConnectionParameters ¶ms) -{ - Q_UNUSED(params); - qCWarning(QT_BT_OSX) << "Connection update not implemented on your platform"; -} - QT_END_NAMESPACE -#include "moc_qlowenergycontroller_osx_p.cpp" diff --git a/src/bluetooth/qlowenergycontroller_osx_p.h b/src/bluetooth/qlowenergycontroller_darwin_p.h index da959895..960d7fbc 100644 --- a/src/bluetooth/qlowenergycontroller_osx_p.h +++ b/src/bluetooth/qlowenergycontroller_darwin_p.h @@ -37,8 +37,8 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#ifndef QLOWENERGYCONTROLLER_OSX_P_H -#define QLOWENERGYCONTROLLER_OSX_P_H +#ifndef QLOWENERGYCONTROLLER_DARWIN_P_H +#define QLOWENERGYCONTROLLER_DARWIN_P_H // // W A R N I N G @@ -51,46 +51,64 @@ // We mean it. // -#include "osx/osxbtperipheralmanager_p.h" #include "qlowenergyserviceprivate_p.h" -#include "osx/osxbtcentralmanager_p.h" #include "qlowenergycontrollerbase_p.h" #include "qlowenergycontroller.h" #include "osx/osxbtnotifier_p.h" -#include "osx/osxbtutility_p.h" #include "qbluetoothaddress.h" #include "qbluetoothuuid.h" +#include "osx/btraii_p.h" #include <QtCore/qsharedpointer.h> -#include <QtCore/qsysinfo.h> #include <QtCore/qglobal.h> #include <QtCore/qstring.h> #include <QtCore/qmap.h> QT_BEGIN_NAMESPACE -namespace OSXBluetooth -{ - -class LECBManagerNotifier; - -} - class QByteArray; -// Suffix 'OSX' is a legacy, it's also iOS. -class QLowEnergyControllerPrivateOSX : public QLowEnergyControllerPrivate +class QLowEnergyControllerPrivateDarwin : public QLowEnergyControllerPrivate { friend class QLowEnergyController; friend class QLowEnergyService; Q_OBJECT public: - QLowEnergyControllerPrivateOSX(QLowEnergyController::Role role, QLowEnergyController *q, - const QBluetoothDeviceInfo &info = QBluetoothDeviceInfo()); - ~QLowEnergyControllerPrivateOSX(); - - bool isValid() const; + QLowEnergyControllerPrivateDarwin(); + ~QLowEnergyControllerPrivateDarwin(); + + void init() override; + void connectToDevice() override; + void disconnectFromDevice() override; + void discoverServices() override; + void discoverServiceDetails(const QBluetoothUuid &serviceUuid) override; + + void readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle) override; + void readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QLowEnergyHandle descriptorHandle) override; + + void writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, const QByteArray &newValue, + QLowEnergyService::WriteMode mode) override; + void writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QLowEnergyHandle descriptorHandle, + const QByteArray &newValue) override; + + + void requestConnectionUpdate(const QLowEnergyConnectionParameters ¶ms) override; + void addToGenericAttributeList(const QLowEnergyServiceData &service, + QLowEnergyHandle startHandle) override; + + void startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, + const QLowEnergyAdvertisingData &advertisingData, + const QLowEnergyAdvertisingData &scanResponseData) override; + void stopAdvertising()override; + QLowEnergyService *addServiceHelper(const QLowEnergyServiceData &service) override; + bool isValid() const; // QT6 - delete this logic. private Q_SLOTS: void _q_connected(); @@ -113,75 +131,30 @@ private Q_SLOTS: void _q_CBManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyService::ServiceError error); private: - void connectToDevice(); - void discoverServices(); - void discoverServiceDetails(const QBluetoothUuid &serviceUuid); - void setNotifyValue(QSharedPointer<QLowEnergyServicePrivate> service, QLowEnergyHandle charHandle, const QByteArray &newValue); - void readCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle charHandle); - void writeCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle charHandle, const QByteArray &newValue, - QLowEnergyService::WriteMode mode); - quint16 updateValueOfCharacteristic(QLowEnergyHandle charHandle, const QByteArray &value, bool appendValue); - void readDescriptor(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle charHandle); - void writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service, - QLowEnergyHandle descriptorHandle, - const QByteArray &newValue); - - quint16 updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descHandle, const QByteArray &value, bool appendValue); - // 'Lookup' functions: - QSharedPointer<QLowEnergyServicePrivate> serviceForHandle(QLowEnergyHandle serviceHandle); - QLowEnergyCharacteristic characteristicForHandle(QLowEnergyHandle charHandle); - QLowEnergyDescriptor descriptorForHandle(QLowEnergyHandle descriptorHandle); - void setErrorDescription(QLowEnergyController::Error errorCode); - void invalidateServices(); bool connectSlots(OSXBluetooth::LECBManagerNotifier *notifier); - QLowEnergyController *q_ptr; - QBluetoothUuid deviceUuid; - QString deviceName; - - QString errorString; - QLowEnergyController::Error lastError; - - QBluetoothAddress localAddress; - QBluetoothAddress remoteAddress; - - QLowEnergyController::Role role; - - QLowEnergyController::ControllerState controllerState; - QLowEnergyController::RemoteAddressType addressType; - - typedef QT_MANGLE_NAMESPACE(OSXBTCentralManager) ObjCCentralManager; - typedef OSXBluetooth::ObjCScopedPointer<ObjCCentralManager> CentralManager; - CentralManager centralManager; + DarwinBluetooth::ScopedPointer centralManager; #ifndef Q_OS_TVOS - typedef QT_MANGLE_NAMESPACE(OSXBTPeripheralManager) ObjCPeripheralManager; - typedef OSXBluetooth::ObjCScopedPointer<ObjCPeripheralManager> PeripheralManager; - PeripheralManager peripheralManager; + DarwinBluetooth::ScopedPointer peripheralManager; #endif - typedef QMap<QBluetoothUuid, QSharedPointer<QLowEnergyServicePrivate> > ServiceMap; - typedef ServiceMap::const_iterator ConstServiceIterator; - typedef ServiceMap::iterator ServiceIterator; - ServiceMap discoveredServices; + using ServiceMap = QMap<QBluetoothUuid, QSharedPointer<QLowEnergyServicePrivate>>; }; QT_END_NAMESPACE -#endif +#endif // QLOWENERGYCONTROLLER_DARWIN_P_H diff --git a/src/bluetooth/qlowenergycontrollerbase.cpp b/src/bluetooth/qlowenergycontrollerbase.cpp index 86108648..de72808e 100644 --- a/src/bluetooth/qlowenergycontrollerbase.cpp +++ b/src/bluetooth/qlowenergycontrollerbase.cpp @@ -61,7 +61,7 @@ QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() bool QLowEnergyControllerPrivate::isValidLocalAdapter() { -#ifdef QT_WINRT_BLUETOOTH +#if defined(QT_WINRT_BLUETOOTH) || defined(Q_OS_DARWIN) return true; #endif if (localAdapter.isNull()) diff --git a/src/bluetooth/qlowenergycontrollerbase_p.h b/src/bluetooth/qlowenergycontrollerbase_p.h index a8d1c676..169ba07b 100644 --- a/src/bluetooth/qlowenergycontrollerbase_p.h +++ b/src/bluetooth/qlowenergycontrollerbase_p.h @@ -51,24 +51,6 @@ // We mean it. // -#if defined(QT_OSX_BLUETOOTH) || defined(QT_IOS_BLUETOOTH) - -#include <QtCore/qglobal.h> -#include <QtCore/qobject.h> - -QT_BEGIN_NAMESPACE - -class QLowEnergyControllerPrivate : public QObject -{ -public: - // This class is required to make shared pointer machinery and - // moc (== Obj-C syntax) happy on both OS X and iOS. -}; - -QT_END_NAMESPACE - -#else - #include <qglobal.h> #include <QtCore/qobject.h> @@ -135,7 +117,6 @@ public: virtual QLowEnergyService *addServiceHelper( const QLowEnergyServiceData &service); - // common backend methods bool isValidLocalAdapter(); void setError(QLowEnergyController::Error newError); @@ -174,6 +155,7 @@ protected: QLowEnergyHandle lastLocalHandle{}; QString remoteName; // device name of the remote + QBluetoothUuid deviceUuid; // quite useless anywhere but Darwin (CoreBluetooth). Q_DECLARE_PUBLIC(QLowEnergyController) QLowEnergyController *q_ptr; @@ -181,6 +163,4 @@ protected: QT_END_NAMESPACE -#endif //defined(QT_OSX_BLUETOOTH) || defined(QT_IOS_BLUETOOTH) - #endif // QLOWENERGYCONTROLLERPRIVATEBASE_P_H diff --git a/src/bluetooth/qlowenergydescriptor.h b/src/bluetooth/qlowenergydescriptor.h index 62ca5fd3..84f48fbc 100644 --- a/src/bluetooth/qlowenergydescriptor.h +++ b/src/bluetooth/qlowenergydescriptor.h @@ -83,7 +83,7 @@ protected: friend class QLowEnergyControllerPrivateBluez; friend class QLowEnergyControllerPrivateBluezDBus; friend class QLowEnergyControllerPrivateCommon; - friend class QLowEnergyControllerPrivateOSX; + friend class QLowEnergyControllerPrivateDarwin; friend class QLowEnergyControllerPrivateWinRT; friend class QLowEnergyControllerPrivateWinRTNew; QLowEnergyDescriptorPrivate *data = nullptr; diff --git a/src/bluetooth/qlowenergyservice.cpp b/src/bluetooth/qlowenergyservice.cpp index 1529d3c2..2e6d1f9b 100644 --- a/src/bluetooth/qlowenergyservice.cpp +++ b/src/bluetooth/qlowenergyservice.cpp @@ -47,6 +47,10 @@ #include "qlowenergycontrollerbase_p.h" #include "qlowenergyserviceprivate_p.h" +#ifdef Q_OS_DARWIN +#include "qlowenergycontroller_darwin_p.h" +#endif // Q_OS_DARWIN + QT_BEGIN_NAMESPACE /*! @@ -809,6 +813,21 @@ void QLowEnergyService::writeDescriptor(const QLowEnergyDescriptor &descriptor, d->setError(QLowEnergyService::OperationError); return; } +#ifdef Q_OS_DARWIN + if (descriptor.uuid() == QBluetoothUuid::ClientCharacteristicConfiguration) { + // We have to identify a special case - ClientCharacteristicConfiguration + // since with CoreBluetooth: + // + // "You cannot use this method to write the value of a client configuration descriptor + // (represented by the CBUUIDClientCharacteristicConfigurationString constant), + // which describes how notification or indications are configured for a + // characteristic’s value with respect to a client. If you want to manage + // notifications or indications for a characteristic’s value, you must + // use the setNotifyValue:forCharacteristic: method instead." + auto controller = static_cast<QLowEnergyControllerPrivateDarwin *>(d->controller.data()); + return controller->setNotifyValue(descriptor.d_ptr, descriptor.characteristicHandle(), newValue); + } +#endif // Q_OS_DARWIN d->controller->writeDescriptor(descriptor.d_ptr, descriptor.characteristicHandle(), diff --git a/src/bluetooth/qlowenergyservice.h b/src/bluetooth/qlowenergyservice.h index 9de65a84..a2715471 100644 --- a/src/bluetooth/qlowenergyservice.h +++ b/src/bluetooth/qlowenergyservice.h @@ -136,6 +136,7 @@ private: friend class QLowEnergyControllerPrivate; friend class QLowEnergyControllerPrivateBluez; friend class QLowEnergyControllerPrivateAndroid; + friend class QLowEnergyControllerPrivateDarwin; QLowEnergyService(QSharedPointer<QLowEnergyServicePrivate> p, QObject *parent = nullptr); }; diff --git a/src/bluetooth/qlowenergyservice_osx.mm b/src/bluetooth/qlowenergyservice_osx.mm deleted file mode 100644 index c294b693..00000000 --- a/src/bluetooth/qlowenergyservice_osx.mm +++ /dev/null @@ -1,277 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Javier S. Pedro <maemo@javispedro.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtBluetooth module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qlowenergycontroller_osx_p.h" -#include "qlowenergyserviceprivate_p.h" -#include "qlowenergycharacteristic.h" -#include "qlowenergydescriptor.h" -#include "qlowenergyservice.h" -#include "qbluetoothuuid.h" - -#include <QtCore/qcoreapplication.h> -#include <QtCore/qstring.h> -#include <QtCore/qlist.h> - -#include <algorithm> - -QT_BEGIN_NAMESPACE - -namespace { - -QLowEnergyControllerPrivateOSX *qt_mac_le_controller(QSharedPointer<QLowEnergyServicePrivate> d_ptr) -{ - if (d_ptr.isNull()) - return nullptr; - - return static_cast<QLowEnergyControllerPrivateOSX *>(d_ptr->controller.data()); -} - -} - -QLowEnergyService::QLowEnergyService(QSharedPointer<QLowEnergyServicePrivate> d, QObject *parent) - : QObject(parent), - d_ptr(d) -{ - qRegisterMetaType<QLowEnergyService::ServiceState>(); - qRegisterMetaType<QLowEnergyService::ServiceError>(); - - connect(d.data(), SIGNAL(error(QLowEnergyService::ServiceError)), - this, SIGNAL(error(QLowEnergyService::ServiceError))); - connect(d.data(), SIGNAL(stateChanged(QLowEnergyService::ServiceState)), - this, SIGNAL(stateChanged(QLowEnergyService::ServiceState))); - connect(d.data(), SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)), - this, SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray))); - connect(d.data(), SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)), - this, SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray))); - connect(d.data(), SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray)), - this, SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray))); - connect(d.data(), SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray)), - this, SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray))); - connect(d.data(), SIGNAL(descriptorRead(QLowEnergyDescriptor,QByteArray)), - this, SIGNAL(descriptorRead(QLowEnergyDescriptor,QByteArray))); - -} - -QLowEnergyService::~QLowEnergyService() -{ -} - -QList<QBluetoothUuid> QLowEnergyService::includedServices() const -{ - return d_ptr->includedServices; -} - -QLowEnergyService::ServiceTypes QLowEnergyService::type() const -{ - return d_ptr->type; -} - -QLowEnergyService::ServiceState QLowEnergyService::state() const -{ - return d_ptr->state; -} - -QLowEnergyCharacteristic QLowEnergyService::characteristic(const QBluetoothUuid &uuid) const -{ - CharacteristicDataMap::const_iterator charIt = d_ptr->characteristicList.constBegin(); - for ( ; charIt != d_ptr->characteristicList.constEnd(); ++charIt) { - const QLowEnergyHandle charHandle = charIt.key(); - const QLowEnergyServicePrivate::CharData &charDetails = charIt.value(); - - if (charDetails.uuid == uuid) - return QLowEnergyCharacteristic(d_ptr, charHandle); - } - - return QLowEnergyCharacteristic(); -} - -QList<QLowEnergyCharacteristic> QLowEnergyService::characteristics() const -{ - QList<QLowEnergyCharacteristic> result; - QList<QLowEnergyHandle> handles(d_ptr->characteristicList.keys()); - - std::sort(handles.begin(), handles.end()); - - for (const QLowEnergyHandle &handle : qAsConst(handles)) { - QLowEnergyCharacteristic characteristic(d_ptr, handle); - result.append(characteristic); - } - - return result; -} - -QBluetoothUuid QLowEnergyService::serviceUuid() const -{ - return d_ptr->uuid; -} - -QString QLowEnergyService::serviceName() const -{ - bool ok = false; - const quint16 clsId = d_ptr->uuid.toUInt16(&ok); - if (ok) { - QBluetoothUuid::ServiceClassUuid uuid - = static_cast<QBluetoothUuid::ServiceClassUuid>(clsId); - const QString name = QBluetoothUuid::serviceClassToString(uuid); - if (!name.isEmpty()) - return name; - } - - return qApp ? qApp->translate("QBluetoothServiceDiscoveryAgent", "Unknown Service") : - QStringLiteral("Unknown Service"); -} - -void QLowEnergyService::discoverDetails() -{ - QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr); - - if (!controller || d_ptr->state == InvalidService) { - d_ptr->setError(OperationError); - return; - } - - if (d_ptr->state != DiscoveryRequired) - return; - - d_ptr->setState(QLowEnergyService::DiscoveringServices); - controller->discoverServiceDetails(d_ptr->uuid); -} - -QLowEnergyService::ServiceError QLowEnergyService::error() const -{ - return d_ptr->lastError; -} - -bool QLowEnergyService::contains(const QLowEnergyCharacteristic &characteristic) const -{ - if (characteristic.d_ptr.isNull() || !characteristic.data) - return false; - - if (d_ptr == characteristic.d_ptr - && d_ptr->characteristicList.contains(characteristic.attributeHandle())) { - return true; - } - - return false; -} - -void QLowEnergyService::readCharacteristic(const QLowEnergyCharacteristic &characteristic) -{ - QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr); - if (controller == nullptr || state() != ServiceDiscovered || !contains(characteristic)) { - d_ptr->setError(OperationError); - return; - } - - controller->readCharacteristic(characteristic.d_ptr, characteristic.attributeHandle()); -} - - -void QLowEnergyService::writeCharacteristic(const QLowEnergyCharacteristic &ch, const QByteArray &newValue, - WriteMode mode) -{ - QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr); - if (controller == nullptr || - (controller->role == QLowEnergyController::CentralRole && state() != ServiceDiscovered) || - !contains(ch)) { - d_ptr->setError(QLowEnergyService::OperationError); - return; - } - - controller->writeCharacteristic(ch.d_ptr, ch.attributeHandle(), newValue, mode); -} - -bool QLowEnergyService::contains(const QLowEnergyDescriptor &descriptor) const -{ - if (descriptor.d_ptr.isNull() || !descriptor.data) - return false; - - const QLowEnergyHandle charHandle = descriptor.characteristicHandle(); - if (!charHandle) - return false; - - if (d_ptr == descriptor.d_ptr && d_ptr->characteristicList.contains(charHandle) - && d_ptr->characteristicList[charHandle].descriptorList.contains(descriptor.handle())) - { - return true; - } - - return false; -} - -void QLowEnergyService::readDescriptor(const QLowEnergyDescriptor &descriptor) -{ - QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr); - if (controller == nullptr || state() != ServiceDiscovered || !contains(descriptor)) { - d_ptr->setError(OperationError); - return; - } - - controller->readDescriptor(descriptor.d_ptr, descriptor.handle()); -} - -void QLowEnergyService::writeDescriptor(const QLowEnergyDescriptor &descriptor, - const QByteArray &newValue) -{ - QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr); - if (controller == nullptr || state() != ServiceDiscovered || !contains(descriptor)) { - // This operation error also includes LE controller in the peripheral role: - // on iOS/OS X - descriptors are immutable. - d_ptr->setError(OperationError); - return; - } - - if (descriptor.uuid() == QBluetoothUuid::ClientCharacteristicConfiguration) { - // We have to identify a special case - ClientCharacteristicConfiguration - // since with Core Bluetooth: - // - // "You cannot use this method to write the value of a client configuration descriptor - // (represented by the CBUUIDClientCharacteristicConfigurationString constant), - // which describes how notification or indications are configured for a - // characteristic’s value with respect to a client. If you want to manage - // notifications or indications for a characteristic’s value, you must - // use the setNotifyValue:forCharacteristic: method instead." - controller->setNotifyValue(descriptor.d_ptr, descriptor.characteristicHandle(), newValue); - } else { - controller->writeDescriptor(descriptor.d_ptr, descriptor.handle(), newValue); - } -} - -QT_END_NAMESPACE |