Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qbluetoothservicediscoveryagent.cpp
Go to the documentation of this file.
1
// Copyright (C) 2016 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
"qbluetoothhostinfo.h"
5
#
include
"qbluetoothlocaldevice.h"
6
#
include
"qbluetoothservicediscoveryagent.h"
7
#
include
"qbluetoothservicediscoveryagent_p.h"
8
9
#
include
"qbluetoothdevicediscoveryagent.h"
10
11
QT_BEGIN_NAMESPACE
12
13
/*!
14
\class QBluetoothServiceDiscoveryAgentPrivate
15
\inmodule QtBluetooth
16
\internal
17
*/
18
19
/*!
20
\class QBluetoothServiceDiscoveryAgent
21
\inmodule QtBluetooth
22
\brief The QBluetoothServiceDiscoveryAgent class enables you to query for
23
Bluetooth services.
24
25
\since 5.2
26
27
The discovery process relies on the Bluetooth Service Discovery Process (SDP).
28
The following steps are required to query the services provided by all contactable
29
Bluetooth devices:
30
31
\list
32
\li create an instance of QBluetoothServiceDiscoveryAgent,
33
\li connect to either the serviceDiscovered() or finished() signals,
34
\li and call start().
35
\endlist
36
37
\snippet doc_src_qtbluetooth.cpp service_discovery
38
39
By default a minimal service discovery is performed. In this mode, the returned \l QBluetoothServiceInfo
40
objects are guaranteed to contain only device and service UUID information. Depending
41
on platform and device capabilities, other service information may also be available.
42
The minimal service discovery mode relies on cached SDP data of the platform. Therefore it
43
is possible that this discovery does not find a device although it is physically available.
44
In such cases a full discovery must be performed to force an update of the platform cache.
45
However for most use cases a minimal discovery is adequate as it is much quicker and other
46
classes which require up-to-date information such as QBluetoothSocket::connectToService()
47
will perform additional discovery if required. If the full service information is required,
48
pass \l FullDiscovery as the discoveryMode parameter to start().
49
50
This class may internally utilize \l QBluetoothDeviceDiscoveryAgent to find unknown devices.
51
52
The service discovery may find Bluetooth Low Energy services too if the target device
53
is a combination of a classic and Low Energy device. Those devices are required to advertise
54
their Low Energy services via SDP. If the target device only supports Bluetooth Low
55
Energy services, it is likely to not advertise them via SDP. The \l QLowEnergyController class
56
should be utilized to perform the service discovery on Low Energy devices.
57
58
On iOS, this class cannot be used because the platform does not expose
59
an API which may permit access to QBluetoothServiceDiscoveryAgent related features.
60
61
\sa QBluetoothDeviceDiscoveryAgent, QLowEnergyController
62
*/
63
64
/*!
65
\enum QBluetoothServiceDiscoveryAgent::Error
66
67
This enum describes errors that can occur during service discovery.
68
69
\value NoError No error has occurred.
70
\value PoweredOffError The Bluetooth adaptor is powered off, power it on before doing discovery.
71
\value InputOutputError Writing or reading from the device resulted in an error.
72
\value [since 5.3] InvalidBluetoothAdapterError The passed local adapter address does not
73
match the physical adapter address of any
74
local Bluetooth device.
75
\value [since 6.4] MissingPermissionsError The operating system requests
76
permissions which were not
77
granted by the user.
78
\value UnknownError An unknown error has occurred.
79
*/
80
81
/*!
82
\enum QBluetoothServiceDiscoveryAgent::DiscoveryMode
83
84
This enum describes the service discovery mode.
85
86
\value MinimalDiscovery Performs a minimal service discovery. The QBluetoothServiceInfo
87
objects returned may be incomplete and are only guaranteed to contain device and service UUID information.
88
Since a minimal discovery relies on cached SDP data it may not find a physically existing
89
device until a \c FullDiscovery is performed.
90
\value FullDiscovery Performs a full service discovery.
91
*/
92
93
/*!
94
\fn QBluetoothServiceDiscoveryAgent::serviceDiscovered(const QBluetoothServiceInfo &info)
95
96
This signal is emitted when the Bluetooth service described by \a info is discovered.
97
98
\note The passed \l QBluetoothServiceInfo parameter may contain a Bluetooth Low Energy
99
service if the target device advertises the service via SDP. This is required from device
100
which support both, classic Bluetooth (BaseRate) and Low Energy services.
101
102
\note On HarmonyOS the system confirms only the audio and input profiles
103
it tracks itself and never an RFCOMM service, so no serial port service
104
is discoverable by an inquiry, whichever UUID it carries. When
105
\l setRemoteAddress() restricts the discovery to one device, the UUIDs
106
\l setUuidFilter() names are reported for that device without the
107
platform having confirmed them, and the application has to establish
108
whether such a service answers by connecting. That is the only way to
109
obtain an RFCOMM service record here; a discovery which is not restricted
110
to one device reports the tracked profiles only.
111
112
\sa QBluetoothDeviceInfo::coreConfigurations()
113
*/
114
115
/*!
116
\fn QBluetoothServiceDiscoveryAgent::finished()
117
118
This signal is emitted when the Bluetooth service discovery completes.
119
120
Unlike the \l QBluetoothDeviceDiscoveryAgent::finished() signal this
121
signal will even be emitted when an error occurred during the service discovery. Therefore
122
it is recommended to check the \l errorOccurred() signal to evaluate the success of the
123
service discovery discovery.
124
*/
125
126
/*!
127
\fn void QBluetoothServiceDiscoveryAgent::errorOccurred(QBluetoothServiceDiscoveryAgent::Error
128
error)
129
130
This signal is emitted when an \a error occurs. The \a error parameter describes the error that
131
occurred.
132
133
\since 6.2
134
*/
135
136
/*!
137
Constructs a new QBluetoothServiceDiscoveryAgent with \a parent. The search is performed via the
138
local default Bluetooth adapter.
139
*/
140
QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(QObject *parent)
141
: QObject(parent),
142
d_ptr(
new
QBluetoothServiceDiscoveryAgentPrivate(
this
, QBluetoothAddress()))
143
{
144
}
145
146
/*!
147
Constructs a new QBluetoothServiceDiscoveryAgent for \a deviceAdapter and with \a parent.
148
149
It uses \a deviceAdapter for the service search. If \a deviceAdapter is default constructed
150
the resulting QBluetoothServiceDiscoveryAgent object will use the local default Bluetooth adapter.
151
152
If a \a deviceAdapter is specified that is not a local adapter \l error() will be set to
153
\l InvalidBluetoothAdapterError. Therefore it is recommended to test the error flag immediately after
154
using this constructor.
155
156
\note On WinRT the passed adapter address will be ignored.
157
158
\note On Android passing any \a deviceAdapter address is meaningless as Android 6.0 or later does not publish
159
the local Bluetooth address anymore. Subsequently, the passed adapter address can never be matched
160
against the local adapter address. Therefore the subsequent call to \l start() will always trigger
161
\l InvalidBluetoothAdapterError.
162
163
\sa error()
164
*/
165
QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(
const
QBluetoothAddress &deviceAdapter, QObject *parent)
166
: QObject(parent),
167
d_ptr(
new
QBluetoothServiceDiscoveryAgentPrivate(
this
, deviceAdapter))
168
{
169
if
(!deviceAdapter.isNull()) {
170
const
QList<QBluetoothHostInfo> localDevices = QBluetoothLocalDevice::allDevices();
171
for
(
const
QBluetoothHostInfo &hostInfo : localDevices) {
172
if
(hostInfo.address() == deviceAdapter)
173
return
;
174
}
175
d_ptr->error = InvalidBluetoothAdapterError;
176
d_ptr->errorString = tr(
"Invalid Bluetooth adapter address"
);
177
}
178
}
179
180
/*!
181
182
Destructor for QBluetoothServiceDiscoveryAgent
183
184
*/
185
186
QBluetoothServiceDiscoveryAgent::~QBluetoothServiceDiscoveryAgent()
187
{
188
if
(isActive()) {
189
disconnect();
//don't emit any signals due to stop()
190
stop();
191
}
192
193
delete
d_ptr;
194
}
195
196
/*!
197
Returns the list of all discovered services.
198
199
This list of services accumulates newly discovered services from multiple calls
200
to \l start(). Unless \l clear() is called the list cannot decrease in size. This implies
201
that if a remote Bluetooth device moves out of range in between two subsequent calls
202
to \l start() the list may contain stale entries.
203
204
\note The list of services should always be cleared before the discovery mode is changed.
205
206
\sa clear()
207
*/
208
QList<QBluetoothServiceInfo> QBluetoothServiceDiscoveryAgent::discoveredServices()
const
209
{
210
Q_D(
const
QBluetoothServiceDiscoveryAgent);
211
212
return
d->discoveredServices;
213
}
214
/*!
215
Sets the UUID filter to \a uuids. Only services matching the UUIDs in \a uuids will be
216
returned. The matching applies to the service's
217
\l {QBluetoothServiceInfo::ServiceId}{ServiceId} and \l {QBluetoothServiceInfo::ServiceClassIds} {ServiceClassIds}
218
attributes.
219
220
An empty UUID list is equivalent to a list containing only QBluetoothUuid::ServiceClassUuid::PublicBrowseGroup.
221
222
\sa uuidFilter()
223
*/
224
void
QBluetoothServiceDiscoveryAgent::setUuidFilter(
const
QList<QBluetoothUuid> &uuids)
225
{
226
Q_D(QBluetoothServiceDiscoveryAgent);
227
228
d->uuidFilter = uuids;
229
}
230
231
/*!
232
This is an overloaded member function, provided for convenience.
233
234
Sets the UUID filter to a list containing the single element \a uuid.
235
The matching applies to the service's \l {QBluetoothServiceInfo::ServiceId}{ServiceId}
236
and \l {QBluetoothServiceInfo::ServiceClassIds} {ServiceClassIds}
237
attributes.
238
239
\sa uuidFilter()
240
*/
241
void
QBluetoothServiceDiscoveryAgent::setUuidFilter(
const
QBluetoothUuid &uuid)
242
{
243
Q_D(QBluetoothServiceDiscoveryAgent);
244
245
d->uuidFilter.clear();
246
d->uuidFilter.append(uuid);
247
}
248
249
/*!
250
Returns the UUID filter.
251
252
\sa setUuidFilter()
253
*/
254
QList<QBluetoothUuid> QBluetoothServiceDiscoveryAgent::uuidFilter()
const
255
{
256
Q_D(
const
QBluetoothServiceDiscoveryAgent);
257
258
return
d->uuidFilter;
259
}
260
261
/*!
262
Sets the remote device address to \a address. If \a address is default constructed,
263
services will be discovered on all contactable Bluetooth devices. A new remote
264
address can only be set while there is no service discovery in progress; otherwise
265
this function returns false.
266
267
On some platforms the service discovery might lead to pairing requests.
268
Therefore it is not recommended to do service discoveries on all devices.
269
This function can be used to restrict the service discovery to a particular device.
270
271
\sa remoteAddress()
272
*/
273
bool
QBluetoothServiceDiscoveryAgent::setRemoteAddress(
const
QBluetoothAddress &address)
274
{
275
if
(isActive())
276
return
false
;
277
if
(!address.isNull())
278
d_ptr->singleDevice =
true
;
279
d_ptr->deviceAddress = address;
280
return
true
;
281
}
282
283
/*!
284
Returns the remote device address. If \l setRemoteAddress() is not called, the function
285
will return a default constructed \l QBluetoothAddress.
286
287
\sa setRemoteAddress()
288
*/
289
QBluetoothAddress QBluetoothServiceDiscoveryAgent::remoteAddress()
const
290
{
291
if
(d_ptr->singleDevice ==
true
)
292
return
d_ptr->deviceAddress;
293
else
294
return
QBluetoothAddress();
295
}
296
297
namespace
DarwinBluetooth
{
298
299
void
qt_test_iobluetooth_runloop
();
300
301
}
302
303
304
/*!
305
Starts service discovery. \a mode specifies the type of service discovery to perform.
306
307
On some platforms, device discovery may lead to pairing requests.
308
309
\sa DiscoveryMode
310
*/
311
void
QBluetoothServiceDiscoveryAgent::start(DiscoveryMode mode)
312
{
313
Q_D(QBluetoothServiceDiscoveryAgent);
314
#
ifdef
QT_OSX_BLUETOOTH
315
// Make sure we are on the right thread/have a run loop:
316
DarwinBluetooth::qt_test_iobluetooth_runloop();
317
#
endif
318
319
if
(d->discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::Inactive
320
&& d->error != InvalidBluetoothAdapterError) {
321
#
if
QT_CONFIG
(
bluez
)
322
// done to avoid repeated parsing for adapter address
323
// on Bluez5
324
d->foundHostAdapterPath.clear();
325
#
endif
326
d->setDiscoveryMode(mode);
327
// Clear any possible previous errors
328
d->error = QBluetoothServiceDiscoveryAgent::NoError;
329
d->errorString.clear();
330
if
(d->deviceAddress.isNull()) {
331
d->startDeviceDiscovery();
332
}
else
{
333
d->discoveredDevices << QBluetoothDeviceInfo(d->deviceAddress, QString(), 0);
334
d->startServiceDiscovery();
335
}
336
}
337
}
338
339
/*!
340
Stops the service discovery process. The \l canceled() signal will be emitted once
341
the search has stopped.
342
*/
343
void
QBluetoothServiceDiscoveryAgent::stop()
344
{
345
Q_D(QBluetoothServiceDiscoveryAgent);
346
347
if
(d->error == InvalidBluetoothAdapterError || !isActive())
348
return
;
349
350
switch
(d->discoveryState()) {
351
case
QBluetoothServiceDiscoveryAgentPrivate::DeviceDiscovery:
352
d->stopDeviceDiscovery();
353
break
;
354
case
QBluetoothServiceDiscoveryAgentPrivate::ServiceDiscovery:
355
d->stopServiceDiscovery();
356
break
;
357
default
:
358
break
;
359
}
360
361
d->discoveredDevices.clear();
362
}
363
364
/*!
365
Clears the results of previous service discoveries and resets \l uuidFilter().
366
This function does nothing during an ongoing service discovery (see \l isActive()).
367
368
\sa discoveredServices()
369
*/
370
void
QBluetoothServiceDiscoveryAgent::clear()
371
{
372
Q_D(QBluetoothServiceDiscoveryAgent);
373
374
//don't clear the list while the search is ongoing
375
if
(isActive())
376
return
;
377
378
d->discoveredDevices.clear();
379
d->discoveredServices.clear();
380
d->uuidFilter.clear();
381
}
382
383
/*!
384
Returns \c true if the service discovery is currently active; otherwise returns \c false.
385
An active discovery can be stopped by calling \l stop().
386
*/
387
bool
QBluetoothServiceDiscoveryAgent::isActive()
const
388
{
389
Q_D(
const
QBluetoothServiceDiscoveryAgent);
390
391
return
d->state != QBluetoothServiceDiscoveryAgentPrivate::Inactive;
392
}
393
394
/*!
395
Returns the type of error that last occurred. If the service discovery is done
396
for a single \l remoteAddress() it will return errors that occurred while trying to discover
397
services on that device. If the \l remoteAddress() is not set and devices are
398
discovered by a scan, errors during service discovery on individual
399
devices are not saved and no signals are emitted. In this case, errors are
400
fairly normal as some devices may not respond to discovery or
401
may no longer be in range. Such errors are suppressed. If no services
402
are returned, it can be assumed no services could be discovered.
403
404
Any possible previous errors are cleared upon restarting the discovery.
405
*/
406
QBluetoothServiceDiscoveryAgent::Error QBluetoothServiceDiscoveryAgent::error()
const
407
{
408
Q_D(
const
QBluetoothServiceDiscoveryAgent);
409
410
return
d->error;
411
}
412
413
/*!
414
Returns a human-readable description of the last error that occurred during the
415
service discovery.
416
417
\sa error(), errorOccurred()
418
*/
419
QString QBluetoothServiceDiscoveryAgent::errorString()
const
420
{
421
Q_D(
const
QBluetoothServiceDiscoveryAgent);
422
return
d->errorString;
423
}
424
425
426
/*!
427
\fn QBluetoothServiceDiscoveryAgent::canceled()
428
429
This signal is triggered when the service discovery was canceled via a call to \l stop().
430
*/
431
432
433
/*!
434
Starts device discovery.
435
*/
436
void
QBluetoothServiceDiscoveryAgentPrivate::startDeviceDiscovery()
437
{
438
Q_Q(QBluetoothServiceDiscoveryAgent);
439
440
if
(!deviceDiscoveryAgent) {
441
#
if
QT_CONFIG
(
bluez
)
442
deviceDiscoveryAgent =
new
QBluetoothDeviceDiscoveryAgent(m_deviceAdapterAddress, q);
443
#
else
444
deviceDiscoveryAgent =
new
QBluetoothDeviceDiscoveryAgent(q);
445
#
endif
446
QObject::connect(deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished,
447
q, [
this
](){
448
this
->_q_deviceDiscoveryFinished();
449
});
450
QObject::connect(deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,
451
q, [
this
](
const
QBluetoothDeviceInfo &info){
452
this
->_q_deviceDiscovered(info);
453
});
454
QObject::connect(deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::errorOccurred, q,
455
[
this
](QBluetoothDeviceDiscoveryAgent::Error newError) {
456
this
->_q_deviceDiscoveryError(newError);
457
});
458
}
459
460
setDiscoveryState(DeviceDiscovery);
461
462
deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::ClassicMethod);
463
}
464
465
/*!
466
Stops device discovery.
467
*/
468
void
QBluetoothServiceDiscoveryAgentPrivate::stopDeviceDiscovery()
469
{
470
// disconnect to avoid recursion during stop() - QTBUG-60131
471
// we don't care about a potential signals from device discovery agent anymore
472
deviceDiscoveryAgent->disconnect();
473
474
deviceDiscoveryAgent->stop();
475
delete
deviceDiscoveryAgent;
476
deviceDiscoveryAgent =
nullptr
;
477
478
setDiscoveryState(Inactive);
479
480
Q_Q(QBluetoothServiceDiscoveryAgent);
481
emit q->canceled();
482
}
483
484
/*!
485
Called when device discovery finishes.
486
*/
487
void
QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryFinished()
488
{
489
if
(deviceDiscoveryAgent->error() != QBluetoothDeviceDiscoveryAgent::NoError) {
490
//Forward the device discovery error
491
error =
static_cast
<QBluetoothServiceDiscoveryAgent::Error>(deviceDiscoveryAgent->error());
492
errorString = deviceDiscoveryAgent->errorString();
493
setDiscoveryState(Inactive);
494
Q_Q(QBluetoothServiceDiscoveryAgent);
495
emit q->errorOccurred(error);
496
emit q->finished();
497
return
;
498
}
499
500
delete
deviceDiscoveryAgent;
501
deviceDiscoveryAgent =
nullptr
;
502
503
startServiceDiscovery();
504
}
505
506
void
QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscovered(
const
QBluetoothDeviceInfo &info)
507
{
508
// look for duplicates, and cached entries
509
const
auto
addressEquals = [](
const
auto
&a) {
510
return
[a](
const
auto
&info) {
return
info.address() == a; };
511
};
512
erase_if(discoveredDevices, addressEquals(info.address()));
513
discoveredDevices.prepend(info);
514
}
515
516
void
QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error newError)
517
{
518
error =
static_cast
<QBluetoothServiceDiscoveryAgent::Error>(newError);
519
errorString = deviceDiscoveryAgent->errorString();
520
521
// disconnect to avoid recursion during stop() - QTBUG-60131
522
// we don't care about a potential signals from device discovery agent anymore
523
deviceDiscoveryAgent->disconnect();
524
525
deviceDiscoveryAgent->stop();
526
delete
deviceDiscoveryAgent;
527
deviceDiscoveryAgent =
nullptr
;
528
529
setDiscoveryState(Inactive);
530
Q_Q(QBluetoothServiceDiscoveryAgent);
531
emit q->errorOccurred(error);
532
emit q->finished();
533
}
534
535
/*!
536
Starts service discovery for the next device.
537
*/
538
void
QBluetoothServiceDiscoveryAgentPrivate::startServiceDiscovery()
539
{
540
Q_Q(QBluetoothServiceDiscoveryAgent);
541
542
if
(discoveredDevices.isEmpty()) {
543
setDiscoveryState(Inactive);
544
emit q->finished();
545
return
;
546
}
547
548
setDiscoveryState(ServiceDiscovery);
549
start(discoveredDevices.at(0).address());
550
}
551
552
/*!
553
Stops service discovery.
554
*/
555
void
QBluetoothServiceDiscoveryAgentPrivate::stopServiceDiscovery()
556
{
557
stop();
558
559
setDiscoveryState(Inactive);
560
}
561
562
void
QBluetoothServiceDiscoveryAgentPrivate::_q_serviceDiscoveryFinished()
563
{
564
if
(!discoveredDevices.isEmpty()) {
565
discoveredDevices.removeFirst();
566
}
567
568
startServiceDiscovery();
569
}
570
571
bool
QBluetoothServiceDiscoveryAgentPrivate::isDuplicatedService(
572
const
QBluetoothServiceInfo &serviceInfo)
const
573
{
574
//check the service is not already part of our known list
575
for
(
const
QBluetoothServiceInfo &info : discoveredServices) {
576
if
(info.device() == serviceInfo.device()
577
&& info.serviceClassUuids() == serviceInfo.serviceClassUuids()
578
&& info.serviceUuid() == serviceInfo.serviceUuid()
579
&& info.serverChannel() == serviceInfo.serverChannel()) {
580
return
true
;
581
}
582
}
583
return
false
;
584
}
585
586
QT_END_NAMESPACE
587
588
#
include
"moc_qbluetoothservicediscoveryagent.cpp"
DarwinBluetooth
Definition
btcentralmanager.mm:22
DarwinBluetooth::qt_test_iobluetooth_runloop
void qt_test_iobluetooth_runloop()
Definition
btutility.mm:125
qtconnectivity
src
bluetooth
qbluetoothservicediscoveryagent.cpp
Generated on
for Qt by
1.18.0