diff options
Diffstat (limited to 'src/bluetooth/osx/osxbtcentralmanager.mm')
-rw-r--r-- | src/bluetooth/osx/osxbtcentralmanager.mm | 462 |
1 files changed, 223 insertions, 239 deletions
diff --git a/src/bluetooth/osx/osxbtcentralmanager.mm b/src/bluetooth/osx/osxbtcentralmanager.mm index 73d9d5bc..2fc74e36 100644 --- a/src/bluetooth/osx/osxbtcentralmanager.mm +++ b/src/bluetooth/osx/osxbtcentralmanager.mm @@ -65,7 +65,7 @@ NSUInteger qt_countGATTEntries(CBService *service) // No checks if NSUInteger is big enough :) // Let's assume such number of entries is not possible :) - Q_ASSERT_X(service, "qt_countGATTEntries", "invalid service (nil)"); + Q_ASSERT_X(service, Q_FUNC_INFO, "invalid service (nil)"); QT_BT_MAC_AUTORELEASEPOOL; @@ -112,7 +112,7 @@ using namespace QT_NAMESPACE; startingFrom:(CBDescriptor *)descriptor; - (CBDescriptor *)descriptor:(const QBluetoothUuid &)dUuid forCharacteristic:(CBCharacteristic *)ch; -// TODO: check _what_ exactly I have to reset ... +- (bool)cacheWriteValue:(const QByteArray &)value for:(NSObject *)obj; - (void)reset; @end @@ -121,7 +121,7 @@ using namespace QT_NAMESPACE; - (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::CentralManagerDelegate *)aDelegate { - Q_ASSERT_X(aDelegate, "-initWithDelegate:", "invalid delegate (null)"); + Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)"); if (self = [super init]) { manager = nil; @@ -159,8 +159,8 @@ using namespace QT_NAMESPACE; - (QLowEnergyController::Error)connectToDevice:(const QBluetoothUuid &)aDeviceUuid { - Q_ASSERT_X(managerState == OSXBluetooth::CentralManagerIdle, "-connectToDevice", - "invalid state"); // QLowEnergyController connects only if in UnconnectedState. + Q_ASSERT_X(managerState == OSXBluetooth::CentralManagerIdle, + Q_FUNC_INFO, "invalid state"); deviceUuid = aDeviceUuid; @@ -169,7 +169,7 @@ using namespace QT_NAMESPACE; manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; if (!manager) { managerState = OSXBluetooth::CentralManagerIdle; - qCWarning(QT_BT_OSX) << "-connectToDevice:, failed to allocate a " + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to allocate a " "central manager"; return QLowEnergyController::ConnectionError; } @@ -181,12 +181,12 @@ using namespace QT_NAMESPACE; - (QLowEnergyController::Error)connectToDevice { - Q_ASSERT_X(manager, "-connectToDevice", "invalid central manager (nil)"); + Q_ASSERT_X(manager, Q_FUNC_INFO, "invalid central manager (nil)"); Q_ASSERT_X(managerState == OSXBluetooth::CentralManagerIdle, - "-connectToDevice", "invalid state"); + Q_FUNC_INFO, "invalid state"); if ([self isConnected]) { - qCDebug(QT_BT_OSX) << "-connectToDevice, already connected"; + qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "already connected"; delegate->connectSuccess(); return QLowEnergyController::NoError; } else if (peripheral) { @@ -201,7 +201,7 @@ using namespace QT_NAMESPACE; // Retrieve a peripheral first ... ObjCScopedPointer<NSMutableArray> uuids([[NSMutableArray alloc] init]); if (!uuids) { - qCWarning(QT_BT_OSX) << "-connectToDevice, failed to allocate identifiers"; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to allocate identifiers"; return QLowEnergyController::ConnectionError; } @@ -212,7 +212,7 @@ using namespace QT_NAMESPACE; std::copy(qtUuidData.data, qtUuidData.data + 16, uuidData); const ObjCScopedPointer<NSUUID> nsUuid([[NSUUID alloc] initWithUUIDBytes:uuidData]); if (!nsUuid) { - qCWarning(QT_BT_OSX) << "-connectToDevice, failed to allocate NSUUID identifier"; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to allocate NSUUID identifier"; return QLowEnergyController::ConnectionError; } @@ -222,7 +222,7 @@ using namespace QT_NAMESPACE; QT_BT_MAC_AUTORELEASEPOOL; NSArray *const peripherals = [manager retrievePeripheralsWithIdentifiers:uuids]; if (!peripherals || peripherals.count != 1) { - qCWarning(QT_BT_OSX) << "-connectToDevice, failed to retrive a peripheral"; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to retrive a peripheral"; return QLowEnergyController::UnknownRemoteDeviceError; } @@ -233,7 +233,7 @@ using namespace QT_NAMESPACE; #else OSXBluetooth::CFStrongReference<CFUUIDRef> cfUuid(OSXBluetooth::cf_uuid(deviceUuid)); if (!cfUuid) { - qCWarning(QT_BT_OSX) << "-connectToDevice:, failed to create CFUUID object"; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to create CFUUID object"; return QLowEnergyController::ConnectionError; } // TODO: With ARC this cast will be illegal: @@ -248,17 +248,17 @@ using namespace QT_NAMESPACE; - (void)connectToPeripheral { - Q_ASSERT_X(manager, "-connectToPeripheral", "invalid central manager (nil)"); - Q_ASSERT_X(peripheral, "-connectToPeripheral", "invalid peripheral (nil)"); + Q_ASSERT_X(manager, Q_FUNC_INFO, "invalid central manager (nil)"); + Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)"); Q_ASSERT_X(managerState == OSXBluetooth::CentralManagerIdle, - "-connectToPeripheral", "invalid state"); + Q_FUNC_INFO, "invalid state"); // The state is still the same - connecting. if ([self isConnected]) { - qCDebug(QT_BT_OSX) << "-connectToPeripheral, already connected"; + qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "already connected"; delegate->connectSuccess(); } else { - qCDebug(QT_BT_OSX) << "-connectToPeripheral, trying to connect"; + qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "trying to connect"; managerState = OSXBluetooth::CentralManagerConnecting; [manager connectPeripheral:peripheral options:nil]; } @@ -300,9 +300,9 @@ using namespace QT_NAMESPACE; - (void)discoverServices { - Q_ASSERT_X(peripheral, "-discoverServices", "invalid peripheral (nil)"); + Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)"); Q_ASSERT_X(managerState == OSXBluetooth::CentralManagerIdle, - "-discoverServices", "invalid state"); + Q_FUNC_INFO, "invalid state"); // From Apple's docs: // @@ -321,17 +321,17 @@ using namespace QT_NAMESPACE; { using namespace OSXBluetooth; - Q_ASSERT_X(managerState == CentralManagerIdle, "-discoverIncludedServices", + Q_ASSERT_X(managerState == CentralManagerIdle, Q_FUNC_INFO, "invalid state"); - Q_ASSERT_X(manager, "-discoverIncludedServices", "invalid manager (nil)"); - Q_ASSERT_X(peripheral, "-discoverIncludedServices", "invalid peripheral (nil)"); + 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 ... // A peripheral without any services at all. - Q_ASSERT_X(delegate, "-discoverIncludedServices", + Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); delegate->serviceDiscoveryFinished(ObjCStrongReference<NSArray>()); } else { @@ -356,16 +356,13 @@ using namespace QT_NAMESPACE; using namespace OSXBluetooth; - Q_ASSERT_X(managerState != CentralManagerUpdating, "-discoverServiceDetails:", - "invalid state"); - Q_ASSERT_X(!serviceUuid.isNull(), "-discoverServiceDetails:", - "invalid service UUID"); - Q_ASSERT_X(peripheral, "-discoverServiceDetailsl:", - "invalid peripheral (nil)"); + Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO, "invalid state"); + Q_ASSERT_X(!serviceUuid.isNull(), Q_FUNC_INFO, "invalid service UUID"); + Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)"); if (servicesToDiscoverDetails.contains(serviceUuid)) { - qCWarning(QT_BT_OSX) << "-discoverServiceDetails: " - "already discovering for " << serviceUuid; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO <<"already discovering for " + << serviceUuid; return true; } @@ -377,8 +374,8 @@ using namespace QT_NAMESPACE; return true; } - qCWarning(QT_BT_OSX) << "-discoverServiceDetails:, invalid service - " - "unknown uuid " << serviceUuid; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown service uuid " + << serviceUuid; return false; } @@ -387,17 +384,17 @@ using namespace QT_NAMESPACE; { // This method does not change 'managerState', we can // have several 'detail discoveries' active. - Q_ASSERT_X(service, "-readCharacteristics:", "invalid service (nil)"); + Q_ASSERT_X(service, Q_FUNC_INFO, "invalid service (nil)"); using namespace OSXBluetooth; QT_BT_MAC_AUTORELEASEPOOL; - Q_ASSERT_X(managerState != CentralManagerUpdating, "-readCharacteristics:", + Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO, "invalid state"); - Q_ASSERT_X(manager, "-readCharacteristics:", "invalid manager (nil)"); - Q_ASSERT_X(peripheral, "-readCharacteristics:", "invalid peripheral (nil)"); - Q_ASSERT_X(delegate, "-readCharacteristics:", "invalid delegate (null)"); + 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]; @@ -416,16 +413,16 @@ using namespace QT_NAMESPACE; { // This method does not change 'managerState', we can have // several discoveries active. - Q_ASSERT_X(service, "-discoverDescriptors:", "invalid service (nil)"); + Q_ASSERT_X(service, Q_FUNC_INFO, "invalid service (nil)"); using namespace OSXBluetooth; QT_BT_MAC_AUTORELEASEPOOL; - Q_ASSERT_X(managerState != CentralManagerUpdating, "-discoverDescriptors", - "invalid state"); - Q_ASSERT_X(manager, "-discoverDescriptors:", "invalid manager (nil)"); - Q_ASSERT_X(peripheral, "-discoverDescriptors:", "invalid peripheral (nil)"); + 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)"); if (!service.characteristics || !service.characteristics.count) { [self serviceDetailsDiscoveryFinished:service]; @@ -463,10 +460,8 @@ using namespace QT_NAMESPACE; - (void)serviceDetailsDiscoveryFinished:(CBService *)service { // - Q_ASSERT_X(service, "-serviceDetailsDiscoveryFinished:", - "invalid service (nil)"); - Q_ASSERT_X(delegate, "-serviceDetailsDiscoveryFinished:", - "invalid delegate (null)"); + Q_ASSERT_X(service, Q_FUNC_INFO, "invalid service (nil)"); + Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); using namespace OSXBluetooth; @@ -476,16 +471,13 @@ using namespace QT_NAMESPACE; servicesToDiscoverDetails.removeAll(serviceUuid); const NSUInteger nHandles = qt_countGATTEntries(service); - Q_ASSERT_X(nHandles, "-serviceDetailsDiscoveryFinished:", - "unexpected number of GATT entires"); + Q_ASSERT_X(nHandles, Q_FUNC_INFO, "unexpected number of GATT entires"); const QLowEnergyHandle maxHandle = std::numeric_limits<QLowEnergyHandle>::max(); if (nHandles >= maxHandle || lastValidHandle > maxHandle - nHandles) { // Well, that's unlikely :) But we must be sure. - qCWarning(QT_BT_OSX) << "-serviceDetailsDiscoveryFinished:", - "can not allocate more handles"; - // TODO: add more 'error' functions not to use fake handles (0)? - delegate->error(serviceUuid, 0, QLowEnergyService::OperationError); + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "can not allocate more handles"; + delegate->error(serviceUuid, QLowEnergyService::OperationError); return; } @@ -530,6 +522,11 @@ using namespace QT_NAMESPACE; newDesc.uuid = qt_uuid(d.UUID); newDesc.value = qt_bytearray(static_cast<NSObject *>(d.value)); descList[lastValidHandle] = newDesc; + // Check, if it's client characteristic configuration descriptor: + if (newDesc.uuid == QBluetoothUuid::ClientCharacteristicConfiguration) { + if (newDesc.value.size() && (newDesc.value[0] & 3)) + [peripheral setNotifyValue:YES forCharacteristic:c]; + } } newChar.descriptorList = descList; @@ -551,8 +548,7 @@ using namespace QT_NAMESPACE; { using namespace OSXBluetooth; - Q_ASSERT_X(peripheral, "-performNextWriteRequest", - "invalid peripheral (nil)"); + Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)"); if (writePending || !writeQueue.size()) return; @@ -561,59 +557,67 @@ using namespace QT_NAMESPACE; if (request.isDescriptor) { if (!descMap.contains(request.handle)) { - qCWarning(QT_BT_OSX) << "-performNextWriteRequest, descriptor with " - "handle: " << request.handle << " not found"; - [self performNextWriteRequest]; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "handle: " + << request.handle << " not found"; + return [self performNextWriteRequest]; } - CBDescriptor *const d = descMap[request.handle]; + CBDescriptor *const descriptor = descMap[request.handle]; ObjCStrongReference<NSData> data(data_from_bytearray(request.value)); if (!data) { // Even if qtData.size() == 0, we still need NSData object. - qCWarning(QT_BT_OSX) << "-write:descHandle:, failed " + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed " "to allocate an NSData object"; - [self performNextWriteRequest]; + return [self performNextWriteRequest]; } + if (![self cacheWriteValue:request.value for:descriptor]) + return [self performNextWriteRequest]; + writePending = true; - return [peripheral writeValue:data.data() forDescriptor:d]; + return [peripheral writeValue:data.data() forDescriptor:descriptor]; } else { - CBCharacteristic *const ch = charMap[request.handle]; - Q_ASSERT_X(ch, "-performNextWriteRequest", "invalid characteristic (nil)"); + if (!charMap.contains(request.handle)) { + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "characteristic with " + "handle: " << request.handle << " not found"; + return [self performNextWriteRequest]; + } + + CBCharacteristic *const characteristic = charMap[request.handle]; if (request.isClientConfiguration) { - if (valuesToWrite.remove(request.handle)) { - // It can happen if something went wrong - we - // tried to set notification value and never received - // a callback. - qCDebug(QT_BT_OSX) << "-performNextWriteRequest:, " - "valuesToWrite already contains " - "a value for a given client configuration " - "descriptor, replacing it"; - } - // We save the original value to report it later ... - valuesToWrite[request.handle] = request.value; - const bool enable = request.value[0] & 3; + const QBluetoothUuid qtUuid(QBluetoothUuid::ClientCharacteristicConfiguration); + CBDescriptor *const descriptor = [self descriptor:qtUuid forCharacteristic:characteristic]; + Q_ASSERT_X(descriptor, Q_FUNC_INFO, "no client characteristic " + "configuration descriptor found"); + + if (![self cacheWriteValue:request.value for:descriptor]) + return [self performNextWriteRequest]; + + bool enable = false; + if (request.value.size()) + enable = request.value[0] & 3; + writePending = true; - [peripheral setNotifyValue:enable forCharacteristic:ch]; + [peripheral setNotifyValue:enable forCharacteristic:characteristic]; } else { ObjCStrongReference<NSData> data(data_from_bytearray(request.value)); if (!data) { // Even if qtData.size() == 0, we still need NSData object. - qCWarning(QT_BT_OSX) << "-performNextWriteRequest, " - "failed to allocate NSData object"; - [self performNextWriteRequest]; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to allocate NSData object"; + return [self performNextWriteRequest]; } // TODO: check what happens if I'm using NSData with length 0. if (request.withResponse) { - NSLog(@"trying to write %@", data.data()); - NSLog(@"initial value: %@", ch.value); + if (![self cacheWriteValue:request.value for:characteristic]) + return [self performNextWriteRequest]; + writePending = true; - [peripheral writeValue:data.data() forCharacteristic:ch + [peripheral writeValue:data.data() forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse]; } else { - [peripheral writeValue:data.data() forCharacteristic:ch + [peripheral writeValue:data.data() forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse]; [self performNextWriteRequest]; } @@ -624,12 +628,22 @@ using namespace QT_NAMESPACE; - (bool)setNotifyValue:(const QByteArray &)value forCharacteristic:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle { - Q_ASSERT_X(charHandle, "-setNotifyValue:forCharacteristic:", - "invalid characteristic handle (0)"); + 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) << "-setNotifyValue:forCharacteristic:, " - "unknown characteristic handle " << charHandle; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO + << "unknown characteristic handle " << charHandle; + return false; + } + + // At the moment we call setNotifyValue _only_ from 'writeDescriptor'; + // from Qt's API POV it's a descriptor write oprtation 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; } @@ -653,13 +667,12 @@ using namespace QT_NAMESPACE; { using namespace OSXBluetooth; - Q_ASSERT_X(charHandle, "-write:charHandle:withResponse:", "invalid characteristic handle (0)"); + Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle (0)"); QT_BT_MAC_AUTORELEASEPOOL; if (!charMap.contains(charHandle)) { - qCWarning(QT_BT_OSX) << "-write:charHandle:withResponse:, " - "characteristic: " << charHandle << " not found"; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "characteristic: " << charHandle << " not found"; return false; } @@ -675,39 +688,17 @@ using namespace QT_NAMESPACE; // write actually. If I have any problems with the order later, // I'll use performSelector afterDelay with some delay. return true; -/* - // Write without responce is not serialized - no way I can - // know about the write operation success/failure - and - // I will never perform the next write. - CBCharacteristic *const ch = charMap[charHandle]; - Q_ASSERT_X(ch, "-write:charHandle:withResponse:", "invalid characteristic (nil) for a give handle"); - Q_ASSERT_X(peripheral, "-write:charHandle:withResponse:", "invalid peripheral (nil)"); - - ObjCStrongReference<NSData> data(data_from_bytearray(value)); - if (!data) { - // Even if qtData.size() == 0, we still need NSData object. - qCWarning(QT_BT_OSX) << "-write:charHandle:withResponse:, " - "failed to allocate NSData object"; - return false; - } - - // TODO: check what happens if I'm using NSData with length 0. - [peripheral writeValue:data.data() forCharacteristic:ch - type:CBCharacteristicWriteWithoutResponse]; - - return true;*/ } - (bool)write:(const QByteArray &)value descHandle:(QLowEnergyHandle)descHandle { using namespace OSXBluetooth; - Q_ASSERT_X(descHandle, "-write:descHandle:", - "invalid descriptor handle (0)"); + Q_ASSERT_X(descHandle, Q_FUNC_INFO, "invalid descriptor handle (0)"); if (!descMap.contains(descHandle)) { - qCWarning(QT_BT_OSX) << "-write:descHandle:, descriptor with " - "handle: " << descHandle << " not found"; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "handle: " + << descHandle << " not found"; return false; } @@ -730,10 +721,8 @@ using namespace QT_NAMESPACE; { using namespace OSXBluetooth; - Q_ASSERT_X(!qtUuid.isNull(), "-serviceForUUID:", - "invalid uuid"); - Q_ASSERT_X(peripheral, "-serviceForUUID:", - "invalid peripherla (nil)"); + Q_ASSERT_X(!qtUuid.isNull(), Q_FUNC_INFO, "invalid uuid"); + Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)"); ObjCStrongReference<NSMutableArray> toVisit([NSMutableArray arrayWithArray:peripheral.services], true); ObjCStrongReference<NSMutableArray> toVisitNext([[NSMutableArray alloc] init], false); @@ -763,14 +752,10 @@ using namespace QT_NAMESPACE; - (CBCharacteristic *)nextCharacteristicForService:(CBService*)service startingFrom:(CBCharacteristic *)characteristic { - Q_ASSERT_X(service, "-nextCharacteristicForService:startingFrom:", - "invalid service (nil)"); - Q_ASSERT_X(characteristic, "-nextCharacteristicForService:startingFrom:", - "invalid characteristic (nil)"); - Q_ASSERT_X(service.characteristics, "-nextCharacteristicForService:startingFrom:", - "invalid service"); - Q_ASSERT_X(service.characteristics.count, "-nextCharacteristicForService:startingFrom:", - "invalid service"); + Q_ASSERT_X(service, Q_FUNC_INFO, "invalid service (nil)"); + Q_ASSERT_X(characteristic, Q_FUNC_INFO, "invalid characteristic (nil)"); + Q_ASSERT_X(service.characteristics, Q_FUNC_INFO, "invalid service"); + Q_ASSERT_X(service.characteristics.count, Q_FUNC_INFO, "invalid service"); QT_BT_MAC_AUTORELEASEPOOL; @@ -790,8 +775,7 @@ using namespace QT_NAMESPACE; } } - Q_ASSERT_X([cs objectAtIndex:0] == characteristic, - "-nextCharacteristicForService:startingFrom:", + Q_ASSERT_X([cs objectAtIndex:0] == characteristic, Q_FUNC_INFO, "characteristic was not found in service.characteristics"); return [cs objectAtIndex:1]; @@ -801,14 +785,10 @@ using namespace QT_NAMESPACE; startingFrom:(CBCharacteristic *)characteristic properties:(CBCharacteristicProperties)properties { - Q_ASSERT_X(service, "-nextCharacteristicForService:startingFrom:properties:", - "invalid service (nil)"); - Q_ASSERT_X(characteristic, "-nextCharacteristicForService:startingFrom:properties:", - "invalid characteristic (nil)"); - Q_ASSERT_X(service.characteristics, "-nextCharacteristicForService:startingFrom:properties:", - "invalid service"); - Q_ASSERT_X(service.characteristics.count, "-nextCharacteristicForService:startingFrom:properties:", - "invalid service"); + Q_ASSERT_X(service, Q_FUNC_INFO, "invalid service (nil)"); + Q_ASSERT_X(characteristic, Q_FUNC_INFO, "invalid characteristic (nil)"); + Q_ASSERT_X(service.characteristics, Q_FUNC_INFO, "invalid service"); + Q_ASSERT_X(service.characteristics.count, Q_FUNC_INFO, "invalid service"); QT_BT_MAC_AUTORELEASEPOOL; @@ -832,8 +812,7 @@ using namespace QT_NAMESPACE; } if (!index) { - Q_ASSERT_X([cs objectAtIndex:0] == characteristic, - "-nextCharacteristicForService:startingFrom:properties:", + Q_ASSERT_X([cs objectAtIndex:0] == characteristic, Q_FUNC_INFO, "characteristic not found in service.characteristics"); index = 1; } @@ -850,16 +829,10 @@ using namespace QT_NAMESPACE; - (CBDescriptor *)nextDescriptorForCharacteristic:(CBCharacteristic *)characteristic startingFrom:(CBDescriptor *)descriptor { - Q_ASSERT_X(characteristic, "-nextDescriptorForCharacteristic:startingFrom:", - "invalid characteristic (nil)"); - Q_ASSERT_X(descriptor, "-nextDescriptorForCharacteristic:startingFrom:", - "invalid descriptor (nil)"); - Q_ASSERT_X(characteristic.descriptors, - "-nextDescriptorForCharacteristic:startingFrom:", - "invalid characteristic"); - Q_ASSERT_X(characteristic.descriptors.count, - "-nextDescriptorForCharacteristic:startingFrom:", - "invalid characteristic"); + Q_ASSERT_X(characteristic, Q_FUNC_INFO, "invalid characteristic (nil)"); + Q_ASSERT_X(descriptor, Q_FUNC_INFO, "invalid descriptor (nil)"); + Q_ASSERT_X(characteristic.descriptors, Q_FUNC_INFO, "invalid characteristic"); + Q_ASSERT_X(characteristic.descriptors.count, Q_FUNC_INFO, "invalid characteristic"); QT_BT_MAC_AUTORELEASEPOOL; @@ -876,8 +849,7 @@ using namespace QT_NAMESPACE; } } - Q_ASSERT_X([ds objectAtIndex:0] == descriptor, - "-nextDescriptorForCharacteristic:startingFrom:", + Q_ASSERT_X([ds objectAtIndex:0] == descriptor, Q_FUNC_INFO, "descriptor was not found in characteristic.descriptors"); return [ds objectAtIndex:1]; @@ -905,6 +877,41 @@ using namespace QT_NAMESPACE; return descriptor; } +- (bool)cacheWriteValue:(const QByteArray &)value for:(NSObject *)obj +{ + Q_ASSERT_X(obj, Q_FUNC_INFO, "invalid object (nil)"); + + if ([obj isKindOfClass:[CBCharacteristic class]]) { + CBCharacteristic *const ch = static_cast<CBCharacteristic *>(obj); + if (!charMap.key(ch)) { + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unexpected " + "characteristic, no handle found"; + return false; + } + } else if ([obj isKindOfClass:[CBDescriptor class]]) { + CBDescriptor *const d = static_cast<CBDescriptor *>(obj); + if (!descMap.key(d)) { + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unexpected " + "descriptor, no handle found"; + return false; + } + } else { + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "invalid object, " + "characteristic or descriptor required"; + return false; + } + + if (valuesToWrite.contains(obj)) { + // It can be a result of some previous errors - for example, + // we never got a callback from a previous write. + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "already has a cached value for this " + "object, the value will be replaced"; + } + + valuesToWrite[obj] = value; + return true; +} + - (void)reset { writePending = false; @@ -923,7 +930,7 @@ using namespace QT_NAMESPACE; - (void)centralManagerDidUpdateState:(CBCentralManager *)central { - Q_ASSERT_X(delegate, "-centralManagerDidUpdateState:", "invalid delegate (null)"); + Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); using namespace OSXBluetooth; @@ -983,7 +990,7 @@ using namespace QT_NAMESPACE; } } else { // We actually handled all known states, but .. Core Bluetooth can change? - Q_ASSERT_X(0, "-centralManagerDidUpdateState:", "invalid centra's state"); + Q_ASSERT_X(0, Q_FUNC_INFO, "invalid centra's state"); } } @@ -992,7 +999,7 @@ using namespace QT_NAMESPACE; Q_UNUSED(central) // This method is required for iOS before 7.0 and OS X below 10.9. - Q_ASSERT_X(manager, "-centralManager:didRetrivePeripherals:", "invalid central manager (nil)"); + Q_ASSERT_X(manager, Q_FUNC_INFO, "invalid central manager (nil)"); if (managerState != OSXBluetooth::CentralManagerConnecting) { // Canceled by calling -disconnectFromDevice method. @@ -1002,11 +1009,10 @@ using namespace QT_NAMESPACE; managerState = OSXBluetooth::CentralManagerIdle; if (!peripherals || peripherals.count != 1) { - Q_ASSERT_X(delegate, "-centralManager:didRetrievePeripherals:", + Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); - qCDebug(QT_BT_OSX) << "-centralManager:didRetrievePeripherals:, " - "unexpected number of peripherals (!= 1)"; + qCDebug(QT_BT_OSX) << Q_FUNC_INFO <<"unexpected number of peripherals (!= 1)"; delegate->error(QLowEnergyController::UnknownRemoteDeviceError); } else { @@ -1020,8 +1026,7 @@ using namespace QT_NAMESPACE; Q_UNUSED(central) Q_UNUSED(aPeripheral) - Q_ASSERT_X(delegate, "-centralManager:didConnectPeripheral:", - "invalid delegate (null)"); + Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); if (managerState != OSXBluetooth::CentralManagerConnecting) { // We called cancel but before disconnected, managed to connect? @@ -1039,8 +1044,7 @@ using namespace QT_NAMESPACE; Q_UNUSED(aPeripheral) Q_UNUSED(error) - Q_ASSERT_X(delegate, "-centralManager:didFailToConnectPeripheral:", - "invalid delegate (null)"); + Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); if (managerState != OSXBluetooth::CentralManagerConnecting) { // Canceled already. @@ -1058,16 +1062,14 @@ using namespace QT_NAMESPACE; Q_UNUSED(central) Q_UNUSED(aPeripheral) - Q_ASSERT_X(delegate, "-centralManager:didDisconnectPeripheral:error:", - "invalid delegate (null)"); + 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) << "-centralManager:didDisconnectPeripheral:, " - "failed to disconnect"; + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to disconnect"; // TODO: instead of 'unknown' - .. ? delegate->error(QLowEnergyController::UnknownRemoteDeviceError); } else { @@ -1091,7 +1093,7 @@ using namespace QT_NAMESPACE; if (error) { // NSLog, not qCDebug/Warning - to print the error. - NSLog(@"-peripheral:didDiscoverServices:, failed with error %@", error); + NSLog(@"%s failed with error %@", Q_FUNC_INFO, error); // TODO: better error mapping required. delegate->error(QLowEnergyController::UnknownError); } else { @@ -1114,18 +1116,16 @@ using namespace QT_NAMESPACE; QT_BT_MAC_AUTORELEASEPOOL; - Q_ASSERT_X(delegate, "-peripheral:didDiscoverIncludedServicesForService:", - "invalid delegate (null)"); - Q_ASSERT_X(peripheral, "-peripheral:didDiscoverIncludedServicesForService:", - "invalid peripheral (nil)"); + 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(@"-peripheral:didDiscoverIncludedServicesForService:, finished with error %@ for service %@", - error, service.UUID); + NSLog(@"%s: finished with error %@ for service %@", + Q_FUNC_INFO, error, service.UUID); } else if (service.includedServices && service.includedServices.count) { // Now we have even more services to do included services discovery ... if (!servicesToVisitNext) @@ -1186,15 +1186,13 @@ using namespace QT_NAMESPACE; using namespace OSXBluetooth; Q_ASSERT_X(managerState != CentralManagerUpdating, - "-peripheral:didDiscoverCharacteristicsForService:", - "invalid state"); - Q_ASSERT_X(delegate, "-peripheral:didDiscoverCharacteristicsForService:", + Q_FUNC_INFO, "invalid state"); + Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); if (error) { // NSLog to show the actual NSError (can contain something interesting). - NSLog(@"-peripheral:didDiscoverCharacteristicsForService:error, failed with error: %@", - error); + 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); @@ -1210,11 +1208,8 @@ using namespace QT_NAMESPACE; using namespace OSXBluetooth; - Q_ASSERT_X(managerState != CentralManagerUpdating, - "-peripheral:didUpdateValueForCharacteristic:error:", - "invalid state"); - Q_ASSERT_X(peripheral, "-peripheral:didUpdateValueForCharacteristic:error:", - "invalid peripheral (nil)"); + Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO, "invalid state"); + Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)"); QT_BT_MAC_AUTORELEASEPOOL; // First, let's check if we're discovering a service details now. @@ -1224,9 +1219,7 @@ using namespace QT_NAMESPACE; if (error) { // Use NSLog, not qCDebug/qCWarning to log the actual error. - NSLog(@"-peripheral:didUpdateValueForCharacteristic:error:, failed with error %@", - error); - + NSLog(@"%s failed with error %@", Q_FUNC_INFO, error); if (!isDetailsDiscovery) { // TODO: this can be something else in a future (if needed at all). return; @@ -1250,13 +1243,12 @@ using namespace QT_NAMESPACE; // updated values ... // TODO: this must be properly tested. if (!chHandle) { - qCCritical(QT_BT_OSX) << "-peripheral:didUpdateValueForCharacteristic:error:, " - "unexpected update notification, no characteristic handle found"; + qCCritical(QT_BT_OSX) << Q_FUNC_INFO << "unexpected update notification, " + "no characteristic handle found"; return; } - Q_ASSERT_X(delegate, "-peripheral:didUpdateValueForCharacteristic:error:", - "invalid delegate (null)"); + Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); delegate->characteristicUpdateNotification(chHandle, qt_bytearray(characteristic.value)); } @@ -1276,8 +1268,7 @@ using namespace QT_NAMESPACE; if (error) { // Log the error using NSLog: - NSLog(@"-peripheral:didDiscoverDescriptorsForCharacteristic:error:, failed with error %@", - error); + NSLog(@"%s failed with error %@", Q_FUNC_INFO, error); // Probably, we can continue though ... } @@ -1296,8 +1287,7 @@ using namespace QT_NAMESPACE; { Q_UNUSED(aPeripheral) - Q_ASSERT_X(peripheral, "-peripheral:didUpdateValueForDescriptor:error:", - "invalid peripheral (nil)"); + Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)"); QT_BT_MAC_AUTORELEASEPOOL; @@ -1309,8 +1299,7 @@ using namespace QT_NAMESPACE; if (error) { // NSLog to log the actual error ... - NSLog(@"-peripheral:didUpdateValueForDescriptor:error:, failed with error %@", - error); + NSLog(@"%s failed with error %@", Q_FUNC_INFO, error); if (!isDetailsDiscovery) { // TODO: probably will be required in a future. return; @@ -1365,24 +1354,26 @@ using namespace QT_NAMESPACE; writePending = false; - Q_ASSERT_X(delegate, "-peripheral:didWriteValueForCharacteristic:error", - "invalid delegate (null)"); + 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())); + if (!valuesToWrite.remove(characteristic)) { + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no updated value found" + " for characteristic"; + } if (error) { // Use NSLog to log the actual error: - NSLog(@"-peripheral:didWriteValueForCharacteristic:error:, failed with error: %@", - error); - // TODO: no char handle at the moment, have to change to char index instead - // and calculate the right handle in the LE controller. - delegate->error(qt_uuid(characteristic.service.UUID), 0, + NSLog(@"%s failed with error %@", Q_FUNC_INFO, error); + delegate->error(qt_uuid(characteristic.service.UUID), QLowEnergyService::CharacteristicWriteError); } else { // Keys are unique. const QLowEnergyHandle cHandle = charMap.key(characteristic); - Q_ASSERT_X(cHandle, "-peripheral:didWriteValueForCharacteristic:error", - "invalid handle, not found in the characteristics map"); - NSLog(@"characteristic written, the value is %@", characteristic.value); - delegate->characteristicWriteNotification(cHandle, qt_bytearray(characteristic.value)); + Q_ASSERT_X(cHandle, Q_FUNC_INFO, "invalid handle, not found in the characteristics map"); + delegate->characteristicWriteNotification(cHandle, valueToReport); } [self performNextWriteRequest]; @@ -1394,6 +1385,8 @@ using namespace QT_NAMESPACE; { Q_UNUSED(aPeripheral) + Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); + using namespace OSXBluetooth; QT_BT_MAC_AUTORELEASEPOOL; @@ -1402,21 +1395,21 @@ using namespace QT_NAMESPACE; 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(@"-peripheral:didWriteValueForDescriptor:error:, failed with error %@", - error); - // TODO: this error function is not good at all - it takes charHandle, - // which is noop at the moment and ... we actually work with a descriptor handle - // here. - delegate->error(qt_uuid(descriptor.characteristic.service.UUID), 0, + NSLog(@"%s failed with error %@", Q_FUNC_INFO, error); + delegate->error(qt_uuid(descriptor.characteristic.service.UUID), QLowEnergyService::DescriptorWriteError); } else { - // We know that keys are unique, so we can find a key for a given descriptor. const QLowEnergyHandle dHandle = descMap.key(descriptor); - Q_ASSERT_X(dHandle, "-peripheral:didWriteValueForDescriptor:error:", - "invalid descriptor, not found in the descriptors map"); - delegate->descriptorWriteNotification(dHandle, qt_bytearray(static_cast<NSObject *>(descriptor.value))); + Q_ASSERT_X(dHandle, Q_FUNC_INFO, + "descriptor not found in the descriptors map"); + delegate->descriptorWriteNotification(dHandle, valueToReport); } [self performNextWriteRequest]; @@ -1434,39 +1427,30 @@ using namespace QT_NAMESPACE; writePending = false; - Q_ASSERT_X(delegate, "-peripheral:didUpdateNotificationStateForCharacteristic:", - "invalid delegate (nil)"); + Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (nil)"); const QBluetoothUuid qtUuid(QBluetoothUuid::ClientCharacteristicConfiguration); CBDescriptor *const descriptor = [self descriptor:qtUuid forCharacteristic:characteristic]; - const QLowEnergyHandle dHandle = descMap.key(descriptor);// 0 if descriptor is nil or unknown. - const QByteArray valueToReport(valuesToWrite.value(dHandle, QByteArray())); - - if (!valuesToWrite.remove(dHandle)) { - // In future it can be a special case: we can, in principle, - // set notify value on a characteristic without 'writeDescriptor'. - // It can also be some error. Right now we report it as error, can change. - qCWarning(QT_BT_OSX) << "-peripheral:didUpdateNotificationStateForCharacteristic:, " - "setNotifyValue called, but no client characteristic descriptor " - "found or no writeDescriptor call"; - } + const QByteArray valueToReport(valuesToWrite.value(descriptor, QByteArray())); + const int nRemoved = valuesToWrite.remove(descriptor); if (error) { // NSLog to log the actual NSError: - NSLog(@"-peripheral:didUpdateNotificationStateForCharacteristic:, failed with error %@", - error); - delegate->error(qt_uuid(characteristic.service.UUID), 0, + 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 (!valueToReport.isNull()) { + if (nRemoved) { + const QLowEnergyHandle dHandle = descMap.key(descriptor); delegate->descriptorWriteNotification(dHandle, valueToReport); } else { - // TODO: can we in future have another way to set notify value without writeDescriptor? - qCWarning(QT_BT_OSX) << "-peripheral:didUpdateNotificationStateForCharacteristic:, " - "notification value set to " << int(characteristic.isNotifying) + /* + 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"; + " or no previous writeDescriptor request found"; + */ } } |