diff options
author | Timur Pocheptsov <Timur.Pocheptsov@digia.com> | 2014-11-21 17:57:36 +0100 |
---|---|---|
committer | Timur Pocheptsov <Timur.Pocheptsov@digia.com> | 2014-11-25 15:29:16 +0100 |
commit | 4c25743910622a4834d8ebb7f3acc59e025a3c41 (patch) | |
tree | 4b2a249207ff980bace12c0d9ea75ac451241f87 /src/bluetooth/qlowenergycontroller_osx.mm | |
parent | 216e28ef1209ee5b28f4ca4f88cfa68a46f60c5a (diff) |
QLowEnergyController - service details discovery (OS X and iOS)
Implement details discovery: characteristics, their values (if any),
descriptors. We have to emulate handles (QLowEnergyHandle) - while
Core Bluetooth internally has the notion of handles, it never exposes
them as a public API.
Change-Id: I09158433ce6835dd34fe8ad47d047212dab59e8e
Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'src/bluetooth/qlowenergycontroller_osx.mm')
-rw-r--r-- | src/bluetooth/qlowenergycontroller_osx.mm | 102 |
1 files changed, 97 insertions, 5 deletions
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm index b576e76f..9a86c392 100644 --- a/src/bluetooth/qlowenergycontroller_osx.mm +++ b/src/bluetooth/qlowenergycontroller_osx.mm @@ -91,6 +91,7 @@ ServicePrivate qt_createLEService(QLowEnergyControllerPrivateOSX *controller, CB if (included) newService->type |= QLowEnergyService::IncludedService; + // TODO: isPrimary is ... always 'NO' - to be investigated. /* #if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_6_0) if (!cbService.isPrimary) { @@ -128,7 +129,8 @@ QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyControl isConnecting(false), lastError(QLowEnergyController::NoError), controllerState(QLowEnergyController::UnconnectedState), - addressType(QLowEnergyController::PublicAddress) + addressType(QLowEnergyController::PublicAddress), + lastValidHandle(0) // 0 == invalid. { // This is the "wrong" constructor - no valid device UUID to connect later. Q_ASSERT_X(q, "QLowEnergyControllerPrivate", "invalid q_ptr (null)"); @@ -149,7 +151,8 @@ QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyControl isConnecting(false), lastError(QLowEnergyController::NoError), controllerState(QLowEnergyController::UnconnectedState), - addressType(QLowEnergyController::PublicAddress) + addressType(QLowEnergyController::PublicAddress), + lastValidHandle(0) // 0 == invalid. { Q_ASSERT_X(q, "QLowEnergyControllerPrivateOSX", "invalid q_ptr (null)"); centralManager.reset([[ObjCCentralManager alloc] initWithDelegate:this]); @@ -284,6 +287,64 @@ void QLowEnergyControllerPrivateOSX::serviceDiscoveryFinished(LEServices service QMetaObject::invokeMethod(q_ptr, "discoveryFinished", Qt::QueuedConnection); } +void QLowEnergyControllerPrivateOSX::serviceDetailsDiscoveryFinished(LEService service) +{ + if (!service) { + qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::serviceDetailsDiscoveryFinished(), " + "invalid service (nil)"; + return; + } + + QT_BT_MAC_AUTORELEASEPOOL; + + const QBluetoothUuid qtUuid(OSXBluetooth::qt_uuid(service.data().UUID)); + if (!discoveredServices.contains(qtUuid)) { + qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::serviceDetailsDiscoveryFinished(), " + "unknown service uuid: " << qtUuid; + return; + } + + ServicePrivate qtService(discoveredServices.value(qtUuid)); + qtService->startHandle = ++lastValidHandle; + // Now we iterate on characteristics and descriptors (if any). + NSArray *const cs = service.data().characteristics; + if (cs && cs.count) { + QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> charList; + // That's not a real handle - just a key into a hash map. + for (CBCharacteristic *c in cs) { + QLowEnergyServicePrivate::CharData newChar = {}; + newChar.uuid = OSXBluetooth::qt_uuid(c.UUID); + // CBCharacteristicProperty enum has the same values as Qt + + // a couple of values above 'extended' we do not support yet - mask them out. + // All other possible enumerators are the same: + const int cbProps = c.properties & 0xff; + newChar.properties = static_cast<QLowEnergyCharacteristic::PropertyTypes>(cbProps); + newChar.value = OSXBluetooth::qt_bytearray(c.value); + newChar.valueHandle = ++lastValidHandle; + + NSArray *const ds = c.descriptors; + if (ds && ds.count) { + QHash<QLowEnergyHandle, QLowEnergyServicePrivate::DescData> descList; + for (CBDescriptor *d in ds) { + QLowEnergyServicePrivate::DescData newDesc = {}; + newDesc.uuid = OSXBluetooth::qt_uuid(d.UUID); + newDesc.value = OSXBluetooth::qt_bytearray(d.value); + descList[++lastValidHandle] = newDesc; + } + + newChar.descriptorList = descList; + } + + charList[newChar.valueHandle] = newChar; + } + + qtService->characteristicList = charList; + } + + qtService->endHandle = lastValidHandle; + qtService->stateChanged(QLowEnergyService::ServiceDiscovered); +} + void QLowEnergyControllerPrivateOSX::disconnected() { controllerState = QLowEnergyController::UnconnectedState; @@ -324,8 +385,16 @@ void QLowEnergyControllerPrivateOSX::error(const QBluetoothUuid &serviceUuid, QLowEnergyController::Error errorCode) { // Errors reported while discovering service details etc. - Q_UNUSED(serviceUuid) - Q_UNUSED(errorCode) + Q_UNUSED(errorCode) // TODO: setError? + + // We failed to discover any characteristics/descriptors. + if (discoveredServices.contains(serviceUuid)) { + ServicePrivate qtService(discoveredServices.value(serviceUuid)); + qtService->stateChanged(QLowEnergyService::InvalidService); + } else { + qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::error(), " + "error reported for unknown service "<<serviceUuid; + } } void QLowEnergyControllerPrivateOSX::connectToDevice() @@ -377,7 +446,29 @@ void QLowEnergyControllerPrivateOSX::discoverServices() void QLowEnergyControllerPrivateOSX::discoverServiceDetails(const QBluetoothUuid &serviceUuid) { - Q_UNUSED(serviceUuid); + Q_ASSERT_X(isValid(), "discoverServiceDetails", "invalid private controller"); + + if (controllerState != QLowEnergyController::DiscoveredState) { + qCWarning(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::discoverServiceDetails(), " + "can not discover service details in the current state, " + "QLowEnergyController::DiscoveredState is expected"; + return; + } + + if (!discoveredServices.contains(serviceUuid)) { + qCWarning(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::discoverServiceDetails(), " + "unknown service: " << serviceUuid; + return; + } + + ServicePrivate qtService(discoveredServices.value(serviceUuid)); + if ([centralManager discoverServiceDetails:serviceUuid]) { + qtService->stateChanged(QLowEnergyService::DiscoveringServices); + } else { + // The error is returned by CentralManager - no + // service with a given UUID found on a peripheral. + qtService->stateChanged(QLowEnergyService::InvalidService); + } } void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::Error errorCode) @@ -413,6 +504,7 @@ void QLowEnergyControllerPrivateOSX::invalidateServices() service->setState(QLowEnergyService::InvalidService); } + lastValidHandle = 0; discoveredServices.clear(); } |