1// Copyright (C) 2017 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 "qlowenergycontroller.h"
5
6#include "qlowenergycharacteristicdata.h"
7#include "qlowenergyconnectionparameters.h"
8#include "qlowenergydescriptordata.h"
9#include "qlowenergyservicedata.h"
10
11#include <QtBluetooth/QBluetoothLocalDevice>
12#include <QtCore/QLoggingCategory>
13
14
15#if QT_CONFIG(bluez) && !defined(QT_BLUEZ_NO_BTLE)
16#include "bluez/bluez5_helper_p.h"
17#include "qlowenergycontroller_bluezdbus_p.h"
18#include "qlowenergycontroller_bluez_p.h"
19#elif defined(QT_ANDROID_BLUETOOTH)
20#include "qlowenergycontroller_android_p.h"
21#include "android/androidutils_p.h"
22#elif defined(QT_WINRT_BLUETOOTH)
23#include "qtbluetoothglobal_p.h"
24#include "qlowenergycontroller_winrt_p.h"
25#elif defined(Q_OS_DARWIN)
26#include "qlowenergycontroller_darwin_p.h"
27#else
28#include "qlowenergycontroller_dummy_p.h"
29#endif
30
31#include <algorithm>
32
33QT_BEGIN_NAMESPACE
34
35QT_IMPL_METATYPE_EXTERN_TAGGED(QLowEnergyController::Error, QLowEnergyController__Error)
36QT_IMPL_METATYPE_EXTERN_TAGGED(QLowEnergyController::ControllerState,
37 QLowEnergyController__ControllerState)
38QT_IMPL_METATYPE_EXTERN_TAGGED(QLowEnergyController::RemoteAddressType,
39 QLowEnergyController__RemoteAddressType)
40QT_IMPL_METATYPE_EXTERN_TAGGED(QLowEnergyController::Role, QLowEnergyController__Role)
41
42Q_DECLARE_LOGGING_CATEGORY(QT_BT)
43#if defined(QT_ANDROID_BLUETOOTH)
44Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
45#endif
46
47/*!
48 \class QLowEnergyController
49 \inmodule QtBluetooth
50 \brief The QLowEnergyController class provides access to Bluetooth
51 Low Energy Devices.
52
53 \since 5.4
54
55 QLowEnergyController acts as the entry point for Bluetooth Low Energy
56 development.
57
58 Bluetooth Low Energy defines two types of devices; the peripheral and
59 the central. Each role performs a different task. The peripheral device
60 provides data which is utilized by central devices. An example might be a
61 humidity sensor which measures the moisture in a winter garden. A device
62 such as a mobile phone might read the sensor's value and display it to the user
63 in the greater context of all sensors in the same environment. In this case
64 the sensor is the peripheral device and the mobile phone acts as the
65 central device.
66
67 A controller in the central role is created via the \l createCentral() factory method.
68 Such an object essentially acts as a placeholder towards a remote Low Energy peripheral
69 device, enabling features such as service discovery and state tracking.
70
71 After having created a controller object in the central role, the first step is to establish
72 a connection via \l connectToDevice().
73 Once the connection has been established, the controller's \l state()
74 changes to \l QLowEnergyController::ConnectedState and the \l connected()
75 signal is emitted. It is important to mention that some platforms such as
76 a BlueZ based Linux cannot maintain two connected instances of
77 \l QLowEnergyController to the same remote device. In such cases the second
78 call to \l connectToDevice() may fail. This limitation may disappear at some
79 stage in the future. The \l disconnectFromDevice() function is used to break
80 the existing connection.
81
82 The second step after establishing the connection is to discover the services
83 offered by the remote peripheral device. This process is started via
84 \l discoverServices() and has finished once the \l discoveryFinished() signal
85 has been emitted. The discovered services can be enumerated via
86 \l services().
87
88 The last step is to create service objects. The \l createServiceObject()
89 function acts as factory for each service object and expects the service
90 UUID as parameter. The calling context should take ownership of the returned
91 \l QLowEnergyService instance.
92
93 Any \l QLowEnergyService, \l QLowEnergyCharacteristic or
94 \l QLowEnergyDescriptor instance which is later created from this controller's
95 connection becomes invalid as soon as the controller disconnects from the
96 remote Bluetooth Low Energy device.
97
98 A controller in the peripheral role is created via the \l createPeripheral() factory method.
99 Such an object acts as a peripheral device itself, enabling features such as advertising
100 services and allowing clients to get notified about changes to characteristic values.
101
102 After having created a controller object in the peripheral role, the first step is to
103 populate the set of GATT services offered to client devices via calls to \l addService().
104 Afterwards, one would call \l startAdvertising() to let the device broadcast some data
105 and, depending on the type of advertising being done, also listen for incoming connections
106 from GATT clients.
107
108 \sa QLowEnergyService, QLowEnergyCharacteristic, QLowEnergyDescriptor
109 \sa QLowEnergyAdvertisingParameters, QLowEnergyAdvertisingData
110*/
111
112/*!
113 \enum QLowEnergyController::Error
114
115 Indicates all possible error conditions found during the controller's
116 existence.
117
118 \value NoError No error has occurred.
119 \value UnknownError An unknown error has occurred.
120 \value UnknownRemoteDeviceError The remote Bluetooth Low Energy device with the address passed to
121 the constructor of this class cannot be found.
122 \value NetworkError The attempt to read from or write to the
123 remote device failed.
124 \value InvalidBluetoothAdapterError The local Bluetooth device with the address passed to
125 the constructor of this class cannot be found or
126 there is no local Bluetooth device.
127 \value [since 5.5] ConnectionError The attempt to connect to the remote device failed.
128 \value [since 5.7] AdvertisingError The attempt to start advertising failed.
129 \value [since 5.10] RemoteHostClosedError The remote device closed the connection.
130 \value [since 5.14] AuthorizationError The local Bluetooth device closed the connection
131 due to insufficient authorization.
132 \value [since 6.4] MissingPermissionsError The operating system requests
133 permissions which were not
134 granted by the user.
135 \value [since 6.5] RssiReadError An attempt to read RSSI (received signal strength indicator)
136 of a remote device finished with error.
137*/
138
139/*!
140 \enum QLowEnergyController::ControllerState
141
142 Indicates the state of the controller object.
143
144 \value UnconnectedState The controller is not connected to a remote device.
145 \value ConnectingState The controller is attempting to connect to a remote device.
146 \value ConnectedState The controller is connected to a remote device.
147 \value DiscoveringState The controller is retrieving the list of services offered
148 by the remote device.
149 \value DiscoveredState The controller has discovered all services offered by the
150 remote device.
151 \value ClosingState The controller is about to be disconnected from the remote device.
152 \value [since 5.7] AdvertisingState The controller is currently advertising data.
153*/
154
155/*!
156 \enum QLowEnergyController::RemoteAddressType
157
158 Indicates what type of Bluetooth address the remote device uses.
159
160 \value PublicAddress The remote device uses a public Bluetooth address.
161 \value RandomAddress A random address is a Bluetooth Low Energy security feature.
162 Peripherals using such addresses may frequently change their
163 Bluetooth address. This information is needed when trying to
164 connect to a peripheral.
165 */
166
167/*!
168 \enum QLowEnergyController::Role
169
170 Indicates the role of the controller object.
171
172 \value CentralRole
173 The controller acts as a client interacting with a remote device which is in the peripheral
174 role. The controller can initiate connections, discover services and
175 read and write characteristics.
176 \value PeripheralRole
177 The controller can be used to advertise services and handle incoming
178 connections and client requests, acting as a GATT server. A remote device connected to
179 the controller is in the central role.
180
181 \sa QLowEnergyController::createCentral()
182 \sa QLowEnergyController::createPeripheral()
183 \since 5.7
184 \note The peripheral role is not supported on Windows. In addition on Linux, handling the
185 "Signed Write" ATT command on the server side requires BlueZ 5 and kernel version 3.7
186 or newer.
187 */
188
189
190/*!
191 \fn void QLowEnergyController::connected()
192
193 This signal is emitted when the controller successfully connects to the remote
194 Low Energy device (if the controller is in the \l CentralRole) or if a remote Low Energy
195 device connected to the controller (if the controller is in the \l PeripheralRole).
196 On iOS, macOS, and Android this signal is not reliable if the controller is in the
197 \l PeripheralRole. On iOS and macOS the controller only guesses that some central
198 connected to our peripheral as soon as this central tries to write/read a
199 characteristic/descriptor. On Android the controller monitors all connected GATT
200 devices. On Linux BlueZ DBus peripheral backend the remote is considered connected
201 when it first reads/writes a characteristic or a descriptor.
202*/
203
204/*!
205 \fn void QLowEnergyController::mtuChanged(int mtu)
206
207 This signal is emitted as a result of a successful MTU change. \a mtu
208 represents the new value.
209
210 \note If the controller is in the \l PeripheralRole, the MTU value is negotiated
211 for each client/central device individually. Therefore this signal can be emitted
212 several times in a row for one or several devices.
213
214 \sa mtu()
215*/
216
217/*!
218 \fn void QLowEnergyController::rssiRead(qint16 rssi)
219
220 This signal is emitted after successful read of RSSI (received
221 signal strength indicator) for a connected remote device.
222 \a rssi parameter represents the new value.
223
224 \sa readRssi()
225 \since 6.5
226*/
227
228/*!
229 \fn void QLowEnergyController::disconnected()
230
231 This signal is emitted when the controller disconnects from the remote
232 Low Energy device or vice versa. On iOS and macOS this signal is unreliable
233 if the controller is in the \l PeripheralRole. On Android the signal is emitted
234 when the last connected device is disconnected. On BlueZ DBus backend the controller
235 is considered disconnected when last client which has accessed the attributes has
236 disconnected.
237*/
238
239/*!
240 \fn void QLowEnergyController::stateChanged(ControllerState state)
241
242 This signal is emitted when the controller's state changes. The new
243 \a state can also be retrieved via \l state().
244
245 \sa state()
246*/
247
248/*!
249 \fn void QLowEnergyController::errorOccurred(QLowEnergyController::Error newError)
250
251 This signal is emitted when an error occurs.
252 The \a newError parameter describes the error that occurred.
253
254 \sa error(), errorString()
255 \since 6.2
256*/
257
258/*!
259 \fn void QLowEnergyController::serviceDiscovered(const QBluetoothUuid &newService)
260
261 This signal is emitted each time a new service is discovered. The
262 \a newService parameter contains the UUID of the found service.
263
264 This signal can only be emitted if the controller is in the \c CentralRole.
265
266 \sa discoverServices(), discoveryFinished()
267*/
268
269/*!
270 \fn void QLowEnergyController::discoveryFinished()
271
272 This signal is emitted when the running service discovery finishes.
273 The signal is not emitted if the discovery process finishes with
274 an error.
275
276 This signal can only be emitted if the controller is in the \l CentralRole.
277
278 \sa discoverServices(), error()
279*/
280
281/*!
282 \fn void QLowEnergyController::connectionUpdated(const QLowEnergyConnectionParameters &newParameters)
283
284 This signal is emitted when the connection parameters change. This can happen as a result
285 of calling \l requestConnectionUpdate() or due to other reasons, for instance because
286 the other side of the connection requested new parameters. The new values can be retrieved
287 from \a newParameters.
288
289 \since 5.7
290 \sa requestConnectionUpdate()
291*/
292
293
294void registerQLowEnergyControllerMetaType()
295{
296 static bool initDone = false;
297 if (!initDone) {
298 qRegisterMetaType<QLowEnergyController::ControllerState>();
299 qRegisterMetaType<QLowEnergyController::Error>();
300 qRegisterMetaType<QLowEnergyConnectionParameters>();
301 qRegisterMetaType<QLowEnergyCharacteristic>();
302 qRegisterMetaType<QLowEnergyDescriptor>();
303 initDone = true;
304 }
305}
306
307static QLowEnergyControllerPrivate *privateController(
308 QLowEnergyController::Role role,
309 const QBluetoothAddress& localDevice = QBluetoothAddress{})
310{
311#if QT_CONFIG(bluez) && !defined(QT_BLUEZ_NO_BTLE)
312 // Central role
313 // The minimum Bluez DBus version for central role is 5.42, otherwise we
314 // fall back to kernel ATT backend. Application can also force the choice
315 // with an environment variable (see bluetoothdVersion())
316 //
317 // Peripheral role
318 // For the dbus peripheral backend we check the presence of the required DBus APIs,
319 // bluez version, and in addition the application needs to opt-in to the DBus peripheral
320 // role by setting the environment variable. Otherwise we fall back to the kernel ATT
321 // backend
322 //
323 // ### Qt 7 consider removing the non-dbus bluez (kernel ATT) support
324
325 QString adapterPathWithPeripheralSupport;
326 if (role == QLowEnergyController::PeripheralRole
327 && bluetoothdVersion() >= QVersionNumber(5, 56)
328 && qEnvironmentVariableIsSet(varName: "QT_BLUETOOTH_USE_DBUS_PERIPHERAL")) {
329 adapterPathWithPeripheralSupport = adapterWithDBusPeripheralInterface(localAddress: localDevice);
330 }
331
332 if (role == QLowEnergyController::CentralRole
333 && bluetoothdVersion() >= QVersionNumber(5, 42)) {
334 qCDebug(QT_BT) << "Using BlueZ LE DBus API for central";
335 return new QLowEnergyControllerPrivateBluezDBus();
336 } else if (!adapterPathWithPeripheralSupport.isEmpty()) {
337 qCDebug(QT_BT) << "Using BlueZ LE DBus API for peripheral";
338 return new QLowEnergyControllerPrivateBluezDBus(adapterPathWithPeripheralSupport);
339 } else {
340 qCDebug(QT_BT) << "Using BlueZ kernel ATT interface for"
341 << (role == QLowEnergyController::CentralRole ? "central" : "peripheral");
342 return new QLowEnergyControllerPrivateBluez();
343 }
344#elif defined(QT_ANDROID_BLUETOOTH)
345 Q_UNUSED(role);
346 Q_UNUSED(localDevice);
347 return new QLowEnergyControllerPrivateAndroid();
348#elif defined(QT_WINRT_BLUETOOTH)
349 Q_UNUSED(role);
350 Q_UNUSED(localDevice);
351 return new QLowEnergyControllerPrivateWinRT();
352#elif defined(Q_OS_DARWIN)
353 Q_UNUSED(role);
354 Q_UNUSED(localDevice);
355 return new QLowEnergyControllerPrivateDarwin();
356#else
357 Q_UNUSED(role);
358 Q_UNUSED(localDevice);
359 return new QLowEnergyControllerPrivateCommon();
360#endif
361}
362
363/*!
364 Constructs a new instance of this class with \a parent.
365
366 The \a remoteDevice must contain the address of the
367 remote Bluetooth Low Energy device to which this object
368 should attempt to connect later on.
369
370 The connection is established via \a localDevice. If \a localDevice
371 is invalid, the local default device is automatically selected. If
372 \a localDevice specifies a local device that is not a local Bluetooth
373 adapter, \l error() is set to \l InvalidBluetoothAdapterError once
374 \l connectToDevice() is called.
375
376 \note This is only supported on BlueZ
377 */
378QLowEnergyController::QLowEnergyController(
379 const QBluetoothDeviceInfo &remoteDevice,
380 const QBluetoothAddress &localDevice,
381 QObject *parent)
382 : QObject(parent)
383{
384 d_ptr = privateController(role: CentralRole);
385
386 Q_D(QLowEnergyController);
387 d->q_ptr = this;
388 d->role = CentralRole;
389 d->deviceUuid = remoteDevice.deviceUuid();
390 d->remoteDevice = remoteDevice.address();
391
392 if (localDevice.isNull())
393 d->localAdapter = QBluetoothLocalDevice().address();
394 else
395 d->localAdapter = localDevice;
396
397 d->addressType = QLowEnergyController::PublicAddress;
398 d->remoteName = remoteDevice.name();
399 d->init();
400}
401
402/*!
403 Returns a new object of this class that is in the \l CentralRole and has the
404 parent object \a parent.
405 The \a remoteDevice refers to the device that a connection will be established to later.
406
407 The controller uses the local default Bluetooth adapter for the connection management.
408
409 \sa QLowEnergyController::CentralRole
410 \since 5.7
411 */
412QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDeviceInfo &remoteDevice,
413 QObject *parent)
414{
415 return new QLowEnergyController(remoteDevice, QBluetoothAddress(), parent);
416}
417
418/*!
419 Returns a new instance of this class with \a parent.
420
421 The \a remoteDevice must contain the address of the remote Bluetooth Low
422 Energy device to which this object should attempt to connect later on.
423
424 The connection is established via \a localDevice. If \a localDevice is invalid,
425 the local default device is automatically selected. If \a localDevice specifies
426 a local device that is not a local Bluetooth adapter, \l error() is set to
427 \l InvalidBluetoothAdapterError once \l connectToDevice() is called.
428
429 Note that specifying the local device to be used for the connection is only
430 possible when using BlueZ. All other platforms do not support this feature.
431
432 \since 5.14
433 */
434QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDeviceInfo &remoteDevice,
435 const QBluetoothAddress &localDevice,
436 QObject *parent)
437{
438 return new QLowEnergyController(remoteDevice, localDevice, parent);
439}
440
441
442/*!
443 Returns a new object of this class that is in the \l PeripheralRole and has the
444 parent object \a parent.
445 Typically, the next steps are to add some services and finally
446 call \l startAdvertising() on the returned object.
447
448 The controller uses the local default Bluetooth adapter for the connection management.
449
450 \sa QLowEnergyController::PeripheralRole
451 \since 5.7
452 */
453QLowEnergyController *QLowEnergyController::createPeripheral(QObject *parent)
454{
455 return new QLowEnergyController(QBluetoothAddress(), parent);
456}
457
458/*!
459 Returns a new object of this class that is in the \l PeripheralRole and has the
460 parent object \a parent and is using \a localDevice.
461 Typically, the next steps are to add some services and finally
462 call \l startAdvertising() on the returned object.
463
464 The peripheral is created on \a localDevice. If \a localDevice is invalid,
465 the local default device is automatically selected. If \a localDevice specifies
466 a local device that is not a local Bluetooth adapter, \l error() is set to
467 \l InvalidBluetoothAdapterError.
468
469 Selecting \a localDevice is only supported on Linux. On other platform,
470 the parameter is ignored.
471
472 \sa QLowEnergyController::PeripheralRole
473 \since 6.2
474 */
475QLowEnergyController *QLowEnergyController::createPeripheral(const QBluetoothAddress &localDevice,
476 QObject *parent)
477{
478 return new QLowEnergyController(localDevice, parent);
479}
480
481QLowEnergyController::QLowEnergyController(const QBluetoothAddress &localDevice, QObject *parent)
482 : QObject(parent)
483{
484 d_ptr = privateController(role: PeripheralRole, localDevice);
485
486 Q_D(QLowEnergyController);
487 d->q_ptr = this;
488 d->role = PeripheralRole;
489
490 if (localDevice.isNull())
491 d->localAdapter = QBluetoothLocalDevice().address();
492 else
493 d->localAdapter = localDevice;
494
495 d->init();
496}
497
498/*!
499 Destroys the QLowEnergyController instance.
500 */
501QLowEnergyController::~QLowEnergyController()
502{
503 disconnectFromDevice(); //in case we were connected
504 delete d_ptr;
505}
506
507/*!
508 Returns the address of the local Bluetooth adapter being used for the
509 communication.
510
511 If this class instance was requested to use the default adapter
512 but there was no default adapter when creating this
513 class instance, the returned \l QBluetoothAddress will be null.
514
515 \sa QBluetoothAddress::isNull()
516 */
517QBluetoothAddress QLowEnergyController::localAddress() const
518{
519 return d_ptr->localAdapter;
520}
521
522/*!
523 Returns the address of the remote Bluetooth Low Energy device.
524
525 For a controller in the \l CentralRole, this value will always be the one passed in when
526 the controller object was created. For a controller in the \l PeripheralRole, this value
527 is one of the currently connected client device addresses. This address will
528 be invalid if the controller is not currently in the \l ConnectedState.
529 */
530QBluetoothAddress QLowEnergyController::remoteAddress() const
531{
532 return d_ptr->remoteDevice;
533}
534
535/*!
536 Returns the unique identifier of the remote Bluetooth Low Energy device.
537
538 On macOS/iOS/tvOS CoreBluetooth does not expose/accept hardware addresses for
539 LE devices; instead developers are supposed to use unique 128-bit UUIDs, generated
540 by CoreBluetooth. These UUIDS will stay constant for the same central <-> peripheral
541 pair and we use them when connecting to a remote device. For a controller in the
542 \l CentralRole, this value will always be the one passed in when the controller
543 object was created. For a controller in the \l PeripheralRole, this value is invalid.
544
545 \since 5.8
546 */
547QBluetoothUuid QLowEnergyController::remoteDeviceUuid() const
548{
549 return d_ptr->deviceUuid;
550}
551
552/*!
553 Returns the name of the remote Bluetooth Low Energy device, if the controller is in the
554 \l CentralRole. Otherwise the result is unspecified.
555
556 \since 5.5
557 */
558QString QLowEnergyController::remoteName() const
559{
560 return d_ptr->remoteName;
561}
562
563/*!
564 Returns the current state of the controller.
565
566 \sa stateChanged()
567 */
568QLowEnergyController::ControllerState QLowEnergyController::state() const
569{
570 return d_ptr->state;
571}
572
573/*!
574 Returns the type of \l remoteAddress(). By default, this value is initialized
575 to \l PublicAddress.
576
577 \sa setRemoteAddressType()
578 */
579QLowEnergyController::RemoteAddressType QLowEnergyController::remoteAddressType() const
580{
581 return d_ptr->addressType;
582}
583
584/*!
585 Sets the remote address \a type. The type is required to connect
586 to the remote Bluetooth Low Energy device.
587
588 This attribute is only required to be set on Linux/BlueZ systems with older
589 Linux kernels (v3.3 or lower), or if CAP_NET_ADMIN is not set for the executable.
590 The default value of the attribute is \l RandomAddress.
591
592 \note All other platforms handle this flag transparently and therefore applications
593 can ignore it entirely. On Linux, the address type flag is not directly exposed
594 by BlueZ although some use cases do require this information. The only way to detect
595 the flag is via the Linux kernel's Bluetooth Management API (kernel
596 version 3.4+ required). This API requires CAP_NET_ADMIN capabilities though. If the
597 local QtBluetooth process has this capability set QtBluetooth will use the API. This
598 assumes that \l QBluetoothDeviceDiscoveryAgent was used prior to calling
599 \l QLowEnergyController::connectToDevice().
600 */
601void QLowEnergyController::setRemoteAddressType(
602 QLowEnergyController::RemoteAddressType type)
603{
604 d_ptr->addressType = type;
605}
606
607/*!
608 Connects to the remote Bluetooth Low Energy device.
609
610 This function does nothing if the controller's \l state()
611 is not equal to \l UnconnectedState. The \l connected() signal is emitted
612 once the connection is successfully established.
613
614 On Linux/BlueZ systems, it is not possible to connect to the same
615 remote device using two instances of this class. The second call
616 to this function may fail with an error. This limitation may
617 be removed in future releases.
618
619 \sa disconnectFromDevice()
620 */
621void QLowEnergyController::connectToDevice()
622{
623 Q_D(QLowEnergyController);
624
625 if (role() != CentralRole) {
626 qCWarning(QT_BT) << "Connection can only be established while in central role";
627 return;
628 }
629
630 if (!d->isValidLocalAdapter()) {
631 qCWarning(QT_BT) << "connectToDevice() LE controller has invalid adapter";
632 d->setError(QLowEnergyController::InvalidBluetoothAdapterError);
633 return;
634 }
635
636 if (state() != QLowEnergyController::UnconnectedState)
637 return;
638
639 d->connectToDevice();
640}
641
642/*!
643 Disconnects from the remote device.
644
645 Any \l QLowEnergyService, \l QLowEnergyCharacteristic or \l QLowEnergyDescriptor
646 instance that resulted from the current connection is automatically invalidated.
647 Once any of those objects become invalid they remain invalid even if this
648 controller object reconnects.
649
650 This function does nothing if the controller is in the \l UnconnectedState.
651
652 If the controller is in the peripheral role, it stops advertising and removes
653 all services which have previously been added via \l addService().
654 To reuse the QLowEnergyController instance the application must re-add services
655 and restart the advertising mode by calling \l startAdvertising().
656
657 \sa connectToDevice()
658 */
659void QLowEnergyController::disconnectFromDevice()
660{
661 Q_D(QLowEnergyController);
662
663 if (state() == QLowEnergyController::UnconnectedState)
664 return;
665
666 d->invalidateServices();
667 d->disconnectFromDevice();
668}
669
670/*!
671 Initiates the service discovery process.
672
673 The discovery progress is indicated via the \l serviceDiscovered() signal.
674 The \l discoveryFinished() signal is emitted when the process has finished.
675
676 If the controller instance is not connected or the controller has performed
677 the service discovery already this function will do nothing.
678
679 \note Some platforms internally cache the service list of a device
680 which was discovered in the past. This can be problematic if the remote device
681 changed its list of services or their inclusion tree. If this behavior is a
682 problem, the best workaround is to temporarily turn Bluetooth off. This
683 causes a reset of the cache data. Currently Android exhibits such a
684 cache behavior.
685 */
686void QLowEnergyController::discoverServices()
687{
688 Q_D(QLowEnergyController);
689
690 if (d->role != CentralRole) {
691 qCWarning(QT_BT) << "Cannot discover services in peripheral role";
692 return;
693 }
694 if (d->state != QLowEnergyController::ConnectedState)
695 return;
696
697 d->setState(QLowEnergyController::DiscoveringState);
698 d->discoverServices();
699}
700
701/*!
702 Returns the list of services offered by the remote device, if the controller is in
703 the \l CentralRole. Otherwise, the result is unspecified.
704
705 The list contains all primary and secondary services.
706
707 \sa createServiceObject()
708 */
709QList<QBluetoothUuid> QLowEnergyController::services() const
710{
711 return d_ptr->serviceList.keys();
712}
713
714/*!
715 Creates an instance of the service represented by \a serviceUuid.
716 The \a serviceUuid parameter must have been obtained via
717 \l services().
718
719 The caller takes ownership of the returned pointer and may pass
720 a \a parent parameter as default owner.
721
722 This function returns a null pointer if no service with
723 \a serviceUuid can be found on the remote device or the controller
724 is disconnected.
725
726 This function can return instances for secondary services
727 too. The include relationships between services can be expressed
728 via \l QLowEnergyService::includedServices().
729
730 If this function is called multiple times using the same service UUID,
731 the returned \l QLowEnergyService instances share their internal
732 data. Therefore if one of the instances initiates the discovery
733 of the service details, the other instances automatically
734 transition into the discovery state too.
735
736 \sa services()
737 */
738QLowEnergyService *QLowEnergyController::createServiceObject(
739 const QBluetoothUuid &serviceUuid, QObject *parent)
740{
741 Q_D(QLowEnergyController);
742
743 QLowEnergyService *service = nullptr;
744
745 ServiceDataMap::const_iterator it = d->serviceList.constFind(key: serviceUuid);
746 if (it != d->serviceList.constEnd()) {
747 const QSharedPointer<QLowEnergyServicePrivate> &serviceData = it.value();
748
749 service = new QLowEnergyService(serviceData, parent);
750 }
751
752 return service;
753}
754
755/*!
756 Starts advertising the data given in \a advertisingData and \a scanResponseData, using
757 the parameters set in \a parameters. The controller has to be in the \l PeripheralRole.
758 If \a parameters indicates that the advertisement should be connectable, then this function
759 also starts listening for incoming client connections.
760
761 Providing \a scanResponseData is not required, as it is not applicable for certain
762 configurations of \c parameters. \a advertisingData and \a scanResponseData are limited
763 to 31 byte user data. If, for example, several 128bit uuids are added to \a advertisingData,
764 the advertised packets may not contain all uuids. The existing limit may have caused the truncation
765 of uuids. In such cases \a scanResponseData may be used for additional information.
766
767 On BlueZ DBus backend BlueZ decides if, and which data, to use in a scan response. Therefore
768 all advertisement data is recommended to set in the main \a advertisingData parameter. If both
769 advertisement and scan response data is set, the scan response data is given precedence.
770
771 If this object is currently not in the \l UnconnectedState, nothing happens.
772
773 \since 5.7
774 \sa stopAdvertising()
775 */
776void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameters &parameters,
777 const QLowEnergyAdvertisingData &advertisingData,
778 const QLowEnergyAdvertisingData &scanResponseData)
779{
780 Q_D(QLowEnergyController);
781 if (role() != PeripheralRole) {
782 qCWarning(QT_BT) << "Cannot start advertising in central role" << state();
783 return;
784 }
785 if (state() != UnconnectedState) {
786 qCWarning(QT_BT) << "Cannot start advertising in state" << state();
787 return;
788 }
789 d->startAdvertising(params: parameters, advertisingData, scanResponseData);
790}
791
792/*!
793 Stops advertising, if this object is currently in the advertising state.
794
795 The controller has to be in the \l PeripheralRole for this function to work.
796 It does not invalidate services which have previously been added via \l addService().
797
798 \since 5.7
799 \sa startAdvertising()
800 */
801void QLowEnergyController::stopAdvertising()
802{
803 Q_D(QLowEnergyController);
804 if (state() != AdvertisingState) {
805 qCDebug(QT_BT) << "stopAdvertising called in state" << state();
806 return;
807 }
808 d->stopAdvertising();
809}
810
811/*!
812 Constructs and returns a \l QLowEnergyService object with \a parent from \a service.
813 The controller must be in the \l PeripheralRole and in the \l UnconnectedState. The \a service
814 object must be valid.
815
816 \note Once the peripheral instance is disconnected from the remote central device or
817 if \l disconnectFromDevice() is manually called, every service definition that was
818 previously added via this function is removed from the peripheral. Therefore this function
819 must be called again before re-advertising this peripheral controller instance. The described
820 behavior is connection specific and therefore not dependent on whether \l stopAdvertising()
821 was called.
822
823 \since 5.7
824 \sa stopAdvertising(), disconnectFromDevice(), QLowEnergyServiceData::addIncludedService
825 */
826QLowEnergyService *QLowEnergyController::addService(const QLowEnergyServiceData &service,
827 QObject *parent)
828{
829 if (role() != PeripheralRole) {
830 qCWarning(QT_BT) << "Services can only be added in the peripheral role";
831 return nullptr;
832 }
833 if (state() != UnconnectedState) {
834 qCWarning(QT_BT) << "Services can only be added in unconnected state";
835 return nullptr;
836 }
837 if (!service.isValid()) {
838 qCWarning(QT_BT) << "Not adding invalid service";
839 return nullptr;
840 }
841
842#if defined(QT_ANDROID_BLUETOOTH)
843 if (!ensureAndroidPermission(QBluetoothPermission::Access)) {
844 qCWarning(QT_BT_ANDROID) << "addService() failed due to missing permissions";
845 return nullptr;
846 }
847#endif
848
849 Q_D(QLowEnergyController);
850 QLowEnergyService *newService = d->addServiceHelper(service);
851 if (newService)
852 newService->setParent(parent);
853
854 return newService;
855}
856
857
858/*!
859 Requests the controller to update the connection according to \a parameters.
860 If the request is successful, the \l connectionUpdated() signal will be emitted
861 with the actual new parameters. See the \l QLowEnergyConnectionParameters class for more
862 information on connection parameters.
863
864 Android only indirectly permits the adjustment of this parameter set.
865 The connection parameters are separated into three categories (high, low & balanced priority).
866 Each category implies a pre-configured set of values for
867 \l QLowEnergyConnectionParameters::minimumInterval(),
868 \l QLowEnergyConnectionParameters::maximumInterval() and
869 \l QLowEnergyConnectionParameters::latency(). Although the connection request is an asynchronous
870 operation, Android does not provide a callback stating the result of the request. This is
871 an acknowledged Android bug. Due to this bug Android does not emit the \l connectionUpdated()
872 signal.
873
874 \note Currently, this functionality is only implemented on Linux kernel backend and Android.
875
876 \sa connectionUpdated()
877 \since 5.7
878 */
879void QLowEnergyController::requestConnectionUpdate(const QLowEnergyConnectionParameters &parameters)
880{
881 switch (state()) {
882 case ConnectedState:
883 case DiscoveredState:
884 case DiscoveringState:
885 d_ptr->requestConnectionUpdate(params: parameters);
886 break;
887 default:
888 qCWarning(QT_BT) << "Connection update request only possible in connected state";
889 }
890}
891
892/*!
893 Returns the last occurred error or \l NoError.
894*/
895QLowEnergyController::Error QLowEnergyController::error() const
896{
897 return d_ptr->error;
898}
899
900/*!
901 Returns a textual representation of the last occurred error.
902 The string is translated.
903*/
904QString QLowEnergyController::errorString() const
905{
906 return d_ptr->errorString;
907}
908
909/*!
910 Returns the role that this controller object is in.
911
912 The role is determined when constructing a QLowEnergyController instance
913 using \l createCentral() or \l createPeripheral().
914
915 \since 5.7
916 */
917QLowEnergyController::Role QLowEnergyController::role() const
918{
919 return d_ptr->role;
920}
921
922/*!
923 Returns the MTU size.
924
925 During connection setup, the ATT MTU size is negotiated.
926 This method provides the result of this negotiation.
927 It can be used to optimize packet sizes in some situations.
928 The maximum amount of data which can be transferred in a single
929 packet is \b {mtu-3} bytes. 3 bytes are required for the ATT
930 protocol header.
931
932 Before the connection setup and MTU negotiation, the
933 default value of \c 23 will be returned.
934
935 Not every platform exposes the MTU value. On those platforms (e.g. Linux)
936 this function always returns \c -1.
937
938 If the controller is in the \l PeripheralRole, there might be several
939 central devices connected to it. In those cases this function returns
940 the MTU of the last connection that was negotiated.
941
942 \since 6.2
943 */
944int QLowEnergyController::mtu() const
945{
946 return d_ptr->mtu();
947}
948
949/*!
950 readRssi() reads RSSI (received signal strength indicator) for a connected remote device.
951 If the read was successful, the RSSI is then reported by rssiRead() signal.
952
953 \note Prior to calling readRssi(), this controller must be connected to a peripheral.
954 This controller must be created using createCentral().
955
956 \note In case Bluetooth backend you are using does not support reading RSSI,
957 the errorOccurred() signal is emitted with an error code QLowEnergyController::RssiReadError.
958 At the moment platforms supporting reading RSSI include Android, iOS and macOS.
959
960 \sa rssiRead(), connectToDevice(), state(), createCentral(), errorOccurred()
961 \since 6.5
962*/
963void QLowEnergyController::readRssi()
964{
965 if (role() != CentralRole) {
966 qCWarning(QT_BT, "Invalid role (peripheral), cannot read RSSI");
967 return d_ptr->setError(RssiReadError); // This also emits.
968 }
969
970 switch (state()) {
971 case UnconnectedState:
972 case ConnectingState:
973 case ClosingState:
974 qCWarning(QT_BT, "Cannot read RSSI while not in 'Connected' state, connect first");
975 return d_ptr->setError(RssiReadError); // Will emit.
976 default:
977 d_ptr->readRssi();
978 }
979}
980
981QT_END_NAMESPACE
982
983#include "moc_qlowenergycontroller.cpp"
984

source code of qtconnectivity/src/bluetooth/qlowenergycontroller.cpp