10#include <QtCore/qloggingcategory.h>
11#include <QtCore/qdebug.h>
12#include <QtCore/qendian.h>
13#include <QtCore/qlist.h>
24 return QBluetoothUuid();
27 [nsUuid getUUIDBytes:uuidData];
28 QUuid::Id128Bytes qtUuidData = {};
29 std::copy(uuidData, uuidData + 16, qtUuidData.data);
30 return QBluetoothUuid(qtUuidData);
58 if (!advertisementData)
63 NSObject *value = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
64 if (value && [value isKindOfClass:[NSString
class]])
65 localName = QString::fromNSString(
static_cast<NSString *>(value));
70 value = [advertisementData objectForKey:CBAdvertisementDataServiceUUIDsKey];
71 if (value && [value isKindOfClass:[NSArray
class]]) {
72 NSArray *uuids =
static_cast<NSArray *>(value);
73 for (CBUUID *cbUuid in uuids)
74 serviceUuids << qt_uuid(cbUuid);
97@interface DarwinBTLEDeviceInquiry (PrivateAPI)
102@implementation DarwinBTLEDeviceInquiry
104 LECBManagerNotifier *notifier;
105 ObjCScopedPointer<CBCentralManager> manager;
107 QList<QBluetoothDeviceInfo> devices;
108 LEInquiryState internalState;
109 int inquiryTimeoutMS;
111 QT_PREPEND_NAMESPACE(DarwinBluetooth)::GCDTimer elapsedTimer;
114-(
id)initWithNotifier:(LECBManagerNotifier *)aNotifier
116 if (self = [super init]) {
118 notifier = aNotifier;
119 internalState = InquiryStarting;
120 inquiryTimeoutMS = DarwinBluetooth::defaultLEScanTimeoutMS;
129 [manager setDelegate:nil];
130 [elapsedTimer cancelTimer];
135- (
void)timeout:(id)sender
139 if (internalState == InquiryActive) {
141 [manager setDelegate:nil];
142 internalState = InquiryFinished;
144 emit notifier->discoveryFinished();
145 }
else if (internalState == InquiryStarting) {
149 [manager setDelegate:nil];
150 internalState = ErrorPoweredOff;
152 emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
156- (
void)startWithTimeout:(
int)timeout
158 dispatch_queue_t leQueue(DarwinBluetooth::qt_LE_queue());
160 inquiryTimeoutMS = timeout;
161 manager.reset([[CBCentralManager alloc] initWithDelegate:self queue:leQueue],
162 DarwinBluetooth::RetainPolicy::noInitialRetain);
165- (
void)centralManagerDidUpdateState:(CBCentralManager *)central
167#pragma clang diagnostic push
168#pragma clang diagnostic ignored "-Wunguarded-availability-new"
170 if (central != manager)
173 if (internalState != InquiryActive && internalState != InquiryStarting)
178 using namespace DarwinBluetooth;
180 const auto state = central.state;
181 if (state == CBManagerStatePoweredOn) {
182 if (internalState == InquiryStarting) {
183 internalState = InquiryActive;
185 if (inquiryTimeoutMS > 0) {
186 [elapsedTimer cancelTimer];
187 elapsedTimer.reset([[DarwinBTGCDTimer alloc] initWithDelegate:self], RetainPolicy::noInitialRetain);
188 [elapsedTimer startWithTimeout:inquiryTimeoutMS step:timeStepMS];
193 const int env = qEnvironmentVariableIntValue(
"QT_BLUETOOTH_SCAN_ENABLE_DUPLICATES", &envOk);
195 [manager scanForPeripheralsWithServices:nil
196 options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES}];
198 [manager scanForPeripheralsWithServices:nil options:nil];
201 }
else if (state == CBManagerStateUnsupported) {
202 if (internalState == InquiryActive) {
206 internalState = ErrorPoweredOff;
207 emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
209 internalState = ErrorLENotSupported;
210 emit notifier->LEnotSupported();
212 [manager setDelegate:nil];
213 }
else if (state == CBManagerStateUnauthorized) {
214 if (internalState == InquiryActive)
216 internalState = ErrorNotAuthorized;
217 emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::MissingPermissionsError);
218 [manager setDelegate:nil];
219 }
else if (state == CBManagerStatePoweredOff) {
222 if (internalState == InquiryStarting) {
227 [elapsedTimer cancelTimer];
228 elapsedTimer.reset([[DarwinBTGCDTimer alloc] initWithDelegate:self], RetainPolicy::noInitialRetain);
229 [elapsedTimer startWithTimeout:powerOffTimeoutMS step:300];
233 Q_UNUSED(powerOffTimeoutMS);
235 [elapsedTimer cancelTimer];
237 [manager setDelegate:nil];
238 internalState = ErrorPoweredOff;
242 emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
257#pragma clang diagnostic pop
267#pragma clang diagnostic push
268#pragma clang diagnostic ignored "-Wunguarded-availability-new"
270 if (internalState == InquiryActive) {
271 const auto state = manager.get().state;
272 if (state == CBManagerStatePoweredOn)
276#pragma clang diagnostic pop
282 notifier->disconnect();
283 notifier->deleteLater();
291 [manager setDelegate:nil];
292 [elapsedTimer cancelTimer];
294 internalState = InquiryCancelled;
297- (
void)centralManager:(CBCentralManager *)central
298 didDiscoverPeripheral:(CBPeripheral *)peripheral
299 advertisementData:(NSDictionary *)advertisementData
300 RSSI:(NSNumber *)RSSI
302 using namespace DarwinBluetooth;
304 if (central != manager)
307 if (internalState != InquiryActive)
313 QBluetoothUuid deviceUuid;
315 if (!peripheral.identifier) {
316 qCWarning(QT_BT_DARWIN) <<
"peripheral without NSUUID";
320 deviceUuid = DarwinBluetooth::qt_uuid(peripheral.identifier);
322 if (deviceUuid.isNull()) {
323 qCWarning(QT_BT_DARWIN) <<
"no way to address peripheral, QBluetoothUuid is null";
327 const AdvertisementData qtAdvData(advertisementData);
328 QString name(qtAdvData.localName);
329 if (!name.size() && peripheral.name)
330 name = QString::fromNSString(peripheral.name);
333 QBluetoothDeviceInfo newDeviceInfo(deviceUuid, name, 0);
335 newDeviceInfo.setRssi([RSSI shortValue]);
337 if (qtAdvData.serviceUuids.size())
338 newDeviceInfo.setServiceUuids(qtAdvData.serviceUuids);
340 const QList<quint16> keysManufacturer = qtAdvData.manufacturerData.keys();
341 for (quint16 key : keysManufacturer)
342 newDeviceInfo.setManufacturerData(key, qtAdvData.manufacturerData.value(key));
344 const QList<QBluetoothUuid> keysService = qtAdvData.serviceData.keys();
345 for (QBluetoothUuid key : keysService)
346 newDeviceInfo.setServiceData(key, qtAdvData.serviceData.value(key));
349 newDeviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
350 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