4#include "darwin/btconnectionmonitor_p.h"
7#include "darwin/btdevicepair_p.h"
8#include "darwin/btdelegates_p.h"
9#include "darwin/btutility_p.h"
11#include <QtCore/qloggingcategory.h>
12#include <QtCore/qstring.h>
13#include <QtCore/qglobal.h>
14#include <QtCore/qdebug.h>
15#include <QtCore/qmap.h>
18#include <Foundation/Foundation.h>
20#include <IOBluetooth/IOBluetooth.h>
21#include <CoreBluetooth/CoreBluetooth.h>
26class QBluetoothLocalDevicePrivate;
29@interface QT_MANGLE_NAMESPACE(QDarwinBluetoothStateMonitor) : NSObject <CBCentralManagerDelegate>
31@property (strong, nonatomic) CBCentralManager *manager;
32@property (assign, nonatomic) QT_PREPEND_NAMESPACE(QBluetoothLocalDevicePrivate) *localDevicePrivate;
34- (instancetype)initWith:(QT_PREPEND_NAMESPACE(QBluetoothLocalDevicePrivate) *)localDevicePrivate;
35- (CBManagerState)currentState;
36- (
void)startMonitoring;
37- (
void)stopMonitoring;
43class QBluetoothLocalDevicePrivate :
public DarwinBluetooth::PairingDelegate,
44 public DarwinBluetooth::ConnectionMonitor
46 friend class QBluetoothLocalDevice;
47 Q_DECLARE_PUBLIC(QBluetoothLocalDevice)
49 typedef QBluetoothLocalDevice::Pairing Pairing;
51 QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *,
const QBluetoothAddress & =
53 ~QBluetoothLocalDevicePrivate();
56 void requestPairing(
const QBluetoothAddress &address, Pairing pairing);
57 Pairing pairingStatus(
const QBluetoothAddress &address)
const;
59 void bluetoothStateChanged(CBManagerState newState);
64 void connecting(
void *pair) override;
65 void requestPIN(
void *pair) override;
66 void requestUserConfirmation(
void *pair,
67 BluetoothNumericValue) override;
68 void passkeyNotification(
void *pair,
69 BluetoothPasskey passkey) override;
70 void error(
void *pair, IOReturn errorCode) override;
71 void pairingFinished(
void *pair) override;
74 void deviceConnected(
const QBluetoothAddress &deviceAddress) override;
75 void deviceDisconnected(
const QBluetoothAddress &deviceAddress) override;
77 void emitPairingFinished(
const QBluetoothAddress &deviceAddress, Pairing pairing,
bool queued);
78 void emitError(QBluetoothLocalDevice::Error error,
bool queued);
80 void unpair(
const QBluetoothAddress &deviceAddress);
82 DarwinBluetooth::ObjCScopedPointer<QT_MANGLE_NAMESPACE(QDarwinBluetoothStateMonitor)> bluetoothStateMonitor;
83 QBluetoothLocalDevice::HostMode hostMode = QBluetoothLocalDevice::HostMode::HostPoweredOff;
85 QBluetoothLocalDevice *q_ptr;
87 using HostController = DarwinBluetooth::ObjCScopedPointer<IOBluetoothHostController>;
88 HostController hostController;
90 using PairingRequest = DarwinBluetooth::ObjCStrongReference<DarwinBTClassicPairing>;
91 using RequestMap = QMap<QBluetoothAddress, PairingRequest>;
93 RequestMap pairingRequests;
94 DarwinBluetooth::ObjCScopedPointer<DarwinBTConnectionMonitor> connectionMonitor;
95 QList<QBluetoothAddress> discoveredDevices;
99 const QBluetoothAddress &address) :
102 registerQBluetoothLocalDeviceMetaType();
103 Q_ASSERT_X(q, Q_FUNC_INFO,
"invalid q_ptr (null)");
109 ObjCScopedPointer<IOBluetoothHostController> defaultController([IOBluetoothHostController defaultController],
110 RetainPolicy::doInitialRetain);
111 if (!defaultController) {
112 qCCritical(QT_BT_DARWIN) <<
"failed to init a host controller object";
116 if (!address.isNull()) {
117 NSString *
const hciAddress = [defaultController addressAsString];
119 qCCritical(QT_BT_DARWIN) <<
"failed to obtain an address";
123 BluetoothDeviceAddress iobtAddress = {};
124 if (IOBluetoothNSStringToDeviceAddress(hciAddress, &iobtAddress) != kIOReturnSuccess) {
125 qCCritical(QT_BT_DARWIN) <<
"invalid local device's address";
129 if (address != DarwinBluetooth::qt_address(&iobtAddress)) {
130 qCCritical(QT_BT_DARWIN) <<
"invalid local device's address";
135 defaultController.swap(hostController);
137 connectionMonitor.reset([[DarwinBTConnectionMonitor alloc] initWithMonitor:
this],
138 DarwinBluetooth::RetainPolicy::noInitialRetain);
140 if ([hostController powerState])
141 hostMode = QBluetoothLocalDevice::HostConnectable;
143 hostMode = QBluetoothLocalDevice::HostPoweredOff;
146 bluetoothStateMonitor.reset([[QT_MANGLE_NAMESPACE(QDarwinBluetoothStateMonitor) alloc] initWith:
this],
147 DarwinBluetooth::RetainPolicy::doInitialRetain);
148 [bluetoothStateMonitor startMonitoring];
153 [bluetoothStateMonitor stopMonitoring];
154 [connectionMonitor stopMonitoring];
159 Q_Q(QBluetoothLocalDevice);
160 qCDebug(QT_BT_DARWIN) <<
"Bluetooth state changed to" << state;
164 QBluetoothLocalDevice::HostMode mode;
165 if (state == CBManagerState::CBManagerStatePoweredOff)
166 mode = QBluetoothLocalDevice::HostPoweredOff;
167 else if (state == CBManagerState::CBManagerStatePoweredOn)
168 mode = QBluetoothLocalDevice::HostConnectable;
172 if (hostMode != mode) {
174 emit q->hostModeStateChanged(hostMode);
180 return hostController;
185 Q_ASSERT_X(isValid(), Q_FUNC_INFO,
"invalid local device");
186 Q_ASSERT_X(!address.isNull(), Q_FUNC_INFO,
"invalid device address");
193 if (pairing == QBluetoothLocalDevice::Unpaired)
194 return unpair(address);
198 if (pairing == QBluetoothLocalDevice::AuthorizedPaired)
199 pairing = QBluetoothLocalDevice::Paired;
201 RequestMap::iterator pos = pairingRequests.find(address);
202 if (pos != pairingRequests.end()) {
203 if ([pos.value() isActive])
207 IOBluetoothDevice *
const device = [pos.value() targetDevice];
208 if ([device isPaired]) {
209 emitPairingFinished(address, pairing,
true);
210 }
else if ([pos.value() start] != kIOReturnSuccess) {
211 qCCritical(QT_BT_DARWIN) <<
"failed to start a new pairing request";
212 emitError(QBluetoothLocalDevice::PairingError,
true);
220 PairingRequest newRequest([[DarwinBTClassicPairing alloc] initWithTarget:address delegate:
this],
221 RetainPolicy::noInitialRetain);
223 qCCritical(QT_BT_DARWIN) <<
"failed to allocate a new pairing request";
224 emitError(QBluetoothLocalDevice::PairingError,
true);
228 pos = pairingRequests.insert(address, newRequest);
229 const IOReturn result = [newRequest start];
230 if (result != kIOReturnSuccess) {
231 pairingRequests.erase(pos);
232 qCCritical(QT_BT_DARWIN) <<
"failed to start a new pairing request";
233 emitError(QBluetoothLocalDevice::PairingError,
true);
239 Q_ASSERT_X(isValid(), Q_FUNC_INFO,
"invalid local device");
240 Q_ASSERT_X(!address.isNull(), Q_FUNC_INFO,
"invalid address");
247 RequestMap::const_iterator it = pairingRequests.find(address);
248 if (it != pairingRequests.end()) {
250 IOBluetoothDevice *
const device = [it.value() targetDevice];
251 if (device && [device isPaired])
252 return QBluetoothLocalDevice::Paired;
256 if (device && [device isPaired])
257 return QBluetoothLocalDevice::Paired;
260 return QBluetoothLocalDevice::Unpaired;
294 emitError(QBluetoothLocalDevice::PairingError,
false);
299 auto pair =
static_cast<DarwinBTClassicPairing *>(generic);
300 Q_ASSERT_X(pair, Q_FUNC_INFO,
"invalid pairing request (nil)");
302 const QBluetoothAddress &deviceAddress = [pair targetAddress];
303 Q_ASSERT_X(!deviceAddress.isNull(), Q_FUNC_INFO,
304 "invalid target address");
306 emitPairingFinished(deviceAddress, QBluetoothLocalDevice::Paired,
false);
311 if (!discoveredDevices.contains(deviceAddress))
312 discoveredDevices.append(deviceAddress);
314 QMetaObject::invokeMethod(q_ptr,
"deviceConnected", Qt::QueuedConnection,
315 Q_ARG(QBluetoothAddress, deviceAddress));
320 QList<QBluetoothAddress>::iterator devicePos =std::find(discoveredDevices.begin(),
321 discoveredDevices.end(),
324 if (devicePos != discoveredDevices.end())
325 discoveredDevices.erase(devicePos);
327 QMetaObject::invokeMethod(q_ptr,
"deviceDisconnected", Qt::QueuedConnection,
328 Q_ARG(QBluetoothAddress, deviceAddress));
334 QMetaObject::invokeMethod(q_ptr,
"errorOccurred", Qt::QueuedConnection,
335 Q_ARG(QBluetoothLocalDevice::Error, error));
337 emit q_ptr->errorOccurred(QBluetoothLocalDevice::PairingError);
342 Pairing pairing,
bool queued)
344 Q_ASSERT_X(!deviceAddress.isNull(), Q_FUNC_INFO,
"invalid target device address");
345 Q_ASSERT_X(q_ptr, Q_FUNC_INFO,
"invalid q_ptr (null)");
348 QMetaObject::invokeMethod(q_ptr,
"pairingFinished", Qt::QueuedConnection,
349 Q_ARG(QBluetoothAddress, deviceAddress),
350 Q_ARG(QBluetoothLocalDevice::Pairing, pairing));
352 emit q_ptr->pairingFinished(deviceAddress, pairing);
358 Q_ASSERT_X(!deviceAddress.isNull(), Q_FUNC_INFO,
359 "invalid target address");
361 emitPairingFinished(deviceAddress, QBluetoothLocalDevice::Unpaired,
true);
364QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) :
366 d_ptr(
new QBluetoothLocalDevicePrivate(
this))
370QBluetoothLocalDevice::QBluetoothLocalDevice(
const QBluetoothAddress &address, QObject *parent) :
372 d_ptr(
new QBluetoothLocalDevicePrivate(
this, address))
376QBluetoothLocalDevice::~QBluetoothLocalDevice()
381bool QBluetoothLocalDevice::isValid()
const
383 return d_ptr->isValid();
387QString QBluetoothLocalDevice::name()
const
392 if (NSString *
const nsn = [d_ptr->hostController nameAsString])
393 return QString::fromNSString(nsn);
394 qCCritical(QT_BT_DARWIN) << Q_FUNC_INFO <<
"failed to obtain a name";
400QBluetoothAddress QBluetoothLocalDevice::address()
const
405 if (NSString *
const nsa = [d_ptr->hostController addressAsString])
406 return QBluetoothAddress(DarwinBluetooth::qt_address(nsa));
408 qCCritical(QT_BT_DARWIN) << Q_FUNC_INFO <<
"failed to obtain an address";
410 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO <<
"invalid local device";
413 return QBluetoothAddress();
416void QBluetoothLocalDevice::powerOn()
419 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO <<
"invalid local device";
422void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode)
427 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO <<
"invalid local device";
430QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode()
const
432 Q_D(
const QBluetoothLocalDevice);
434 return HostPoweredOff;
436 auto state = [d->bluetoothStateMonitor currentState];
442 if (state == CBManagerState::CBManagerStatePoweredOff)
443 return HostPoweredOff;
444 else if (state == CBManagerState::CBManagerStatePoweredOn)
445 return HostConnectable;
447 return [d->hostController powerState] ? HostConnectable : HostPoweredOff;
450QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices()
const
454 QList<QBluetoothAddress> connectedDevices;
457 NSArray *
const pairedDevices = [IOBluetoothDevice pairedDevices];
458 for (IOBluetoothDevice *device in pairedDevices) {
459 if ([device isConnected]) {
460 const QBluetoothAddress address(DarwinBluetooth::qt_address([device getAddress]));
461 if (!address.isNull())
462 connectedDevices.append(address);
467 connectedDevices += d_ptr->discoveredDevices;
470 std::sort(connectedDevices.begin(), connectedDevices.end());
471 connectedDevices.erase(std::unique(connectedDevices.begin(),
472 connectedDevices.end()),
473 connectedDevices.end());
475 return connectedDevices;
478QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
480 QList<QBluetoothHostInfo> localDevices;
482 QBluetoothLocalDevice defaultAdapter;
483 if (!defaultAdapter.isValid() || defaultAdapter.address().isNull()) {
484 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO <<
"no valid device found";
488 QBluetoothHostInfo deviceInfo;
489 deviceInfo.setName(defaultAdapter.name());
490 deviceInfo.setAddress(defaultAdapter.address());
492 localDevices.append(deviceInfo);
497void QBluetoothLocalDevice::requestPairing(
const QBluetoothAddress &address, Pairing pairing)
500 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO <<
"invalid local device";
502 if (!isValid() || address.isNull()) {
503 d_ptr->emitError(PairingError,
true);
507 DarwinBluetooth::qt_test_iobluetooth_runloop();
509 return d_ptr->requestPairing(address, pairing);
512QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(
const QBluetoothAddress &address)
const
515 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO <<
"invalid local device";
517 if (!isValid() || address.isNull())
520 return d_ptr->pairingStatus(address);
525@implementation QT_MANGLE_NAMESPACE(QDarwinBluetoothStateMonitor)
527- (instancetype)initWith:(QT_PREPEND_NAMESPACE(QBluetoothLocalDevicePrivate) *)localDevicePrivate
529 if ((self = [super init])) {
531 self.localDevicePrivate = localDevicePrivate;
536- (
void)startMonitoring
538 if (self.manager != nil)
540 self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
543- (
void)stopMonitoring
545 if (self.manager == nil)
547 self.manager.delegate = nil;
551- (CBManagerState)currentState
553 Q_ASSERT(self.manager);
554 return self.manager.state;
557- (
void)centralManagerDidUpdateState:(CBCentralManager *)aManager
559 Q_ASSERT(self.manager);
560 Q_ASSERT(self.localDevicePrivate);
561 Q_ASSERT(self.manager == aManager);
562 self.localDevicePrivate->bluetoothStateChanged(aManager.state);
#define QT_BT_MAC_AUTORELEASEPOOL
ObjCStrongReference< IOBluetoothDevice > device_with_address(const QBluetoothAddress &address)