10#include <QtCore/qloggingcategory.h>
11#include <QtCore/qdebug.h>
12#include <QtCore/qmap.h>
18Q_DECLARE_METATYPE(QLowEnergyHandle)
31 Q_ASSERT_X(service, Q_FUNC_INFO,
"invalid service (nil)");
35 NSArray *
const cs = service.characteristics;
39 NSUInteger n = 1 + cs.count;
40 for (CBCharacteristic *c in cs) {
41 NSArray *
const ds = c.descriptors;
56 NSError *nsError = [[NSError alloc] initWithDomain:CBErrorDomain
57 code:CBErrorOperationCancelled
59 return ObjCStrongReference<NSError>(nsError, RetainPolicy::noInitialRetain);
64 return std::find_if(watchdogs.begin(), watchdogs.end(), [object, type](
const GCDTimer &other){
65 return [other objectUnderWatch] == object && [other timeoutType] == type;});
74@interface DarwinBTCentralManager (PrivateAPI)
76- (
void)watchAfter:(id)object timeout:(DarwinBluetooth::OperationTimeout)type;
77- (
bool)objectIsUnderWatch:(id)object operation:(DarwinBluetooth::OperationTimeout)type;
78- (
void)stopWatchingAfter:(id)object operation:(DarwinBluetooth::OperationTimeout)type;
80- (
void)retrievePeripheralAndConnect;
81- (
void)connectToPeripheral;
82- (
void)discoverIncludedServices;
83- (
void)readCharacteristics:(CBService *)service;
84- (
void)serviceDetailsDiscoveryFinished:(CBService *)service;
85- (
void)performNextRequest;
86- (
void)performNextReadRequest;
87- (
void)performNextWriteRequest;
90- (CBService *)serviceForUUID:(
const QBluetoothUuid &)qtUuid;
91- (CBCharacteristic *)nextCharacteristicForService:(CBService*)service
92 startingFrom:(CBCharacteristic *)from;
93- (CBCharacteristic *)nextCharacteristicForService:(CBService*)service
94 startingFrom:(CBCharacteristic *)from
95 withProperties:(CBCharacteristicProperties)properties;
96- (CBDescriptor *)nextDescriptorForCharacteristic:(CBCharacteristic *)characteristic
97 startingFrom:(CBDescriptor *)descriptor;
98- (CBDescriptor *)descriptor:(
const QBluetoothUuid &)dUuid
99 forCharacteristic:(CBCharacteristic *)ch;
100- (
bool)cacheWriteValue:(
const QByteArray &)value
for:(NSObject *)obj;
101- (
void)handleReadWriteError:(NSError *)error;
106using DiscoveryMode = QLowEnergyService::DiscoveryMode;
108@implementation DarwinBTCentralManager
111 CBCentralManager *manager;
112 DarwinBluetooth::CentralManagerState managerState;
113 bool disconnectPending;
115 QBluetoothUuid deviceUuid;
117 DarwinBluetooth::LECBManagerNotifier *notifier;
121 DarwinBluetooth::ObjCStrongReference<NSMutableArray> servicesToVisit;
123 NSUInteger currentService;
125 DarwinBluetooth::ObjCStrongReference<NSMutableArray> servicesToVisitNext;
127 DarwinBluetooth::ObjCStrongReference<NSMutableSet> visitedServices;
129 QMap<QBluetoothUuid, DiscoveryMode> servicesToDiscoverDetails;
131 DarwinBluetooth::ServiceHash serviceMap;
132 DarwinBluetooth::CharHash charMap;
133 DarwinBluetooth::DescHash descMap;
135 QLowEnergyHandle lastValidHandle;
138 DarwinBluetooth::RequestQueue requests;
139 QLowEnergyHandle currentReadHandle;
141 DarwinBluetooth::ValueHash valuesToWrite;
144 std::vector<DarwinBluetooth::GCDTimer> timeoutWatchdogs;
146 CBPeripheral *peripheral;
150- (
id)initWith:(DarwinBluetooth::LECBManagerNotifier *)aNotifier
152 using namespace DarwinBluetooth;
154 if (self = [super init]) {
156 managerState = CentralManagerIdle;
157 disconnectPending =
false;
159 notifier = aNotifier;
162 requestPending =
false;
163 currentReadHandle = 0;
165 if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(
"BLUETOOTH_GATT_TIMEOUT"))) {
167 const int value = qEnvironmentVariableIntValue(
"BLUETOOTH_GATT_TIMEOUT", &ok);
168 if (ok && value >= 0)
175 lastKnownMtu = defaultMtu;
188 visitedServices.reset();
189 servicesToVisit.reset();
190 servicesToVisitNext.reset();
192 [manager setDelegate:nil];
195 [peripheral setDelegate:nil];
196 [peripheral release];
199 notifier->deleteLater();
205- (CBPeripheral *)peripheral
210- (
void)watchAfter:(id)object timeout:(DarwinBluetooth::OperationTimeout)type
212 using namespace DarwinBluetooth;
214 GCDTimer newWatcher([[DarwinBTGCDTimer alloc] initWithDelegate:self], RetainPolicy::noInitialRetain);
215 [newWatcher watchAfter:object withTimeoutType:type];
216 timeoutWatchdogs.push_back(newWatcher);
217 [newWatcher startWithTimeout:timeoutMS step:200];
220- (
bool)objectIsUnderWatch:(id)object operation:(DarwinBluetooth::OperationTimeout)type
222 return DarwinBluetooth::qt_find_watchdog(timeoutWatchdogs, object, type) != timeoutWatchdogs.end();
225- (
void)stopWatchingAfter:(id)object operation:(DarwinBluetooth::OperationTimeout)type
227 auto pos = DarwinBluetooth::qt_find_watchdog(timeoutWatchdogs, object, type);
228 if (pos != timeoutWatchdogs.end()) {
230 timeoutWatchdogs.erase(pos);
236 for (
auto &watchdog : timeoutWatchdogs)
237 [watchdog cancelTimer];
238 timeoutWatchdogs.clear();
241- (
void)timeout:(id)sender
245 using namespace DarwinBluetooth;
247 DarwinBTGCDTimer *watcher =
static_cast<DarwinBTGCDTimer *>(sender);
248 id cbObject = [watcher objectUnderWatch];
249 const OperationTimeout type = [watcher timeoutType];
251 Q_ASSERT([self objectIsUnderWatch:cbObject operation:type]);
253 NSLog(@
"Timeout caused by: %@", cbObject);
257 const ObjCStrongReference<NSError> nsError(qt_timeoutNSError(type));
259 case OperationTimeout::serviceDiscovery:
260 qCWarning(QT_BT_DARWIN,
"Timeout in services discovery");
261 [self peripheral:peripheral didDiscoverServices:nsError];
263 case OperationTimeout::includedServicesDiscovery:
264 qCWarning(QT_BT_DARWIN,
"Timeout in included services discovery");
265 [self peripheral:peripheral didDiscoverIncludedServicesForService:cbObject error:nsError];
267 case OperationTimeout::characteristicsDiscovery:
268 qCWarning(QT_BT_DARWIN,
"Timeout in characteristics discovery");
269 [self peripheral:peripheral didDiscoverCharacteristicsForService:cbObject error:nsError];
271 case OperationTimeout::characteristicRead:
272 qCWarning(QT_BT_DARWIN,
"Timeout while reading a characteristic");
273 [self peripheral:peripheral didUpdateValueForCharacteristic:cbObject error:nsError];
275 case OperationTimeout::descriptorsDiscovery:
276 qCWarning(QT_BT_DARWIN,
"Timeout in descriptors discovery");
277 [self peripheral:peripheral didDiscoverDescriptorsForCharacteristic:cbObject error:nsError];
279 case OperationTimeout::descriptorRead:
280 qCWarning(QT_BT_DARWIN,
"Timeout while reading a descriptor");
281 [self peripheral:peripheral didUpdateValueForDescriptor:cbObject error:nsError];
283 case OperationTimeout::characteristicWrite:
284 qCWarning(QT_BT_DARWIN,
"Timeout while writing a characteristic with response");
285 [self peripheral:peripheral didWriteValueForCharacteristic:cbObject error:nsError];
290- (
void)connectToDevice:(
const QBluetoothUuid &)aDeviceUuid
292 disconnectPending =
false;
293 deviceUuid = aDeviceUuid;
298 if (
const dispatch_queue_t leQueue = DarwinBluetooth::qt_LE_queue()) {
299 managerState = DarwinBluetooth::CentralManagerUpdating;
300 manager = [[CBCentralManager alloc] initWithDelegate:self queue:leQueue];
304 managerState = DarwinBluetooth::CentralManagerIdle;
305 qCWarning(QT_BT_DARWIN) <<
"failed to allocate a central manager";
307 emit notifier->CBManagerError(QLowEnergyController::ConnectionError);
309 }
else if (managerState != DarwinBluetooth::CentralManagerUpdating) {
310 [self retrievePeripheralAndConnect];
314- (
void)retrievePeripheralAndConnect
316 Q_ASSERT_X(manager, Q_FUNC_INFO,
"invalid central manager (nil)");
317 Q_ASSERT_X(managerState == DarwinBluetooth::CentralManagerIdle,
318 Q_FUNC_INFO,
"invalid state");
320 if ([self isConnected]) {
321 qCDebug(QT_BT_DARWIN) <<
"already connected";
323 emit notifier->connected();
325 }
else if (peripheral) {
328 [self connectToPeripheral];
332 using namespace DarwinBluetooth;
335 const ObjCScopedPointer<NSMutableArray> uuids([[NSMutableArray alloc] init], RetainPolicy::noInitialRetain);
337 qCWarning(QT_BT_DARWIN) <<
"failed to allocate identifiers";
339 emit notifier->CBManagerError(QLowEnergyController::ConnectionError);
344 const QUuid::Id128Bytes qtUuidData(deviceUuid.toBytes());
345 uuid_t uuidData = {};
346 std::copy(qtUuidData.data, qtUuidData.data + 16, uuidData);
347 const ObjCScopedPointer<NSUUID> nsUuid([[NSUUID alloc] initWithUUIDBytes:uuidData], RetainPolicy::noInitialRetain);
349 qCWarning(QT_BT_DARWIN) <<
"failed to allocate NSUUID identifier";
351 emit notifier->CBManagerError(QLowEnergyController::ConnectionError);
355 [uuids addObject:nsUuid];
358 NSArray *
const peripherals = [manager retrievePeripheralsWithIdentifiers:uuids];
359 if (!peripherals || peripherals.count != 1) {
360 qCWarning(QT_BT_DARWIN) <<
"failed to retrieve a peripheral";
362 emit notifier->CBManagerError(QLowEnergyController::UnknownRemoteDeviceError);
366 peripheral = [
static_cast<CBPeripheral *>([peripherals objectAtIndex:0]) retain];
367 [self connectToPeripheral];
370- (
void)connectToPeripheral
372 using namespace DarwinBluetooth;
374 Q_ASSERT_X(manager, Q_FUNC_INFO,
"invalid central manager (nil)");
375 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
376 Q_ASSERT_X(managerState == CentralManagerIdle, Q_FUNC_INFO,
"invalid state");
379 if ([self isConnected]) {
380 qCDebug(QT_BT_DARWIN) <<
"already connected";
382 emit notifier->connected();
384 [self setMtu:defaultMtu];
385 qCDebug(QT_BT_DARWIN) <<
"trying to connect";
386 managerState = CentralManagerConnecting;
387 [manager connectPeripheral:peripheral options:nil];
396 return peripheral.state == CBPeripheralStateConnected;
399- (
void)disconnectFromDevice
403 if (managerState == DarwinBluetooth::CentralManagerUpdating) {
404 disconnectPending =
true;
411 emit notifier->disconnected();
414 disconnectPending =
false;
415 if ([self isConnected])
416 managerState = DarwinBluetooth::CentralManagerDisconnecting;
418 managerState = DarwinBluetooth::CentralManagerIdle;
425 [manager cancelPeripheralConnection:peripheral];
429- (
void)discoverServices
431 using namespace DarwinBluetooth;
433 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
434 Q_ASSERT_X(managerState == CentralManagerIdle, Q_FUNC_INFO,
"invalid state");
443 managerState = CentralManagerDiscovering;
444 [self watchAfter:peripheral timeout:OperationTimeout::serviceDiscovery];
445 [peripheral discoverServices:nil];
448- (
void)discoverIncludedServices
450 using namespace DarwinBluetooth;
452 Q_ASSERT_X(managerState == CentralManagerIdle, Q_FUNC_INFO,
"invalid state");
453 Q_ASSERT_X(manager, Q_FUNC_INFO,
"invalid manager (nil)");
454 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
458 NSArray *
const services = peripheral.services;
459 if (!services || !services.count) {
462 emit notifier->serviceDiscoveryFinished();
465 servicesToVisitNext.reset();
466 servicesToVisit.reset([NSMutableArray arrayWithArray:services], RetainPolicy::doInitialRetain);
468 visitedServices.reset([NSMutableSet setWithCapacity:peripheral.services.count], RetainPolicy::doInitialRetain);
470 CBService *
const s = [services objectAtIndex:currentService];
471 [visitedServices addObject:s];
472 managerState = CentralManagerDiscovering;
473 [self watchAfter:s timeout:OperationTimeout::includedServicesDiscovery];
474 [peripheral discoverIncludedServices:nil forService:s];
478- (
void)discoverServiceDetails:(
const QBluetoothUuid &)serviceUuid
479 readValues:(
bool) read
485 using namespace DarwinBluetooth;
487 Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO,
"invalid state");
488 Q_ASSERT_X(!serviceUuid.isNull(), Q_FUNC_INFO,
"invalid service UUID");
489 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
491 if (servicesToDiscoverDetails.contains(serviceUuid)) {
492 qCWarning(QT_BT_DARWIN) <<
"already discovering for"
499 if (CBService *
const service = [self serviceForUUID:serviceUuid]) {
500 const auto mode = read ? DiscoveryMode::FullDiscovery : DiscoveryMode::SkipValueDiscovery;
501 servicesToDiscoverDetails[serviceUuid] = mode;
502 [self watchAfter:service timeout:OperationTimeout::characteristicsDiscovery];
503 [peripheral discoverCharacteristics:nil forService:service];
507 qCWarning(QT_BT_DARWIN) <<
"unknown service uuid"
511 emit notifier->CBManagerError(serviceUuid, QLowEnergyService::UnknownError);
514- (
void)readCharacteristics:(CBService *)service
518 Q_ASSERT_X(service, Q_FUNC_INFO,
"invalid service (nil)");
520 using namespace DarwinBluetooth;
524 Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO,
"invalid state");
525 Q_ASSERT_X(manager, Q_FUNC_INFO,
"invalid manager (nil)");
526 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
528 if (!service.characteristics || !service.characteristics.count)
529 return [self serviceDetailsDiscoveryFinished:service];
531 NSArray *
const cs = service.characteristics;
532 for (CBCharacteristic *c in cs) {
533 if (c.properties & CBCharacteristicPropertyRead) {
534 [self watchAfter:c timeout:OperationTimeout::characteristicRead];
535 return [peripheral readValueForCharacteristic:c];
540 [self discoverDescriptors:service];
543- (
void)discoverDescriptors:(CBService *)service
547 Q_ASSERT_X(service, Q_FUNC_INFO,
"invalid service (nil)");
549 using namespace DarwinBluetooth;
553 Q_ASSERT_X(managerState != CentralManagerUpdating,
554 Q_FUNC_INFO,
"invalid state");
555 Q_ASSERT_X(manager, Q_FUNC_INFO,
"invalid manager (nil)");
556 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
558 if (!service.characteristics || !service.characteristics.count) {
559 [self serviceDetailsDiscoveryFinished:service];
562 CBCharacteristic *ch = [service.characteristics objectAtIndex:0];
563 [self watchAfter:ch timeout:OperationTimeout::descriptorsDiscovery];
564 [peripheral discoverDescriptorsForCharacteristic:ch];
568- (
void)readDescriptors:(CBService *)service
570 using namespace DarwinBluetooth;
572 Q_ASSERT_X(service, Q_FUNC_INFO,
"invalid service (nil)");
573 Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO,
"invalid state");
574 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
578 NSArray *
const cs = service.characteristics;
580 Q_ASSERT_X(cs && cs.count, Q_FUNC_INFO,
"invalid service");
581 for (CBCharacteristic *c in cs) {
582 if (c.descriptors && c.descriptors.count) {
583 CBDescriptor *desc = [c.descriptors objectAtIndex:0];
584 [self watchAfter:desc timeout:OperationTimeout::descriptorRead];
585 return [peripheral readValueForDescriptor:desc];
590 [self serviceDetailsDiscoveryFinished:service];
593- (
void)serviceDetailsDiscoveryFinished:(CBService *)service
595 Q_ASSERT_X(service, Q_FUNC_INFO,
"invalid service (nil)");
597 using namespace DarwinBluetooth;
601 const QBluetoothUuid serviceUuid(qt_uuid(service.UUID));
602 const bool skipValues = servicesToDiscoverDetails[serviceUuid] == DiscoveryMode::SkipValueDiscovery;
603 servicesToDiscoverDetails.remove(serviceUuid);
605 const NSUInteger nHandles = qt_countGATTEntries(service);
606 Q_ASSERT_X(nHandles, Q_FUNC_INFO,
"unexpected number of GATT entires");
608 const QLowEnergyHandle maxHandle = std::numeric_limits<QLowEnergyHandle>::max();
609 if (nHandles >= maxHandle || lastValidHandle > maxHandle - nHandles) {
611 qCWarning(QT_BT_DARWIN) <<
"can not allocate more handles";
613 notifier->CBManagerError(serviceUuid, QLowEnergyService::OperationError);
620 QSharedPointer<QLowEnergyServicePrivate> qtService(
new QLowEnergyServicePrivate);
621 qtService->uuid = serviceUuid;
626 serviceMap[lastValidHandle] = service;
627 qtService->startHandle = lastValidHandle;
629 NSArray *
const cs = service.characteristics;
631 if (cs && cs.count) {
632 QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> charList;
634 for (CBCharacteristic *c in cs) {
637 charMap[lastValidHandle] = c;
639 QLowEnergyServicePrivate::CharData newChar = {};
640 newChar.uuid = qt_uuid(c.UUID);
641 const int cbProps = c.properties & 0xff;
642 newChar.properties =
static_cast<QLowEnergyCharacteristic::PropertyTypes>(cbProps);
644 newChar.value = qt_bytearray(c.value);
645 newChar.valueHandle = lastValidHandle;
647 NSArray *
const ds = c.descriptors;
648 if (ds && ds.count) {
649 QHash<QLowEnergyHandle, QLowEnergyServicePrivate::DescData> descList;
650 for (CBDescriptor *d in ds) {
653 descMap[lastValidHandle] = d;
655 QLowEnergyServicePrivate::DescData newDesc = {};
656 newDesc.uuid = qt_uuid(d.UUID);
657 newDesc.value = qt_bytearray(
static_cast<NSObject *>(d.value));
658 descList[lastValidHandle] = newDesc;
660 if (newDesc.uuid == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration) {
661 if (newDesc.value.size() && (newDesc.value[0] & 3))
662 [peripheral setNotifyValue:YES forCharacteristic:c];
666 newChar.descriptorList = descList;
669 charList[newChar.valueHandle] = newChar;
672 qtService->characteristicList = charList;
675 qtService->endHandle = lastValidHandle;
678 emit notifier->serviceDetailsDiscoveryFinished(qtService);
681- (
void)performNextRequest
683 using namespace DarwinBluetooth;
685 if (requestPending || !requests.size())
688 switch (requests.head().type) {
689 case LERequest::CharRead:
690 case LERequest::DescRead:
691 return [self performNextReadRequest];
692 case LERequest::CharWrite:
693 case LERequest::DescWrite:
694 case LERequest::ClientConfiguration:
695 return [self performNextWriteRequest];
702- (
void)performNextReadRequest
704 using namespace DarwinBluetooth;
706 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
707 Q_ASSERT_X(!requestPending, Q_FUNC_INFO,
"processing another request");
708 Q_ASSERT_X(requests.size(), Q_FUNC_INFO,
"no requests to handle");
709 Q_ASSERT_X(requests.head().type == LERequest::CharRead
710 || requests.head().type == LERequest::DescRead,
711 Q_FUNC_INFO,
"not a read request");
713 const LERequest request(requests.dequeue());
714 if (request.type == LERequest::CharRead) {
715 if (!charMap.contains(request.handle)) {
716 qCWarning(QT_BT_DARWIN) <<
"characteristic with handle"
717 << request.handle <<
"not found";
718 return [self performNextRequest];
721 requestPending =
true;
722 currentReadHandle = request.handle;
725 [peripheral readValueForCharacteristic:charMap[request.handle]];
727 if (!descMap.contains(request.handle)) {
728 qCWarning(QT_BT_DARWIN) <<
"descriptor with handle"
729 << request.handle <<
"not found";
730 return [self performNextRequest];
733 requestPending =
true;
734 currentReadHandle = request.handle;
736 [peripheral readValueForDescriptor:descMap[request.handle]];
740- (
void)performNextWriteRequest
742 using namespace DarwinBluetooth;
744 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
745 Q_ASSERT_X(!requestPending, Q_FUNC_INFO,
"processing another request");
746 Q_ASSERT_X(requests.size(), Q_FUNC_INFO,
"no requests to handle");
747 Q_ASSERT_X(requests.head().type == LERequest::CharWrite
748 || requests.head().type == LERequest::DescWrite
749 || requests.head().type == LERequest::ClientConfiguration,
750 Q_FUNC_INFO,
"not a write request");
752 const LERequest request(requests.dequeue());
754 if (request.type == LERequest::DescWrite) {
755 if (!descMap.contains(request.handle)) {
756 qCWarning(QT_BT_DARWIN) <<
"handle:" << request.handle
758 return [self performNextRequest];
761 CBDescriptor *
const descriptor = descMap[request.handle];
762 ObjCStrongReference<NSData> data(data_from_bytearray(request.value));
765 qCWarning(QT_BT_DARWIN) <<
"failed to allocate an NSData object";
766 return [self performNextRequest];
769 if (![self cacheWriteValue:request.value
for:descriptor])
770 return [self performNextRequest];
772 requestPending =
true;
773 return [peripheral writeValue:data.data() forDescriptor:descriptor];
775 if (!charMap.contains(request.handle)) {
776 qCWarning(QT_BT_DARWIN) <<
"characteristic with handle:"
777 << request.handle <<
"not found";
778 return [self performNextRequest];
781 CBCharacteristic *
const characteristic = charMap[request.handle];
783 if (request.type == LERequest::ClientConfiguration) {
784 const QBluetoothUuid qtUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration);
785 CBDescriptor *
const descriptor = [self descriptor:qtUuid forCharacteristic:characteristic];
786 Q_ASSERT_X(descriptor, Q_FUNC_INFO,
"no client characteristic "
787 "configuration descriptor found");
789 if (![self cacheWriteValue:request.value
for:descriptor])
790 return [self performNextRequest];
793 if (request.value.size())
794 enable = request.value[0] & 3;
796 requestPending =
true;
797 [peripheral setNotifyValue:enable forCharacteristic:characteristic];
799 ObjCStrongReference<NSData> data(data_from_bytearray(request.value));
802 qCWarning(QT_BT_DARWIN) <<
"failed to allocate NSData object";
803 return [self performNextRequest];
807 if (request.withResponse) {
808 if (![self cacheWriteValue:request.value
for:characteristic])
809 return [self performNextRequest];
811 requestPending =
true;
812 [self watchAfter:characteristic timeout:OperationTimeout::characteristicWrite];
813 [peripheral writeValue:data.data() forCharacteristic:characteristic
814 type:CBCharacteristicWriteWithResponse];
816 [peripheral writeValue:data.data() forCharacteristic:characteristic
817 type:CBCharacteristicWriteWithoutResponse];
818 [self performNextRequest];
824- (
void)setMtu:(
int)newMtu
826 if (lastKnownMtu == newMtu)
828 lastKnownMtu = newMtu;
830 emit notifier->mtuChanged(newMtu);
835 using namespace DarwinBluetooth;
837 if (![self isConnected]) {
838 [self setMtu:defaultMtu];
842 Q_ASSERT(peripheral);
843 const NSUInteger maxLen = [peripheral maximumWriteValueLengthForType:
844 CBCharacteristicWriteWithoutResponse];
845 if (maxLen > std::numeric_limits<
int>::max() - 3)
846 [self setMtu:defaultMtu];
848 [self setMtu:
int(maxLen) + 3];
855 Q_ASSERT([self isConnected]);
856 [peripheral readRSSI];
859- (
void)setNotifyValue:(
const QByteArray &)value
860 forCharacteristic:(QLowEnergyHandle)charHandle
861 onService:(
const QBluetoothUuid &)serviceUuid
863 using namespace DarwinBluetooth;
865 Q_ASSERT_X(charHandle, Q_FUNC_INFO,
"invalid characteristic handle (0)");
867 if (!charMap.contains(charHandle)) {
868 qCWarning(QT_BT_DARWIN) <<
"unknown characteristic handle"
871 emit notifier->CBManagerError(serviceUuid,
872 QLowEnergyService::DescriptorWriteError);
880 const QBluetoothUuid qtUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration);
881 if (![self descriptor:qtUuid forCharacteristic:charMap[charHandle]]) {
882 qCWarning(QT_BT_DARWIN) <<
"no client characteristic configuration found";
884 emit notifier->CBManagerError(serviceUuid,
885 QLowEnergyService::DescriptorWriteError);
891 request.type = LERequest::ClientConfiguration;
892 request.handle = charHandle;
893 request.value = value;
895 requests.enqueue(request);
896 [self performNextRequest];
899- (
void)readCharacteristic:(QLowEnergyHandle)charHandle
900 onService:(
const QBluetoothUuid &)serviceUuid
902 using namespace DarwinBluetooth;
904 Q_ASSERT_X(charHandle, Q_FUNC_INFO,
"invalid characteristic handle (0)");
908 if (!charMap.contains(charHandle)) {
909 qCWarning(QT_BT_DARWIN) <<
"characteristic:" << charHandle <<
"not found";
911 emit notifier->CBManagerError(serviceUuid,
912 QLowEnergyService::CharacteristicReadError);
919 request.type = LERequest::CharRead;
920 request.handle = charHandle;
922 requests.enqueue(request);
923 [self performNextRequest];
926- (
void)write:(
const QByteArray &)value
927 charHandle:(QLowEnergyHandle)charHandle
928 onService:(
const QBluetoothUuid &)serviceUuid
929 withResponse:(
bool)withResponse
931 using namespace DarwinBluetooth;
933 Q_ASSERT_X(charHandle, Q_FUNC_INFO,
"invalid characteristic handle (0)");
937 if (!charMap.contains(charHandle)) {
938 qCWarning(QT_BT_DARWIN) <<
"characteristic:" << charHandle <<
"not found";
940 emit notifier->CBManagerError(serviceUuid,
941 QLowEnergyService::CharacteristicWriteError);
947 request.type = LERequest::CharWrite;
948 request.withResponse = withResponse;
949 request.handle = charHandle;
950 request.value = value;
952 requests.enqueue(request);
953 [self performNextRequest];
956- (
void)readDescriptor:(QLowEnergyHandle)descHandle
957 onService:(
const QBluetoothUuid &)serviceUuid
959 using namespace DarwinBluetooth;
961 Q_ASSERT_X(descHandle, Q_FUNC_INFO,
"invalid descriptor handle (0)");
963 if (!descMap.contains(descHandle)) {
964 qCWarning(QT_BT_DARWIN) <<
"handle:" << descHandle <<
"not found";
966 emit notifier->CBManagerError(serviceUuid,
967 QLowEnergyService::DescriptorReadError);
973 request.type = LERequest::DescRead;
974 request.handle = descHandle;
976 requests.enqueue(request);
977 [self performNextRequest];
980- (
void)write:(
const QByteArray &)value
981 descHandle:(QLowEnergyHandle)descHandle
982 onService:(
const QBluetoothUuid &)serviceUuid
984 using namespace DarwinBluetooth;
986 Q_ASSERT_X(descHandle, Q_FUNC_INFO,
"invalid descriptor handle (0)");
988 if (!descMap.contains(descHandle)) {
989 qCWarning(QT_BT_DARWIN) <<
"handle:" << descHandle <<
"not found";
991 emit notifier->CBManagerError(serviceUuid,
992 QLowEnergyService::DescriptorWriteError);
998 request.type = LERequest::DescWrite;
999 request.handle = descHandle;
1000 request.value = value;
1002 requests.enqueue(request);
1003 [self performNextRequest];
1008- (CBService *)serviceForUUID:(
const QBluetoothUuid &)qtUuid
1010 using namespace DarwinBluetooth;
1012 Q_ASSERT_X(!qtUuid.isNull(), Q_FUNC_INFO,
"invalid uuid");
1013 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
1015 ObjCStrongReference<NSMutableArray> toVisit([NSMutableArray arrayWithArray:peripheral.services], RetainPolicy::doInitialRetain);
1016 ObjCStrongReference<NSMutableArray> toVisitNext([[NSMutableArray alloc] init], RetainPolicy::noInitialRetain);
1017 ObjCStrongReference<NSMutableSet> visitedNodes([[NSMutableSet alloc] init], RetainPolicy::noInitialRetain);
1020 for (NSUInteger i = 0, e = [toVisit count]; i < e; ++i) {
1021 CBService *
const s = [toVisit objectAtIndex:i];
1022 if (equal_uuids(s.UUID, qtUuid))
1024 if (![visitedNodes containsObject:s] && s.includedServices && s.includedServices.count) {
1025 [visitedNodes addObject:s];
1026 [toVisitNext addObjectsFromArray:s.includedServices];
1030 if (![toVisitNext count])
1033 toVisit.swap(toVisitNext);
1034 toVisitNext.reset([[NSMutableArray alloc] init], RetainPolicy::noInitialRetain);
1040- (CBCharacteristic *)nextCharacteristicForService:(CBService*)service
1041 startingFrom:(CBCharacteristic *)characteristic
1043 Q_ASSERT_X(service, Q_FUNC_INFO,
"invalid service (nil)");
1044 Q_ASSERT_X(characteristic, Q_FUNC_INFO,
"invalid characteristic (nil)");
1045 Q_ASSERT_X(service.characteristics, Q_FUNC_INFO,
"invalid service");
1046 Q_ASSERT_X(service.characteristics.count, Q_FUNC_INFO,
"invalid service");
1053 NSArray *
const cs = service.characteristics;
1057 for (NSUInteger index = cs.count - 1; index != 0; --index) {
1058 if ([cs objectAtIndex:index] == characteristic) {
1059 if (index + 1 == cs.count)
1062 return [cs objectAtIndex:index + 1];
1066 Q_ASSERT_X([cs objectAtIndex:0] == characteristic, Q_FUNC_INFO,
1067 "characteristic was not found in service.characteristics");
1069 return [cs objectAtIndex:1];
1072- (CBCharacteristic *)nextCharacteristicForService:(CBService*)service
1073 startingFrom:(CBCharacteristic *)characteristic
1074 properties:(CBCharacteristicProperties)properties
1076 Q_ASSERT_X(service, Q_FUNC_INFO,
"invalid service (nil)");
1077 Q_ASSERT_X(characteristic, Q_FUNC_INFO,
"invalid characteristic (nil)");
1078 Q_ASSERT_X(service.characteristics, Q_FUNC_INFO,
"invalid service");
1079 Q_ASSERT_X(service.characteristics.count, Q_FUNC_INFO,
"invalid service");
1086 NSArray *
const cs = service.characteristics;
1090 NSUInteger index = cs.count - 1;
1091 for (; index != 0; --index) {
1092 if ([cs objectAtIndex:index] == characteristic) {
1093 if (index + 1 == cs.count) {
1103 Q_ASSERT_X([cs objectAtIndex:0] == characteristic, Q_FUNC_INFO,
1104 "characteristic not found in service.characteristics");
1108 for (
const NSUInteger e = cs.count; index < e; ++index) {
1109 CBCharacteristic *
const c = [cs objectAtIndex:index];
1110 if (c.properties & properties)
1117- (CBDescriptor *)nextDescriptorForCharacteristic:(CBCharacteristic *)characteristic
1118 startingFrom:(CBDescriptor *)descriptor
1120 Q_ASSERT_X(characteristic, Q_FUNC_INFO,
"invalid characteristic (nil)");
1121 Q_ASSERT_X(descriptor, Q_FUNC_INFO,
"invalid descriptor (nil)");
1122 Q_ASSERT_X(characteristic.descriptors, Q_FUNC_INFO,
"invalid characteristic");
1123 Q_ASSERT_X(characteristic.descriptors.count, Q_FUNC_INFO,
"invalid characteristic");
1127 NSArray *
const ds = characteristic.descriptors;
1131 for (NSUInteger index = ds.count - 1; index != 0; --index) {
1132 if ([ds objectAtIndex:index] == descriptor) {
1133 if (index + 1 == ds.count)
1136 return [ds objectAtIndex:index + 1];
1140 Q_ASSERT_X([ds objectAtIndex:0] == descriptor, Q_FUNC_INFO,
1141 "descriptor was not found in characteristic.descriptors");
1143 return [ds objectAtIndex:1];
1146- (CBDescriptor *)descriptor:(
const QBluetoothUuid &)qtUuid
1147 forCharacteristic:(CBCharacteristic *)ch
1149 if (qtUuid.isNull() || !ch)
1154 CBDescriptor *descriptor = nil;
1155 NSArray *
const ds = ch.descriptors;
1156 if (ds && ds.count) {
1157 for (CBDescriptor *d in ds) {
1158 if (DarwinBluetooth::equal_uuids(d.UUID, qtUuid)) {
1168- (
bool)cacheWriteValue:(
const QByteArray &)value
for:(NSObject *)obj
1170 Q_ASSERT_X(obj, Q_FUNC_INFO,
"invalid object (nil)");
1172 if ([obj isKindOfClass:[CBCharacteristic
class]]) {
1173 CBCharacteristic *
const ch =
static_cast<CBCharacteristic *>(obj);
1174 if (!charMap.key(ch)) {
1175 qCWarning(QT_BT_DARWIN) <<
"unexpected characteristic, no handle found";
1178 }
else if ([obj isKindOfClass:[CBDescriptor
class]]) {
1179 CBDescriptor *
const d =
static_cast<CBDescriptor *>(obj);
1180 if (!descMap.key(d)) {
1181 qCWarning(QT_BT_DARWIN) <<
"unexpected descriptor, no handle found";
1185 qCWarning(QT_BT_DARWIN) <<
"invalid object, characteristic "
1186 "or descriptor required";
1190 if (valuesToWrite.contains(obj)) {
1193 qCWarning(QT_BT_DARWIN) <<
"already has a cached value for this "
1194 "object, the value will be replaced";
1197 valuesToWrite[obj] = value;
1203 requestPending =
false;
1204 valuesToWrite.clear();
1206 servicesToDiscoverDetails.clear();
1207 lastValidHandle = 0;
1211 currentReadHandle = 0;
1212 [self stopWatchers];
1216- (
void)handleReadWriteError:(NSError *)error
1220 switch (error.code) {
1223 emit notifier->CBManagerError(QLowEnergyController::AuthorizationError);
1233- (
void)centralManagerDidUpdateState:(CBCentralManager *)central
1235 using namespace DarwinBluetooth;
1237#pragma clang diagnostic push
1238#pragma clang diagnostic ignored "-Wunguarded-availability-new"
1240 const auto state = central.state;
1241 if (state == CBManagerStateUnknown || state == CBManagerStateResetting) {
1251 if (state == CBManagerStateUnsupported || state == CBManagerStateUnauthorized) {
1252 managerState = CentralManagerIdle;
1255 if (state == CBManagerStateUnsupported)
1256 emit notifier->LEnotSupported();
1258 emit notifier->CBManagerError(QLowEnergyController::MissingPermissionsError);
1260 [self stopWatchers];
1264 if (state == CBManagerStatePoweredOff) {
1265 if (managerState == CentralManagerUpdating) {
1266 managerState = CentralManagerIdle;
1269 emit notifier->LEnotSupported();
1271 managerState = CentralManagerIdle;
1275 emit notifier->CBManagerError(QLowEnergyController::InvalidBluetoothAdapterError);
1277 [self stopWatchers];
1281 if (state == CBManagerStatePoweredOn) {
1282 if (managerState == CentralManagerUpdating && !disconnectPending) {
1283 managerState = CentralManagerIdle;
1284 [self retrievePeripheralAndConnect];
1288 Q_ASSERT_X(0, Q_FUNC_INFO,
"invalid central's state");
1291#pragma clang diagnostic pop
1294- (
void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)aPeripheral
1297 Q_UNUSED(aPeripheral);
1299 if (managerState != DarwinBluetooth::CentralManagerConnecting) {
1306 [peripheral setDelegate:self];
1308 managerState = DarwinBluetooth::CentralManagerIdle;
1310 emit notifier->connected();
1313- (
void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)aPeripheral
1314 error:(NSError *)error
1317 Q_UNUSED(aPeripheral);
1320 if (managerState != DarwinBluetooth::CentralManagerConnecting) {
1325 managerState = DarwinBluetooth::CentralManagerIdle;
1328 notifier->CBManagerError(QLowEnergyController::UnknownRemoteDeviceError);
1331- (
void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)aPeripheral
1332 error:(NSError *)error
1335 Q_UNUSED(aPeripheral);
1340 if (error && managerState == DarwinBluetooth::CentralManagerDisconnecting) {
1341 managerState = DarwinBluetooth::CentralManagerIdle;
1342 qCWarning(QT_BT_DARWIN) <<
"failed to disconnect";
1344 emit notifier->CBManagerError(QLowEnergyController::UnknownRemoteDeviceError);
1346 managerState = DarwinBluetooth::CentralManagerIdle;
1348 emit notifier->disconnected();
1354- (
void)peripheral:(CBPeripheral *)aPeripheral didDiscoverServices:(NSError *)error
1356 Q_UNUSED(aPeripheral);
1358 using namespace DarwinBluetooth;
1360 if (managerState != CentralManagerDiscovering) {
1365 if (![self objectIsUnderWatch:aPeripheral operation:OperationTimeout::serviceDiscovery])
1370 [self stopWatchingAfter:aPeripheral operation:OperationTimeout::serviceDiscovery];
1372 managerState = CentralManagerIdle;
1375 NSLog(@
"%s failed with error %@", Q_FUNC_INFO, error);
1379 emit notifier->CBManagerError(QLowEnergyController::UnknownError);
1383 [self discoverIncludedServices];
1386- (
void)peripheral:(CBPeripheral *)aPeripheral
1387 didModifyServices:(NSArray<CBService *> *)invalidatedServices
1389 Q_UNUSED(aPeripheral);
1390 Q_UNUSED(invalidatedServices);
1392 qCWarning(QT_BT_DARWIN) <<
"The peripheral has modified its services.";
1406 managerState = DarwinBluetooth::CentralManagerIdle;
1408 emit notifier->servicesWereModified();
1411- (
void)peripheral:(CBPeripheral *)aPeripheral didDiscoverIncludedServicesForService:(CBService *)service
1412 error:(NSError *)error
1414 Q_UNUSED(aPeripheral);
1416 using namespace DarwinBluetooth;
1418 if (managerState != CentralManagerDiscovering) {
1423 if (![self objectIsUnderWatch:service operation:OperationTimeout::includedServicesDiscovery])
1428 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
1430 [self stopWatchingAfter:service operation:OperationTimeout::includedServicesDiscovery];
1431 managerState = CentralManagerIdle;
1434 NSLog(@
"%s: finished with error %@ for service %@",
1435 Q_FUNC_INFO, error, service.UUID);
1436 }
else if (service.includedServices && service.includedServices.count) {
1438 if (!servicesToVisitNext)
1439 servicesToVisitNext.reset([NSMutableArray arrayWithArray:service.includedServices], RetainPolicy::doInitialRetain);
1441 [servicesToVisitNext addObjectsFromArray:service.includedServices];
1447 for (
const NSUInteger e = [servicesToVisit count]; currentService < e; ++currentService) {
1448 CBService *
const s = [servicesToVisit objectAtIndex:currentService];
1449 if (![visitedServices containsObject:s]) {
1451 [visitedServices addObject:s];
1452 managerState = CentralManagerDiscovering;
1453 [self watchAfter:s timeout:OperationTimeout::includedServicesDiscovery];
1454 return [peripheral discoverIncludedServices:nil forService:s];
1460 if (servicesToVisitNext && [servicesToVisitNext count]) {
1461 servicesToVisit.swap(servicesToVisitNext);
1462 servicesToVisitNext.reset();
1465 for (
const NSUInteger e = [servicesToVisit count]; currentService < e; ++currentService) {
1466 CBService *
const s = [servicesToVisit objectAtIndex:currentService];
1467 if (![visitedServices containsObject:s]) {
1468 [visitedServices addObject:s];
1469 managerState = CentralManagerDiscovering;
1470 [self watchAfter:s timeout:OperationTimeout::includedServicesDiscovery];
1471 return [peripheral discoverIncludedServices:nil forService:s];
1479 visitedServices.reset();
1480 servicesToVisit.reset();
1481 servicesToVisitNext.reset();
1484 emit notifier->serviceDiscoveryFinished();
1487- (
void)peripheral:(CBPeripheral *)aPeripheral didDiscoverCharacteristicsForService:(CBService *)service
1488 error:(NSError *)error
1492 Q_UNUSED(aPeripheral);
1499 using namespace DarwinBluetooth;
1501 if (![self objectIsUnderWatch:service operation:OperationTimeout::characteristicsDiscovery])
1506 [self stopWatchingAfter:service operation:OperationTimeout::characteristicsDiscovery];
1508 const auto qtUuid = qt_uuid(service.UUID);
1509 const bool skipRead = servicesToDiscoverDetails[qtUuid] == DiscoveryMode::SkipValueDiscovery;
1511 Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO,
"invalid state");
1514 NSLog(@
"%s failed with error: %@", Q_FUNC_INFO, error);
1517 emit notifier->CBManagerError(qtUuid, QLowEnergyController::UnknownError);
1521 [self serviceDetailsDiscoveryFinished:service];
1524 [self readCharacteristics:service];
1527- (
void)peripheral:(CBPeripheral *)aPeripheral
1528 didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
1529 error:(NSError *)error
1531 Q_UNUSED(aPeripheral);
1536 using namespace DarwinBluetooth;
1540 const bool readMatch = [self objectIsUnderWatch:characteristic operation:OperationTimeout::characteristicRead];
1542 [self stopWatchingAfter:characteristic operation:OperationTimeout::characteristicRead];
1544 Q_ASSERT_X(managerState != CentralManagerUpdating, Q_FUNC_INFO,
"invalid state");
1545 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
1549 CBService *
const service = characteristic.service;
1550 const QBluetoothUuid qtUuid(qt_uuid(service.UUID));
1551 const bool isDetailsDiscovery = servicesToDiscoverDetails.contains(qtUuid);
1552 const QLowEnergyHandle chHandle = charMap.key(characteristic);
1555 NSLog(@
"%s failed with error %@", Q_FUNC_INFO, error);
1556 if (!isDetailsDiscovery) {
1557 if (chHandle && chHandle == currentReadHandle) {
1558 currentReadHandle = 0;
1559 requestPending =
false;
1560 emit notifier->CBManagerError(qtUuid, QLowEnergyService::CharacteristicReadError);
1561 [self handleReadWriteError:error];
1562 [self performNextRequest];
1568 if (isDetailsDiscovery) {
1571 CBCharacteristic *
const next = [self nextCharacteristicForService:characteristic.service
1572 startingFrom:characteristic properties:CBCharacteristicPropertyRead];
1574 [self watchAfter:next timeout:OperationTimeout::characteristicRead];
1575 [peripheral readValueForCharacteristic:next];
1577 [self discoverDescriptors:characteristic.service];
1588 qCCritical(QT_BT_DARWIN) <<
"unexpected update notification, "
1589 "no characteristic handle found";
1593 if (currentReadHandle == chHandle) {
1596 requestPending =
false;
1597 currentReadHandle = 0;
1599 emit notifier->characteristicRead(chHandle, qt_bytearray(characteristic.value));
1600 [self performNextRequest];
1602 emit notifier->characteristicUpdated(chHandle, qt_bytearray(characteristic.value));
1607- (
void)peripheral:(CBPeripheral *)aPeripheral
1608 didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic
1609 error:(NSError *)error
1613 Q_UNUSED(aPeripheral);
1622 using namespace DarwinBluetooth;
1624 if (![self objectIsUnderWatch:characteristic operation:OperationTimeout::descriptorsDiscovery])
1627 [self stopWatchingAfter:characteristic operation:OperationTimeout::descriptorsDiscovery];
1630 NSLog(@
"%s failed with error %@", Q_FUNC_INFO, error);
1635 CBCharacteristic *
const next = [self nextCharacteristicForService:characteristic.service
1636 startingFrom:characteristic];
1638 [self watchAfter:next timeout:OperationTimeout::descriptorsDiscovery];
1639 [peripheral discoverDescriptorsForCharacteristic:next];
1641 [self readDescriptors:characteristic.service];
1645- (
void)peripheral:(CBPeripheral *)aPeripheral
1646 didUpdateValueForDescriptor:(CBDescriptor *)descriptor
1647 error:(NSError *)error
1649 Q_UNUSED(aPeripheral);
1651 Q_ASSERT_X(peripheral, Q_FUNC_INFO,
"invalid peripheral (nil)");
1660 using namespace DarwinBluetooth;
1662 if (![self objectIsUnderWatch:descriptor operation:OperationTimeout::descriptorRead])
1665 [self stopWatchingAfter:descriptor operation:OperationTimeout::descriptorRead];
1667 CBService *
const service = descriptor.characteristic.service;
1668 const QBluetoothUuid qtUuid(qt_uuid(service.UUID));
1669 const bool isDetailsDiscovery = servicesToDiscoverDetails.contains(qtUuid);
1670 const QLowEnergyHandle dHandle = descMap.key(descriptor);
1673 NSLog(@
"%s failed with error %@", Q_FUNC_INFO, error);
1675 if (!isDetailsDiscovery) {
1676 if (dHandle && dHandle == currentReadHandle) {
1677 currentReadHandle = 0;
1678 requestPending =
false;
1679 emit notifier->CBManagerError(qtUuid, QLowEnergyService::DescriptorReadError);
1680 [self handleReadWriteError:error];
1681 [self performNextRequest];
1687 if (isDetailsDiscovery) {
1689 CBDescriptor *
const next = [self nextDescriptorForCharacteristic:descriptor.characteristic
1690 startingFrom:descriptor];
1692 [self watchAfter:next timeout:OperationTimeout::descriptorRead];
1693 [peripheral readValueForDescriptor:next];
1698 CBCharacteristic *
const ch = descriptor.characteristic;
1699 CBCharacteristic *nextCh = [self nextCharacteristicForService:ch.service
1702 if (nextCh.descriptors && nextCh.descriptors.count) {
1703 CBDescriptor *desc = [nextCh.descriptors objectAtIndex:0];
1704 [self watchAfter:desc timeout:OperationTimeout::descriptorRead];
1705 return [peripheral readValueForDescriptor:desc];
1708 nextCh = [self nextCharacteristicForService:ch.service
1709 startingFrom:nextCh];
1712 [self serviceDetailsDiscoveryFinished:service];
1716 qCCritical(QT_BT_DARWIN) <<
"unexpected value update notification, "
1717 "no descriptor handle found";
1721 if (dHandle == currentReadHandle) {
1722 currentReadHandle = 0;
1723 requestPending =
false;
1724 emit notifier->descriptorRead(dHandle, qt_bytearray(
static_cast<NSObject *>(descriptor.value)));
1725 [self performNextRequest];
1730- (
void)peripheral:(CBPeripheral *)aPeripheral
1731 didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
1732 error:(NSError *)error
1734 Q_UNUSED(aPeripheral);
1735 Q_UNUSED(characteristic);
1749 using namespace DarwinBluetooth;
1753 if (![self objectIsUnderWatch:characteristic operation:OperationTimeout::characteristicWrite])
1756 [self stopWatchingAfter:characteristic operation:OperationTimeout::characteristicWrite];
1757 requestPending =
false;
1760 const QByteArray valueToReport(valuesToWrite.value(characteristic, QByteArray()));
1761 if (!valuesToWrite.remove(characteristic)) {
1762 qCWarning(QT_BT_DARWIN) <<
"no updated value found "
1763 "for characteristic";
1767 NSLog(@
"%s failed with error %@", Q_FUNC_INFO, error);
1768 emit notifier->CBManagerError(qt_uuid(characteristic.service.UUID),
1769 QLowEnergyService::CharacteristicWriteError);
1770 [self handleReadWriteError:error];
1772 const QLowEnergyHandle cHandle = charMap.key(characteristic);
1773 emit notifier->characteristicWritten(cHandle, valueToReport);
1776 [self performNextRequest];
1779- (
void)peripheral:(CBPeripheral *)aPeripheral
1780 didWriteValueForDescriptor:(CBDescriptor *)descriptor
1781 error:(NSError *)error
1783 Q_UNUSED(aPeripheral);
1790 using namespace DarwinBluetooth;
1794 requestPending =
false;
1797 const QByteArray valueToReport(valuesToWrite.value(descriptor, QByteArray()));
1798 if (!valuesToWrite.remove(descriptor))
1799 qCWarning(QT_BT_DARWIN) <<
"no updated value found";
1802 NSLog(@
"%s failed with error %@", Q_FUNC_INFO, error);
1803 emit notifier->CBManagerError(qt_uuid(descriptor.characteristic.service.UUID),
1804 QLowEnergyService::DescriptorWriteError);
1805 [self handleReadWriteError:error];
1807 const QLowEnergyHandle dHandle = descMap.key(descriptor);
1808 Q_ASSERT_X(dHandle, Q_FUNC_INFO,
"descriptor not found in the descriptors map");
1809 emit notifier->descriptorWritten(dHandle, valueToReport);
1812 [self performNextRequest];
1815- (
void)peripheral:(CBPeripheral *)aPeripheral
1816 didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
1817 error:(NSError *)error
1819 Q_UNUSED(aPeripheral);
1824 using namespace DarwinBluetooth;
1828 requestPending =
false;
1830 const QBluetoothUuid qtUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration);
1831 CBDescriptor *
const descriptor = [self descriptor:qtUuid forCharacteristic:characteristic];
1832 const QByteArray valueToReport(valuesToWrite.value(descriptor, QByteArray()));
1833 const int nRemoved = valuesToWrite.remove(descriptor);
1836 NSLog(@
"%s failed with error %@", Q_FUNC_INFO, error);
1838 emit notifier->CBManagerError(qt_uuid(characteristic.service.UUID),
1839 QLowEnergyService::DescriptorWriteError);
1840 }
else if (nRemoved) {
1841 const QLowEnergyHandle dHandle = descMap.key(descriptor);
1842 emit notifier->descriptorWritten(dHandle, valueToReport);
1845 [self performNextRequest];
1848- (
void)peripheral:(CBPeripheral *)aPeripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error
1850 Q_UNUSED(aPeripheral);
1856 NSLog(@
"Reading RSSI finished with error: %@", error);
1857 return emit notifier->CBManagerError(QLowEnergyController::RssiReadError);
1861 qCWarning(QT_BT_DARWIN,
"Reading RSSI returned no value");
1862 return emit notifier->CBManagerError(QLowEnergyController::RssiReadError);
1865 emit notifier->rssiUpdated(qint16([RSSI shortValue]));
1871 notifier->disconnect();
1872 notifier->deleteLater();
1876 [self stopWatchers];
1877 [self disconnectFromDevice];
#define QT_BT_MAC_AUTORELEASEPOOL
ObjCStrongReference< NSError > qt_timeoutNSError(OperationTimeout type)
auto qt_find_watchdog(const std::vector< GCDTimer > &watchdogs, id object, OperationTimeout type)
NSUInteger qt_countGATTEntries(CBService *service)