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 "qbluetoothservicediscoveryagent_p.h"
41#include "qbluetoothservicediscoveryagent.h"
42#include "qbluetoothdevicediscoveryagent.h"
43#include "qbluetoothlocaldevice.h"
44#include "osx/osxbtsdpinquiry_p.h"
45#include "qbluetoothhostinfo.h"
46#include "osx/osxbtutility_p.h"
47#include "osx/osxbluetooth_p.h"
48#include "osx/uistrings_p.h"
49
50#include <QtCore/qloggingcategory.h>
51#include <QtCore/qscopedpointer.h>
52#include <QtCore/qstring.h>
53#include <QtCore/qglobal.h>
54#include <QtCore/qdebug.h>
55#include <QtCore/qlist.h>
56
57#include <Foundation/Foundation.h>
58
59QT_BEGIN_NAMESPACE
60
61namespace {
62
63using DarwinBluetooth::RetainPolicy;
64using ObjCServiceInquiry = QT_MANGLE_NAMESPACE(OSXBTSDPInquiry);
65
66}
67
68QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
69 QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &localAddress) :
70
71 error(QBluetoothServiceDiscoveryAgent::NoError),
72 m_deviceAdapterAddress(localAddress),
73 state(Inactive),
74 mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery),
75 singleDevice(false),
76 q_ptr(qp)
77
78{
79 Q_ASSERT(q_ptr);
80 serviceInquiry.reset([[ObjCServiceInquiry alloc] initWithDelegate:this], RetainPolicy::noInitialRetain);
81}
82
83QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate()
84{
85}
86
87void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &deviceAddress)
88{
89 QT_BT_MAC_AUTORELEASEPOOL;
90
91 if (deviceAddress.isNull()) {
92 // This can happen: LE scan works with CoreBluetooth, but CBPeripherals
93 // do not expose hardware addresses.
94 // Pop the current QBluetoothDeviceInfo and decide what to do next.
95 return _q_serviceDiscoveryFinished();
96 }
97
98 // Autoreleased object.
99 IOBluetoothHostController *const hc = [IOBluetoothHostController defaultController];
100 if (![hc powerState]) {
101 discoveredDevices.clear();
102 if (singleDevice) {
103 error = QBluetoothServiceDiscoveryAgent::PoweredOffError;
104 errorString = QCoreApplication::translate(SERVICE_DISCOVERY, SD_LOCAL_DEV_OFF);
105 emit q_ptr->error(error);
106 }
107
108 return _q_serviceDiscoveryFinished();
109 }
110
111 if (DiscoveryMode() == QBluetoothServiceDiscoveryAgent::MinimalDiscovery) {
112 performMinimalServiceDiscovery(deviceAddress);
113 } else {
114 IOReturn result = kIOReturnSuccess;
115 auto nativeInquiry = serviceInquiry.getAs<ObjCServiceInquiry>();
116 if (uuidFilter.size())
117 result = [nativeInquiry performSDPQueryWithDevice:deviceAddress filters:uuidFilter];
118 else
119 result = [nativeInquiry performSDPQueryWithDevice:deviceAddress];
120
121 if (result != kIOReturnSuccess) {
122 // Failed immediately to perform an SDP inquiry on IOBluetoothDevice:
123 SDPInquiryError(nil, result);
124 }
125 }
126}
127
128void QBluetoothServiceDiscoveryAgentPrivate::stop()
129{
130 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
131
132 discoveredDevices.clear();
133
134 // "Stops" immediately.
135 [serviceInquiry.getAs<ObjCServiceInquiry>() stopSDPQuery];
136
137 emit q_ptr->canceled();
138}
139
140void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryFinished(void *generic)
141{
142 auto device = static_cast<IOBluetoothDevice *>(generic);
143 Q_ASSERT_X(device, Q_FUNC_INFO, "invalid IOBluetoothDevice (nil)");
144
145 if (state == Inactive)
146 return;
147
148 QT_BT_MAC_AUTORELEASEPOOL;
149
150 NSArray *const records = device.services;
151 for (IOBluetoothSDPServiceRecord *record in records) {
152 QBluetoothServiceInfo serviceInfo;
153 Q_ASSERT_X(discoveredDevices.size() >= 1, Q_FUNC_INFO, "invalid number of devices");
154
155 serviceInfo.setDevice(discoveredDevices.at(0));
156 OSXBluetooth::extract_service_record(record, serviceInfo);
157
158 if (!serviceInfo.isValid())
159 continue;
160
161 if (!isDuplicatedService(serviceInfo)) {
162 discoveredServices.append(serviceInfo);
163 emit q_ptr->serviceDiscovered(serviceInfo);
164 // Here a user code can ... interrupt us by calling
165 // stop. Check this.
166 if (state == Inactive)
167 break;
168 }
169 }
170
171 _q_serviceDiscoveryFinished();
172}
173
174void QBluetoothServiceDiscoveryAgentPrivate::SDPInquiryError(void *device, IOReturn errorCode)
175{
176 Q_UNUSED(device)
177
178 qCWarning(QT_BT_OSX) << "inquiry failed with IOKit code:" << int(errorCode);
179
180 discoveredDevices.clear();
181 // TODO: find a better mapping from IOReturn to QBluetoothServiceDiscoveryAgent::Error.
182 if (singleDevice) {
183 error = QBluetoothServiceDiscoveryAgent::UnknownError;
184 errorString = QCoreApplication::translate(DEV_DISCOVERY, DD_UNKNOWN_ERROR);
185 emit q_ptr->error(error);
186 }
187
188 _q_serviceDiscoveryFinished();
189}
190
191void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress)
192{
193 Q_ASSERT_X(!deviceAddress.isNull(), Q_FUNC_INFO, "invalid device address");
194
195 QT_BT_MAC_AUTORELEASEPOOL;
196
197 const BluetoothDeviceAddress iobtAddress = OSXBluetooth::iobluetooth_address(deviceAddress);
198 IOBluetoothDevice *const device = [IOBluetoothDevice deviceWithAddress:&iobtAddress];
199 if (!device || !device.services) {
200 if (singleDevice) {
201 error = QBluetoothServiceDiscoveryAgent::UnknownError;
202 errorString = QCoreApplication::translate(SERVICE_DISCOVERY, SD_MINIMAL_FAILED);
203 emit q_ptr->error(error);
204 }
205 } else {
206
207 NSArray *const records = device.services;
208 for (IOBluetoothSDPServiceRecord *record in records) {
209 QBluetoothServiceInfo serviceInfo;
210 Q_ASSERT_X(discoveredDevices.size() >= 1, Q_FUNC_INFO,
211 "invalid number of devices");
212
213 serviceInfo.setDevice(discoveredDevices.at(0));
214 OSXBluetooth::extract_service_record(record, serviceInfo);
215
216 if (!serviceInfo.isValid())
217 continue;
218
219 if (!uuidFilter.isEmpty() && !serviceHasMatchingUuid(serviceInfo))
220 continue;
221
222 if (!isDuplicatedService(serviceInfo)) {
223 discoveredServices.append(serviceInfo);
224 emit q_ptr->serviceDiscovered(serviceInfo);
225 }
226 }
227 }
228
229 _q_serviceDiscoveryFinished();
230}
231
232bool QBluetoothServiceDiscoveryAgentPrivate::serviceHasMatchingUuid(const QBluetoothServiceInfo &serviceInfo) const
233{
234 for (const auto &requestedUuid : uuidFilter) {
235 if (serviceInfo.serviceUuid() == requestedUuid)
236 return true;
237 if (serviceInfo.serviceClassUuids().contains(requestedUuid))
238 return true;
239 }
240 return false;
241}
242
243QT_END_NAMESPACE
244

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