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** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtBluetooth module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "osx/osxbtconnectionmonitor_p.h"
41#include "qbluetoothlocaldevice_p.h"
42#include "qbluetoothlocaldevice.h"
43#include "osx/osxbtdevicepair_p.h"
44#include "osx/osxbtutility_p.h"
45#include "osx/osxbluetooth_p.h"
46
47#include <QtCore/qloggingcategory.h>
48#include <QtCore/qstring.h>
49#include <QtCore/qglobal.h>
50#include <QtCore/qdebug.h>
51#include <QtCore/qmap.h>
52
53
54#include <Foundation/Foundation.h>
55
56#include <algorithm>
57
58QT_BEGIN_NAMESPACE
59
60class QBluetoothLocalDevicePrivate : public OSXBluetooth::PairingDelegate,
61 public OSXBluetooth::ConnectionMonitor
62{
63 friend class QBluetoothLocalDevice;
64public:
65 typedef QBluetoothLocalDevice::Pairing Pairing;
66
67 QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *, const QBluetoothAddress & =
68 QBluetoothAddress());
69
70 bool isValid() const;
71 void requestPairing(const QBluetoothAddress &address, Pairing pairing);
72 Pairing pairingStatus(const QBluetoothAddress &address) const;
73
74private:
75
76 // PairingDelegate:
77 void connecting(ObjCPairingRequest *pair) override;
78 void requestPIN(ObjCPairingRequest *pair) override;
79 void requestUserConfirmation(ObjCPairingRequest *pair,
80 BluetoothNumericValue) override;
81 void passkeyNotification(ObjCPairingRequest *pair,
82 BluetoothPasskey passkey) override;
83 void error(ObjCPairingRequest *pair, IOReturn errorCode) override;
84 void pairingFinished(ObjCPairingRequest *pair) override;
85
86 // ConnectionMonitor
87 void deviceConnected(const QBluetoothAddress &deviceAddress) override;
88 void deviceDisconnected(const QBluetoothAddress &deviceAddress) override;
89
90 void emitPairingFinished(const QBluetoothAddress &deviceAddress, Pairing pairing, bool queued);
91 void emitError(QBluetoothLocalDevice::Error error, bool queued);
92
93 void unpair(const QBluetoothAddress &deviceAddress);
94
95 QBluetoothLocalDevice *q_ptr;
96
97 typedef OSXBluetooth::ObjCScopedPointer<IOBluetoothHostController> HostController;
98 HostController hostController;
99
100 typedef OSXBluetooth::ObjCStrongReference<ObjCPairingRequest> PairingRequest;
101 typedef QMap<QBluetoothAddress, PairingRequest> RequestMap;
102
103 RequestMap pairingRequests;
104
105 OSXBluetooth::ObjCScopedPointer<ObjCConnectionMonitor> connectionMonitor;
106 QList<QBluetoothAddress> discoveredDevices;
107};
108
109QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q,
110 const QBluetoothAddress &address) :
111 q_ptr(q)
112{
113 registerQBluetoothLocalDeviceMetaType();
114
115 Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)");
116
117 QT_BT_MAC_AUTORELEASEPOOL;
118
119 HostController defaultController([[IOBluetoothHostController defaultController] retain]);
120 if (!defaultController) {
121 qCCritical(QT_BT_OSX) << "failed to init a host controller object";
122 return;
123 }
124
125 if (!address.isNull()) {
126 NSString *const hciAddress = [defaultController addressAsString];
127 if (!hciAddress) {
128 qCCritical(QT_BT_OSX) << "failed to obtain an address";
129 return;
130 }
131
132 BluetoothDeviceAddress iobtAddress = {};
133 if (IOBluetoothNSStringToDeviceAddress(hciAddress, &iobtAddress) != kIOReturnSuccess) {
134 qCCritical(QT_BT_OSX) << "invalid local device's address";
135 return;
136 }
137
138 if (address != OSXBluetooth::qt_address(&iobtAddress)) {
139 qCCritical(QT_BT_OSX) << "invalid local device's address";
140 return;
141 }
142 }
143
144 hostController.reset(defaultController.take());
145
146 // This one is optional, if it fails to initialize, we do not care at all.
147 connectionMonitor.reset([[ObjCConnectionMonitor alloc] initWithMonitor:this]);
148}
149
150bool QBluetoothLocalDevicePrivate::isValid() const
151{
152 return hostController;
153}
154
155void QBluetoothLocalDevicePrivate::requestPairing(const QBluetoothAddress &address, Pairing pairing)
156{
157 Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid local device");
158 Q_ASSERT_X(!address.isNull(), Q_FUNC_INFO, "invalid device address");
159
160 using OSXBluetooth::device_with_address;
161 using OSXBluetooth::ObjCStrongReference;
162
163 // That's a really special case on OS X.
164 if (pairing == QBluetoothLocalDevice::Unpaired)
165 return unpair(address);
166
167 QT_BT_MAC_AUTORELEASEPOOL;
168
169 if (pairing == QBluetoothLocalDevice::AuthorizedPaired)
170 pairing = QBluetoothLocalDevice::Paired;
171
172 RequestMap::iterator pos = pairingRequests.find(address);
173 if (pos != pairingRequests.end()) {
174 if ([pos.value() isActive]) // Still trying to pair, continue.
175 return;
176
177 // 'device' is autoreleased:
178 IOBluetoothDevice *const device = [pos.value() targetDevice];
179 if ([device isPaired]) {
180 emitPairingFinished(address, pairing, true);
181 } else if ([pos.value() start] != kIOReturnSuccess) {
182 qCCritical(QT_BT_OSX) << "failed to start a new pairing request";
183 emitError(QBluetoothLocalDevice::PairingError, true);
184 }
185 return;
186 }
187
188 // That's a totally new request ('Paired', since we are here).
189 // Even if this device is paired (not by our local device), I still create a pairing request,
190 // it'll just finish with success (skipping any intermediate steps).
191 PairingRequest newRequest([[ObjCPairingRequest alloc] initWithTarget:address delegate:this], false);
192 if (!newRequest) {
193 qCCritical(QT_BT_OSX) << "failed to allocate a new pairing request";
194 emitError(QBluetoothLocalDevice::PairingError, true);
195 return;
196 }
197
198 pos = pairingRequests.insert(address, newRequest);
199 const IOReturn result = [newRequest start];
200 if (result != kIOReturnSuccess) {
201 pairingRequests.erase(pos);
202 qCCritical(QT_BT_OSX) << "failed to start a new pairing request";
203 emitError(QBluetoothLocalDevice::PairingError, true);
204 }
205}
206
207QBluetoothLocalDevice::Pairing QBluetoothLocalDevicePrivate::pairingStatus(const QBluetoothAddress &address)const
208{
209 Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid local device");
210 Q_ASSERT_X(!address.isNull(), Q_FUNC_INFO, "invalid address");
211
212 using OSXBluetooth::device_with_address;
213 using OSXBluetooth::ObjCStrongReference;
214
215 QT_BT_MAC_AUTORELEASEPOOL;
216
217 RequestMap::const_iterator it = pairingRequests.find(address);
218 if (it != pairingRequests.end()) {
219 // All Obj-C objects are autoreleased.
220 IOBluetoothDevice *const device = [it.value() targetDevice];
221 if (device && [device isPaired])
222 return QBluetoothLocalDevice::Paired;
223 } else {
224 // Try even if device was not paired by this local device ...
225 const ObjCStrongReference<IOBluetoothDevice> device(device_with_address(address));
226 if (device && [device isPaired])
227 return QBluetoothLocalDevice::Paired;
228 }
229
230 return QBluetoothLocalDevice::Unpaired;
231}
232
233void QBluetoothLocalDevicePrivate::connecting(ObjCPairingRequest *pair)
234{
235 Q_UNUSED(pair)
236}
237
238void QBluetoothLocalDevicePrivate::requestPIN(ObjCPairingRequest *pair)
239{
240 Q_UNUSED(pair)
241}
242
243void QBluetoothLocalDevicePrivate::requestUserConfirmation(ObjCPairingRequest *pair, BluetoothNumericValue intPin)
244{
245 Q_UNUSED(pair)
246 Q_UNUSED(intPin)
247}
248
249void QBluetoothLocalDevicePrivate::passkeyNotification(ObjCPairingRequest *pair,
250 BluetoothPasskey passkey)
251{
252 Q_UNUSED(pair)
253 Q_UNUSED(passkey)
254}
255
256void QBluetoothLocalDevicePrivate::error(ObjCPairingRequest *pair, IOReturn errorCode)
257{
258 Q_UNUSED(pair)
259 Q_UNUSED(errorCode)
260
261 emitError(QBluetoothLocalDevice::PairingError, false);
262}
263
264void QBluetoothLocalDevicePrivate::pairingFinished(ObjCPairingRequest *pair)
265{
266 Q_ASSERT_X(pair, Q_FUNC_INFO, "invalid pairing request (nil)");
267
268 const QBluetoothAddress &deviceAddress = [pair targetAddress];
269 Q_ASSERT_X(!deviceAddress.isNull(), Q_FUNC_INFO,
270 "invalid target address");
271
272 emitPairingFinished(deviceAddress, QBluetoothLocalDevice::Paired, false);
273}
274
275void QBluetoothLocalDevicePrivate::deviceConnected(const QBluetoothAddress &deviceAddress)
276{
277 if (!discoveredDevices.contains(deviceAddress))
278 discoveredDevices.append(deviceAddress);
279
280 QMetaObject::invokeMethod(q_ptr, "deviceConnected", Qt::QueuedConnection,
281 Q_ARG(QBluetoothAddress, deviceAddress));
282}
283
284void QBluetoothLocalDevicePrivate::deviceDisconnected(const QBluetoothAddress &deviceAddress)
285{
286 QList<QBluetoothAddress>::iterator devicePos =std::find(discoveredDevices.begin(),
287 discoveredDevices.end(),
288 deviceAddress);
289
290 if (devicePos != discoveredDevices.end())
291 discoveredDevices.erase(devicePos);
292
293 QMetaObject::invokeMethod(q_ptr, "deviceDisconnected", Qt::QueuedConnection,
294 Q_ARG(QBluetoothAddress, deviceAddress));
295}
296
297void QBluetoothLocalDevicePrivate::emitError(QBluetoothLocalDevice::Error error, bool queued)
298{
299 if (queued) {
300 QMetaObject::invokeMethod(q_ptr, "error", Qt::QueuedConnection,
301 Q_ARG(QBluetoothLocalDevice::Error, error));
302 } else {
303 emit q_ptr->error(QBluetoothLocalDevice::PairingError);
304 }
305}
306
307void QBluetoothLocalDevicePrivate::emitPairingFinished(const QBluetoothAddress &deviceAddress,
308 Pairing pairing, bool queued)
309{
310 Q_ASSERT_X(!deviceAddress.isNull(), Q_FUNC_INFO, "invalid target device address");
311 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
312
313 if (queued) {
314 QMetaObject::invokeMethod(q_ptr, "pairingFinished", Qt::QueuedConnection,
315 Q_ARG(QBluetoothAddress, deviceAddress),
316 Q_ARG(QBluetoothLocalDevice::Pairing, pairing));
317 } else {
318 emit q_ptr->pairingFinished(deviceAddress, pairing);
319 }
320}
321
322void QBluetoothLocalDevicePrivate::unpair(const QBluetoothAddress &deviceAddress)
323{
324 Q_ASSERT_X(!deviceAddress.isNull(), Q_FUNC_INFO,
325 "invalid target address");
326
327 emitPairingFinished(deviceAddress, QBluetoothLocalDevice::Unpaired, true);
328}
329
330QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) :
331 QObject(parent),
332 d_ptr(new QBluetoothLocalDevicePrivate(this))
333{
334}
335
336QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent) :
337 QObject(parent),
338 d_ptr(new QBluetoothLocalDevicePrivate(this, address))
339{
340}
341
342QBluetoothLocalDevice::~QBluetoothLocalDevice()
343{
344 delete d_ptr;
345}
346
347bool QBluetoothLocalDevice::isValid() const
348{
349 return d_ptr->isValid();
350}
351
352
353QString QBluetoothLocalDevice::name() const
354{
355 QT_BT_MAC_AUTORELEASEPOOL;
356
357 if (isValid()) {
358 if (NSString *const nsn = [d_ptr->hostController nameAsString])
359 return QString::fromNSString(nsn);
360 qCCritical(QT_BT_OSX) << Q_FUNC_INFO << "failed to obtain a name";
361 }
362
363 return QString();
364}
365
366QBluetoothAddress QBluetoothLocalDevice::address() const
367{
368 QT_BT_MAC_AUTORELEASEPOOL;
369
370 if (isValid()) {
371 if (NSString *const nsa = [d_ptr->hostController addressAsString])
372 return QBluetoothAddress(OSXBluetooth::qt_address(nsa));
373
374 qCCritical(QT_BT_OSX) << Q_FUNC_INFO << "failed to obtain an address";
375 } else {
376 qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "invalid local device";
377 }
378
379 return QBluetoothAddress();
380}
381
382void QBluetoothLocalDevice::powerOn()
383{
384 if (!isValid())
385 qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "invalid local device";
386}
387
388void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode)
389{
390 Q_UNUSED(mode)
391
392 if (!isValid())
393 qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "invalid local device";
394}
395
396QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const
397{
398 if (!isValid() || ![d_ptr->hostController powerState])
399 return HostPoweredOff;
400
401 return HostConnectable;
402}
403
404QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
405{
406 QT_BT_MAC_AUTORELEASEPOOL;
407
408 QList<QBluetoothAddress> connectedDevices;
409
410 // Take the devices known to IOBluetooth to be paired and connected first:
411 NSArray *const pairedDevices = [IOBluetoothDevice pairedDevices];
412 for (IOBluetoothDevice *device in pairedDevices) {
413 if ([device isConnected]) {
414 const QBluetoothAddress address(OSXBluetooth::qt_address([device getAddress]));
415 if (!address.isNull())
416 connectedDevices.append(address);
417 }
418 }
419
420 // Add devices, discovered by the connection monitor:
421 connectedDevices += d_ptr->discoveredDevices;
422 // Find something more elegant? :)
423 // But after all, addresses are integers.
424 std::sort(connectedDevices.begin(), connectedDevices.end());
425 connectedDevices.erase(std::unique(connectedDevices.begin(),
426 connectedDevices.end()),
427 connectedDevices.end());
428
429 return connectedDevices;
430}
431
432QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
433{
434 QList<QBluetoothHostInfo> localDevices;
435
436 QBluetoothLocalDevice defaultAdapter;
437 if (!defaultAdapter.isValid() || defaultAdapter.address().isNull()) {
438 qCCritical(QT_BT_OSX) << Q_FUNC_INFO <<"no valid device found";
439 return localDevices;
440 }
441
442 QBluetoothHostInfo deviceInfo;
443 deviceInfo.setName(defaultAdapter.name());
444 deviceInfo.setAddress(defaultAdapter.address());
445
446 localDevices.append(deviceInfo);
447
448 return localDevices;
449}
450
451void QBluetoothLocalDevice::pairingConfirmation(bool confirmation)
452{
453 Q_UNUSED(confirmation)
454}
455
456
457void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing)
458{
459 if (!isValid())
460 qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "invalid local device";
461
462 if (!isValid() || address.isNull()) {
463 d_ptr->emitError(PairingError, true);
464 return;
465 }
466
467 OSXBluetooth::qt_test_iobluetooth_runloop();
468
469 return d_ptr->requestPairing(address, pairing);
470}
471
472QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(const QBluetoothAddress &address) const
473{
474 if (!isValid())
475 qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "invalid local device";
476
477 if (!isValid() || address.isNull())
478 return Unpaired;
479
480 return d_ptr->pairingStatus(address);
481}
482
483QT_END_NAMESPACE
484

Warning: That file was not part of the compilation database. It may have many parsing errors.