diff options
Diffstat (limited to 'src/bluetooth/qlowenergycontroller_osx.mm')
-rw-r--r-- | src/bluetooth/qlowenergycontroller_osx.mm | 457 |
1 files changed, 299 insertions, 158 deletions
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm index 47f65965..6e85e630 100644 --- a/src/bluetooth/qlowenergycontroller_osx.mm +++ b/src/bluetooth/qlowenergycontroller_osx.mm @@ -38,11 +38,14 @@ ** ****************************************************************************/ +#include "osx/osxbtnotifier_p.h" #include "osx/osxbtutility_p.h" #include "osx/uistrings_p.h" + #include "qlowenergyserviceprivate_p.h" #include "qlowenergycontroller_osx_p.h" +#include "qlowenergyservicedata.h" #include "qbluetoothlocaldevice.h" #include "qbluetoothdeviceinfo.h" #include "qlowenergycontroller.h" @@ -51,7 +54,6 @@ #include <QtCore/qloggingcategory.h> #include <QtCore/qsharedpointer.h> #include <QtCore/qbytearray.h> -#include <QtCore/qsysinfo.h> #include <QtCore/qglobal.h> #include <QtCore/qstring.h> #include <QtCore/qlist.h> @@ -84,8 +86,7 @@ ServicePrivate qt_createLEService(QLowEnergyControllerPrivateOSX *controller, CB CBUUID *const cbUuid = cbService.UUID; if (!cbUuid) { - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "invalid service, " - "UUID is nil"; + qCDebug(QT_BT_OSX) << "invalid service, UUID is nil"; return ServicePrivate(); } @@ -102,18 +103,12 @@ ServicePrivate qt_createLEService(QLowEnergyControllerPrivateOSX *controller, CB // TODO: isPrimary is ... always 'NO' - to be investigated. /* - #if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_6_0) - using OSXBluetooth::qt_OS_limit; - if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_6_0)) { - if (!cbService.isPrimary) { - // Our guess included/not was probably wrong. - newService->type &= ~QLowEnergyService::PrimaryService; - newService->type |= QLowEnergyService::IncludedService; - } + if (!cbService.isPrimary) { + // Our guess included/not was probably wrong. + newService->type &= ~QLowEnergyService::PrimaryService; + newService->type |= QLowEnergyService::IncludedService; } - #endif */ - // No such property before 10_9/6_0. return newService; } @@ -136,36 +131,8 @@ UUIDList qt_servicesUuids(NSArray *services) } -QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyController *q) - : q_ptr(q), - lastError(QLowEnergyController::NoError), - controllerState(QLowEnergyController::UnconnectedState), - addressType(QLowEnergyController::PublicAddress) -{ - registerQLowEnergyControllerMetaType(); - - // This is the "wrong" constructor - no valid device UUID to connect later. - Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)"); - - using OSXBluetooth::LECentralNotifier; - - // We still create a manager, to simplify error handling later. - QScopedPointer<LECentralNotifier> notifier(new LECentralNotifier); - centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()]); - if (!centralManager) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO - << "failed to initialize central manager"; - return; - } else if (!connectSlots(notifier.data())) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO - << "failed to connect to notifier's signals"; - } - - // Ownership was taken by central manager. - notifier.take(); -} - -QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyController *q, +QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyController::Role r, + QLowEnergyController *q, const QBluetoothDeviceInfo &deviceInfo) : q_ptr(q), deviceUuid(deviceInfo.deviceUuid()), @@ -178,44 +145,67 @@ QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyControl Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)"); - using OSXBluetooth::LECentralNotifier; + using OSXBluetooth::LECBManagerNotifier; + + role = r; - QScopedPointer<LECentralNotifier> notifier(new LECentralNotifier); - centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()]); - if (!centralManager) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO - << "failed to initialize central manager"; + QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier); + if (role == QLowEnergyController::PeripheralRole) { +#ifndef Q_OS_TVOS + peripheralManager.reset([[ObjCPeripheralManager alloc] initWith:notifier.data()]); + if (!peripheralManager) { + qCWarning(QT_BT_OSX) << "failed to initialize peripheral manager"; + return; + } +#else + qCWarning(QT_BT_OSX) << "peripheral role is not supported on your platform"; return; - } else if (!connectSlots(notifier.data())) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO - << "failed to connect to notifier's signals"; +#endif + } else { + centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()]); + if (!centralManager) { + qCWarning(QT_BT_OSX) << "failed to initialize central manager"; + return; + } } + 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() { - // TODO: dispatch_sync 'setDelegate:Q_NULLPRT' to our CBCentralManager's delegate. - if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { - ObjCCentralManager *manager = centralManager.data(); - dispatch_sync(leQueue, ^{ - [manager detach]; - }); + if (const auto leQueue = OSXBluetooth::qt_LE_queue()) { + if (role == QLowEnergyController::CentralRole) { + const auto manager = centralManager.data(); + dispatch_sync(leQueue, ^{ + [manager detach]; + }); + } else { +#ifndef Q_OS_TVOS + const auto manager = peripheralManager.data(); + dispatch_sync(leQueue, ^{ + [manager detach]; + }); +#endif + } } } bool QLowEnergyControllerPrivateOSX::isValid() const { +#ifdef Q_OS_TVOS return centralManager; +#else + return centralManager || peripheralManager; +#endif } void QLowEnergyControllerPrivateOSX::_q_connected() { - Q_ASSERT_X(controllerState == QLowEnergyController::ConnectingState, - Q_FUNC_INFO, "invalid state"); - controllerState = QLowEnergyController::ConnectedState; emit q_ptr->stateChanged(QLowEnergyController::ConnectedState); @@ -226,10 +216,11 @@ void QLowEnergyControllerPrivateOSX::_q_disconnected() { controllerState = QLowEnergyController::UnconnectedState; - invalidateServices(); + if (role == QLowEnergyController::CentralRole) + invalidateServices(); + emit q_ptr->stateChanged(QLowEnergyController::UnconnectedState); emit q_ptr->disconnected(); - } void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() @@ -258,7 +249,7 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() continue; if (discoveredServices.contains(newService->uuid)) { // It's a bit stupid we first created it ... - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "discovered service with a duplicated UUID " + qCDebug(QT_BT_OSX) << "discovered service with a duplicated UUID" << newService->uuid; continue; } @@ -310,7 +301,7 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished() toVisitNext.resetWithoutRetain([[NSMutableArray alloc] init]); } } else { - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "no services found"; + qCDebug(QT_BT_OSX) << "no services found"; } for (ServiceMap::const_iterator it = discoveredServices.constBegin(); it != discoveredServices.constEnd(); ++it) { @@ -332,7 +323,7 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedP Q_ASSERT(service); if (!discoveredServices.contains(service->uuid)) { - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "unknown service uuid: " + qCDebug(QT_BT_OSX) << "unknown service uuid:" << service->uuid; return; } @@ -357,7 +348,7 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle char QLowEnergyCharacteristic characteristic(characteristicForHandle(charHandle)); if (!characteristic.isValid()) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown characteristic"; + qCWarning(QT_BT_OSX) << "unknown characteristic"; return; } @@ -374,14 +365,14 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicWritten(QLowEnergyHandle c ServicePrivate service(serviceForHandle(charHandle)); if (service.isNull()) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "can not find service for characteristic handle " + qCWarning(QT_BT_OSX) << "can not find service for characteristic handle" << charHandle; return; } QLowEnergyCharacteristic characteristic(characteristicForHandle(charHandle)); if (!characteristic.isValid()) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown characteristic"; + qCWarning(QT_BT_OSX) << "unknown characteristic"; return; } @@ -410,7 +401,7 @@ void QLowEnergyControllerPrivateOSX::_q_characteristicUpdated(QLowEnergyHandle c QLowEnergyCharacteristic characteristic(characteristicForHandle(charHandle)); if (!characteristic.isValid()) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown characteristic"; + qCWarning(QT_BT_OSX) << "unknown characteristic"; return; } @@ -427,7 +418,7 @@ void QLowEnergyControllerPrivateOSX::_q_descriptorRead(QLowEnergyHandle dHandle, const QLowEnergyDescriptor qtDescriptor(descriptorForHandle(dHandle)); if (!qtDescriptor.isValid()) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown descriptor " << dHandle; + qCWarning(QT_BT_OSX) << "unknown descriptor" << dHandle; return; } @@ -443,7 +434,7 @@ void QLowEnergyControllerPrivateOSX::_q_descriptorWritten(QLowEnergyHandle dHand const QLowEnergyDescriptor qtDescriptor(descriptorForHandle(dHandle)); if (!qtDescriptor.isValid()) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown descriptor " << dHandle; + qCWarning(QT_BT_OSX) << "unknown descriptor" << dHandle; return; } @@ -461,7 +452,7 @@ void QLowEnergyControllerPrivateOSX::_q_LEnotSupported() // be supported. } -void QLowEnergyControllerPrivateOSX::_q_CBCentralManagerError(QLowEnergyController::Error errorCode) +void QLowEnergyControllerPrivateOSX::_q_CBManagerError(QLowEnergyController::Error errorCode) { // Errors reported during connect and general errors. @@ -478,8 +469,8 @@ void QLowEnergyControllerPrivateOSX::_q_CBCentralManagerError(QLowEnergyControll // a service/characteristic - related error. } -void QLowEnergyControllerPrivateOSX::_q_CBCentralManagerError(const QBluetoothUuid &serviceUuid, - QLowEnergyController::Error errorCode) +void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &serviceUuid, + QLowEnergyController::Error errorCode) { // Errors reported while discovering service details etc. Q_UNUSED(errorCode) // TODO: setError? @@ -489,16 +480,16 @@ void QLowEnergyControllerPrivateOSX::_q_CBCentralManagerError(const QBluetoothUu ServicePrivate qtService(discoveredServices.value(serviceUuid)); qtService->setState(QLowEnergyService::InvalidService); } else { - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "error reported for unknown service " + qCDebug(QT_BT_OSX) << "error reported for unknown service" << serviceUuid; } } -void QLowEnergyControllerPrivateOSX::_q_CBCentralManagerError(const QBluetoothUuid &serviceUuid, - QLowEnergyService::ServiceError errorCode) +void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &serviceUuid, + QLowEnergyService::ServiceError errorCode) { if (!discoveredServices.contains(serviceUuid)) { - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "unknown service uuid: " + qCDebug(QT_BT_OSX) << "unknown service uuid:" << serviceUuid; return; } @@ -514,10 +505,12 @@ void QLowEnergyControllerPrivateOSX::connectToDevice() 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) << Q_FUNC_INFO << "no LE queue found"; + qCWarning(QT_BT_OSX) << "no LE queue found"; setErrorDescription(QLowEnergyController::UnknownError); return; } @@ -537,10 +530,12 @@ 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) << Q_FUNC_INFO << "no LE queue found"; + qCWarning(QT_BT_OSX) << "no LE queue found"; setErrorDescription(QLowEnergyController::UnknownError); return; } @@ -559,20 +554,21 @@ void QLowEnergyControllerPrivateOSX::discoverServiceDetails(const QBluetoothUuid Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller"); if (controllerState != QLowEnergyController::DiscoveredState) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO - << "can not discover service details in the current state, " - << "QLowEnergyController::DiscoveredState is expected"; + // 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) << Q_FUNC_INFO << "unknown service: " << serviceUuid; + qCWarning(QT_BT_OSX) << "unknown service: " << serviceUuid; return; } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); if (!leQueue) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no LE queue found"; + qCWarning(QT_BT_OSX) << "no LE queue found"; return; } @@ -593,32 +589,37 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer 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)"; + service->setError(QLowEnergyService::DescriptorWriteError); + return; + } + if (newValue.size() > 2) { // Qt's API requires an error on such write. // With Core Bluetooth we do not write any descriptor, // but instead call a special method. So it's better to // intercept wrong data size here: - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "client characteristic configuration descriptor " + qCWarning(QT_BT_OSX) << "client characteristic configuration descriptor" "is 2 bytes, but value size is: " << newValue.size(); service->setError(QLowEnergyService::DescriptorWriteError); return; } if (!discoveredServices.contains(service->uuid)) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no service with uuid: " - << service->uuid << " found"; + qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << "found"; return; } if (!service->characteristicList.contains(charHandle)) { - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "no characteristic with handle: " - << charHandle << " found"; + qCDebug(QT_BT_OSX) << "no characteristic with handle:" + << charHandle << "found"; return; } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); if (!leQueue) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no LE queue found"; + qCWarning(QT_BT_OSX) << "no LE queue found"; return; } ObjCCentralManager *manager = centralManager.data(); @@ -637,21 +638,26 @@ void QLowEnergyControllerPrivateOSX::readCharacteristic(QSharedPointer<QLowEnerg 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)) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no service with uuid:" + qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << "found"; return; } if (!service->characteristicList.contains(charHandle)) { - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "no characteristic with handle:" + qCDebug(QT_BT_OSX) << "no characteristic with handle:" << charHandle << "found"; return; } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); if (!leQueue) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no LE queue found"; + qCWarning(QT_BT_OSX) << "no LE queue found"; return; } // Attention! We have to copy UUID. @@ -669,36 +675,47 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner 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), - // otherwise we can not write anything at all. + // We can work only with services found on a given peripheral + // (== created by the given LE controller). + if (!discoveredServices.contains(service->uuid)) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no service with uuid: " + qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << " found"; return; } if (!service->characteristicList.contains(charHandle)) { - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "no characteristic with handle: " + qCDebug(QT_BT_OSX) << "no characteristic with handle:" << charHandle << " found"; return; } dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); if (!leQueue) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no LE queue found"; + qCWarning(QT_BT_OSX) << "no LE queue found"; return; } - // Attention! Copy objects! - const QBluetoothUuid serviceUuid(service->uuid); + // Attention! We have to copy objects! const QByteArray newValueCopy(newValue); - ObjCCentralManager *const manager = centralManager.data(); - dispatch_async(leQueue, ^{ - [manager write:newValueCopy - charHandle:charHandle + if (role == QLowEnergyController::CentralRole) { + const QBluetoothUuid serviceUuid(service->uuid); + const auto manager = centralManager.data(); + dispatch_async(leQueue, ^{ + [manager write:newValueCopy + charHandle:charHandle onService:serviceUuid withResponse:mode == QLowEnergyService::WriteWithResponse]; - }); + }); + } else { +#ifndef Q_OS_TVOS + const auto manager = peripheralManager.data(); + dispatch_async(leQueue, ^{ + [manager write:newValueCopy charHandle:charHandle]; + }); +#else + qCWarning(QT_BT_OSX) << "peripheral role is not supported on your platform"; +#endif + } } quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHandle charHandle, @@ -728,15 +745,20 @@ void QLowEnergyControllerPrivateOSX::readDescriptor(QSharedPointer<QLowEnergySer 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)) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no service with 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) << Q_FUNC_INFO << "no LE queue found"; + qCWarning(QT_BT_OSX) << "no LE queue found"; return; } // Attention! Copy objects! @@ -755,18 +777,23 @@ void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergySe 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; + } + // 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)) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no service with 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) << Q_FUNC_INFO << "no LE queue found"; + qCWarning(QT_BT_OSX) << "no LE queue found"; return; } // Attention! Copy objects! @@ -868,17 +895,23 @@ void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::E errorString.clear(); break; case QLowEnergyController::UnknownRemoteDeviceError: - errorString = QCoreApplication::translate(LE_CONTROLLER, LEC_RDEV_NO_FOUND); + errorString = QLowEnergyController::tr("Remote device cannot be found"); break; case QLowEnergyController::InvalidBluetoothAdapterError: - errorString = QCoreApplication::translate(LE_CONTROLLER, LEC_NO_LOCAL_DEV); + errorString = QLowEnergyController::tr("Cannot find local adapter"); break; case QLowEnergyController::NetworkError: - errorString = QCoreApplication::translate(LE_CONTROLLER, LEC_IO_ERROR); + errorString = QLowEnergyController::tr("Error occurred during connection I/O"); + break; + case QLowEnergyController::ConnectionError: + errorString = QLowEnergyController::tr("Error occurred trying to connect to remote device."); + break; + case QLowEnergyController::AdvertisingError: + errorString = QLowEnergyController::tr("Error occurred trying to start advertising"); break; case QLowEnergyController::UnknownError: default: - errorString = QCoreApplication::translate(LE_CONTROLLER, LEC_UNKNOWN_ERROR); + errorString = QLowEnergyController::tr("Unknown Error"); break; } } @@ -893,38 +926,38 @@ void QLowEnergyControllerPrivateOSX::invalidateServices() discoveredServices.clear(); } -bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECentralNotifier *notifier) +bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECBManagerNotifier *notifier) { - using OSXBluetooth::LECentralNotifier; + using OSXBluetooth::LECBManagerNotifier; Q_ASSERT_X(notifier, Q_FUNC_INFO, "invalid notifier object (null)"); - bool ok = connect(notifier, &LECentralNotifier::connected, + bool ok = connect(notifier, &LECBManagerNotifier::connected, this, &QLowEnergyControllerPrivateOSX::_q_connected); - ok = ok && connect(notifier, &LECentralNotifier::disconnected, + ok = ok && connect(notifier, &LECBManagerNotifier::disconnected, this, &QLowEnergyControllerPrivateOSX::_q_disconnected); - ok = ok && connect(notifier, &LECentralNotifier::serviceDiscoveryFinished, + ok = ok && connect(notifier, &LECBManagerNotifier::serviceDiscoveryFinished, this, &QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished); - ok = ok && connect(notifier, &LECentralNotifier::serviceDetailsDiscoveryFinished, + ok = ok && connect(notifier, &LECBManagerNotifier::serviceDetailsDiscoveryFinished, this, &QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished); - ok = ok && connect(notifier, &LECentralNotifier::characteristicRead, + ok = ok && connect(notifier, &LECBManagerNotifier::characteristicRead, this, &QLowEnergyControllerPrivateOSX::_q_characteristicRead); - ok = ok && connect(notifier, &LECentralNotifier::characteristicWritten, + ok = ok && connect(notifier, &LECBManagerNotifier::characteristicWritten, this, &QLowEnergyControllerPrivateOSX::_q_characteristicWritten); - ok = ok && connect(notifier, &LECentralNotifier::characteristicUpdated, + ok = ok && connect(notifier, &LECBManagerNotifier::characteristicUpdated, this, &QLowEnergyControllerPrivateOSX::_q_characteristicUpdated); - ok = ok && connect(notifier, &LECentralNotifier::descriptorRead, + ok = ok && connect(notifier, &LECBManagerNotifier::descriptorRead, this, &QLowEnergyControllerPrivateOSX::_q_descriptorRead); - ok = ok && connect(notifier, &LECentralNotifier::descriptorWritten, + ok = ok && connect(notifier, &LECBManagerNotifier::descriptorWritten, this, &QLowEnergyControllerPrivateOSX::_q_descriptorWritten); - ok = ok && connect(notifier, &LECentralNotifier::LEnotSupported, + ok = ok && connect(notifier, &LECBManagerNotifier::LEnotSupported, this, &QLowEnergyControllerPrivateOSX::_q_LEnotSupported); - ok = ok && connect(notifier, SIGNAL(CBCentralManagerError(QLowEnergyController::Error)), - this, SLOT(_q_CBCentralManagerError(QLowEnergyController::Error))); - ok = ok && connect(notifier, SIGNAL(CBCentralManagerError(const QBluetoothUuid &, QLowEnergyController::Error)), - this, SLOT(_q_CBCentralManagerError(const QBluetoothUuid &, QLowEnergyController::Error))); - ok = ok && connect(notifier, SIGNAL(CBCentralManagerError(const QBluetoothUuid &, QLowEnergyService::ServiceError)), - this, SLOT(_q_CBCentralManagerError(const QBluetoothUuid &, QLowEnergyService::ServiceError))); + ok = ok && connect(notifier, SIGNAL(CBManagerError(QLowEnergyController::Error)), + this, SLOT(_q_CBManagerError(QLowEnergyController::Error))); + ok = ok && connect(notifier, SIGNAL(CBManagerError(const QBluetoothUuid &, QLowEnergyController::Error)), + this, SLOT(_q_CBManagerError(const QBluetoothUuid &, QLowEnergyController::Error))); + ok = ok && connect(notifier, SIGNAL(CBManagerError(const QBluetoothUuid &, QLowEnergyService::ServiceError)), + this, SLOT(_q_CBManagerError(const QBluetoothUuid &, QLowEnergyService::ServiceError))); if (!ok) notifier->disconnect(); @@ -935,26 +968,24 @@ bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECentralNotifie QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddress, QObject *parent) : QObject(parent), - d_ptr(new QLowEnergyControllerPrivateOSX(this)) + d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this)) { OSX_D_PTR; - osx_d_ptr->role = CentralRole; osx_d_ptr->remoteAddress = remoteAddress; osx_d_ptr->localAddress = QBluetoothLocalDevice().address(); - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "construction with remote 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(this, remoteDevice)) + d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this, remoteDevice)) { OSX_D_PTR; - osx_d_ptr->role = CentralRole; osx_d_ptr->localAddress = QBluetoothLocalDevice().address(); // That's the only "real" ctor - with Core Bluetooth we need a _valid_ deviceUuid // from 'remoteDevice'. @@ -964,24 +995,23 @@ QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddres const QBluetoothAddress &localAddress, QObject *parent) : QObject(parent), - d_ptr(new QLowEnergyControllerPrivateOSX(this)) + d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this)) { OSX_D_PTR; - osx_d_ptr->role = CentralRole; osx_d_ptr->remoteAddress = remoteAddress; osx_d_ptr->localAddress = localAddress; - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "construction with remote/local " + qCWarning(QT_BT_OSX) << "construction with remote/local " "addresses is not supported!"; } QLowEnergyController::QLowEnergyController(QObject *parent) - : QObject(parent), d_ptr(new QLowEnergyControllerPrivateOSX(this)) + : QObject(parent), + d_ptr(new QLowEnergyControllerPrivateOSX(PeripheralRole, this)) { OSX_D_PTR; - osx_d_ptr->role = PeripheralRole; osx_d_ptr->localAddress = QBluetoothLocalDevice().address(); } @@ -1059,11 +1089,16 @@ void QLowEnergyController::connectToDevice() // A memory allocation problem. if (!osx_d_ptr->isValid()) - return osx_d_ptr->_q_CBCentralManagerError(UnknownError); + 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_CBCentralManagerError(UnknownRemoteDeviceError); + return osx_d_ptr->_q_CBManagerError(UnknownRemoteDeviceError); if (osx_d_ptr->controllerState != UnconnectedState) return; @@ -1078,6 +1113,11 @@ void QLowEnergyController::disconnectFromDevice() OSX_D_PTR; + if (role() != CentralRole) { + qCWarning(QT_BT_OSX) << "can not disconnect while in central role"; + return osx_d_ptr->_q_CBManagerError(ConnectionError); + } + if (osx_d_ptr->isValid()) { const ControllerState oldState = osx_d_ptr->controllerState; @@ -1101,14 +1141,19 @@ void QLowEnergyController::disconnectFromDevice() emit stateChanged(UnconnectedState); } } else { - qCCritical(QT_BT_OSX) << Q_FUNC_INFO << "qt LE queue is nil," - << "can not dispatch 'disconnect'"; + 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; @@ -1159,30 +1204,126 @@ void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameter const QLowEnergyAdvertisingData &advertisingData, const QLowEnergyAdvertisingData &scanResponseData) { - Q_UNUSED(params); - Q_UNUSED(advertisingData); - Q_UNUSED(scanResponseData); - qCWarning(QT_BT_OSX) << "LE advertising not implemented for OS X"; +#ifdef Q_OS_TVOS + Q_UNUSED(params) + Q_UNUSED(advertisingData) + 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 (role() != PeripheralRole) { + qCWarning(QT_BT_OSX) << "invalid role"; + return; + } + + if (state() != 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); + return; + } + + [osx_d_ptr->peripheralManager setParameters:params + data:advertisingData + scanResponse:scanResponseData]; + + osx_d_ptr->controllerState = AdvertisingState; + emit stateChanged(AdvertisingState); + + const auto manager = osx_d_ptr->peripheralManager.data(); + dispatch_async(leQueue, ^{ + [manager startAdvertising]; + }); +#endif } void QLowEnergyController::stopAdvertising() { - qCWarning(QT_BT_OSX) << "LE advertising not implemented for OS X"; +#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 (state() != AdvertisingState) { + qCDebug(QT_BT_OSX) << "called in state" << state(); + return; + } + + if (const auto leQueue = OSXBluetooth::qt_LE_queue()) { + const auto manager = osx_d_ptr->peripheralManager.data(); + dispatch_sync(leQueue, ^{ + [manager stopAdvertising]; + }); + + osx_d_ptr->controllerState = UnconnectedState; + emit stateChanged(UnconnectedState); + } else { + qCWarning(QT_BT_OSX) << "no LE queue found"; + osx_d_ptr->setErrorDescription(QLowEnergyController::UnknownError); + return; + } +#endif } -QLowEnergyService *QLowEnergyController::addService(const QLowEnergyServiceData &service, +QLowEnergyService *QLowEnergyController::addService(const QLowEnergyServiceData &data, QObject *parent) { - Q_UNUSED(service); - Q_UNUSED(parent); - qCWarning(QT_BT_OSX) << "GATT server functionality not implemented for OS X"; +#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); + 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 for OS X"; + qCWarning(QT_BT_OSX) << "Connection update not implemented on your platform"; } QT_END_NAMESPACE |