Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Copyright (C) 2016 Javier S. Pedro <maemo@javispedro.com> |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the QtBluetooth module of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:LGPL$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU Lesser General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
22 | ** packaging of this file. Please review the following information to |
23 | ** ensure the GNU Lesser General Public License version 3 requirements |
24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
25 | ** |
26 | ** GNU General Public License Usage |
27 | ** Alternatively, this file may be used under the terms of the GNU |
28 | ** General Public License version 2.0 or (at your option) the GNU General |
29 | ** Public license version 3 or any later version approved by the KDE Free |
30 | ** Qt Foundation. The licenses are as published by the Free Software |
31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
32 | ** included in the packaging of this file. Please review the following |
33 | ** information to ensure the GNU General Public License requirements will |
34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
35 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
36 | ** |
37 | ** $QT_END_LICENSE$ |
38 | ** |
39 | ****************************************************************************/ |
40 | |
41 | #include "osx/osxbtutility_p.h" |
42 | #include "osx/uistrings_p.h" |
43 | |
44 | #ifndef Q_OS_TVOS |
45 | #include "osx/osxbtperipheralmanager_p.h" |
46 | #endif // Q_OS_TVOS |
47 | |
48 | #include "qlowenergycontroller_darwin_p.h" |
49 | #include "qlowenergyserviceprivate_p.h" |
50 | #include "osx/osxbtcentralmanager_p.h" |
51 | |
52 | #include "qlowenergyservicedata.h" |
53 | #include "qbluetoothlocaldevice.h" |
54 | #include "qbluetoothdeviceinfo.h" |
55 | #include "qlowenergycontroller.h" |
56 | #include "qbluetoothuuid.h" |
57 | |
58 | #include <QtCore/qloggingcategory.h> |
59 | #include <QtCore/qsharedpointer.h> |
60 | #include <QtCore/qbytearray.h> |
61 | #include <QtCore/qglobal.h> |
62 | #include <QtCore/qstring.h> |
63 | #include <QtCore/qlist.h> |
64 | |
65 | QT_BEGIN_NAMESPACE |
66 | |
67 | namespace { |
68 | |
69 | typedef QSharedPointer<QLowEnergyServicePrivate> ServicePrivate; |
70 | |
71 | // Convenience function, can return a smart pointer that 'isNull'. |
72 | ServicePrivate qt_createLEService(QLowEnergyControllerPrivateDarwin *controller, CBService *cbService, bool included) |
73 | { |
74 | Q_ASSERT_X(controller, Q_FUNC_INFO, "invalid controller (null)"); |
75 | Q_ASSERT_X(cbService, Q_FUNC_INFO, "invalid service (nil)"); |
76 | |
77 | CBUUID *const cbUuid = cbService.UUID; |
78 | if (!cbUuid) { |
79 | qCDebug(QT_BT_OSX) << "invalid service, UUID is nil"; |
80 | return ServicePrivate(); |
81 | } |
82 | |
83 | const QBluetoothUuid qtUuid(OSXBluetooth::qt_uuid(cbUuid)); |
84 | if (qtUuid.isNull()) // Conversion error is reported by qt_uuid. |
85 | return ServicePrivate(); |
86 | |
87 | ServicePrivate newService(new QLowEnergyServicePrivate); |
88 | newService->uuid = qtUuid; |
89 | newService->setController(controller); |
90 | |
91 | if (included) |
92 | newService->type |= QLowEnergyService::IncludedService; |
93 | |
94 | // TODO: isPrimary is ... always 'NO' - to be investigated. |
95 | /* |
96 | if (!cbService.isPrimary) { |
97 | // Our guess included/not was probably wrong. |
98 | newService->type &= ~QLowEnergyService::PrimaryService; |
99 | newService->type |= QLowEnergyService::IncludedService; |
100 | } |
101 | */ |
102 | return newService; |
103 | } |
104 | |
105 | typedef QList<QBluetoothUuid> UUIDList; |
106 | |
107 | UUIDList qt_servicesUuids(NSArray *services) |
108 | { |
109 | QT_BT_MAC_AUTORELEASEPOOL; |
110 | |
111 | if (!services || !services.count) |
112 | return UUIDList(); |
113 | |
114 | UUIDList uuids; |
115 | |
116 | for (CBService *s in services) |
117 | uuids.append(OSXBluetooth::qt_uuid(s.UUID)); |
118 | |
119 | return uuids; |
120 | } |
121 | |
122 | } // unnamed namespace |
123 | |
124 | #ifndef Q_OS_TVOS |
125 | using ObjCPeripheralManager = QT_MANGLE_NAMESPACE(OSXBTPeripheralManager); |
126 | #endif // Q_OS_TVOS |
127 | |
128 | using ObjCCentralManager = QT_MANGLE_NAMESPACE(OSXBTCentralManager); |
129 | |
130 | QLowEnergyControllerPrivateDarwin::QLowEnergyControllerPrivateDarwin() |
131 | { |
132 | void registerQLowEnergyControllerMetaType(); |
133 | registerQLowEnergyControllerMetaType(); |
134 | qRegisterMetaType<QLowEnergyHandle>("QLowEnergyHandle"); |
135 | qRegisterMetaType<QSharedPointer<QLowEnergyServicePrivate>>(); |
136 | } |
137 | |
138 | QLowEnergyControllerPrivateDarwin::~QLowEnergyControllerPrivateDarwin() |
139 | { |
140 | if (const auto leQueue = OSXBluetooth::qt_LE_queue()) { |
141 | if (role == QLowEnergyController::CentralRole) { |
142 | const auto manager = centralManager.getAs<ObjCCentralManager>(); |
143 | dispatch_sync(leQueue, ^{ |
144 | [manager detach]; |
145 | }); |
146 | } else { |
147 | #ifndef Q_OS_TVOS |
148 | const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); |
149 | dispatch_sync(leQueue, ^{ |
150 | [manager detach]; |
151 | }); |
152 | #endif |
153 | } |
154 | } |
155 | } |
156 | |
157 | bool QLowEnergyControllerPrivateDarwin::isValid() const |
158 | { |
159 | #ifdef Q_OS_TVOS |
160 | return centralManager; |
161 | #else |
162 | return centralManager || peripheralManager; |
163 | #endif |
164 | } |
165 | |
166 | void QLowEnergyControllerPrivateDarwin::init() |
167 | { |
168 | using OSXBluetooth::LECBManagerNotifier; |
169 | |
170 | QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier); |
171 | if (role == QLowEnergyController::PeripheralRole) { |
172 | #ifndef Q_OS_TVOS |
173 | peripheralManager.reset([[ObjCPeripheralManager alloc] initWith:notifier.data()], |
174 | DarwinBluetooth::RetainPolicy::noInitialRetain); |
175 | if (!peripheralManager) { |
176 | qCWarning(QT_BT_OSX) << "failed to create a peripheral manager"; |
177 | return; |
178 | } |
179 | #else |
180 | qCWarning(QT_BT_OSX) << "the peripheral role is not supported on your platform"; |
181 | return; |
182 | #endif // Q_OS_TVOS |
183 | } else { |
184 | centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()], |
185 | DarwinBluetooth::RetainPolicy::noInitialRetain); |
186 | if (!centralManager) { |
187 | qCWarning(QT_BT_OSX) << "failed to initialize a central manager"; |
188 | return; |
189 | } |
190 | } |
191 | |
192 | if (!connectSlots(notifier.data())) |
193 | qCWarning(QT_BT_OSX) << "failed to connect to notifier's signal(s)"; |
194 | |
195 | // Ownership was taken by central manager. |
196 | notifier.take(); |
197 | } |
198 | |
199 | void QLowEnergyControllerPrivateDarwin::connectToDevice() |
200 | { |
201 | Q_ASSERT_X(state == QLowEnergyController::UnconnectedState, |
202 | Q_FUNC_INFO, "invalid state"); |
203 | |
204 | if (!isValid()) { |
205 | // init() had failed for was never called. |
206 | return _q_CBManagerError(QLowEnergyController::UnknownError); |
207 | } |
208 | |
209 | if (deviceUuid.isNull()) { |
210 | // Wrong constructor was used or invalid UUID was provided. |
211 | return _q_CBManagerError(QLowEnergyController::UnknownRemoteDeviceError); |
212 | } |
213 | |
214 | // The logic enforcing the role is in the public class. |
215 | Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, |
216 | Q_FUNC_INFO, "invalid role (peripheral)"); |
217 | |
218 | dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); |
219 | if (!leQueue) { |
220 | qCWarning(QT_BT_OSX) << "no LE queue found"; |
221 | setErrorDescription(QLowEnergyController::UnknownError); |
222 | return; |
223 | } |
224 | |
225 | setErrorDescription(QLowEnergyController::NoError); |
226 | setState(QLowEnergyController::ConnectingState); |
227 | |
228 | const QBluetoothUuid deviceUuidCopy(deviceUuid); |
229 | ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); |
230 | dispatch_async(leQueue, ^{ |
231 | [manager connectToDevice:deviceUuidCopy]; |
232 | }); |
233 | } |
234 | |
235 | void QLowEnergyControllerPrivateDarwin::disconnectFromDevice() |
236 | { |
237 | if (role == QLowEnergyController::PeripheralRole) { |
238 | // CoreBluetooth API intentionally does not provide any way of closing |
239 | // a connection. All we can do here is to stop the advertisement. |
240 | stopAdvertising(); |
241 | return; |
242 | } |
243 | |
244 | if (isValid()) { |
245 | const auto oldState = state; |
246 | |
247 | if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { |
248 | setState(QLowEnergyController::ClosingState); |
249 | invalidateServices(); |
250 | |
251 | auto manager = centralManager.getAs<ObjCCentralManager>(); |
252 | dispatch_async(leQueue, ^{ |
253 | [manager disconnectFromDevice]; |
254 | }); |
255 | |
256 | if (oldState == QLowEnergyController::ConnectingState) { |
257 | // With a pending connect attempt there is no |
258 | // guarantee we'll ever have didDisconnect callback, |
259 | // set the state here and now to make sure we still |
260 | // can connect. |
261 | setState(QLowEnergyController::UnconnectedState); |
262 | } |
263 | } else { |
264 | qCCritical(QT_BT_OSX) << "qt LE queue is nil, " |
265 | "can not dispatch 'disconnect'"; |
266 | } |
267 | } |
268 | } |
269 | |
270 | void QLowEnergyControllerPrivateDarwin::discoverServices() |
271 | { |
272 | Q_ASSERT_X(state != QLowEnergyController::UnconnectedState, |
273 | Q_FUNC_INFO, "not connected to peripheral"); |
274 | Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, |
275 | Q_FUNC_INFO, "invalid role (peripheral)"); |
276 | |
277 | dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); |
278 | Q_ASSERT_X(leQueue, Q_FUNC_INFO, "LE queue not found"); |
279 | |
280 | setState(QLowEnergyController::DiscoveringState); |
281 | |
282 | ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); |
283 | dispatch_async(leQueue, ^{ |
284 | [manager discoverServices]; |
285 | }); |
286 | } |
287 | |
288 | void QLowEnergyControllerPrivateDarwin::discoverServiceDetails(const QBluetoothUuid &serviceUuid) |
289 | { |
290 | if (state != QLowEnergyController::DiscoveredState) { |
291 | qCWarning(QT_BT_OSX) << "can not discover service details in the current state, " |
292 | "QLowEnergyController::DiscoveredState is expected"; |
293 | return; |
294 | } |
295 | |
296 | if (!serviceList.contains(serviceUuid)) { |
297 | qCWarning(QT_BT_OSX) << "unknown service: "<< serviceUuid; |
298 | return; |
299 | } |
300 | |
301 | dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); |
302 | Q_ASSERT(leQueue); |
303 | |
304 | ServicePrivate qtService(serviceList.value(serviceUuid)); |
305 | qtService->setState(QLowEnergyService::DiscoveringServices); |
306 | // Copy objects ... |
307 | ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); |
308 | const QBluetoothUuid serviceUuidCopy(serviceUuid); |
309 | dispatch_async(leQueue, ^{ |
310 | [manager discoverServiceDetails:serviceUuidCopy]; |
311 | }); |
312 | } |
313 | |
314 | void QLowEnergyControllerPrivateDarwin::requestConnectionUpdate(const QLowEnergyConnectionParameters ¶ms) |
315 | { |
316 | Q_UNUSED(params); |
317 | // TODO: implement this, if possible. |
318 | qCWarning(QT_BT_OSX) << "Connection update not implemented on your platform"; |
319 | } |
320 | |
321 | void QLowEnergyControllerPrivateDarwin::addToGenericAttributeList(const QLowEnergyServiceData &service, |
322 | QLowEnergyHandle startHandle) |
323 | { |
324 | Q_UNUSED(service); |
325 | Q_UNUSED(startHandle); |
326 | // TODO: check why I don't need this (apparently it is used in addServiceHelper |
327 | // of the base class). |
328 | } |
329 | |
330 | QLowEnergyService * QLowEnergyControllerPrivateDarwin::addServiceHelper(const QLowEnergyServiceData &service) |
331 | { |
332 | // Three checks below should be removed, they are done in the q_ptr's class. |
333 | #ifdef Q_OS_TVOS |
334 | Q_UNUSED(service); |
335 | qCDebug(QT_BT_OSX, "peripheral role is not supported on tvOS"); |
336 | #else |
337 | if (role != QLowEnergyController::PeripheralRole) { |
338 | qCWarning(QT_BT_OSX) << "not in peripheral role"; |
339 | return nullptr; |
340 | } |
341 | |
342 | if (state != QLowEnergyController::UnconnectedState) { |
343 | qCWarning(QT_BT_OSX) << "invalid state"; |
344 | return nullptr; |
345 | } |
346 | |
347 | if (!service.isValid()) { |
348 | qCWarning(QT_BT_OSX) << "invalid service"; |
349 | return nullptr; |
350 | } |
351 | |
352 | for (auto includedService : service.includedServices()) |
353 | includedService->d_ptr->type |= QLowEnergyService::IncludedService; |
354 | |
355 | const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); |
356 | Q_ASSERT(manager); |
357 | if (const auto servicePrivate = [manager addService:service]) { |
358 | servicePrivate->setController(this); |
359 | servicePrivate->state = QLowEnergyService::LocalService; |
360 | localServices.insert(servicePrivate->uuid, servicePrivate); |
361 | return new QLowEnergyService(servicePrivate); |
362 | } |
363 | #endif // Q_OS_TVOS |
364 | return nullptr; |
365 | } |
366 | |
367 | void QLowEnergyControllerPrivateDarwin::_q_connected() |
368 | { |
369 | setState(QLowEnergyController::ConnectedState); |
370 | emit q_ptr->connected(); |
371 | } |
372 | |
373 | void QLowEnergyControllerPrivateDarwin::_q_disconnected() |
374 | { |
375 | if (role == QLowEnergyController::CentralRole) |
376 | invalidateServices(); |
377 | |
378 | setState(QLowEnergyController::UnconnectedState); |
379 | emit q_ptr->disconnected(); |
380 | } |
381 | |
382 | void QLowEnergyControllerPrivateDarwin::_q_serviceDiscoveryFinished() |
383 | { |
384 | Q_ASSERT_X(state == QLowEnergyController::DiscoveringState, |
385 | Q_FUNC_INFO, "invalid state"); |
386 | |
387 | using namespace OSXBluetooth; |
388 | |
389 | QT_BT_MAC_AUTORELEASEPOOL; |
390 | |
391 | NSArray *const services = [centralManager.getAs<ObjCCentralManager>() peripheral].services; |
392 | // Now we have to traverse the discovered services tree. |
393 | // Essentially it's an iterative version of more complicated code from the |
394 | // OSXBTCentralManager's code. |
395 | // All Obj-C entities either auto-release, or guarded by ObjCScopedReferences. |
396 | if (services && [services count]) { |
397 | QMap<QBluetoothUuid, CBService *> discoveredCBServices; |
398 | //1. The first pass - none of this services is 'included' yet (we'll discover 'included' |
399 | // during the pass 2); we also ignore duplicates (== services with the same UUID) |
400 | // - since we do not have a way to distinguish them later |
401 | // (our API is using uuids when creating QLowEnergyServices). |
402 | for (CBService *cbService in services) { |
403 | const ServicePrivate newService(qt_createLEService(this, cbService, false)); |
404 | if (!newService.data()) |
405 | continue; |
406 | if (serviceList.contains(newService->uuid)) { |
407 | // It's a bit stupid we first created it ... |
408 | qCDebug(QT_BT_OSX) << "discovered service with a duplicated UUID" |
409 | << newService->uuid; |
410 | continue; |
411 | } |
412 | serviceList.insert(newService->uuid, newService); |
413 | discoveredCBServices.insert(newService->uuid, cbService); |
414 | } |
415 | |
416 | ObjCStrongReference<NSMutableArray> toVisit([[NSMutableArray alloc] initWithArray:services], false); |
417 | ObjCStrongReference<NSMutableArray> toVisitNext([[NSMutableArray alloc] init], false); |
418 | ObjCStrongReference<NSMutableSet> visited([[NSMutableSet alloc] init], false); |
419 | |
420 | while (true) { |
421 | for (NSUInteger i = 0, e = [toVisit count]; i < e; ++i) { |
422 | CBService *const s = [toVisit objectAtIndex:i]; |
423 | if (![visited containsObject:s]) { |
424 | [visited addObject:s]; |
425 | if (s.includedServices && s.includedServices.count) |
426 | [toVisitNext addObjectsFromArray:s.includedServices]; |
427 | } |
428 | |
429 | const QBluetoothUuid uuid(qt_uuid(s.UUID)); |
430 | if (serviceList.contains(uuid) && discoveredCBServices.value(uuid) == s) { |
431 | ServicePrivate qtService(serviceList.value(uuid)); |
432 | // Add included UUIDs: |
433 | qtService->includedServices.append(qt_servicesUuids(s.includedServices)); |
434 | }// Else - we ignored this CBService object. |
435 | } |
436 | |
437 | if (![toVisitNext count]) |
438 | break; |
439 | |
440 | for (NSUInteger i = 0, e = [toVisitNext count]; i < e; ++i) { |
441 | CBService *const s = [toVisitNext objectAtIndex:i]; |
442 | const QBluetoothUuid uuid(qt_uuid(s.UUID)); |
443 | if (serviceList.contains(uuid)) { |
444 | if (discoveredCBServices.value(uuid) == s) { |
445 | ServicePrivate qtService(serviceList.value(uuid)); |
446 | qtService->type |= QLowEnergyService::IncludedService; |
447 | } // Else this is the duplicate we ignored already. |
448 | } else { |
449 | // Oh, we do not even have it yet??? |
450 | ServicePrivate newService(qt_createLEService(this, s, true)); |
451 | serviceList.insert(newService->uuid, newService); |
452 | discoveredCBServices.insert(newService->uuid, s); |
453 | } |
454 | } |
455 | |
456 | toVisit.resetWithoutRetain(toVisitNext.take()); |
457 | toVisitNext.resetWithoutRetain([[NSMutableArray alloc] init]); |
458 | } |
459 | } else { |
460 | qCDebug(QT_BT_OSX) << "no services found"; |
461 | } |
462 | |
463 | for (ServiceMap::const_iterator it = serviceList.constBegin(); it != serviceList.constEnd(); ++it) |
464 | emit q_ptr->serviceDiscovered(it.key()); |
465 | |
466 | setState(QLowEnergyController::DiscoveredState); |
467 | emit q_ptr->discoveryFinished(); |
468 | } |
469 | |
470 | void QLowEnergyControllerPrivateDarwin::_q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service) |
471 | { |
472 | QT_BT_MAC_AUTORELEASEPOOL; |
473 | |
474 | Q_ASSERT(service); |
475 | |
476 | if (!serviceList.contains(service->uuid)) { |
477 | qCDebug(QT_BT_OSX) << "unknown service uuid:" |
478 | << service->uuid; |
479 | return; |
480 | } |
481 | |
482 | ServicePrivate qtService(serviceList.value(service->uuid)); |
483 | // Assert on handles? |
484 | qtService->startHandle = service->startHandle; |
485 | qtService->endHandle = service->endHandle; |
486 | qtService->characteristicList = service->characteristicList; |
487 | |
488 | qtService->setState(QLowEnergyService::ServiceDiscovered); |
489 | } |
490 | |
491 | void QLowEnergyControllerPrivateDarwin::_q_servicesWereModified() |
492 | { |
493 | if (!(state == QLowEnergyController::DiscoveringState |
494 | || state == QLowEnergyController::DiscoveredState)) { |
495 | qCWarning(QT_BT_OSX) << "services were modified while controller is not in Discovered/Discovering state"; |
496 | return; |
497 | } |
498 | |
499 | if (state == QLowEnergyController::DiscoveredState) |
500 | invalidateServices(); |
501 | |
502 | setState(QLowEnergyController::ConnectedState); |
503 | q_ptr->discoverServices(); |
504 | } |
505 | |
506 | void QLowEnergyControllerPrivateDarwin::_q_characteristicRead(QLowEnergyHandle charHandle, |
507 | const QByteArray &value) |
508 | { |
509 | Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)"); |
510 | |
511 | ServicePrivate service(serviceForHandle(charHandle)); |
512 | if (service.isNull()) |
513 | return; |
514 | |
515 | QLowEnergyCharacteristic characteristic(characteristicForHandle(charHandle)); |
516 | if (!characteristic.isValid()) { |
517 | qCWarning(QT_BT_OSX) << "unknown characteristic"; |
518 | return; |
519 | } |
520 | |
521 | if (characteristic.properties() & QLowEnergyCharacteristic::Read) |
522 | updateValueOfCharacteristic(charHandle, value, false); |
523 | |
524 | emit service->characteristicRead(characteristic, value); |
525 | } |
526 | |
527 | void QLowEnergyControllerPrivateDarwin::_q_characteristicWritten(QLowEnergyHandle charHandle, |
528 | const QByteArray &value) |
529 | { |
530 | Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)"); |
531 | |
532 | ServicePrivate service(serviceForHandle(charHandle)); |
533 | if (service.isNull()) { |
534 | qCWarning(QT_BT_OSX) << "can not find service for characteristic handle" |
535 | << charHandle; |
536 | return; |
537 | } |
538 | |
539 | QLowEnergyCharacteristic characteristic(characteristicForHandle(charHandle)); |
540 | if (!characteristic.isValid()) { |
541 | qCWarning(QT_BT_OSX) << "unknown characteristic"; |
542 | return; |
543 | } |
544 | |
545 | if (characteristic.properties() & QLowEnergyCharacteristic::Read) |
546 | updateValueOfCharacteristic(charHandle, value, false); |
547 | |
548 | emit service->characteristicWritten(characteristic, value); |
549 | } |
550 | |
551 | void QLowEnergyControllerPrivateDarwin::_q_characteristicUpdated(QLowEnergyHandle charHandle, |
552 | const QByteArray &value) |
553 | { |
554 | // TODO: write/update notifications are quite similar (except asserts/warnings messages |
555 | // and different signals emitted). Merge them into one function? |
556 | Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)"); |
557 | |
558 | ServicePrivate service(serviceForHandle(charHandle)); |
559 | if (service.isNull()) { |
560 | // This can be an error (no characteristic found for this handle), |
561 | // it can also be that we set notify value before the service |
562 | // was reported (serviceDetailsDiscoveryFinished) - this happens, |
563 | // if we read a descriptor (characteristic client configuration), |
564 | // and it's (pre)set. |
565 | return; |
566 | } |
567 | |
568 | QLowEnergyCharacteristic characteristic(characteristicForHandle(charHandle)); |
569 | if (!characteristic.isValid()) { |
570 | qCWarning(QT_BT_OSX) << "unknown characteristic"; |
571 | return; |
572 | } |
573 | |
574 | if (characteristic.properties() & QLowEnergyCharacteristic::Read) |
575 | updateValueOfCharacteristic(charHandle, value, false); |
576 | |
577 | emit service->characteristicChanged(characteristic, value); |
578 | } |
579 | |
580 | void QLowEnergyControllerPrivateDarwin::_q_descriptorRead(QLowEnergyHandle dHandle, |
581 | const QByteArray &value) |
582 | { |
583 | Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)"); |
584 | |
585 | const QLowEnergyDescriptor qtDescriptor(descriptorForHandle(dHandle)); |
586 | if (!qtDescriptor.isValid()) { |
587 | qCWarning(QT_BT_OSX) << "unknown descriptor"<< dHandle; |
588 | return; |
589 | } |
590 | |
591 | ServicePrivate service(serviceForHandle(qtDescriptor.characteristicHandle())); |
592 | updateValueOfDescriptor(qtDescriptor.characteristicHandle(), dHandle, value, false); |
593 | emit service->descriptorRead(qtDescriptor, value); |
594 | } |
595 | |
596 | void QLowEnergyControllerPrivateDarwin::_q_descriptorWritten(QLowEnergyHandle dHandle, |
597 | const QByteArray &value) |
598 | { |
599 | Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)"); |
600 | |
601 | const QLowEnergyDescriptor qtDescriptor(descriptorForHandle(dHandle)); |
602 | if (!qtDescriptor.isValid()) { |
603 | qCWarning(QT_BT_OSX) << "unknown descriptor"<< dHandle; |
604 | return; |
605 | } |
606 | |
607 | ServicePrivate service(serviceForHandle(qtDescriptor.characteristicHandle())); |
608 | // TODO: test if this data is what we expected. |
609 | updateValueOfDescriptor(qtDescriptor.characteristicHandle(), dHandle, value, false); |
610 | emit service->descriptorWritten(qtDescriptor, value); |
611 | } |
612 | |
613 | void QLowEnergyControllerPrivateDarwin::_q_notificationEnabled(QLowEnergyHandle charHandle, |
614 | bool enabled) |
615 | { |
616 | // CoreBluetooth in peripheral role does not allow mutable descriptors, |
617 | // in central we can only call setNotification:enabled/disabled. |
618 | // But from Qt API's point of view, a central has to write into |
619 | // client characteristic configuration descriptor. So here we emulate |
620 | // such a write (we cannot say if it's a notification or indication and |
621 | // report as both). |
622 | |
623 | Q_ASSERT_X(role == QLowEnergyController::PeripheralRole, Q_FUNC_INFO, |
624 | "controller has an invalid role, 'peripheral' expected"); |
625 | Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle (0)"); |
626 | |
627 | const QLowEnergyCharacteristic qtChar(characteristicForHandle(charHandle)); |
628 | if (!qtChar.isValid()) { |
629 | qCWarning(QT_BT_OSX) << "unknown characteristic"<< charHandle; |
630 | return; |
631 | } |
632 | |
633 | const QLowEnergyDescriptor qtDescriptor = |
634 | qtChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration); |
635 | if (!qtDescriptor.isValid()) { |
636 | qCWarning(QT_BT_OSX) << "characteristic"<< charHandle |
637 | << "does not have a client characteristic " |
638 | "descriptor"; |
639 | return; |
640 | } |
641 | |
642 | ServicePrivate service(serviceForHandle(charHandle)); |
643 | if (service.data()) { |
644 | // It's a 16-bit value, the least significant bit is for notifications, |
645 | // the next one - for indications (thus 1 means notifications enabled, |
646 | // 2 - indications enabled). |
647 | // 3 is the maximum value and it means both enabled. |
648 | QByteArray value(2, 0); |
649 | if (enabled) |
650 | value[0] = 3; |
651 | updateValueOfDescriptor(charHandle, qtDescriptor.handle(), value, false); |
652 | emit service->descriptorWritten(qtDescriptor, value); |
653 | } |
654 | } |
655 | |
656 | void QLowEnergyControllerPrivateDarwin::_q_LEnotSupported() |
657 | { |
658 | // Report as an error. But this should not be possible |
659 | // actually: before connecting to any device, we have |
660 | // to discover it, if it was discovered ... LE _must_ |
661 | // be supported. |
662 | } |
663 | |
664 | void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(QLowEnergyController::Error errorCode) |
665 | { |
666 | // This function handles errors reported while connecting to a remote device |
667 | // and also other errors in general. |
668 | setError(errorCode); |
669 | |
670 | if (state == QLowEnergyController::ConnectingState) |
671 | setState(QLowEnergyController::UnconnectedState); |
672 | else if (state == QLowEnergyController::DiscoveringState) |
673 | setState(QLowEnergyController::ConnectedState); |
674 | |
675 | // In any other case we stay in Discovered, it's |
676 | // a service/characteristic - related error. |
677 | } |
678 | |
679 | void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(const QBluetoothUuid &serviceUuid, |
680 | QLowEnergyController::Error errorCode) |
681 | { |
682 | // Errors reported while discovering service details etc. |
683 | Q_UNUSED(errorCode) // TODO: setError? |
684 | |
685 | // We failed to discover any characteristics/descriptors. |
686 | if (serviceList.contains(serviceUuid)) { |
687 | ServicePrivate qtService(serviceList.value(serviceUuid)); |
688 | qtService->setState(QLowEnergyService::InvalidService); |
689 | } else { |
690 | qCDebug(QT_BT_OSX) << "error reported for unknown service" |
691 | << serviceUuid; |
692 | } |
693 | } |
694 | |
695 | void QLowEnergyControllerPrivateDarwin::_q_CBManagerError(const QBluetoothUuid &serviceUuid, |
696 | QLowEnergyService::ServiceError errorCode) |
697 | { |
698 | if (!serviceList.contains(serviceUuid)) { |
699 | qCDebug(QT_BT_OSX) << "unknown service uuid:" |
700 | << serviceUuid; |
701 | return; |
702 | } |
703 | |
704 | ServicePrivate service(serviceList.value(serviceUuid)); |
705 | service->setError(errorCode); |
706 | } |
707 | |
708 | void QLowEnergyControllerPrivateDarwin::setNotifyValue(QSharedPointer<QLowEnergyServicePrivate> service, |
709 | QLowEnergyHandle charHandle, |
710 | const QByteArray &newValue) |
711 | { |
712 | Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); |
713 | |
714 | if (role == QLowEnergyController::PeripheralRole) { |
715 | qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; |
716 | service->setError(QLowEnergyService::DescriptorWriteError); |
717 | return; |
718 | } |
719 | |
720 | if (newValue.size() > 2) { |
721 | // Qt's API requires an error on such write. |
722 | // With Core Bluetooth we do not write any descriptor, |
723 | // but instead call a special method. So it's better to |
724 | // intercept wrong data size here: |
725 | qCWarning(QT_BT_OSX) << "client characteristic configuration descriptor" |
726 | "is 2 bytes, but value size is: "<< newValue.size(); |
727 | service->setError(QLowEnergyService::DescriptorWriteError); |
728 | return; |
729 | } |
730 | |
731 | if (!serviceList.contains(service->uuid)) { |
732 | qCWarning(QT_BT_OSX) << "no service with uuid:"<< service->uuid << "found"; |
733 | return; |
734 | } |
735 | |
736 | if (!service->characteristicList.contains(charHandle)) { |
737 | qCDebug(QT_BT_OSX) << "no characteristic with handle:" |
738 | << charHandle << "found"; |
739 | return; |
740 | } |
741 | |
742 | dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); |
743 | Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); |
744 | |
745 | ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); |
746 | const QBluetoothUuid serviceUuid(service->uuid); |
747 | const QByteArray newValueCopy(newValue); |
748 | dispatch_async(leQueue, ^{ |
749 | [manager setNotifyValue:newValueCopy |
750 | forCharacteristic:charHandle |
751 | onService:serviceUuid]; |
752 | }); |
753 | } |
754 | |
755 | void QLowEnergyControllerPrivateDarwin::readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service, |
756 | const QLowEnergyHandle charHandle) |
757 | { |
758 | Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); |
759 | |
760 | if (role == QLowEnergyController::PeripheralRole) { |
761 | qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; |
762 | return; |
763 | } |
764 | |
765 | if (!serviceList.contains(service->uuid)) { |
766 | qCWarning(QT_BT_OSX) << "no service with uuid:" |
767 | << service->uuid << "found"; |
768 | return; |
769 | } |
770 | |
771 | if (!service->characteristicList.contains(charHandle)) { |
772 | qCDebug(QT_BT_OSX) << "no characteristic with handle:" |
773 | << charHandle << "found"; |
774 | return; |
775 | } |
776 | |
777 | dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); |
778 | Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); |
779 | |
780 | // Attention! We have to copy UUID. |
781 | ObjCCentralManager *manager = centralManager.getAs<ObjCCentralManager>(); |
782 | const QBluetoothUuid serviceUuid(service->uuid); |
783 | dispatch_async(leQueue, ^{ |
784 | [manager readCharacteristic:charHandle onService:serviceUuid]; |
785 | }); |
786 | } |
787 | |
788 | void QLowEnergyControllerPrivateDarwin::writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service, |
789 | const QLowEnergyHandle charHandle, const QByteArray &newValue, |
790 | QLowEnergyService::WriteMode mode) |
791 | { |
792 | Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); |
793 | |
794 | // We can work only with services found on a given peripheral |
795 | // (== created by the given LE controller). |
796 | |
797 | if (!serviceList.contains(service->uuid) && !localServices.contains(service->uuid)) { |
798 | qCWarning(QT_BT_OSX) << "no service with uuid:" |
799 | << service->uuid << " found"; |
800 | return; |
801 | } |
802 | |
803 | if (!service->characteristicList.contains(charHandle)) { |
804 | qCDebug(QT_BT_OSX) << "no characteristic with handle:" |
805 | << charHandle << " found"; |
806 | return; |
807 | } |
808 | |
809 | dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); |
810 | Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); |
811 | // Attention! We have to copy objects! |
812 | const QByteArray newValueCopy(newValue); |
813 | if (role == QLowEnergyController::CentralRole) { |
814 | const QBluetoothUuid serviceUuid(service->uuid); |
815 | const auto manager = centralManager.getAs<ObjCCentralManager>(); |
816 | dispatch_async(leQueue, ^{ |
817 | [manager write:newValueCopy |
818 | charHandle:charHandle |
819 | onService:serviceUuid |
820 | withResponse:mode == QLowEnergyService::WriteWithResponse]; |
821 | }); |
822 | } else { |
823 | #ifndef Q_OS_TVOS |
824 | const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); |
825 | dispatch_async(leQueue, ^{ |
826 | [manager write:newValueCopy charHandle:charHandle]; |
827 | }); |
828 | #else |
829 | qCWarning(QT_BT_OSX) << "peripheral role is not supported on your platform"; |
830 | #endif |
831 | } |
832 | } |
833 | |
834 | quint16 QLowEnergyControllerPrivateDarwin::updateValueOfCharacteristic(QLowEnergyHandle charHandle, |
835 | const QByteArray &value, |
836 | bool appendValue) |
837 | { |
838 | QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); |
839 | if (!service.isNull()) { |
840 | CharacteristicDataMap::iterator charIt = service->characteristicList.find(charHandle); |
841 | if (charIt != service->characteristicList.end()) { |
842 | QLowEnergyServicePrivate::CharData &charData = charIt.value(); |
843 | if (appendValue) |
844 | charData.value += value; |
845 | else |
846 | charData.value = value; |
847 | |
848 | return charData.value.size(); |
849 | } |
850 | } |
851 | |
852 | return 0; |
853 | } |
854 | |
855 | void QLowEnergyControllerPrivateDarwin::readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service, |
856 | const QLowEnergyHandle charHandle, |
857 | const QLowEnergyHandle descriptorHandle) |
858 | { |
859 | Q_UNUSED(charHandle) // Hehe, yes! |
860 | |
861 | Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); |
862 | |
863 | if (role == QLowEnergyController::PeripheralRole) { |
864 | qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; |
865 | return; |
866 | } |
867 | |
868 | if (!serviceList.contains(service->uuid)) { |
869 | qCWarning(QT_BT_OSX) << "no service with uuid:" |
870 | << service->uuid << "found"; |
871 | return; |
872 | } |
873 | |
874 | dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); |
875 | if (!leQueue) { |
876 | qCWarning(QT_BT_OSX) << "no LE queue found"; |
877 | return; |
878 | } |
879 | // Attention! Copy objects! |
880 | const QBluetoothUuid serviceUuid(service->uuid); |
881 | ObjCCentralManager * const manager = centralManager.getAs<ObjCCentralManager>(); |
882 | dispatch_async(leQueue, ^{ |
883 | [manager readDescriptor:descriptorHandle |
884 | onService:serviceUuid]; |
885 | }); |
886 | } |
887 | |
888 | void QLowEnergyControllerPrivateDarwin::writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service, |
889 | const QLowEnergyHandle charHandle, |
890 | const QLowEnergyHandle descriptorHandle, |
891 | const QByteArray &newValue) |
892 | { |
893 | Q_UNUSED(charHandle) |
894 | |
895 | Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)"); |
896 | |
897 | if (role == QLowEnergyController::PeripheralRole) { |
898 | qCWarning(QT_BT_OSX) << "invalid role (peripheral)"; |
899 | return; |
900 | } |
901 | |
902 | // We can work only with services found on a given peripheral |
903 | // (== created by the given LE controller), |
904 | // otherwise we can not write anything at all. |
905 | if (!serviceList.contains(service->uuid)) { |
906 | qCWarning(QT_BT_OSX) << "no service with uuid:" |
907 | << service->uuid << " found"; |
908 | return; |
909 | } |
910 | |
911 | dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); |
912 | Q_ASSERT_X(leQueue, Q_FUNC_INFO, "no LE queue found"); |
913 | // Attention! Copy objects! |
914 | const QBluetoothUuid serviceUuid(service->uuid); |
915 | ObjCCentralManager * const manager = centralManager.getAs<ObjCCentralManager>(); |
916 | const QByteArray newValueCopy(newValue); |
917 | dispatch_async(leQueue, ^{ |
918 | [manager write:newValueCopy |
919 | descHandle:descriptorHandle |
920 | onService:serviceUuid]; |
921 | }); |
922 | } |
923 | |
924 | quint16 QLowEnergyControllerPrivateDarwin::updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descHandle, |
925 | const QByteArray &value, bool appendValue) |
926 | { |
927 | QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); |
928 | if (!service.isNull()) { |
929 | CharacteristicDataMap::iterator charIt = service->characteristicList.find(charHandle); |
930 | if (charIt != service->characteristicList.end()) { |
931 | QLowEnergyServicePrivate::CharData &charData = charIt.value(); |
932 | |
933 | DescriptorDataMap::iterator descIt = charData.descriptorList.find(descHandle); |
934 | if (descIt != charData.descriptorList.end()) { |
935 | QLowEnergyServicePrivate::DescData &descDetails = descIt.value(); |
936 | |
937 | if (appendValue) |
938 | descDetails.value += value; |
939 | else |
940 | descDetails.value = value; |
941 | |
942 | return descDetails.value.size(); |
943 | } |
944 | } |
945 | } |
946 | |
947 | return 0; |
948 | } |
949 | |
950 | void QLowEnergyControllerPrivateDarwin::setErrorDescription(QLowEnergyController::Error errorCode) |
951 | { |
952 | // This function does not emit! |
953 | // TODO: well, it is not a reason to duplicate a significant part of |
954 | // setError though! |
955 | |
956 | error = errorCode; |
957 | |
958 | switch (error) { |
959 | case QLowEnergyController::NoError: |
960 | errorString.clear(); |
961 | break; |
962 | case QLowEnergyController::UnknownRemoteDeviceError: |
963 | errorString = QLowEnergyController::tr("Remote device cannot be found"); |
964 | break; |
965 | case QLowEnergyController::InvalidBluetoothAdapterError: |
966 | errorString = QLowEnergyController::tr("Cannot find local adapter"); |
967 | break; |
968 | case QLowEnergyController::NetworkError: |
969 | errorString = QLowEnergyController::tr("Error occurred during connection I/O"); |
970 | break; |
971 | case QLowEnergyController::ConnectionError: |
972 | errorString = QLowEnergyController::tr("Error occurred trying to connect to remote device."); |
973 | break; |
974 | case QLowEnergyController::AdvertisingError: |
975 | errorString = QLowEnergyController::tr("Error occurred trying to start advertising"); |
976 | break; |
977 | case QLowEnergyController::UnknownError: |
978 | default: |
979 | errorString = QLowEnergyController::tr("Unknown Error"); |
980 | break; |
981 | } |
982 | } |
983 | |
984 | bool QLowEnergyControllerPrivateDarwin::connectSlots(OSXBluetooth::LECBManagerNotifier *notifier) |
985 | { |
986 | using OSXBluetooth::LECBManagerNotifier; |
987 | |
988 | Q_ASSERT_X(notifier, Q_FUNC_INFO, "invalid notifier object (null)"); |
989 | |
990 | bool ok = connect(notifier, &LECBManagerNotifier::connected, |
991 | this, &QLowEnergyControllerPrivateDarwin::_q_connected); |
992 | ok = ok && connect(notifier, &LECBManagerNotifier::disconnected, |
993 | this, &QLowEnergyControllerPrivateDarwin::_q_disconnected); |
994 | ok = ok && connect(notifier, &LECBManagerNotifier::serviceDiscoveryFinished, |
995 | this, &QLowEnergyControllerPrivateDarwin::_q_serviceDiscoveryFinished); |
996 | ok = ok && connect(notifier, &LECBManagerNotifier::servicesWereModified, |
997 | this, &QLowEnergyControllerPrivateDarwin::_q_servicesWereModified); |
998 | ok = ok && connect(notifier, &LECBManagerNotifier::serviceDetailsDiscoveryFinished, |
999 | this, &QLowEnergyControllerPrivateDarwin::_q_serviceDetailsDiscoveryFinished); |
1000 | ok = ok && connect(notifier, &LECBManagerNotifier::characteristicRead, |
1001 | this, &QLowEnergyControllerPrivateDarwin::_q_characteristicRead); |
1002 | ok = ok && connect(notifier, &LECBManagerNotifier::characteristicWritten, |
1003 | this, &QLowEnergyControllerPrivateDarwin::_q_characteristicWritten); |
1004 | ok = ok && connect(notifier, &LECBManagerNotifier::characteristicUpdated, |
1005 | this, &QLowEnergyControllerPrivateDarwin::_q_characteristicUpdated); |
1006 | ok = ok && connect(notifier, &LECBManagerNotifier::descriptorRead, |
1007 | this, &QLowEnergyControllerPrivateDarwin::_q_descriptorRead); |
1008 | ok = ok && connect(notifier, &LECBManagerNotifier::descriptorWritten, |
1009 | this, &QLowEnergyControllerPrivateDarwin::_q_descriptorWritten); |
1010 | ok = ok && connect(notifier, &LECBManagerNotifier::notificationEnabled, |
1011 | this, &QLowEnergyControllerPrivateDarwin::_q_notificationEnabled); |
1012 | ok = ok && connect(notifier, &LECBManagerNotifier::LEnotSupported, |
1013 | this, &QLowEnergyControllerPrivateDarwin::_q_LEnotSupported); |
1014 | ok = ok && connect(notifier, SIGNAL(CBManagerError(QLowEnergyController::Error)), |
1015 | this, SLOT(_q_CBManagerError(QLowEnergyController::Error))); |
1016 | ok = ok && connect(notifier, SIGNAL(CBManagerError(const QBluetoothUuid &, QLowEnergyController::Error)), |
1017 | this, SLOT(_q_CBManagerError(const QBluetoothUuid &, QLowEnergyController::Error))); |
1018 | ok = ok && connect(notifier, SIGNAL(CBManagerError(const QBluetoothUuid &, QLowEnergyService::ServiceError)), |
1019 | this, SLOT(_q_CBManagerError(const QBluetoothUuid &, QLowEnergyService::ServiceError))); |
1020 | |
1021 | if (!ok) |
1022 | notifier->disconnect(); |
1023 | |
1024 | return ok; |
1025 | } |
1026 | |
1027 | void QLowEnergyControllerPrivateDarwin::startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, |
1028 | const QLowEnergyAdvertisingData &advertisingData, |
1029 | const QLowEnergyAdvertisingData &scanResponseData) |
1030 | { |
1031 | #ifdef Q_OS_TVOS |
1032 | Q_UNUSED(params) |
1033 | Q_UNUSED(advertisingData) |
1034 | Q_UNUSED(scanResponseData) |
1035 | qCWarning(QT_BT_OSX) << "advertising is not supported on your platform"; |
1036 | #else |
1037 | |
1038 | if (!isValid()) |
1039 | return _q_CBManagerError(QLowEnergyController::UnknownError); |
1040 | |
1041 | if (role != QLowEnergyController::PeripheralRole) { |
1042 | qCWarning(QT_BT_OSX) << "controller is not a peripheral, cannot start advertising"; |
1043 | return; |
1044 | } |
1045 | |
1046 | if (state != QLowEnergyController::UnconnectedState) { |
1047 | qCWarning(QT_BT_OSX) << "invalid state"<< state; |
1048 | return; |
1049 | } |
1050 | |
1051 | auto leQueue(OSXBluetooth::qt_LE_queue()); |
1052 | if (!leQueue) { |
1053 | qCWarning(QT_BT_OSX) << "no LE queue found"; |
1054 | setErrorDescription(QLowEnergyController::UnknownError); |
1055 | return; |
1056 | } |
1057 | |
1058 | const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); |
1059 | [manager setParameters:params data:advertisingData scanResponse:scanResponseData]; |
1060 | |
1061 | setState(QLowEnergyController::AdvertisingState); |
1062 | |
1063 | dispatch_async(leQueue, ^{ |
1064 | [manager startAdvertising]; |
1065 | }); |
1066 | #endif |
1067 | } |
1068 | |
1069 | void QLowEnergyControllerPrivateDarwin::stopAdvertising() |
1070 | { |
1071 | #ifdef Q_OS_TVOS |
1072 | qCWarning(QT_BT_OSX) << "advertising is not supported on your platform"; |
1073 | #else |
1074 | if (!isValid()) |
1075 | return _q_CBManagerError(QLowEnergyController::UnknownError); |
1076 | |
1077 | if (state != QLowEnergyController::AdvertisingState) { |
1078 | qCDebug(QT_BT_OSX) << "cannot stop advertising, called in state"<< state; |
1079 | return; |
1080 | } |
1081 | |
1082 | if (const auto leQueue = OSXBluetooth::qt_LE_queue()) { |
1083 | const auto manager = peripheralManager.getAs<ObjCPeripheralManager>(); |
1084 | dispatch_sync(leQueue, ^{ |
1085 | [manager stopAdvertising]; |
1086 | }); |
1087 | |
1088 | setState(QLowEnergyController::UnconnectedState); |
1089 | } else { |
1090 | qCWarning(QT_BT_OSX) << "no LE queue found"; |
1091 | setErrorDescription(QLowEnergyController::UnknownError); |
1092 | return; |
1093 | } |
1094 | #endif |
1095 | } |
1096 | |
1097 | QT_END_NAMESPACE |
1098 | |
1099 |
Warning: That file was not part of the compilation database. It may have many parsing errors.