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
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
4#include "btdeviceinquiry_p.h"
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
14
15const uint8_t IOBlueoothInquiryLengthS = 15;
16
18{
19 IOBluetoothDeviceInquiry *m_inquiry;
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
bool m_active
QT_USE_NAMESPACE const uint8_t IOBlueoothInquiryLengthS
DarwinBluetooth::DeviceInquiryDelegate * m_delegate
std::unique_ptr< QTimer > watchDog
IOBluetoothDevice * device
bool isActive
\inmodule QtCore
Definition qtimer.h:20
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
QString self
Definition language.cpp:58
#define Q_FUNC_INFO
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError * error
#define qCCritical(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLenum GLuint id
[7]
GLuint start
GLuint64EXT * result
[6]
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define Q_UNUSED(x)