summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@theqtcompany.com>2015-11-26 18:04:58 +0100
committerTimur Pocheptsov <timur.pocheptsov@theqtcompany.com>2015-12-10 13:41:42 +0000
commit5e73fbe10d14cc1fedba4c2e327ebf2b07bc1a9e (patch)
tree1ed50be64e67a40530f1ea41702c5ee29fea2977
parent2402bb9421ab8ba4479b7a4e9566b682146f70ff (diff)
qlowenergycontroller_osx - move delegate to the qt_LE_queue
ATM CBCentralManager's delegate does its work on the main queue. With CoreFoundation event dispatcher it's now possible to use QtBluetooth from non-gui thread (more generally - from a thread other than main). This makes main queue useless - and we have to move to a dedicated dispatch queue. Also, we have to make sure we do not have race conditioins/dead-locks. This patch: 1. decouples OSXBTLECentralManager and QLowEnergyController so that these two objects working (potentially) on different threads do not share any data and do not have to use locks, removes the explicit 'delegate' interface/inheritance and replaces them with LECentralNotifier class - to be able to use Qt's signal/slot mechanics for inter-thread communication. 2. all OSXBTLECentralManager's are now executed on qt_LE_queue queue to avoid any race-conditions (since they potentially update manager's internal state). 3. Results/errors are now reported using LECentralNotifier's object (QLowEnergyController has corresponding slots connected to the notifier) Task-number: QTBUG-49476 Change-Id: Ie07cdc13ad559c96a7d2ff010843fb7bcce07c99 Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
-rw-r--r--src/bluetooth/osx/osxbt.pri8
-rw-r--r--src/bluetooth/osx/osxbtcentralmanager.mm411
-rw-r--r--src/bluetooth/osx/osxbtcentralmanager_p.h77
-rw-r--r--src/bluetooth/osx/osxbtnotifier.cpp1
-rw-r--r--src/bluetooth/osx/osxbtnotifier_p.h92
-rw-r--r--src/bluetooth/qlowenergycontroller_osx.mm342
-rw-r--r--src/bluetooth/qlowenergycontroller_osx_p.h58
7 files changed, 613 insertions, 376 deletions
diff --git a/src/bluetooth/osx/osxbt.pri b/src/bluetooth/osx/osxbt.pri
index 06389fcc..bb382866 100644
--- a/src/bluetooth/osx/osxbt.pri
+++ b/src/bluetooth/osx/osxbt.pri
@@ -1,4 +1,4 @@
-SOURCES += osx/uistrings.cpp
+SOURCES += osx/uistrings.cpp osx/osxbtnotifier.cpp
PRIVATE_HEADERS += osx/uistrings_p.h
CONFIG(osx) {
@@ -15,7 +15,8 @@ CONFIG(osx) {
osx/osxbtobexsession_p.h \
osx/osxbtledeviceinquiry_p.h \
osx/corebluetoothwrapper_p.h \
- osx/osxbtcentralmanager_p.h
+ osx/osxbtcentralmanager_p.h \
+ osx/osxbtnotifier_p.h
OBJECTIVE_SOURCES += osx/osxbtutility.mm \
osx/osxbtdevicepair.mm \
@@ -34,7 +35,8 @@ CONFIG(osx) {
PRIVATE_HEADERS += osx/osxbtutility_p.h \
osx/osxbtledeviceinquiry_p.h \
osx/corebluetoothwrapper_p.h \
- osx/osxbtcentralmanager_p.h
+ osx/osxbtcentralmanager_p.h \
+ osx/osxbtnotifier_p.h
OBJECTIVE_SOURCES += osx/osxbtutility.mm \
osx/osxbtledeviceinquiry.mm \
diff --git a/src/bluetooth/osx/osxbtcentralmanager.mm b/src/bluetooth/osx/osxbtcentralmanager.mm
index 614e4ae5..2f881f79 100644
--- a/src/bluetooth/osx/osxbtcentralmanager.mm
+++ b/src/bluetooth/osx/osxbtcentralmanager.mm
@@ -34,7 +34,7 @@
#include "qlowenergyserviceprivate_p.h"
#include "qlowenergycharacteristic.h"
#include "osxbtcentralmanager_p.h"
-
+#include "osxbtnotifier_p.h"
#include <QtCore/qloggingcategory.h>
#include <QtCore/qsysinfo.h>
@@ -45,11 +45,11 @@
QT_BEGIN_NAMESPACE
-namespace OSXBluetooth {
+Q_DECLARE_METATYPE(QLowEnergyCharacteristic)
+Q_DECLARE_METATYPE(QLowEnergyDescriptor)
+Q_DECLARE_METATYPE(QLowEnergyHandle)
-CentralManagerDelegate::~CentralManagerDelegate()
-{
-}
+namespace OSXBluetooth {
NSUInteger qt_countGATTEntries(CBService *service)
{
@@ -80,15 +80,12 @@ NSUInteger qt_countGATTEntries(CBService *service)
QT_END_NAMESPACE
-
-#ifdef QT_NAMESPACE
-using namespace QT_NAMESPACE;
-#endif
+QT_USE_NAMESPACE
@interface QT_MANGLE_NAMESPACE(OSXBTCentralManager) (PrivateAPI)
-- (QLowEnergyController::Error)connectToDevice; // "Device" is in Qt's world ...
-- (void)connectToPeripheral; // "Peripheral" is in Core Bluetooth.
+- (void)retrievePeripheralAndConnect;
+- (void)connectToPeripheral;
- (void)discoverIncludedServices;
- (void)readCharacteristics:(CBService *)service;
- (void)serviceDetailsDiscoveryFinished:(CBService *)service;
@@ -114,16 +111,14 @@ using namespace QT_NAMESPACE;
@implementation QT_MANGLE_NAMESPACE(OSXBTCentralManager)
-- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::CentralManagerDelegate *)aDelegate
+- (id)initWith:(OSXBluetooth::LECentralNotifier *)aNotifier
{
- Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)");
-
if (self = [super init]) {
manager = nil;
managerState = OSXBluetooth::CentralManagerIdle;
disconnectPending = false;
peripheral = nil;
- delegate = aDelegate;
+ notifier = aNotifier;
currentService = 0;
lastValidHandle = 0;
requestPending = false;
@@ -150,32 +145,37 @@ using namespace QT_NAMESPACE;
[peripheral setDelegate:nil];
[peripheral release];
+ if (notifier)
+ notifier->deleteLater();
+
[super dealloc];
}
-- (QLowEnergyController::Error)connectToDevice:(const QBluetoothUuid &)aDeviceUuid
+- (void)connectToDevice:(const QBluetoothUuid &)aDeviceUuid
{
- Q_ASSERT_X(managerState == OSXBluetooth::CentralManagerIdle,
- Q_FUNC_INFO, "invalid state");
-
+ disconnectPending = false; // Cancel the previous disconnect if any.
deviceUuid = aDeviceUuid;
if (!manager) {
- managerState = OSXBluetooth::CentralManagerUpdating; // We'll have to wait for updated state.
- manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
+ // The first time we try to connect, no manager created yet,
+ // no status update received.
+ if (const dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) {
+ managerState = OSXBluetooth::CentralManagerUpdating;
+ manager = [[CBCentralManager alloc] initWithDelegate:self queue:leQueue];
+ }
+
if (!manager) {
managerState = OSXBluetooth::CentralManagerIdle;
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to allocate a "
- "central manager";
- return QLowEnergyController::ConnectionError;
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to allocate a central manager";
+ if (notifier)
+ emit notifier->CBCentralManagerError(QLowEnergyController::ConnectionError);
}
- return QLowEnergyController::NoError;
- } else {
- return [self connectToDevice];
+ } else if (managerState != OSXBluetooth::CentralManagerUpdating) {
+ [self retrievePeripheralAndConnect];
}
}
-- (QLowEnergyController::Error)connectToDevice
+- (void)retrievePeripheralAndConnect
{
Q_ASSERT_X(manager, Q_FUNC_INFO, "invalid central manager (nil)");
Q_ASSERT_X(managerState == OSXBluetooth::CentralManagerIdle,
@@ -183,13 +183,14 @@ using namespace QT_NAMESPACE;
if ([self isConnected]) {
qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "already connected";
- delegate->connectSuccess();
- return QLowEnergyController::NoError;
+ if (notifier)
+ emit notifier->connected();
+ return;
} else if (peripheral) {
// Was retrieved already, but not connected
// or disconnected.
[self connectToPeripheral];
- return QLowEnergyController::NoError;
+ return;
}
using namespace OSXBluetooth;
@@ -198,7 +199,9 @@ using namespace QT_NAMESPACE;
ObjCScopedPointer<NSMutableArray> uuids([[NSMutableArray alloc] init]);
if (!uuids) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to allocate identifiers";
- return QLowEnergyController::ConnectionError;
+ if (notifier)
+ emit notifier->CBCentralManagerError(QLowEnergyController::ConnectionError);
+ return;
}
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_7_0)
@@ -210,43 +213,47 @@ using namespace QT_NAMESPACE;
const ObjCScopedPointer<NSUUID> nsUuid([[NSUUID alloc] initWithUUIDBytes:uuidData]);
if (!nsUuid) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to allocate NSUUID identifier";
- return QLowEnergyController::ConnectionError;
+ if (notifier)
+ emit notifier->CBCentralManagerError(QLowEnergyController::ConnectionError);
+ return;
}
[uuids addObject:nsUuid];
-
// With the latest CoreBluetooth, we can synchronously retrive peripherals:
QT_BT_MAC_AUTORELEASEPOOL;
NSArray *const peripherals = [manager retrievePeripheralsWithIdentifiers:uuids];
if (!peripherals || peripherals.count != 1) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to retrive a peripheral";
- return QLowEnergyController::UnknownRemoteDeviceError;
+ if (notifier)
+ emit notifier->CBCentralManagerError(QLowEnergyController::UnknownRemoteDeviceError);
+ return;
}
peripheral = [static_cast<CBPeripheral *>([peripherals objectAtIndex:0]) retain];
[self connectToPeripheral];
-
- return QLowEnergyController::NoError;
+ return;
}
#endif
// Either SDK or the target is below 10.9/7.0
if (![manager respondsToSelector:@selector(retrievePeripherals:)]) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to retrive a peripheral";
- return QLowEnergyController::UnknownRemoteDeviceError;
+ if (notifier)
+ emit notifier->CBCentralManagerError(QLowEnergyController::UnknownRemoteDeviceError);
+ return;
}
OSXBluetooth::CFStrongReference<CFUUIDRef> cfUuid(OSXBluetooth::cf_uuid(deviceUuid));
if (!cfUuid) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to create CFUUID object";
- return QLowEnergyController::ConnectionError;
+ if (notifier)
+ emit notifier->CBCentralManagerError(QLowEnergyController::ConnectionError);
+ return;
}
- // TODO: With ARC this cast will be illegal:
+ // With ARC this cast will be illegal:
[uuids addObject:(id)cfUuid.data()];
// Unfortunately, with old Core Bluetooth this call is asynchronous ...
managerState = OSXBluetooth::CentralManagerConnecting;
[manager performSelector:@selector(retrievePeripherals:) withObject:uuids.data()];
-
- return QLowEnergyController::NoError;
}
- (void)connectToPeripheral
@@ -259,7 +266,8 @@ using namespace QT_NAMESPACE;
// The state is still the same - connecting.
if ([self isConnected]) {
qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "already connected";
- delegate->connectSuccess();
+ if (notifier)
+ emit notifier->connected();
} else {
qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "trying to connect";
managerState = OSXBluetooth::CentralManagerConnecting;
@@ -291,10 +299,17 @@ using namespace QT_NAMESPACE;
[self reset];
if (managerState == OSXBluetooth::CentralManagerUpdating) {
- disconnectPending = true;
+ disconnectPending = true; // this is for 'didUpdate' method.
+ if (notifier) {
+ // We were waiting for the first update
+ // with 'PoweredOn' status, when suddenly got disconnected called.
+ // Since we have not attempted to connect yet, emit now.
+ // Note: we do not change the state, since we still maybe interested
+ // in the status update before the next connect attempt.
+ emit notifier->disconnected();
+ }
} else {
disconnectPending = false;
-
if ([self isConnected])
managerState = OSXBluetooth::CentralManagerDisconnecting;
else
@@ -303,8 +318,9 @@ using namespace QT_NAMESPACE;
// We have to call -cancelPeripheralConnection: even
// if not connected (to cancel a pending connect attempt).
// Unfortunately, didDisconnect callback is not always called
- // (despite of Apple's docs saying it _must_).
- [manager cancelPeripheralConnection:peripheral];
+ // (despite of Apple's docs saying it _must_ be).
+ if (peripheral)
+ [manager cancelPeripheralConnection:peripheral];
}
}
@@ -321,7 +337,6 @@ using namespace QT_NAMESPACE;
//parameter to nil is considerably slower and is not recommended."
//
// ... but we'd like to have them all:
-
[peripheral setDelegate:self];
managerState = OSXBluetooth::CentralManagerDiscovering;
[peripheral discoverServices:nil];
@@ -331,19 +346,17 @@ using namespace QT_NAMESPACE;
{
using namespace OSXBluetooth;
- Q_ASSERT_X(managerState == CentralManagerIdle, Q_FUNC_INFO,
- "invalid state");
+ Q_ASSERT_X(managerState == CentralManagerIdle, Q_FUNC_INFO, "invalid state");
Q_ASSERT_X(manager, Q_FUNC_INFO, "invalid manager (nil)");
Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)");
QT_BT_MAC_AUTORELEASEPOOL;
NSArray *const services = peripheral.services;
- if (!services || !services.count) { // Actually, !services.count works in both cases, but ...
+ if (!services || !services.count) {
// A peripheral without any services at all.
- Q_ASSERT_X(delegate, Q_FUNC_INFO,
- "invalid delegate (null)");
- delegate->serviceDiscoveryFinished(ObjCStrongReference<NSArray>());
+ if (notifier)
+ emit notifier->serviceDiscoveryFinished();
} else {
// 'reset' also calls retain on a parameter.
servicesToVisitNext.reset(nil);
@@ -358,7 +371,7 @@ using namespace QT_NAMESPACE;
}
}
-- (bool)discoverServiceDetails:(const QBluetoothUuid &)serviceUuid
+- (void)discoverServiceDetails:(const QBluetoothUuid &)serviceUuid
{
// This function does not change 'managerState', since it
// can be called concurrently (not waiting for the previous
@@ -373,7 +386,7 @@ using namespace QT_NAMESPACE;
if (servicesToDiscoverDetails.contains(serviceUuid)) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO <<"already discovering for "
<< serviceUuid;
- return true;
+ return;
}
QT_BT_MAC_AUTORELEASEPOOL;
@@ -381,13 +394,16 @@ using namespace QT_NAMESPACE;
if (CBService *const service = [self serviceForUUID:serviceUuid]) {
servicesToDiscoverDetails.append(serviceUuid);
[peripheral discoverCharacteristics:nil forService:service];
- return true;
+ return;
}
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown service uuid "
<< serviceUuid;
- return false;
+ if (notifier) {
+ emit notifier->CBCentralManagerError(serviceUuid,
+ QLowEnergyService::UnknownError);
+ }
}
- (void)readCharacteristics:(CBService *)service
@@ -400,11 +416,9 @@ using namespace QT_NAMESPACE;
QT_BT_MAC_AUTORELEASEPOOL;
- Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO,
- "invalid state");
+ Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO, "invalid state");
Q_ASSERT_X(manager, Q_FUNC_INFO, "invalid manager (nil)");
Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)");
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
if (!service.characteristics || !service.characteristics.count)
return [self serviceDetailsDiscoveryFinished:service];
@@ -444,20 +458,16 @@ using namespace QT_NAMESPACE;
- (void)readDescriptors:(CBService *)service
{
- Q_ASSERT_X(service, "-readDescriptors:",
- "invalid service (nil)");
+ Q_ASSERT_X(service, Q_FUNC_INFO, "invalid service (nil)");
Q_ASSERT_X(managerState != OSXBluetooth::CentralManagerUpdating,
- "-readDescriptors:",
- "invalid state");
- Q_ASSERT_X(peripheral, "-readDescriptors:",
- "invalid peripheral (nil)");
+ Q_FUNC_INFO, "invalid state");
+ Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)");
QT_BT_MAC_AUTORELEASEPOOL;
NSArray *const cs = service.characteristics;
// We can never be here if we have no characteristics.
- Q_ASSERT_X(cs && cs.count, "-readDescriptors:",
- "invalid service");
+ Q_ASSERT_X(cs && cs.count, Q_FUNC_INFO, "invalid service");
for (CBCharacteristic *c in cs) {
if (c.descriptors && c.descriptors.count)
return [peripheral readValueForDescriptor:[c.descriptors objectAtIndex:0]];
@@ -469,9 +479,7 @@ using namespace QT_NAMESPACE;
- (void)serviceDetailsDiscoveryFinished:(CBService *)service
{
- //
Q_ASSERT_X(service, Q_FUNC_INFO, "invalid service (nil)");
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
using namespace OSXBluetooth;
@@ -487,7 +495,8 @@ using namespace QT_NAMESPACE;
if (nHandles >= maxHandle || lastValidHandle > maxHandle - nHandles) {
// Well, that's unlikely :) But we must be sure.
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "can not allocate more handles";
- delegate->error(serviceUuid, QLowEnergyService::OperationError);
+ if (notifier)
+ notifier->CBCentralManagerError(serviceUuid, QLowEnergyService::OperationError);
return;
}
@@ -550,8 +559,8 @@ using namespace QT_NAMESPACE;
qtService->endHandle = lastValidHandle;
- ObjCStrongReference<CBService> leService(service, true);
- delegate->serviceDetailsDiscoveryFinished(qtService);
+ if (notifier)
+ emit notifier->serviceDetailsDiscoveryFinished(qtService);
}
- (void)performNextRequest
@@ -694,28 +703,37 @@ using namespace QT_NAMESPACE;
}
}
-- (bool)setNotifyValue:(const QByteArray &)value
- forCharacteristic:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle
+- (void)setNotifyValue:(const QByteArray &)value
+ forCharacteristic:(QLowEnergyHandle)charHandle
+ onService:(const QBluetoothUuid &)serviceUuid
{
using namespace OSXBluetooth;
Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle (0)");
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
if (!charMap.contains(charHandle)) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO
- << "unknown characteristic handle " << charHandle;
- return false;
+ << "unknown characteristic handle "
+ << charHandle;
+ if (notifier) {
+ emit notifier->CBCentralManagerError(serviceUuid,
+ QLowEnergyService::DescriptorWriteError);
+ }
+ return;
}
// At the moment we call setNotifyValue _only_ from 'writeDescriptor';
- // from Qt's API POV it's a descriptor write oprtation and we must report
+ // from Qt's API POV it's a descriptor write operation and we must report
// it back, so check _now_ that we really have this descriptor.
const QBluetoothUuid qtUuid(QBluetoothUuid::ClientCharacteristicConfiguration);
if (![self descriptor:qtUuid forCharacteristic:charMap[charHandle]]) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO
<< "no client characteristic configuration found";
- return false;
+ if (notifier) {
+ emit notifier->CBCentralManagerError(serviceUuid,
+ QLowEnergyService::DescriptorWriteError);
+ }
+ return;
}
LERequest request;
@@ -725,11 +743,10 @@ using namespace QT_NAMESPACE;
requests.enqueue(request);
[self performNextRequest];
-
- return true;
}
-- (bool)readCharacteristic:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle
+- (void)readCharacteristic:(QLowEnergyHandle)charHandle
+ onService:(const QBluetoothUuid &)serviceUuid
{
using namespace OSXBluetooth;
@@ -739,7 +756,12 @@ using namespace QT_NAMESPACE;
if (!charMap.contains(charHandle)) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "characteristic: " << charHandle << " not found";
- return false;
+ if (notifier) {
+ emit notifier->CBCentralManagerError(serviceUuid,
+ QLowEnergyService::CharacteristicReadError);
+
+ }
+ return;
}
LERequest request;
@@ -748,12 +770,11 @@ using namespace QT_NAMESPACE;
requests.enqueue(request);
[self performNextRequest];
-
- return true;
}
-- (bool)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
- charHandle:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle
+- (void)write:(const QByteArray &)value
+ charHandle:(QLowEnergyHandle)charHandle
+ onService:(const QBluetoothUuid &)serviceUuid
withResponse:(bool)withResponse
{
using namespace OSXBluetooth;
@@ -763,8 +784,13 @@ using namespace QT_NAMESPACE;
QT_BT_MAC_AUTORELEASEPOOL;
if (!charMap.contains(charHandle)) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "characteristic: " << charHandle << " not found";
- return false;
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "characteristic: "
+ << charHandle << " not found";
+ if (notifier) {
+ emit notifier->CBCentralManagerError(serviceUuid,
+ QLowEnergyService::CharacteristicWriteError);
+ }
+ return;
}
LERequest request;
@@ -775,11 +801,10 @@ using namespace QT_NAMESPACE;
requests.enqueue(request);
[self performNextRequest];
-
- return true;
}
-- (bool)readDescriptor:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))descHandle
+- (void)readDescriptor:(QLowEnergyHandle)descHandle
+ onService:(const QBluetoothUuid &)serviceUuid
{
using namespace OSXBluetooth;
@@ -788,7 +813,11 @@ using namespace QT_NAMESPACE;
if (!descMap.contains(descHandle)) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "handle:"
<< descHandle << "not found";
- return false;
+ if (notifier) {
+ emit notifier->CBCentralManagerError(serviceUuid,
+ QLowEnergyService::DescriptorReadError);
+ }
+ return;
}
LERequest request;
@@ -797,11 +826,11 @@ using namespace QT_NAMESPACE;
requests.enqueue(request);
[self performNextRequest];
-
- return true;
}
-- (bool)write:(const QByteArray &)value descHandle:(QLowEnergyHandle)descHandle
+- (void)write:(const QByteArray &)value
+ descHandle:(QLowEnergyHandle)descHandle
+ onService:(const QBluetoothUuid &)serviceUuid
{
using namespace OSXBluetooth;
@@ -810,7 +839,11 @@ using namespace QT_NAMESPACE;
if (!descMap.contains(descHandle)) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "handle: "
<< descHandle << " not found";
- return false;
+ if (notifier) {
+ emit notifier->CBCentralManagerError(serviceUuid,
+ QLowEnergyService::DescriptorWriteError);
+ }
+ return;
}
LERequest request;
@@ -820,8 +853,6 @@ using namespace QT_NAMESPACE;
requests.enqueue(request);
[self performNextRequest];
-
- return true;
}
// Aux. methods:
@@ -1039,8 +1070,6 @@ using namespace QT_NAMESPACE;
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
-
using namespace OSXBluetooth;
const CBCentralManagerState state = central.state;
@@ -1055,47 +1084,42 @@ using namespace QT_NAMESPACE;
return;
}
- if (disconnectPending) {
- disconnectPending = false;
- managerState = OSXBluetooth::CentralManagerIdle;
- return [self disconnectFromDevice];
- }
-
// Let's check some states we do not like first:
if (state == CBCentralManagerStateUnsupported || state == CBCentralManagerStateUnauthorized) {
if (managerState == CentralManagerUpdating) {
// We tried to connect just to realize, LE is not supported. Report this.
managerState = CentralManagerIdle;
- delegate->LEnotSupported();
+ if (notifier)
+ emit notifier->LEnotSupported();
} else {
// TODO: if we are here, LE _was_ supported and we first managed to update
// and reset managerState from CentralManagerUpdating.
managerState = CentralManagerIdle;
- delegate->error(QLowEnergyController::InvalidBluetoothAdapterError);
+ if (notifier)
+ emit notifier->CBCentralManagerError(QLowEnergyController::InvalidBluetoothAdapterError);
}
return;
}
if (state == CBCentralManagerStatePoweredOff) {
+ managerState = CentralManagerIdle;
if (managerState == CentralManagerUpdating) {
- // I've seen this instead of Unsopported on OS X.
- managerState = CentralManagerIdle;
- delegate->LEnotSupported();
+ // I've seen this instead of Unsupported on OS X.
+ if (notifier)
+ emit notifier->LEnotSupported();
} else {
- managerState = CentralManagerIdle;
// TODO: we need a better error +
// what will happen if later the state changes to PoweredOn???
- delegate->error(QLowEnergyController::InvalidBluetoothAdapterError);
+ if (notifier)
+ emit notifier->CBCentralManagerError(QLowEnergyController::InvalidBluetoothAdapterError);
}
return;
}
if (state == CBCentralManagerStatePoweredOn) {
- if (managerState == CentralManagerUpdating) {
+ if (managerState == CentralManagerUpdating && !disconnectPending) {
managerState = CentralManagerIdle;
- const QLowEnergyController::Error status = [self connectToDevice];
- if (status != QLowEnergyController::NoError)// An allocation problem?
- delegate->error(status);
+ [self retrievePeripheralAndConnect];
}
} else {
// We actually handled all known states, but .. Core Bluetooth can change?
@@ -1118,12 +1142,9 @@ using namespace QT_NAMESPACE;
managerState = OSXBluetooth::CentralManagerIdle;
if (!peripherals || peripherals.count != 1) {
- Q_ASSERT_X(delegate, Q_FUNC_INFO,
- "invalid delegate (null)");
-
qCDebug(QT_BT_OSX) << Q_FUNC_INFO <<"unexpected number of peripherals (!= 1)";
-
- delegate->error(QLowEnergyController::UnknownRemoteDeviceError);
+ if (notifier)
+ emit notifier->CBCentralManagerError(QLowEnergyController::UnknownRemoteDeviceError);
} else {
peripheral = [static_cast<CBPeripheral *>([peripherals objectAtIndex:0]) retain];
[self connectToPeripheral];
@@ -1135,15 +1156,14 @@ using namespace QT_NAMESPACE;
Q_UNUSED(central)
Q_UNUSED(aPeripheral)
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
-
if (managerState != OSXBluetooth::CentralManagerConnecting) {
// We called cancel but before disconnected, managed to connect?
return;
}
managerState = OSXBluetooth::CentralManagerIdle;
- delegate->connectSuccess();
+ if (notifier)
+ emit notifier->connected();
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)aPeripheral
@@ -1153,8 +1173,6 @@ using namespace QT_NAMESPACE;
Q_UNUSED(aPeripheral)
Q_UNUSED(error)
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
-
if (managerState != OSXBluetooth::CentralManagerConnecting) {
// Canceled already.
return;
@@ -1162,7 +1180,8 @@ using namespace QT_NAMESPACE;
managerState = OSXBluetooth::CentralManagerIdle;
// TODO: better error mapping is required.
- delegate->error(QLowEnergyController::UnknownRemoteDeviceError);
+ if (notifier)
+ notifier->CBCentralManagerError(QLowEnergyController::UnknownRemoteDeviceError);
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)aPeripheral
@@ -1171,19 +1190,18 @@ using namespace QT_NAMESPACE;
Q_UNUSED(central)
Q_UNUSED(aPeripheral)
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
-
// Clear internal caches/data.
[self reset];
if (error && managerState == OSXBluetooth::CentralManagerDisconnecting) {
managerState = OSXBluetooth::CentralManagerIdle;
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to disconnect";
- // TODO: instead of 'unknown' - .. ?
- delegate->error(QLowEnergyController::UnknownRemoteDeviceError);
+ if (notifier)
+ emit notifier->CBCentralManagerError(QLowEnergyController::UnknownRemoteDeviceError);
} else {
managerState = OSXBluetooth::CentralManagerIdle;
- delegate->disconnected();
+ if (notifier)
+ emit notifier->disconnected();
}
}
@@ -1201,10 +1219,10 @@ using namespace QT_NAMESPACE;
managerState = OSXBluetooth::CentralManagerIdle;
if (error) {
- // NSLog, not qCDebug/Warning - to print the error.
NSLog(@"%s failed with error %@", Q_FUNC_INFO, error);
// TODO: better error mapping required.
- delegate->error(QLowEnergyController::UnknownError);
+ if (notifier)
+ emit notifier->CBCentralManagerError(QLowEnergyController::UnknownError);
} else {
[self discoverIncludedServices];
}
@@ -1225,14 +1243,11 @@ using namespace QT_NAMESPACE;
QT_BT_MAC_AUTORELEASEPOOL;
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)");
- // TODO: asserts on other "pointers" ...
managerState = CentralManagerIdle;
if (error) {
- // NSLog, not qCWarning/Critical - to log the actual NSError and service UUID.
NSLog(@"%s: finished with error %@ for service %@",
Q_FUNC_INFO, error, service.UUID);
} else if (service.includedServices && service.includedServices.count) {
@@ -1279,8 +1294,8 @@ using namespace QT_NAMESPACE;
servicesToVisit.reset(nil);
servicesToVisitNext.reset(nil);
- const ObjCStrongReference<NSArray> services(peripheral.services, true); // true == retain.
- delegate->serviceDiscoveryFinished(services);
+ if (notifier)
+ emit notifier->serviceDiscoveryFinished();
}
- (void)peripheral:(CBPeripheral *)aPeripheral didDiscoverCharacteristicsForService:(CBService *)service
@@ -1290,21 +1305,20 @@ using namespace QT_NAMESPACE;
// discoveries active.
Q_UNUSED(aPeripheral)
- // TODO: check that this can never be called after cancelPeripheralConnection was executed.
+ if (!notifier) {
+ // Detached.
+ return;
+ }
using namespace OSXBluetooth;
- Q_ASSERT_X(managerState != CentralManagerUpdating,
- Q_FUNC_INFO, "invalid state");
- Q_ASSERT_X(delegate, Q_FUNC_INFO,
- "invalid delegate (null)");
+ Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO, "invalid state");
if (error) {
- // NSLog to show the actual NSError (can contain something interesting).
NSLog(@"%s failed with error: %@", Q_FUNC_INFO, error);
// We did not discover any characteristics and can not discover descriptors,
// inform our delegate (it will set a service state also).
- delegate->error(qt_uuid(service.UUID), QLowEnergyController::UnknownError);
+ emit notifier->CBCentralManagerError(qt_uuid(service.UUID), QLowEnergyController::UnknownError);
} else {
[self readCharacteristics:service];
}
@@ -1316,11 +1330,15 @@ using namespace QT_NAMESPACE;
{
Q_UNUSED(aPeripheral)
+ if (!notifier) {
+ // Detached.
+ return;
+ }
+
using namespace OSXBluetooth;
Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO, "invalid state");
Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)");
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
QT_BT_MAC_AUTORELEASEPOOL;
// First, let's check if we're discovering a service details now.
@@ -1330,13 +1348,12 @@ using namespace QT_NAMESPACE;
const QLowEnergyHandle chHandle = charMap.key(characteristic);
if (error) {
- // Use NSLog, not qCDebug/qCWarning to log the actual error.
NSLog(@"%s failed with error %@", Q_FUNC_INFO, error);
if (!isDetailsDiscovery) {
if (chHandle && chHandle == currentReadHandle) {
currentReadHandle = 0;
requestPending = false;
- delegate->error(qtUuid, QLowEnergyService::CharacteristicReadError);
+ emit notifier->CBCentralManagerError(qtUuid, QLowEnergyService::CharacteristicReadError);
[self performNextRequest];
}
return;
@@ -1370,10 +1387,10 @@ using namespace QT_NAMESPACE;
requestPending = false;
currentReadHandle = 0;
//
- delegate->characteristicReadNotification(chHandle, qt_bytearray(characteristic.value));
+ emit notifier->characteristicRead(chHandle, qt_bytearray(characteristic.value));
[self performNextRequest];
} else {
- delegate->characteristicUpdateNotification(chHandle, qt_bytearray(characteristic.value));
+ emit notifier->characteristicUpdated(chHandle, qt_bytearray(characteristic.value));
}
}
}
@@ -1386,12 +1403,16 @@ using namespace QT_NAMESPACE;
// have several discoveries active at the same time.
Q_UNUSED(aPeripheral)
+ if (!notifier) {
+ // Detached, no need to continue ...
+ return;
+ }
+
QT_BT_MAC_AUTORELEASEPOOL;
using namespace OSXBluetooth;
if (error) {
- // Log the error using NSLog:
NSLog(@"%s failed with error %@", Q_FUNC_INFO, error);
// We can continue though ...
}
@@ -1413,6 +1434,11 @@ using namespace QT_NAMESPACE;
Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)");
+ if (!notifier) {
+ // Detached ...
+ return;
+ }
+
QT_BT_MAC_AUTORELEASEPOOL;
using namespace OSXBluetooth;
@@ -1423,14 +1449,13 @@ using namespace QT_NAMESPACE;
const QLowEnergyHandle dHandle = descMap.key(descriptor);
if (error) {
- // NSLog to log the actual error ...
NSLog(@"%s failed with error %@", Q_FUNC_INFO, error);
if (!isDetailsDiscovery) {
if (dHandle && dHandle == currentReadHandle) {
currentReadHandle = 0;
requestPending = false;
- delegate->error(qtUuid, QLowEnergyService::DescriptorReadError);
+ emit notifier->CBCentralManagerError(qtUuid, QLowEnergyService::DescriptorReadError);
[self performNextRequest];
}
return;
@@ -1470,7 +1495,7 @@ using namespace QT_NAMESPACE;
if (dHandle == currentReadHandle) {
currentReadHandle = 0;
requestPending = false;
- delegate->descriptorReadNotification(dHandle, qt_bytearray(static_cast<NSObject *>(descriptor.value)));
+ emit notifier->descriptorRead(dHandle, qt_bytearray(static_cast<NSObject *>(descriptor.value)));
[self performNextRequest];
}
}
@@ -1483,6 +1508,11 @@ using namespace QT_NAMESPACE;
Q_UNUSED(aPeripheral)
Q_UNUSED(characteristic)
+ if (!notifier) {
+ // Detached.
+ return;
+ }
+
// From docs:
//
// "This method is invoked only when your app calls the writeValue:forCharacteristic:type:
@@ -1496,8 +1526,6 @@ using namespace QT_NAMESPACE;
requestPending = false;
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
-
// Error or not, but the cached value has to be deleted ...
const QByteArray valueToReport(valuesToWrite.value(characteristic, QByteArray()));
@@ -1507,15 +1535,12 @@ using namespace QT_NAMESPACE;
}
if (error) {
- // Use NSLog to log the actual error:
NSLog(@"%s failed with error %@", Q_FUNC_INFO, error);
- delegate->error(qt_uuid(characteristic.service.UUID),
- QLowEnergyService::CharacteristicWriteError);
+ emit notifier->CBCentralManagerError(qt_uuid(characteristic.service.UUID),
+ QLowEnergyService::CharacteristicWriteError);
} else {
- // Keys are unique.
const QLowEnergyHandle cHandle = charMap.key(characteristic);
- Q_ASSERT_X(cHandle, Q_FUNC_INFO, "invalid handle, not found in the characteristics map");
- delegate->characteristicWriteNotification(cHandle, valueToReport);
+ emit notifier->characteristicWritten(cHandle, valueToReport);
}
[self performNextRequest];
@@ -1527,7 +1552,10 @@ using namespace QT_NAMESPACE;
{
Q_UNUSED(aPeripheral)
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
+ if (!notifier) {
+ // Detached already.
+ return;
+ }
using namespace OSXBluetooth;
@@ -1535,23 +1563,20 @@ using namespace QT_NAMESPACE;
requestPending = false;
- using namespace OSXBluetooth;
-
// Error or not, a value (if any) must be removed.
const QByteArray valueToReport(valuesToWrite.value(descriptor, QByteArray()));
if (!valuesToWrite.remove(descriptor))
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no updated value found";
if (error) {
- // NSLog to log the actual NSError:
NSLog(@"%s failed with error %@", Q_FUNC_INFO, error);
- delegate->error(qt_uuid(descriptor.characteristic.service.UUID),
- QLowEnergyService::DescriptorWriteError);
+ emit notifier->CBCentralManagerError(qt_uuid(descriptor.characteristic.service.UUID),
+ QLowEnergyService::DescriptorWriteError);
} else {
const QLowEnergyHandle dHandle = descMap.key(descriptor);
Q_ASSERT_X(dHandle, Q_FUNC_INFO,
"descriptor not found in the descriptors map");
- delegate->descriptorWriteNotification(dHandle, valueToReport);
+ emit notifier->descriptorWritten(dHandle, valueToReport);
}
[self performNextRequest];
@@ -1563,40 +1588,42 @@ using namespace QT_NAMESPACE;
{
Q_UNUSED(aPeripheral)
+ if (!notifier)
+ return;
+
using namespace OSXBluetooth;
QT_BT_MAC_AUTORELEASEPOOL;
requestPending = false;
- Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (nil)");
-
const QBluetoothUuid qtUuid(QBluetoothUuid::ClientCharacteristicConfiguration);
CBDescriptor *const descriptor = [self descriptor:qtUuid forCharacteristic:characteristic];
const QByteArray valueToReport(valuesToWrite.value(descriptor, QByteArray()));
const int nRemoved = valuesToWrite.remove(descriptor);
if (error) {
- // NSLog to log the actual NSError:
NSLog(@"%s failed with error %@", Q_FUNC_INFO, error);
- delegate->error(qt_uuid(characteristic.service.UUID),
- // In Qt's API it's a descriptor write actually.
- QLowEnergyService::DescriptorWriteError);
- } else {
- if (nRemoved) {
- const QLowEnergyHandle dHandle = descMap.key(descriptor);
- delegate->descriptorWriteNotification(dHandle, valueToReport);
- } else {
- /*
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << ": notification value set to "
- << int(characteristic.isNotifying)
- << " but no client characteristic configuration descriptor found"
- " or no previous writeDescriptor request found";
- */
- }
+ // In Qt's API it's a descriptor write actually.
+ emit notifier->CBCentralManagerError(qt_uuid(characteristic.service.UUID),
+ QLowEnergyService::DescriptorWriteError);
+ } else if (nRemoved) {
+ const QLowEnergyHandle dHandle = descMap.key(descriptor);
+ emit notifier->descriptorWritten(dHandle, valueToReport);
}
[self performNextRequest];
}
+- (void)detach
+{
+ if (notifier) {
+ notifier->disconnect();
+ notifier->deleteLater();
+ notifier = 0;
+ }
+
+ [self disconnectFromDevice];
+}
+
@end
diff --git a/src/bluetooth/osx/osxbtcentralmanager_p.h b/src/bluetooth/osx/osxbtcentralmanager_p.h
index 429bae91..1ff33c12 100644
--- a/src/bluetooth/osx/osxbtcentralmanager_p.h
+++ b/src/bluetooth/osx/osxbtcentralmanager_p.h
@@ -69,41 +69,7 @@ class QLowEnergyServicePrivate;
namespace OSXBluetooth {
-class CentralManagerDelegate
-{
-public:
- typedef QT_MANGLE_NAMESPACE(OSXBTCentralManager) ObjCCentralManager;
- typedef ObjCStrongReference<NSArray> LEServices;
- typedef QSharedPointer<QLowEnergyServicePrivate> LEService;
- typedef ObjCStrongReference<CBCharacteristic> LECharacteristic;
-
- virtual ~CentralManagerDelegate();
-
- virtual void LEnotSupported() = 0;
- virtual void connectSuccess() = 0;
- virtual void serviceDiscoveryFinished(LEServices services) = 0;
- virtual void serviceDetailsDiscoveryFinished(LEService service) = 0;
- virtual void characteristicReadNotification(QLowEnergyHandle charHandle,
- const QByteArray &value) = 0;
- virtual void characteristicWriteNotification(QLowEnergyHandle charHandle,
- const QByteArray &value) = 0;
- virtual void characteristicUpdateNotification(QLowEnergyHandle charHandle,
- const QByteArray &value) = 0;
- virtual void descriptorReadNotification(QLowEnergyHandle descHandle,
- const QByteArray &value) = 0;
- virtual void descriptorWriteNotification(QLowEnergyHandle descHandle,
- const QByteArray &value) = 0;
- virtual void disconnected() = 0;
-
- // General errors.
- virtual void error(QLowEnergyController::Error error) = 0;
- // Service related errors.
- virtual void error(const QBluetoothUuid &serviceUuid,
- QLowEnergyController::Error error) = 0;
- // Characteristics/descriptors related errors.
- virtual void error(const QBluetoothUuid &serviceUuid,
- QLowEnergyService::ServiceError error) = 0;
-};
+class LECentralNotifier;
enum CentralManagerState
{
@@ -126,8 +92,8 @@ typedef QHash<QLowEnergyHandle, CBService *> ServiceHash;
typedef QHash<QLowEnergyHandle, CBCharacteristic *> CharHash;
typedef QHash<QLowEnergyHandle, CBDescriptor *> DescHash;
-// Descriptor/charactesirsti read/write requests
-// - we have to serialize 'concurrent' write requests.
+// Descriptor/charactesirstic read/write requests
+// - we have to serialize 'concurrent' requests.
struct LERequest {
enum RequestType {
CharRead,
@@ -165,14 +131,14 @@ QT_END_NAMESPACE
@interface QT_MANGLE_NAMESPACE(OSXBTCentralManager) : NSObject<CBCentralManagerDelegate, CBPeripheralDelegate>
{
+@private
CBCentralManager *manager;
QT_PREPEND_NAMESPACE(OSXBluetooth)::CentralManagerState managerState;
bool disconnectPending;
QT_PREPEND_NAMESPACE(QBluetoothUuid) deviceUuid;
- CBPeripheral *peripheral;
- QT_PREPEND_NAMESPACE(OSXBluetooth)::CentralManagerDelegate *delegate;
+ QT_PREPEND_NAMESPACE(OSXBluetooth)::LECentralNotifier *notifier;
// Quite a verbose service discovery machinery
// (a "graph traversal").
@@ -197,32 +163,43 @@ QT_END_NAMESPACE
QT_PREPEND_NAMESPACE(QLowEnergyHandle) currentReadHandle;
QT_PREPEND_NAMESPACE(OSXBluetooth)::ValueHash valuesToWrite;
+@public
+ CBPeripheral *peripheral;
}
-- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::CentralManagerDelegate *)aDelegate;
+- (id)initWith:(QT_PREPEND_NAMESPACE(OSXBluetooth)::LECentralNotifier *)notifier;
- (void)dealloc;
-- (QT_PREPEND_NAMESPACE(QLowEnergyController)::Error)
- connectToDevice:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)aDeviceUuid;
+// IMPORTANT: _all_ these methods are to be executed on qt_LE_queue,
+// when passing parameters - C++ objects _must_ be copied (see the controller's code).
+- (void)connectToDevice:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)aDeviceUuid;
- (void)disconnectFromDevice;
- (void)discoverServices;
-- (bool)discoverServiceDetails:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid;
+- (void)discoverServiceDetails:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid;
-- (bool)setNotifyValue:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
- forCharacteristic:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle;
+- (void)setNotifyValue:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
+ forCharacteristic:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle
+ onService:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid;
-- (bool)readCharacteristic:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle;
+- (void)readCharacteristic:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle
+ onService:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid;
-- (bool)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
+- (void)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
charHandle:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle
+ onService:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid
withResponse:(bool)writeWithResponse;
-- (bool)readDescriptor:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))descHandle;
+- (void)readDescriptor:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))descHandle
+ onService:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid;
+
+- (void)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
+ descHandle:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))descHandle
+ onService:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid;
+
+- (void)detach;
-- (bool)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
- descHandle:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))descHandle;
@end
#endif
diff --git a/src/bluetooth/osx/osxbtnotifier.cpp b/src/bluetooth/osx/osxbtnotifier.cpp
new file mode 100644
index 00000000..0e0343cf
--- /dev/null
+++ b/src/bluetooth/osx/osxbtnotifier.cpp
@@ -0,0 +1 @@
+#include "osxbtnotifier_p.h"
diff --git a/src/bluetooth/osx/osxbtnotifier_p.h b/src/bluetooth/osx/osxbtnotifier_p.h
new file mode 100644
index 00000000..7b4d18e4
--- /dev/null
+++ b/src/bluetooth/osx/osxbtnotifier_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OSXBTNOTIFIER_P_H
+#define OSXBTNOTIFIER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+
+#include "qlowenergycontroller.h"
+#include "qbluetoothuuid.h"
+#include "qbluetooth.h"
+
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyServicePrivate;
+
+namespace OSXBluetooth
+{
+
+class LECentralNotifier : public QObject
+{
+ Q_OBJECT
+
+Q_SIGNALS:
+ void connected();
+ void disconnected();
+
+ void serviceDiscoveryFinished();
+ void serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service);
+ void characteristicRead(QLowEnergyHandle charHandle, const QByteArray &value);
+ void characteristicWritten(QLowEnergyHandle charHandle, const QByteArray &value);
+ void characteristicUpdated(QLowEnergyHandle charHandle, const QByteArray &value);
+ void descriptorRead(QLowEnergyHandle descHandle, const QByteArray &value);
+ void descriptorWritten(QLowEnergyHandle descHandle, const QByteArray &value);
+
+ void LEnotSupported();
+ void CBCentralManagerError(QLowEnergyController::Error error);
+ void CBCentralManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyController::Error error);
+ void CBCentralManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyService::ServiceError error);
+
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm
index 396f82bb..e38442e2 100644
--- a/src/bluetooth/qlowenergycontroller_osx.mm
+++ b/src/bluetooth/qlowenergycontroller_osx.mm
@@ -62,6 +62,8 @@ static void registerQLowEnergyControllerMetaType()
if (!initDone) {
qRegisterMetaType<QLowEnergyController::ControllerState>();
qRegisterMetaType<QLowEnergyController::Error>();
+ qRegisterMetaType<QLowEnergyHandle>("QLowEnergyHandle");
+ qRegisterMetaType<QSharedPointer<QLowEnergyServicePrivate> >();
initDone = true;
}
}
@@ -139,12 +141,23 @@ QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyControl
// 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.
- centralManager.reset([[ObjCCentralManager alloc] initWithDelegate:this]);
+ 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,
@@ -160,33 +173,41 @@ QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyControl
registerQLowEnergyControllerMetaType();
Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)");
- centralManager.reset([[ObjCCentralManager alloc] initWithDelegate:this]);
+
+ using OSXBluetooth::LECentralNotifier;
+
+ 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()
{
+ // 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];
+ });
+ }
}
bool QLowEnergyControllerPrivateOSX::isValid() const
{
- // isValid means only "was able to allocate all resources",
- // nothing more.
return centralManager;
}
-void QLowEnergyControllerPrivateOSX::LEnotSupported()
-{
- // Report as an error. But this should not be possible
- // actually: before connecting to any device, we have
- // to discover it, if it was discovered ... LE _must_
- // be supported.
-}
-
-void QLowEnergyControllerPrivateOSX::connectSuccess()
+void QLowEnergyControllerPrivateOSX::_q_connected()
{
Q_ASSERT_X(controllerState == QLowEnergyController::ConnectingState,
Q_FUNC_INFO, "invalid state");
@@ -199,7 +220,17 @@ void QLowEnergyControllerPrivateOSX::connectSuccess()
}
}
-void QLowEnergyControllerPrivateOSX::serviceDiscoveryFinished(LEServices services)
+void QLowEnergyControllerPrivateOSX::_q_disconnected()
+{
+ controllerState = QLowEnergyController::UnconnectedState;
+
+ if (!isConnecting) {
+ emit q_ptr->stateChanged(QLowEnergyController::UnconnectedState);
+ emit q_ptr->disconnected();
+ }
+}
+
+void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished()
{
Q_ASSERT_X(controllerState == QLowEnergyController::DiscoveringState,
Q_FUNC_INFO, "invalid state");
@@ -208,6 +239,7 @@ void QLowEnergyControllerPrivateOSX::serviceDiscoveryFinished(LEServices service
QT_BT_MAC_AUTORELEASEPOOL;
+ NSArray *const services = centralManager.data()->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.
@@ -218,7 +250,7 @@ void QLowEnergyControllerPrivateOSX::serviceDiscoveryFinished(LEServices service
// during the pass 2); we also ignore duplicates (== services with the same UUID)
// - since we do not have a way to distinguish them later
// (our API is using uuids when creating QLowEnergyServices).
- for (CBService *cbService in services.data()) {
+ for (CBService *cbService in services) {
const ServicePrivate newService(qt_createLEService(this, cbService, false));
if (!newService.data())
continue;
@@ -281,7 +313,6 @@ void QLowEnergyControllerPrivateOSX::serviceDiscoveryFinished(LEServices service
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));
}
@@ -292,12 +323,12 @@ void QLowEnergyControllerPrivateOSX::serviceDiscoveryFinished(LEServices service
QMetaObject::invokeMethod(q_ptr, "discoveryFinished", Qt::QueuedConnection);
}
-void QLowEnergyControllerPrivateOSX::serviceDetailsDiscoveryFinished(LEService service)
+void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service)
{
- Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
-
QT_BT_MAC_AUTORELEASEPOOL;
+ Q_ASSERT(service);
+
if (!discoveredServices.contains(service->uuid)) {
qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "unknown service uuid: "
<< service->uuid;
@@ -313,8 +344,8 @@ void QLowEnergyControllerPrivateOSX::serviceDetailsDiscoveryFinished(LEService s
qtService->setState(QLowEnergyService::ServiceDiscovered);
}
-void QLowEnergyControllerPrivateOSX::characteristicReadNotification(QLowEnergyHandle charHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle charHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)");
@@ -334,8 +365,8 @@ void QLowEnergyControllerPrivateOSX::characteristicReadNotification(QLowEnergyHa
emit service->characteristicRead(characteristic, value);
}
-void QLowEnergyControllerPrivateOSX::characteristicWriteNotification(QLowEnergyHandle charHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateOSX::_q_characteristicWritten(QLowEnergyHandle charHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)");
@@ -358,8 +389,8 @@ void QLowEnergyControllerPrivateOSX::characteristicWriteNotification(QLowEnergyH
emit service->characteristicWritten(characteristic, value);
}
-void QLowEnergyControllerPrivateOSX::characteristicUpdateNotification(QLowEnergyHandle charHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateOSX::_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?
@@ -387,7 +418,8 @@ void QLowEnergyControllerPrivateOSX::characteristicUpdateNotification(QLowEnergy
emit service->characteristicChanged(characteristic, value);
}
-void QLowEnergyControllerPrivateOSX::descriptorReadNotification(QLowEnergyHandle dHandle, const QByteArray &value)
+void QLowEnergyControllerPrivateOSX::_q_descriptorRead(QLowEnergyHandle dHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)");
@@ -402,7 +434,8 @@ void QLowEnergyControllerPrivateOSX::descriptorReadNotification(QLowEnergyHandle
emit service->descriptorRead(qtDescriptor, value);
}
-void QLowEnergyControllerPrivateOSX::descriptorWriteNotification(QLowEnergyHandle dHandle, const QByteArray &value)
+void QLowEnergyControllerPrivateOSX::_q_descriptorWritten(QLowEnergyHandle dHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)");
@@ -418,29 +451,18 @@ void QLowEnergyControllerPrivateOSX::descriptorWriteNotification(QLowEnergyHandl
emit service->descriptorWritten(qtDescriptor, value);
}
-void QLowEnergyControllerPrivateOSX::disconnected()
+void QLowEnergyControllerPrivateOSX::_q_LEnotSupported()
{
- controllerState = QLowEnergyController::UnconnectedState;
-
- if (!isConnecting) {
- emit q_ptr->stateChanged(QLowEnergyController::UnconnectedState);
- emit q_ptr->disconnected();
- }
+ // Report as an error. But this should not be possible
+ // actually: before connecting to any device, we have
+ // to discover it, if it was discovered ... LE _must_
+ // be supported.
}
-void QLowEnergyControllerPrivateOSX::error(QLowEnergyController::Error errorCode)
+void QLowEnergyControllerPrivateOSX::_q_CBCentralManagerError(QLowEnergyController::Error errorCode)
{
// Errors reported during connect and general errors.
- // We're still in connectToDevice,
- // some error was reported synchronously.
- // Return, the error will be correctly set later
- // by connectToDevice.
- if (isConnecting) {
- lastError = errorCode;
- return;
- }
-
setErrorDescription(errorCode);
emit q_ptr->error(lastError);
@@ -454,8 +476,8 @@ void QLowEnergyControllerPrivateOSX::error(QLowEnergyController::Error errorCode
// a service/characteristic - related error.
}
-void QLowEnergyControllerPrivateOSX::error(const QBluetoothUuid &serviceUuid,
- QLowEnergyController::Error errorCode)
+void QLowEnergyControllerPrivateOSX::_q_CBCentralManagerError(const QBluetoothUuid &serviceUuid,
+ QLowEnergyController::Error errorCode)
{
// Errors reported while discovering service details etc.
Q_UNUSED(errorCode) // TODO: setError?
@@ -470,8 +492,8 @@ void QLowEnergyControllerPrivateOSX::error(const QBluetoothUuid &serviceUuid,
}
}
-void QLowEnergyControllerPrivateOSX::error(const QBluetoothUuid &serviceUuid,
- QLowEnergyService::ServiceError errorCode)
+void QLowEnergyControllerPrivateOSX::_q_CBCentralManagerError(const QBluetoothUuid &serviceUuid,
+ QLowEnergyService::ServiceError errorCode)
{
if (!discoveredServices.contains(serviceUuid)) {
qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "unknown service uuid: "
@@ -493,30 +515,21 @@ void QLowEnergyControllerPrivateOSX::connectToDevice()
Q_ASSERT_X(!isConnecting, Q_FUNC_INFO,
"recursive connectToDevice call");
- setErrorDescription(QLowEnergyController::NoError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no LE queue found";
+ setErrorDescription(QLowEnergyController::UnknownError);
+ return;
+ }
- isConnecting = true;// Do not emit signals if some callback is executed synchronously.
+ setErrorDescription(QLowEnergyController::NoError);
controllerState = QLowEnergyController::ConnectingState;
- const QLowEnergyController::Error status = [centralManager connectToDevice:deviceUuid];
- isConnecting = false;
- if (status == QLowEnergyController::NoError && lastError == QLowEnergyController::NoError) {
- emit q_ptr->stateChanged(controllerState);
- if (controllerState == QLowEnergyController::ConnectedState) {
- // If a peripheral is connected already from the Core Bluetooth's
- // POV:
- emit q_ptr->connected();
- } else if (controllerState == QLowEnergyController::UnconnectedState) {
- // Ooops, tried to connect, got peripheral disconnect instead -
- // this happens with Core Bluetooth.
- emit q_ptr->disconnected();
- }
- } else if (status != QLowEnergyController::NoError) {
- error(status);
- } else {
- // Re-set the error/description and emit.
- error(lastError);
- }
+ const QBluetoothUuid deviceUuidCopy(deviceUuid);
+ ObjCCentralManager *manager = centralManager.data();
+ dispatch_async(leQueue, ^{
+ [manager connectToDevice:deviceUuidCopy];
+ });
}
void QLowEnergyControllerPrivateOSX::discoverServices()
@@ -525,9 +538,20 @@ void QLowEnergyControllerPrivateOSX::discoverServices()
Q_ASSERT_X(controllerState != QLowEnergyController::UnconnectedState,
Q_FUNC_INFO, "not connected to peripheral");
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no LE queue found";
+ setErrorDescription(QLowEnergyController::UnknownError);
+ return;
+ }
+
controllerState = QLowEnergyController::DiscoveringState;
emit q_ptr->stateChanged(QLowEnergyController::DiscoveringState);
- [centralManager discoverServices];
+
+ ObjCCentralManager *manager = centralManager.data();
+ dispatch_async(leQueue, ^{
+ [manager discoverServices];
+ });
}
void QLowEnergyControllerPrivateOSX::discoverServiceDetails(const QBluetoothUuid &serviceUuid)
@@ -546,14 +570,20 @@ void QLowEnergyControllerPrivateOSX::discoverServiceDetails(const QBluetoothUuid
return;
}
- ServicePrivate qtService(discoveredServices.value(serviceUuid));
- if ([centralManager discoverServiceDetails:serviceUuid]) {
- qtService->setState(QLowEnergyService::DiscoveringServices);
- } else {
- // The error is returned by CentralManager - no
- // service with a given UUID found on a peripheral.
- qtService->setState(QLowEnergyService::InvalidService);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "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,
@@ -586,8 +616,19 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer
return;
}
- if (![centralManager setNotifyValue:newValue forCharacteristic:charHandle])
- service->setError(QLowEnergyService::DescriptorWriteError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no LE queue found";
+ return;
+ }
+ ObjCCentralManager *manager = centralManager.data();
+ const QBluetoothUuid serviceUuid(service->uuid);
+ const QByteArray newValueCopy(newValue);
+ dispatch_async(leQueue, ^{
+ [manager setNotifyValue:newValueCopy
+ forCharacteristic:charHandle
+ onService:serviceUuid];
+ });
}
void QLowEnergyControllerPrivateOSX::readCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service,
@@ -608,8 +649,17 @@ void QLowEnergyControllerPrivateOSX::readCharacteristic(QSharedPointer<QLowEnerg
return;
}
- if (![centralManager readCharacteristic:charHandle])
- service->setError(QLowEnergyService::CharacteristicReadError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no LE queue found";
+ return;
+ }
+ // Attention! We have to copy UUID.
+ ObjCCentralManager *manager = centralManager.data();
+ const QBluetoothUuid serviceUuid(service->uuid);
+ dispatch_async(leQueue, ^{
+ [manager readCharacteristic:charHandle onService:serviceUuid];
+ });
}
void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service,
@@ -634,11 +684,21 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner
return;
}
- const bool result = [centralManager write:newValue
- charHandle:charHandle
- withResponse:writeWithResponse];
- if (!result)
- service->setError(QLowEnergyService::CharacteristicWriteError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no LE queue found";
+ return;
+ }
+ // Attention! Copy objects!
+ const QBluetoothUuid serviceUuid(service->uuid);
+ const QByteArray newValueCopy(newValue);
+ ObjCCentralManager *const manager = centralManager.data();
+ dispatch_async(leQueue, ^{
+ [manager write:newValueCopy
+ charHandle:charHandle
+ onService:serviceUuid
+ withResponse:writeWithResponse];
+ });
}
quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHandle charHandle,
@@ -650,7 +710,6 @@ quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHa
CharacteristicDataMap::iterator charIt = service->characteristicList.find(charHandle);
if (charIt != service->characteristicList.end()) {
QLowEnergyServicePrivate::CharData &charData = charIt.value();
-
if (appendValue)
charData.value += value;
else
@@ -675,8 +734,18 @@ void QLowEnergyControllerPrivateOSX::readDescriptor(QSharedPointer<QLowEnergySer
return;
}
- if (![centralManager readDescriptor:descriptorHandle])
- service->setError(QLowEnergyService::DescriptorReadError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no LE queue found";
+ return;
+ }
+ // Attention! Copy objects!
+ const QBluetoothUuid serviceUuid(service->uuid);
+ ObjCCentralManager * const manager = centralManager.data();
+ dispatch_async(leQueue, ^{
+ [manager readDescriptor:descriptorHandle
+ onService:serviceUuid];
+ });
}
void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service,
@@ -695,8 +764,20 @@ void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergySe
return;
}
- if (![centralManager write:newValue descHandle:descriptorHandle])
- service->setError(QLowEnergyService::DescriptorWriteError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no LE queue found";
+ return;
+ }
+ // Attention! Copy objects!
+ const QBluetoothUuid serviceUuid(service->uuid);
+ ObjCCentralManager * const manager = centralManager.data();
+ const QByteArray newValueCopy(newValue);
+ dispatch_async(leQueue, ^{
+ [manager write:newValueCopy
+ descHandle:descriptorHandle
+ onService:serviceUuid];
+ });
}
quint16 QLowEnergyControllerPrivateOSX::updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descHandle,
@@ -812,6 +893,45 @@ void QLowEnergyControllerPrivateOSX::invalidateServices()
discoveredServices.clear();
}
+bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECentralNotifier *notifier)
+{
+ using OSXBluetooth::LECentralNotifier;
+
+ Q_ASSERT_X(notifier, Q_FUNC_INFO, "invalid notifier object (null)");
+
+ bool ok = connect(notifier, &LECentralNotifier::connected,
+ this, &QLowEnergyControllerPrivateOSX::_q_connected);
+ ok = ok && connect(notifier, &LECentralNotifier::disconnected,
+ this, &QLowEnergyControllerPrivateOSX::_q_disconnected);
+ ok = ok && connect(notifier, &LECentralNotifier::serviceDiscoveryFinished,
+ this, &QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished);
+ ok = ok && connect(notifier, &LECentralNotifier::serviceDetailsDiscoveryFinished,
+ this, &QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished);
+ ok = ok && connect(notifier, &LECentralNotifier::characteristicRead,
+ this, &QLowEnergyControllerPrivateOSX::_q_characteristicRead);
+ ok = ok && connect(notifier, &LECentralNotifier::characteristicWritten,
+ this, &QLowEnergyControllerPrivateOSX::_q_characteristicWritten);
+ ok = ok && connect(notifier, &LECentralNotifier::characteristicUpdated,
+ this, &QLowEnergyControllerPrivateOSX::_q_characteristicUpdated);
+ ok = ok && connect(notifier, &LECentralNotifier::descriptorRead,
+ this, &QLowEnergyControllerPrivateOSX::_q_descriptorRead);
+ ok = ok && connect(notifier, &LECentralNotifier::descriptorWritten,
+ this, &QLowEnergyControllerPrivateOSX::_q_descriptorWritten);
+ ok = ok && connect(notifier, &LECentralNotifier::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)));
+
+ if (!ok)
+ notifier->disconnect();
+
+ return ok;
+}
+
QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddress,
QObject *parent)
: QObject(parent),
@@ -909,11 +1029,11 @@ void QLowEnergyController::connectToDevice()
// A memory allocation problem.
if (!osx_d_ptr->isValid())
- return osx_d_ptr->error(UnknownError);
+ return osx_d_ptr->_q_CBCentralManagerError(UnknownError);
// No QBluetoothDeviceInfo provided during construction.
if (osx_d_ptr->deviceUuid.isNull())
- return osx_d_ptr->error(UnknownRemoteDeviceError);
+ return osx_d_ptr->_q_CBCentralManagerError(UnknownRemoteDeviceError);
if (osx_d_ptr->controllerState != UnconnectedState)
return;
@@ -931,18 +1051,28 @@ void QLowEnergyController::disconnectFromDevice()
if (osx_d_ptr->isValid()) {
const ControllerState oldState = osx_d_ptr->controllerState;
- osx_d_ptr->controllerState = ClosingState;
- emit stateChanged(ClosingState);
- osx_d_ptr->invalidateServices();
- [osx_d_ptr->centralManager 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);
+ 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) << Q_FUNC_INFO << "qt LE queue is nil,"
+ << "can not dispatch 'disconnect'";
}
}
}
@@ -996,3 +1126,5 @@ QString QLowEnergyController::errorString() const
}
QT_END_NAMESPACE
+
+#include "moc_qlowenergycontroller_osx_p.cpp"
diff --git a/src/bluetooth/qlowenergycontroller_osx_p.h b/src/bluetooth/qlowenergycontroller_osx_p.h
index 1c1cb1cd..2d1b2e95 100644
--- a/src/bluetooth/qlowenergycontroller_osx_p.h
+++ b/src/bluetooth/qlowenergycontroller_osx_p.h
@@ -49,6 +49,7 @@
#include "osx/osxbtcentralmanager_p.h"
#include "qlowenergycontroller_p.h"
#include "qlowenergycontroller.h"
+#include "osx/osxbtnotifier_p.h"
#include "osx/osxbtutility_p.h"
#include "qbluetoothaddress.h"
#include "qbluetoothuuid.h"
@@ -60,14 +61,22 @@
QT_BEGIN_NAMESPACE
+namespace OSXBluetooth
+{
+
+class LECentralNotifier;
+
+}
+
class QByteArray;
-// The suffix OSX is not the very right, it's also iOS.
-class QLowEnergyControllerPrivateOSX : public QLowEnergyControllerPrivate,
- public OSXBluetooth::CentralManagerDelegate
+// While suffix 'OSX', it's also for iOS.
+class QLowEnergyControllerPrivateOSX : public QLowEnergyControllerPrivate
{
friend class QLowEnergyController;
friend class QLowEnergyService;
+
+ Q_OBJECT
public:
QLowEnergyControllerPrivateOSX(QLowEnergyController *q);
QLowEnergyControllerPrivateOSX(QLowEnergyController *q,
@@ -76,30 +85,25 @@ public:
bool isValid() const;
-private:
- // CentralManagerDelegate:
- void LEnotSupported() Q_DECL_OVERRIDE;
- void connectSuccess() Q_DECL_OVERRIDE;
-
- void serviceDiscoveryFinished(LEServices services) Q_DECL_OVERRIDE;
- void serviceDetailsDiscoveryFinished(LEService service) Q_DECL_OVERRIDE;
- void characteristicReadNotification(QLowEnergyHandle charHandle,
- const QByteArray &value) Q_DECL_OVERRIDE;
- void characteristicWriteNotification(QLowEnergyHandle charHandle,
- const QByteArray &newValue) Q_DECL_OVERRIDE;
- void characteristicUpdateNotification(QLowEnergyHandle charHandle,
- const QByteArray &value) Q_DECL_OVERRIDE;
- void descriptorReadNotification(QLowEnergyHandle descHandle,
- const QByteArray &value) Q_DECL_OVERRIDE;
- void descriptorWriteNotification(QLowEnergyHandle descHandle,
- const QByteArray &newValue) Q_DECL_OVERRIDE;
- void disconnected() Q_DECL_OVERRIDE;
- void error(QLowEnergyController::Error errorCode) Q_DECL_OVERRIDE;
- void error(const QBluetoothUuid &serviceUuid,
- QLowEnergyController::Error errorCode) Q_DECL_OVERRIDE;
- void error(const QBluetoothUuid &serviceUuid,
- QLowEnergyService::ServiceError error) Q_DECL_OVERRIDE;
+private Q_SLOTS:
+ void _q_connected();
+ void _q_disconnected();
+
+ void _q_serviceDiscoveryFinished();
+ void _q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service);
+
+ void _q_characteristicRead(QLowEnergyHandle charHandle, const QByteArray &value);
+ void _q_characteristicWritten(QLowEnergyHandle charHandle, const QByteArray &value);
+ void _q_characteristicUpdated(QLowEnergyHandle charHandle, const QByteArray &value);
+ void _q_descriptorRead(QLowEnergyHandle descHandle, const QByteArray &value);
+ void _q_descriptorWritten(QLowEnergyHandle charHandle, const QByteArray &value);
+ void _q_LEnotSupported();
+ void _q_CBCentralManagerError(QLowEnergyController::Error error);
+ void _q_CBCentralManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyController::Error error);
+ void _q_CBCentralManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyService::ServiceError error);
+
+private:
void connectToDevice();
void discoverServices();
void discoverServiceDetails(const QBluetoothUuid &serviceUuid);
@@ -139,6 +143,7 @@ private:
void setErrorDescription(QLowEnergyController::Error errorCode);
void invalidateServices();
+ bool connectSlots(OSXBluetooth::LECentralNotifier *notifier);
QLowEnergyController *q_ptr;
QBluetoothUuid deviceUuid;
@@ -159,6 +164,7 @@ private:
QLowEnergyController::ControllerState controllerState;
QLowEnergyController::RemoteAddressType addressType;
+ typedef QT_MANGLE_NAMESPACE(OSXBTCentralManager) ObjCCentralManager;
typedef OSXBluetooth::ObjCScopedPointer<ObjCCentralManager> CentralManager;
CentralManager centralManager;