Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
btdeviceinquiry.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
5#include "btutility_p.h"
6
7#include <QtCore/qloggingcategory.h>
8#include <QtCore/qtimer.h>
9#include <QtCore/qdebug.h>
10
11#include <memory>
12
13QT_USE_NAMESPACE
14
15const uint8_t IOBlueoothInquiryLengthS = 15;
16
17@implementation DarwinBTClassicDeviceInquiry
18{
19 IOBluetoothDeviceInquiry *m_inquiry;
20 bool m_active;
21 DarwinBluetooth::DeviceInquiryDelegate *m_delegate;//C++ "delegate"
22
23 std::unique_ptr<QTimer> watchDog;
24}
25
26- (id)initWithDelegate:(DarwinBluetooth::DeviceInquiryDelegate *)delegate
27{
28 if (self = [super init]) {
29 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid device inquiry delegate (null)");
30
31 m_inquiry = [[IOBluetoothDeviceInquiry inquiryWithDelegate:self] retain];
32
33 if (m_inquiry) {
34 // Inquiry length is 15 seconds. Starting from macOS 10.15.7
35 // (the lowest version I was able to test on, though initially
36 // the problem was found on macOS 11, arm64 machine and then
37 // confirmed on macOS 12 Beta 4), it seems to be ignored,
38 // thus scan never stops. See -start for how we try to prevent
39 // this.
40 [m_inquiry setInquiryLength:IOBlueoothInquiryLengthS];
41 [m_inquiry setUpdateNewDeviceNames:NO];//Useless, disable!
42 m_delegate = delegate;
43 } else {
44 qCCritical(QT_BT_DARWIN) << "failed to create a device inquiry";
45 }
46
47 m_active = false;
48 }
49
50 return self;
51}
52
53- (void)dealloc
54{
55 // Noop if m_inquiry is nil.
56 [m_inquiry setDelegate:nil];
57 if (m_active)
58 [m_inquiry stop];
59 [m_inquiry release];
60
61 [super dealloc];
62}
63
64- (bool)isActive
65{
66 return m_active;
67}
68
69- (IOReturn)start
70{
71 if (!m_inquiry)
72 return kIOReturnNoPower;
73
74 if (m_active)
75 return kIOReturnBusy;
76
77 m_active = true;
78 [m_inquiry clearFoundDevices];
79
80 qCDebug(QT_BT_DARWIN) << "Starting device inquiry with"
81 << IOBlueoothInquiryLengthS << "second timeout limit.";
82 const IOReturn result = [m_inquiry start];
83 if (result != kIOReturnSuccess) {
84 // QtBluetooth will probably convert an error into UnknownError,
85 // losing the actual information.
86 qCWarning(QT_BT_DARWIN) << "device inquiry start failed with IOKit error code:" << result;
87 m_active = false;
88 } else {
89 // Docs say it's 10 s. by default, we set it to 15 s. (see -initWithDelegate:),
90 // and it may fail to finish.
91 watchDog.reset(new QTimer);
92 watchDog->connect(watchDog.get(), &QTimer::timeout, watchDog.get(), [self]{
93 qCWarning(QT_BT_DARWIN, "Manually interrupting IOBluetoothDeviceInquiry");
94 qCDebug(QT_BT_DARWIN) << "Found devices:" << [m_inquiry foundDevices];
95 [self stop];
96 });
97
98 watchDog->setSingleShot(true);
99 // +2 to give IOBluetooth a chance to stop it first:
100 watchDog->setInterval((IOBlueoothInquiryLengthS + 2) * 1000);
101 watchDog->start();
102 }
103
104 return result;
105}
106
107- (IOReturn)stop
108{
109 if (!m_active)
110 return kIOReturnSuccess;
111
112 Q_ASSERT_X(m_inquiry, Q_FUNC_INFO, "active but nil inquiry");
113
114 return [m_inquiry stop];
115}
116
117- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry *)sender
118 error:(IOReturn)error aborted:(BOOL)aborted
119{
120 qCDebug(QT_BT_DARWIN) << "deviceInquiryComplete, error:" << error
121 << "user-stopped:" << aborted;
122 if (!m_active)
123 return;
124
125 if (sender != m_inquiry) // Can never happen in the current version.
126 return;
127
128 Q_ASSERT_X(m_delegate, Q_FUNC_INFO, "invalid device inquiry delegate (null)");
129
130 if (error != kIOReturnSuccess && !aborted) {
131 // QtBluetooth has not too many error codes, 'UnknownError' is not really
132 // useful, log the actual error code here:
133 qCWarning(QT_BT_DARWIN) << "IOKit error code: " << error;
134 // Let watchDog to stop it, calling -stop at timeout, otherwise,
135 // it looks like inquiry continues even after this error and
136 // keeps reporting new devices found.
137 } else {
138 // Either a normal completion or from a timer slot.
139 watchDog.reset();
140 m_active = false;
141 m_delegate->inquiryFinished();
142 }
143}
144
145- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry *)sender
146 device:(IOBluetoothDevice *)device
147{
148 qCDebug(QT_BT_DARWIN) << "deviceInquiryDeviceFound:" << [device nameOrAddress];
149 if (sender != m_inquiry) // Can never happen in the current version.
150 return;
151
152 if (!m_active) {
153 // We are not expecting new device(s) to be found after we reported 'finished'.
154 qCWarning(QT_BT_DARWIN, "IOBluetooth device found after inquiry complete/interrupted");
155 return;
156 }
157
158 Q_ASSERT_X(m_delegate, Q_FUNC_INFO, "invalid device inquiry delegate (null)");
159 m_delegate->classicDeviceFound(device);
160}
161
162- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry *)sender
163{
164 Q_UNUSED(sender);
165}
166
167@end