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#include "qbluetoothhostinfo.h"
40
41#include "qbluetoothdevicediscoveryagent.h"
42#include "qbluetoothdevicediscoveryagent_p.h"
43#include <QtCore/qloggingcategory.h>
44
45QT_BEGIN_NAMESPACE
46
47Q_DECLARE_LOGGING_CATEGORY(QT_BT)
48
49/*!
50 \class QBluetoothDeviceDiscoveryAgent
51 \inmodule QtBluetooth
52 \brief The QBluetoothDeviceDiscoveryAgent class discovers the Bluetooth
53 devices nearby.
54
55 \since 5.2
56
57 To discover the nearby Bluetooth devices:
58 \list
59 \li create an instance of QBluetoothDeviceDiscoveryAgent,
60 \li connect to either the deviceDiscovered() or finished() signals,
61 \li and call start().
62 \endlist
63
64 \snippet doc_src_qtbluetooth.cpp device_discovery
65
66 To retrieve results asynchronously, connect to the deviceDiscovered() signal. To get a list of
67 all discovered devices, call discoveredDevices() after the finished() signal.
68
69 This class can be used to discover Classic and Low Energy Bluetooth devices.
70 The individual device type can be determined via the
71 \l QBluetoothDeviceInfo::coreConfigurations() attribute.
72 In most cases the list returned by \l discoveredDevices() contains both types
73 of devices. However not every platform can detect both types of devices.
74 On platforms with this limitation (for example iOS only suports Low Energy discovery),
75 the discovery process will limit the search to the type which is supported.
76
77 \note Since Android 6.0 the ability to detect devices requires ACCESS_COARSE_LOCATION.
78
79 \note Due to API limitations it is only possible to find devices that have been paired using
80 Windows' settings on Windows.
81*/
82
83/*!
84 \enum QBluetoothDeviceDiscoveryAgent::Error
85
86 Indicates all possible error conditions found during Bluetooth device discovery.
87
88 \value NoError No error has occurred.
89 \value PoweredOffError The Bluetooth adaptor is powered off, power it on before doing discovery.
90 \value InputOutputError Writing or reading from the device resulted in an error.
91 \value InvalidBluetoothAdapterError The passed local adapter address does not match the physical
92 adapter address of any local Bluetooth device.
93 \value UnsupportedPlatformError Device discovery is not possible or implemented on the current
94 platform. The error is set in response to a call to \l start().
95 An example for such cases are iOS versions below 5.0 which do not support
96 Bluetooth device search at all. This value was introduced by Qt 5.5.
97 \value UnsupportedDiscoveryMethod One of the requested discovery methods is not supported by
98 the current platform. This value was introduced by Qt 5.8.
99 \value UnknownError An unknown error has occurred.
100*/
101
102/*!
103 \enum QBluetoothDeviceDiscoveryAgent::InquiryType
104
105 This enum describes the inquiry type used while discovering Bluetooth devices.
106
107 \value GeneralUnlimitedInquiry A general unlimited inquiry. Discovers all visible Bluetooth
108 devices in the local vicinity.
109 \value LimitedInquiry A limited inquiry discovers devices that are in limited
110 inquiry mode.
111
112 LimitedInquiry is not supported on all platforms. If it is requested on a platform that does not
113 support it, GeneralUnlimitedInquiry will be used instead. Setting LimitedInquiry is useful
114 for multi-player Bluetooth-based games that needs faster communication between the devices.
115 The phone scans for devices in LimitedInquiry and Service Discovery is done on one or two devices
116 to speed up the service scan. After the game has connected to the device it intended to,
117 the device returns to GeneralUnlimitedInquiry.
118*/
119
120/*!
121 \enum QBluetoothDeviceDiscoveryAgent::DiscoveryMethod
122
123 This enum descibes the type of discovery method employed by the QBluetoothDeviceDiscoveryAgent.
124
125 \value NoMethod The discovery is not possible. None of the available
126 methods are supported.
127 \value ClassicMethod The discovery process searches for Bluetooth Classic
128 (BaseRate) devices.
129 \value LowEnergyMethod The discovery process searches for Bluetooth Low Energy
130 devices.
131
132 \sa supportedDiscoveryMethods()
133 \since 5.8
134*/
135
136/*!
137 \fn void QBluetoothDeviceDiscoveryAgent::deviceDiscovered(const QBluetoothDeviceInfo &info)
138
139 This signal is emitted when the Bluetooth device described by \a info is discovered.
140
141 The signal is emitted as soon as the most important device information
142 has been collected. However, as long as the \l finished() signal has not
143 been emitted the information collection continues even for already discovered
144 devices. This is particularly true for signal strength information (RSSI) and
145 manufacturer data updates. If the use case requires continuous manufacturer data
146 or RSSI updates it is advisable to retrieve the device information via
147 \l discoveredDevices() once the discovery has finished or listen to the
148 \l deviceUpdated() signal.
149
150 If \l lowEnergyDiscoveryTimeout() is larger than 0 the signal is only ever
151 emitted when at least one attribute of \a info changes. This reflects the desire to
152 receive updates as more precise information becomes available. The exception to this
153 behavior is the case when \l lowEnergyDiscoveryTimeout is set to \c 0. A timeout of \c 0
154 expresses the desire to monitor the appearance and disappearance of Low Energy devices
155 over time. Under this condition the \l deviceDiscovered() signal is emitted even if
156 \a info has not changed since the last signal emission.
157
158 \sa QBluetoothDeviceInfo::rssi(), lowEnergyDiscoveryTimeout()
159*/
160
161/*!
162 \fn void QBluetoothDeviceDiscoveryAgent::deviceUpdated(const QBluetoothDeviceInfo &info, QBluetoothDeviceInfo::Fields updatedFields)
163
164 This signal is emitted when the agent receives additional information about
165 the Bluetooth device described by \a info. The \a updatedFields flags tell
166 which information has been updated.
167
168 During discovery, some information can change dynamically, such as
169 \l {QBluetoothDeviceInfo::rssi()}{signal strength} and
170 \l {QBluetoothDeviceInfo::manufacturerData()}{manufacturerData}.
171 This signal informs you that if your application is displaying this data, it
172 can be updated, rather than waiting until the discovery has finished.
173
174 \note This signal is only emitted on Android, iOS, macOS, and BlueZ 5.x.
175
176 \sa QBluetoothDeviceInfo::rssi(), lowEnergyDiscoveryTimeout()
177*/
178
179/*!
180 \fn void QBluetoothDeviceDiscoveryAgent::finished()
181
182 This signal is emitted when Bluetooth device discovery completes.
183 The signal is not going to be emitted if the device discovery finishes with an error.
184*/
185
186/*!
187 \fn void QBluetoothDeviceDiscoveryAgent::error(QBluetoothDeviceDiscoveryAgent::Error error)
188
189 This signal is emitted when an \a error occurs during Bluetooth device discovery.
190 The \a error parameter describes the error that occurred.
191
192 \sa error(), errorString()
193*/
194
195/*!
196 \fn void QBluetoothDeviceDiscoveryAgent::canceled()
197
198 This signal is emitted when device discovery is aborted by a call to stop().
199*/
200
201/*!
202 \fn bool QBluetoothDeviceDiscoveryAgent::isActive() const
203
204 Returns true if the agent is currently discovering Bluetooth devices, otherwise returns false.
205*/
206
207/*!
208 Constructs a new Bluetooth device discovery agent with parent \a parent.
209*/
210QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(QObject *parent) :
211 QObject(parent),
212 d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(QBluetoothAddress(), this))
213{
214}
215
216/*!
217 Constructs a new Bluetooth device discovery agent with \a parent.
218
219 It uses \a deviceAdapter for the device search. If \a deviceAdapter is default constructed the resulting
220 QBluetoothDeviceDiscoveryAgent object will use the local default Bluetooth adapter.
221
222 If a \a deviceAdapter is specified that is not a local adapter \l error() will be set to
223 \l InvalidBluetoothAdapterError. Therefore it is recommended to test the error flag immediately after
224 using this constructor.
225
226 \sa error()
227*/
228QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(
229 const QBluetoothAddress &deviceAdapter, QObject *parent) :
230 QObject(parent),
231 d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(deviceAdapter, this))
232{
233 if (!deviceAdapter.isNull()) {
234 const QList<QBluetoothHostInfo> localDevices = QBluetoothLocalDevice::allDevices();
235 for (const QBluetoothHostInfo &hostInfo : localDevices) {
236 if (hostInfo.address() == deviceAdapter)
237 return;
238 }
239 d_ptr->lastError = InvalidBluetoothAdapterError;
240 d_ptr->errorString = tr("Invalid Bluetooth adapter address");
241 }
242}
243
244/*!
245 Destructor for ~QBluetoothDeviceDiscoveryAgent()
246*/
247QBluetoothDeviceDiscoveryAgent::~QBluetoothDeviceDiscoveryAgent()
248{
249 delete d_ptr;
250}
251
252/*!
253 \property QBluetoothDeviceDiscoveryAgent::inquiryType
254 \brief type of inquiry scan to be used while discovering devices
255
256 This property affects the type of inquiry scan which is performed while discovering devices.
257
258 By default, this property is set to GeneralUnlimitedInquiry.
259
260 Not all platforms support LimitedInquiry.
261
262 \sa InquiryType
263*/
264QBluetoothDeviceDiscoveryAgent::InquiryType QBluetoothDeviceDiscoveryAgent::inquiryType() const
265{
266 Q_D(const QBluetoothDeviceDiscoveryAgent);
267 return d->inquiryType;
268}
269
270void QBluetoothDeviceDiscoveryAgent::setInquiryType(QBluetoothDeviceDiscoveryAgent::InquiryType type)
271{
272 Q_D(QBluetoothDeviceDiscoveryAgent);
273 d->inquiryType = type;
274}
275
276/*!
277 Returns a list of all discovered Bluetooth devices.
278*/
279QList<QBluetoothDeviceInfo> QBluetoothDeviceDiscoveryAgent::discoveredDevices() const
280{
281 Q_D(const QBluetoothDeviceDiscoveryAgent);
282 return d->discoveredDevices;
283}
284
285/*!
286 Sets the maximum search time for Bluetooth Low Energy device search to
287 \a timeout in milliseconds. If \a timeout is \c 0 the discovery runs
288 until \l stop() is called.
289
290 This reflects the fact that the discovery process for Bluetooth Low Energy devices
291 is mostly open ended. The platform continues to look for more devices until the search is
292 manually stopped. The timeout ensures that the search is aborted after \a timeout milliseconds.
293 Of course, it is still possible to manually abort the discovery by calling \l stop().
294
295 The new timeout value does not take effect until the device search is restarted.
296 In addition the timeout does not affect the classic Bluetooth device search. Depending on
297 the platform the classic search may add more time to the total discovery process
298 beyond \a timeout.
299
300 \sa lowEnergyDiscoveryTimeout()
301 \since 5.8
302 */
303void QBluetoothDeviceDiscoveryAgent::setLowEnergyDiscoveryTimeout(int timeout)
304{
305 Q_D(QBluetoothDeviceDiscoveryAgent);
306
307 // cannot deliberately turn it off
308 if (d->lowEnergySearchTimeout < 0 || timeout < 0) {
309 qCDebug(QT_BT) << "The Bluetooth Low Energy device discovery timeout cannot be negative "
310 "or set on a backend which does not support this feature.";
311 return;
312 }
313
314 d->lowEnergySearchTimeout = timeout;
315}
316
317/*!
318 Returns a timeout in milliseconds that is applied to the Bluetooth Low Energy device search.
319 A value of \c -1 implies that the platform does not support this property and the timeout for
320 the device search cannot be adjusted. A return value of \c 0
321 implies a never-ending search which must be manually stopped via \l stop().
322
323 \sa setLowEnergyDiscoveryTimeout()
324 \since 5.8
325 */
326int QBluetoothDeviceDiscoveryAgent::lowEnergyDiscoveryTimeout() const
327{
328 Q_D(const QBluetoothDeviceDiscoveryAgent);
329 return d->lowEnergySearchTimeout;
330}
331
332/*!
333 \fn QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods()
334
335 This function returns the discovery methods supported by the current platform.
336 It can be used to limit the scope of the device discovery.
337
338 \since 5.8
339*/
340
341/*!
342 Starts Bluetooth device discovery, if it is not already started.
343
344 The deviceDiscovered() signal is emitted as each device is discovered. The finished() signal
345 is emitted once device discovery is complete. The discovery utilizes the maximum set of
346 supported discovery methods on the platform.
347
348 \sa supportedDiscoveryMethods()
349*/
350void QBluetoothDeviceDiscoveryAgent::start()
351{
352 Q_D(QBluetoothDeviceDiscoveryAgent);
353 if (!isActive() && d->lastError != InvalidBluetoothAdapterError)
354 d->start(supportedDiscoveryMethods());
355}
356
357/*!
358 Starts Bluetooth device discovery, if it is not already started and the provided
359 \a methods are supported.
360 The discovery \a methods limit the scope of the device search.
361 For example, if the target service or device is a Bluetooth Low Energy device,
362 this function could be used to limit the search to Bluetooth Low Energy devices and
363 thereby reduces the discovery time significantly.
364
365 \note \a methods only determines the type of discovery and does not imply
366 the filtering of the results. For example, the search may still contain classic bluetooth devices
367 despite \a methods being set to \l {QBluetoothDeviceDiscoveryAgent::LowEnergyMethod}
368 {LowEnergyMethod} only. This may happen due to previously cached search results
369 which may be incorporated into the search results.
370
371 \since 5.8
372*/
373void QBluetoothDeviceDiscoveryAgent::start(DiscoveryMethods methods)
374{
375 if (methods == NoMethod)
376 return;
377
378 DiscoveryMethods supported =
379 QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods();
380
381 Q_D(QBluetoothDeviceDiscoveryAgent);
382 if (!((supported & methods) == methods)) {
383 d->lastError = UnsupportedDiscoveryMethod;
384 d->errorString = QBluetoothDeviceDiscoveryAgent::tr("One or more device discovery methods "
385 "are not supported on this platform");
386 emit error(d->lastError);
387 return;
388 }
389
390 if (!isActive() && d->lastError != InvalidBluetoothAdapterError)
391 d->start(methods);
392}
393
394/*!
395 Stops Bluetooth device discovery. The cancel() signal is emitted once the
396 device discovery is canceled. start() maybe called before the cancel signal is
397 received. Once start() has been called the cancel signal from the prior
398 discovery will be discarded.
399*/
400void QBluetoothDeviceDiscoveryAgent::stop()
401{
402 Q_D(QBluetoothDeviceDiscoveryAgent);
403 if (isActive() && d->lastError != InvalidBluetoothAdapterError)
404 d->stop();
405}
406
407bool QBluetoothDeviceDiscoveryAgent::isActive() const
408{
409 Q_D(const QBluetoothDeviceDiscoveryAgent);
410 return d->isActive();
411}
412
413/*!
414 Returns the last error.
415*/
416QBluetoothDeviceDiscoveryAgent::Error QBluetoothDeviceDiscoveryAgent::error() const
417{
418 Q_D(const QBluetoothDeviceDiscoveryAgent);
419
420 return d->lastError;
421}
422
423/*!
424 Returns a human-readable description of the last error.
425*/
426QString QBluetoothDeviceDiscoveryAgent::errorString() const
427{
428 Q_D(const QBluetoothDeviceDiscoveryAgent);
429 return d->errorString;
430}
431
432QT_END_NAMESPACE
433
434#include "moc_qbluetoothdevicediscoveryagent.cpp"
435