11#include <QtCore/qloggingcategory.h>
12#include <QtCore/qdebug.h>
13#include <QtCore/qendian.h>
14#include <QtCore/qlist.h>
25 return QBluetoothUuid();
28 [nsUuid getUUIDBytes:uuidData];
29 QUuid::Id128Bytes qtUuidData = {};
30 std::copy(uuidData, uuidData + 16, qtUuidData.data);
31 return QBluetoothUuid(qtUuidData);
59 if (!advertisementData)
64 NSObject *value = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
65 if (value && [value isKindOfClass:[NSString
class]])
66 localName = QString::fromNSString(
static_cast<NSString *>(value));
71 value = [advertisementData objectForKey:CBAdvertisementDataServiceUUIDsKey];
72 if (value && [value isKindOfClass:[NSArray
class]]) {
73 NSArray *uuids =
static_cast<NSArray *>(value);
74 for (CBUUID *cbUuid in uuids)
75 serviceUuids << qt_uuid(cbUuid);
99@interface DarwinBTLEDeviceInquiry (PrivateAPI)
104@implementation DarwinBTLEDeviceInquiry
106 LECBManagerNotifier *notifier;
107 ObjCScopedPointer<CBCentralManager> manager;
109 QList<QBluetoothDeviceInfo> devices;
110 LEInquiryState internalState;
111 int inquiryTimeoutMS;
113 QT_PREPEND_NAMESPACE(DarwinBluetooth)::GCDTimer elapsedTimer;
116-(
id)initWithNotifier:(LECBManagerNotifier *)aNotifier
118 if (self = [super init]) {
120 notifier = aNotifier;
121 internalState = InquiryStarting;
122 inquiryTimeoutMS = DarwinBluetooth::defaultLEScanTimeoutMS;
131 [manager setDelegate:nil];
132 [elapsedTimer cancelTimer];
137- (
void)timeout:(id)sender
141 if (internalState == InquiryActive) {
143 [manager setDelegate:nil];
144 internalState = InquiryFinished;
146 emit notifier->discoveryFinished();
147 }
else if (internalState == InquiryStarting) {
151 [manager setDelegate:nil];
152 internalState = ErrorPoweredOff;
154 emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
158- (
void)startWithTimeout:(
int)timeout
160 dispatch_queue_t leQueue(DarwinBluetooth::qt_LE_queue());
162 inquiryTimeoutMS = timeout;
163 manager.reset([[CBCentralManager alloc] initWithDelegate:self queue:leQueue],
164 DarwinBluetooth::RetainPolicy::noInitialRetain);
167- (
void)centralManagerDidUpdateState:(CBCentralManager *)central
169#pragma clang diagnostic push
170#pragma clang diagnostic ignored "-Wunguarded-availability-new"
172 if (central != manager)
175 if (internalState != InquiryActive && internalState != InquiryStarting)
180 using namespace DarwinBluetooth;
182 const auto state = central.state;
183 if (state == CBManagerStatePoweredOn) {
184 if (internalState == InquiryStarting) {
185 internalState = InquiryActive;
187 if (inquiryTimeoutMS > 0) {
188 [elapsedTimer cancelTimer];
189 elapsedTimer.reset([[DarwinBTGCDTimer alloc] initWithDelegate:self], RetainPolicy::noInitialRetain);
190 [elapsedTimer startWithTimeout:inquiryTimeoutMS step:timeStepMS];
195 const int env = qEnvironmentVariableIntValue(
"QT_BLUETOOTH_SCAN_ENABLE_DUPLICATES", &envOk);
197 [manager scanForPeripheralsWithServices:nil
198 options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES}];
200 [manager scanForPeripheralsWithServices:nil options:nil];
203 }
else if (state == CBManagerStateUnsupported) {
204 if (internalState == InquiryActive) {
208 internalState = ErrorPoweredOff;
209 emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
211 internalState = ErrorLENotSupported;
212 emit notifier->LEnotSupported();
214 [manager setDelegate:nil];
215 }
else if (state == CBManagerStateUnauthorized) {
216 if (internalState == InquiryActive)
218 internalState = ErrorNotAuthorized;
219 emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::MissingPermissionsError);
220 [manager setDelegate:nil];
221 }
else if (state == CBManagerStatePoweredOff) {
224 if (internalState == InquiryStarting) {
229 [elapsedTimer cancelTimer];
230 elapsedTimer.reset([[DarwinBTGCDTimer alloc] initWithDelegate:self], RetainPolicy::noInitialRetain);
231 [elapsedTimer startWithTimeout:powerOffTimeoutMS step:300];
235 Q_UNUSED(powerOffTimeoutMS);
237 [elapsedTimer cancelTimer];
239 [manager setDelegate:nil];
240 internalState = ErrorPoweredOff;
244 emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
259#pragma clang diagnostic pop
269#pragma clang diagnostic push
270#pragma clang diagnostic ignored "-Wunguarded-availability-new"
272 if (internalState == InquiryActive) {
273 const auto state = manager.get().state;
274 if (state == CBManagerStatePoweredOn)
278#pragma clang diagnostic pop
284 notifier->disconnect();
285 notifier->deleteLater();
293 [manager setDelegate:nil];
294 [elapsedTimer cancelTimer];
296 internalState = InquiryCancelled;
299- (
void)centralManager:(CBCentralManager *)central
300 didDiscoverPeripheral:(CBPeripheral *)peripheral
301 advertisementData:(NSDictionary *)advertisementData
302 RSSI:(NSNumber *)RSSI
304 using namespace DarwinBluetooth;
306 if (central != manager)
309 if (internalState != InquiryActive)
315 QBluetoothUuid deviceUuid;
317 if (!peripheral.identifier) {
318 qCWarning(QT_BT_DARWIN) <<
"peripheral without NSUUID";
322 deviceUuid = DarwinBluetooth::qt_uuid(peripheral.identifier);
324 if (deviceUuid.isNull()) {
325 qCWarning(QT_BT_DARWIN) <<
"no way to address peripheral, QBluetoothUuid is null";
329 const AdvertisementData qtAdvData(advertisementData);
330 QString name(qtAdvData.localName);
331 if (!name.size() && peripheral.name)
332 name = QString::fromNSString(peripheral.name);
335 QBluetoothDeviceInfo newDeviceInfo(deviceUuid, name, 0);
337 newDeviceInfo.setRssi([RSSI shortValue]);
339 if (qtAdvData.serviceUuids.size())
340 newDeviceInfo.setServiceUuids(qtAdvData.serviceUuids);
342 const QList<quint16> keysManufacturer = qtAdvData.manufacturerData.keys();
343 for (quint16 key : keysManufacturer)
344 newDeviceInfo.setManufacturerData(key, qtAdvData.manufacturerData.value(key));
346 const QList<QBluetoothUuid> keysService = qtAdvData.serviceData.keys();
347 for (QBluetoothUuid key : keysService)
348 newDeviceInfo.setServiceData(key, qtAdvData.serviceData.value(key));
351 newDeviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
352 emit notifier->deviceDiscovered(newDeviceInfo);
const int powerOffTimeoutMS
QBluetoothUuid qt_uuid(IOBluetoothSDPUUID *uuid)
AdvertisementData(NSDictionary *AdvertisementData)
QHash< QBluetoothUuid, QByteArray > serviceData
QList< QBluetoothUuid > serviceUuids
QHash< quint16, QByteArray > manufacturerData