Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qbluetoothlocaldevice_macos.mm
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
10
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>
16
17
18#include <Foundation/Foundation.h>
19
20#include <IOBluetooth/IOBluetooth.h>
21
22#include <algorithm>
23
25
26class QBluetoothLocalDevicePrivate : public DarwinBluetooth::PairingDelegate,
27 public DarwinBluetooth::ConnectionMonitor
28{
30public:
32
36
37 bool isValid() const;
38 void requestPairing(const QBluetoothAddress &address, Pairing pairing);
40
41private:
42
43 // PairingDelegate:
44 void connecting(void *pair) override;
45 void requestPIN(void *pair) override;
46 void requestUserConfirmation(void *pair,
47 BluetoothNumericValue) override;
48 void passkeyNotification(void *pair,
49 BluetoothPasskey passkey) override;
50 void error(void *pair, IOReturn errorCode) override;
51 void pairingFinished(void *pair) override;
52
53 // ConnectionMonitor
54 void deviceConnected(const QBluetoothAddress &deviceAddress) override;
55 void deviceDisconnected(const QBluetoothAddress &deviceAddress) override;
56
57 void emitPairingFinished(const QBluetoothAddress &deviceAddress, Pairing pairing, bool queued);
58 void emitError(QBluetoothLocalDevice::Error error, bool queued);
59
60 void unpair(const QBluetoothAddress &deviceAddress);
61
63
65 HostController hostController;
66
68 using RequestMap = QMap<QBluetoothAddress, PairingRequest>;
69
70 RequestMap pairingRequests;
72 QList<QBluetoothAddress> discoveredDevices;
73};
74
77 q_ptr(q)
78{
80 Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)");
81
83
84 using namespace DarwinBluetooth;
85
86 ObjCScopedPointer<IOBluetoothHostController> defaultController([IOBluetoothHostController defaultController],
87 RetainPolicy::doInitialRetain);
88 if (!defaultController) {
89 qCCritical(QT_BT_DARWIN) << "failed to init a host controller object";
90 return;
91 }
92
93 if (!address.isNull()) {
94 NSString *const hciAddress = [defaultController addressAsString];
95 if (!hciAddress) {
96 qCCritical(QT_BT_DARWIN) << "failed to obtain an address";
97 return;
98 }
99
100 BluetoothDeviceAddress iobtAddress = {};
101 if (IOBluetoothNSStringToDeviceAddress(hciAddress, &iobtAddress) != kIOReturnSuccess) {
102 qCCritical(QT_BT_DARWIN) << "invalid local device's address";
103 return;
104 }
105
106 if (address != DarwinBluetooth::qt_address(&iobtAddress)) {
107 qCCritical(QT_BT_DARWIN) << "invalid local device's address";
108 return;
109 }
110 }
111
112 defaultController.swap(hostController);
113 // This one is optional, if it fails to initialize, we do not care at all.
114 connectionMonitor.reset([[DarwinBTConnectionMonitor alloc] initWithMonitor:this],
115 DarwinBluetooth::RetainPolicy::noInitialRetain);
116}
117
119{
120
121 [connectionMonitor stopMonitoring];
122}
123
125{
126 return hostController;
127}
128
130{
131 Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid local device");
132 Q_ASSERT_X(!address.isNull(), Q_FUNC_INFO, "invalid device address");
133
137
138 // That's a really special case on OS X.
139 if (pairing == QBluetoothLocalDevice::Unpaired)
140 return unpair(address);
141
143
146
147 RequestMap::iterator pos = pairingRequests.find(address);
148 if (pos != pairingRequests.end()) {
149 if ([pos.value() isActive]) // Still trying to pair, continue.
150 return;
151
152 // 'device' is autoreleased:
153 IOBluetoothDevice *const device = [pos.value() targetDevice];
154 if ([device isPaired]) {
155 emitPairingFinished(address, pairing, true);
156 } else if ([pos.value() start] != kIOReturnSuccess) {
157 qCCritical(QT_BT_DARWIN) << "failed to start a new pairing request";
159 }
160 return;
161 }
162
163 // That's a totally new request ('Paired', since we are here).
164 // Even if this device is paired (not by our local device), I still create a pairing request,
165 // it'll just finish with success (skipping any intermediate steps).
166 PairingRequest newRequest([[DarwinBTClassicPairing alloc] initWithTarget:address delegate:this],
167 RetainPolicy::noInitialRetain);
168 if (!newRequest) {
169 qCCritical(QT_BT_DARWIN) << "failed to allocate a new pairing request";
171 return;
172 }
173
174 pos = pairingRequests.insert(address, newRequest);
175 const IOReturn result = [newRequest start];
176 if (result != kIOReturnSuccess) {
177 pairingRequests.erase(pos);
178 qCCritical(QT_BT_DARWIN) << "failed to start a new pairing request";
180 }
181}
182
184{
185 Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid local device");
186 Q_ASSERT_X(!address.isNull(), Q_FUNC_INFO, "invalid address");
187
190
192
193 RequestMap::const_iterator it = pairingRequests.find(address);
194 if (it != pairingRequests.end()) {
195 // All Obj-C objects are autoreleased.
196 IOBluetoothDevice *const device = [it.value() targetDevice];
197 if (device && [device isPaired])
199 } else {
200 // Try even if device was not paired by this local device ...
201 const ObjCStrongReference<IOBluetoothDevice> device(device_with_address(address));
202 if (device && [device isPaired])
204 }
205
207}
208
210{
211 // TODO: why unused and if cannot be used - remove?
212 Q_UNUSED(pair);
213}
214
216{
217 // TODO: why unused and if cannot be used - remove?
218 Q_UNUSED(pair);
219}
220
221void QBluetoothLocalDevicePrivate::requestUserConfirmation(void *pair, BluetoothNumericValue intPin)
222{
223 // TODO: why unused and if cannot be used - remove?
224 Q_UNUSED(pair);
225 Q_UNUSED(intPin);
226}
227
228void QBluetoothLocalDevicePrivate::passkeyNotification(void *pair, BluetoothPasskey passkey)
229{
230 // TODO: why unused and if cannot be used - remove?
231 Q_UNUSED(pair);
232 Q_UNUSED(passkey);
233}
234
235void QBluetoothLocalDevicePrivate::error(void *pair, IOReturn errorCode)
236{
237 Q_UNUSED(pair);
238 Q_UNUSED(errorCode);
239
240 emitError(QBluetoothLocalDevice::PairingError, false);
241}
242
244{
245 auto pair = static_cast<DarwinBTClassicPairing *>(generic);
246 Q_ASSERT_X(pair, Q_FUNC_INFO, "invalid pairing request (nil)");
247
248 const QBluetoothAddress &deviceAddress = [pair targetAddress];
249 Q_ASSERT_X(!deviceAddress.isNull(), Q_FUNC_INFO,
250 "invalid target address");
251
252 emitPairingFinished(deviceAddress, QBluetoothLocalDevice::Paired, false);
253}
254
256{
257 if (!discoveredDevices.contains(deviceAddress))
258 discoveredDevices.append(deviceAddress);
259
260 QMetaObject::invokeMethod(q_ptr, "deviceConnected", Qt::QueuedConnection,
261 Q_ARG(QBluetoothAddress, deviceAddress));
262}
263
265{
266 QList<QBluetoothAddress>::iterator devicePos =std::find(discoveredDevices.begin(),
267 discoveredDevices.end(),
268 deviceAddress);
269
270 if (devicePos != discoveredDevices.end())
271 discoveredDevices.erase(devicePos);
272
273 QMetaObject::invokeMethod(q_ptr, "deviceDisconnected", Qt::QueuedConnection,
274 Q_ARG(QBluetoothAddress, deviceAddress));
275}
276
277void QBluetoothLocalDevicePrivate::emitError(QBluetoothLocalDevice::Error error, bool queued)
278{
279 if (queued) {
280 QMetaObject::invokeMethod(q_ptr, "errorOccurred", Qt::QueuedConnection,
282 } else {
284 }
285}
286
287void QBluetoothLocalDevicePrivate::emitPairingFinished(const QBluetoothAddress &deviceAddress,
288 Pairing pairing, bool queued)
289{
290 Q_ASSERT_X(!deviceAddress.isNull(), Q_FUNC_INFO, "invalid target device address");
291 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
292
293 if (queued) {
294 QMetaObject::invokeMethod(q_ptr, "pairingFinished", Qt::QueuedConnection,
295 Q_ARG(QBluetoothAddress, deviceAddress),
297 } else {
298 emit q_ptr->pairingFinished(deviceAddress, pairing);
299 }
300}
301
302void QBluetoothLocalDevicePrivate::unpair(const QBluetoothAddress &deviceAddress)
303{
304 Q_ASSERT_X(!deviceAddress.isNull(), Q_FUNC_INFO,
305 "invalid target address");
306
307 emitPairingFinished(deviceAddress, QBluetoothLocalDevice::Unpaired, true);
308}
309
313{
314}
315
319{
320}
321
323{
324 delete d_ptr;
325}
326
328{
329 return d_ptr->isValid();
330}
331
332
334{
336
337 if (isValid()) {
338 if (NSString *const nsn = [d_ptr->hostController nameAsString])
339 return QString::fromNSString(nsn);
340 qCCritical(QT_BT_DARWIN) << Q_FUNC_INFO << "failed to obtain a name";
341 }
342
343 return QString();
344}
345
347{
349
350 if (isValid()) {
351 if (NSString *const nsa = [d_ptr->hostController addressAsString])
353
354 qCCritical(QT_BT_DARWIN) << Q_FUNC_INFO << "failed to obtain an address";
355 } else {
356 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO << "invalid local device";
357 }
358
359 return QBluetoothAddress();
360}
361
363{
364 if (!isValid())
365 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO << "invalid local device";
366}
367
369{
370 Q_UNUSED(mode);
371
372 if (!isValid())
373 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO << "invalid local device";
374}
375
377{
378 if (!isValid() || ![d_ptr->hostController powerState])
379 return HostPoweredOff;
380
381 return HostConnectable;
382}
383
384QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
385{
387
388 QList<QBluetoothAddress> connectedDevices;
389
390 // Take the devices known to IOBluetooth to be paired and connected first:
391 NSArray *const pairedDevices = [IOBluetoothDevice pairedDevices];
392 for (IOBluetoothDevice *device in pairedDevices) {
393 if ([device isConnected]) {
395 if (!address.isNull())
397 }
398 }
399
400 // Add devices, discovered by the connection monitor:
401 connectedDevices += d_ptr->discoveredDevices;
402 // Find something more elegant? :)
403 // But after all, addresses are integers.
408
409 return connectedDevices;
410}
411
412QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
413{
414 QList<QBluetoothHostInfo> localDevices;
415
416 QBluetoothLocalDevice defaultAdapter;
417 if (!defaultAdapter.isValid() || defaultAdapter.address().isNull()) {
418 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO <<"no valid device found";
419 return localDevices;
420 }
421
422 QBluetoothHostInfo deviceInfo;
423 deviceInfo.setName(defaultAdapter.name());
424 deviceInfo.setAddress(defaultAdapter.address());
425
426 localDevices.append(deviceInfo);
427
428 return localDevices;
429}
430
432{
433 if (!isValid())
434 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO << "invalid local device";
435
436 if (!isValid() || address.isNull()) {
437 d_ptr->emitError(PairingError, true);
438 return;
439 }
440
442
443 return d_ptr->requestPairing(address, pairing);
444}
445
447{
448 if (!isValid())
449 qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO << "invalid local device";
450
451 if (!isValid() || address.isNull())
452 return Unpaired;
453
454 return d_ptr->pairingStatus(address);
455}
456
IOBluetoothDevice * device
bool isActive
#define QT_BT_MAC_AUTORELEASEPOOL
Definition btutility_p.h:78
\inmodule QtBluetooth
\inmodule QtBluetooth
void setAddress(const QBluetoothAddress &address)
Sets the Bluetooth address for this Bluetooth host info object.
void setName(const QString &name)
Sets the name of the host info object.
QBluetoothLocalDevice::Pairing Pairing
QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *, const QBluetoothAddress &=QBluetoothAddress())
void error(void *pair, IOReturn errorCode) override
void passkeyNotification(void *pair, BluetoothPasskey passkey) override
void deviceConnected(const QBluetoothAddress &deviceAddress) override
void deviceDisconnected(const QBluetoothAddress &deviceAddress) override
void requestUserConfirmation(void *pair, BluetoothNumericValue) override
Pairing pairingStatus(const QBluetoothAddress &address) const
void requestPairing(const QBluetoothAddress &address, Pairing pairing)
\inmodule QtBluetooth
void powerOn()
Powers on the device after returning it to the hostMode() state, if it was powered off.
Pairing
This enum describes the pairing state between the two Bluetooth devices.
void errorOccurred(QBluetoothLocalDevice::Error error)
Signal emitted if there's an exceptional error while pairing.
void requestPairing(const QBluetoothAddress &address, Pairing pairing)
Set the pairing status with address.
HostMode
This enum describes the most of the local Bluetooth device.
HostMode hostMode() const
Returns the current host mode of this local Bluetooth device.
Error
This enum describes errors that maybe returned.
virtual ~QBluetoothLocalDevice()
Destroys the QBluetoothLocalDevice.
QList< QBluetoothAddress > connectedDevices() const
QBluetoothLocalDevice(QObject *parent=nullptr)
Constructs a QBluetoothLocalDevice with parent.
QString name() const
Returns the name assgined by the user to this Bluetooth device.
static QList< QBluetoothHostInfo > allDevices()
Returns a list of all available local Bluetooth devices.
Pairing pairingStatus(const QBluetoothAddress &address) const
Returns the current bluetooth pairing status of address, if it's unpaired, paired,...
QBluetoothAddress address() const
Returns the MAC address of this Bluetooth device.
void pairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing)
Pairing or unpairing has completed with address.
void setHostMode(QBluetoothLocalDevice::HostMode mode)
Sets the host mode of this local Bluetooth device to mode.
iterator erase(const_iterator begin, const_iterator end)
Definition qlist.h:889
iterator end()
Definition qlist.h:626
iterator begin()
Definition qlist.h:625
void append(parameter_type t)
Definition qlist.h:458
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
iterator erase(const_iterator it)
Definition qmap.h:619
iterator find(const Key &key)
Definition qmap.h:641
iterator end()
Definition qmap.h:602
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
#define this
Definition dialogs.cpp:9
QSet< QString >::iterator it
QString qt_address(NSString *address)
Definition btutility.mm:36
void qt_test_iobluetooth_runloop()
Definition btutility.mm:125
ObjCStrongReference< IOBluetoothDevice > device_with_address(const QBluetoothAddress &address)
Combined button and popup list for selecting options.
@ QueuedConnection
QT_BEGIN_NAMESPACE void registerQBluetoothLocalDeviceMetaType()
#define Q_FUNC_INFO
DBusConnection const char DBusError * error
#define qCCritical(category,...)
#define qCWarning(category,...)
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
GLenum mode
GLuint start
GLuint in
GLuint GLuint64EXT address
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
#define emit
#define Q_UNUSED(x)
bool contains(const AT &t) const noexcept
Definition qlist.h:45
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...