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
qbluetoothservicediscoveryagent_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
8#include "darwin/btsdpinquiry_p.h"
10#include "darwin/btutility_p.h"
11#include "darwin/uistrings_p.h"
12
13#include <QtCore/qoperatingsystemversion.h>
14#include <QtCore/qcoreapplication.h>
15#include <QtCore/qloggingcategory.h>
16#include <QtCore/qstring.h>
17#include <QtCore/qglobal.h>
18#include <QtCore/qdebug.h>
19#include <QtCore/qlist.h>
20
21#include <Foundation/Foundation.h>
22
23#include <IOBluetooth/IOBluetooth.h>
24
26
27namespace {
28
29using DarwinBluetooth::RetainPolicy;
30
31}
32
34 QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &localAddress) :
35
40 singleDevice(false),
41 q_ptr(qp)
42
43{
44 Q_ASSERT(q_ptr);
45 serviceInquiry.reset([[DarwinBTSDPInquiry alloc] initWithDelegate:this], RetainPolicy::noInitialRetain);
46}
47
49{
50}
51
52void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &deviceAddress)
53{
55
56 if (deviceAddress.isNull()) {
57 // This can happen: LE scan works with CoreBluetooth, but CBPeripherals
58 // do not expose hardware addresses.
59 // Pop the current QBluetoothDeviceInfo and decide what to do next.
61 }
62
63 // Autoreleased object.
64 IOBluetoothHostController *const hc = [IOBluetoothHostController defaultController];
65 if (![hc powerState]) {
66 discoveredDevices.clear();
67 if (singleDevice) {
68 error = QBluetoothServiceDiscoveryAgent::PoweredOffError;
69 errorString = QCoreApplication::translate(SERVICE_DISCOVERY, SD_LOCAL_DEV_OFF);
70 emit q_ptr->errorOccurred(error);
71 }
72
74 }
75
76 if (DiscoveryMode() == QBluetoothServiceDiscoveryAgent::MinimalDiscovery) {
77 performMinimalServiceDiscovery(deviceAddress);
78 } else {
79 IOReturn result = kIOReturnSuccess;
80 auto nativeInquiry = serviceInquiry.getAs<DarwinBTSDPInquiry>();
81 if (uuidFilter.size())
82 result = [nativeInquiry performSDPQueryWithDevice:deviceAddress filters:uuidFilter];
83 else
84 result = [nativeInquiry performSDPQueryWithDevice:deviceAddress];
85
86 if (result != kIOReturnSuccess) {
87 // Failed immediately to perform an SDP inquiry on IOBluetoothDevice:
88 SDPInquiryError(nil, result);
89 }
90 }
91}
92
94{
95 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
96
97 discoveredDevices.clear();
98
99 // "Stops" immediately.
100 [serviceInquiry.getAs<DarwinBTSDPInquiry>() stopSDPQuery];
101
102 emit q_ptr->canceled();
103}
104
105void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryFinished(void *generic)
106{
107 auto device = static_cast<IOBluetoothDevice *>(generic);
108 Q_ASSERT_X(device, Q_FUNC_INFO, "invalid IOBluetoothDevice (nil)");
109
110 if (state == Inactive)
111 return;
112
114
115 NSArray *const records = device.services;
116 qCDebug(QT_BT_DARWIN) << "SDP finished for device" << [device nameOrAddress]
117 << ", services found:" << [records count];
118 for (IOBluetoothSDPServiceRecord *record in records) {
119 QBluetoothServiceInfo serviceInfo;
120 Q_ASSERT_X(discoveredDevices.size() >= 1, Q_FUNC_INFO, "invalid number of devices");
121
122 qCDebug(QT_BT_DARWIN) << "Processing service" << [record getServiceName];
123 serviceInfo.setDevice(discoveredDevices.at(0));
124 DarwinBluetooth::extract_service_record(record, serviceInfo);
125
126 if (!serviceInfo.isValid()) {
127 qCDebug(QT_BT_DARWIN) << "Discarding invalid service";
128 continue;
129 }
130
131 if (QOperatingSystemVersion::current() > QOperatingSystemVersion::MacOSBigSur
132 && uuidFilter.size()) {
133 const auto &serviceId = serviceInfo.serviceUuid();
134 bool match = !serviceId.isNull() && uuidFilter.contains(serviceId);
135 if (!match) {
136 const auto &classUuids = serviceInfo.serviceClassUuids();
137 for (const auto &uuid : classUuids) {
138 if (uuidFilter.contains(uuid)) {
139 match = true;
140 break;
141 }
142 }
143 if (!match)
144 continue;
145 }
146 }
147
148
149 if (!isDuplicatedService(serviceInfo)) {
150 discoveredServices.append(serviceInfo);
151 emit q_ptr->serviceDiscovered(serviceInfo);
152 // Here a user code can ... interrupt us by calling
153 // stop. Check this.
154 if (state == Inactive)
155 break;
156 }
157 }
158
160}
161
162void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryError(void *device, IOReturn errorCode)
163{
164 Q_UNUSED(device);
165
166 qCWarning(QT_BT_DARWIN) << "inquiry failed with IOKit code:" << int(errorCode);
167
168 discoveredDevices.clear();
169 // TODO: find a better mapping from IOReturn to QBluetoothServiceDiscoveryAgent::Error.
170 if (singleDevice) {
171 error = QBluetoothServiceDiscoveryAgent::UnknownError;
172 errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_UNKNOWN_ERROR);
173 emit q_ptr->errorOccurred(error);
174 }
175
177}
178
179void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress)
180{
181 Q_ASSERT_X(!deviceAddress.isNull(), Q_FUNC_INFO, "invalid device address");
182
184
185 const BluetoothDeviceAddress iobtAddress = DarwinBluetooth::iobluetooth_address(deviceAddress);
186 IOBluetoothDevice *const device = [IOBluetoothDevice deviceWithAddress:&iobtAddress];
187 if (!device || !device.services) {
188 if (singleDevice) {
189 error = QBluetoothServiceDiscoveryAgent::UnknownError;
190 errorString = QCoreApplication::translate(SERVICE_DISCOVERY, SD_MINIMAL_FAILED);
191 emit q_ptr->errorOccurred(error);
192 }
193 } else {
194
195 NSArray *const records = device.services;
196 for (IOBluetoothSDPServiceRecord *record in records) {
197 QBluetoothServiceInfo serviceInfo;
198 Q_ASSERT_X(discoveredDevices.size() >= 1, Q_FUNC_INFO,
199 "invalid number of devices");
200
201 serviceInfo.setDevice(discoveredDevices.at(0));
202 DarwinBluetooth::extract_service_record(record, serviceInfo);
203
204 if (!serviceInfo.isValid())
205 continue;
206
207 if (!uuidFilter.isEmpty() && !serviceHasMatchingUuid(serviceInfo))
208 continue;
209
210 if (!isDuplicatedService(serviceInfo)) {
211 discoveredServices.append(serviceInfo);
212 emit q_ptr->serviceDiscovered(serviceInfo);
213 }
214 }
215 }
216
218}
219
220bool QBluetoothServiceDiscoveryAgentPrivate::serviceHasMatchingUuid(const QBluetoothServiceInfo &serviceInfo) const
221{
222 for (const auto &requestedUuid : uuidFilter) {
223 if (serviceInfo.serviceUuid() == requestedUuid)
224 return true;
225 if (serviceInfo.serviceClassUuids().contains(requestedUuid))
226 return true;
227 }
228 return false;
229}
230
231QT_END_NAMESPACE
#define QT_BT_MAC_AUTORELEASEPOOL
Definition btutility_p.h:78
QBluetoothServiceDiscoveryAgentPrivate(QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter)