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 | |
33 | QT_BEGIN_NAMESPACE |
34 | |
35 | QT_IMPL_METATYPE_EXTERN_TAGGED(QLowEnergyController::Error, QLowEnergyController__Error) |
36 | QT_IMPL_METATYPE_EXTERN_TAGGED(QLowEnergyController::ControllerState, |
37 | QLowEnergyController__ControllerState) |
38 | QT_IMPL_METATYPE_EXTERN_TAGGED(QLowEnergyController::RemoteAddressType, |
39 | QLowEnergyController__RemoteAddressType) |
40 | QT_IMPL_METATYPE_EXTERN_TAGGED(QLowEnergyController::Role, QLowEnergyController__Role) |
41 | |
42 | Q_DECLARE_LOGGING_CATEGORY(QT_BT) |
43 | #if defined(QT_ANDROID_BLUETOOTH) |
44 | Q_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 | |
294 | void 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 | |
307 | static 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 | */ |
378 | QLowEnergyController::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 | */ |
412 | QLowEnergyController *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 | */ |
434 | QLowEnergyController *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 | */ |
453 | QLowEnergyController *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 | */ |
475 | QLowEnergyController *QLowEnergyController::createPeripheral(const QBluetoothAddress &localDevice, |
476 | QObject *parent) |
477 | { |
478 | return new QLowEnergyController(localDevice, parent); |
479 | } |
480 | |
481 | QLowEnergyController::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 | */ |
501 | QLowEnergyController::~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 | */ |
517 | QBluetoothAddress 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 | */ |
530 | QBluetoothAddress 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 | */ |
547 | QBluetoothUuid 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 | */ |
558 | QString 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 | */ |
568 | QLowEnergyController::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 | */ |
579 | QLowEnergyController::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 | */ |
601 | void 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 | */ |
621 | void 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 | */ |
659 | void 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 | */ |
686 | void 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 | */ |
709 | QList<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 | */ |
738 | QLowEnergyService *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 | */ |
776 | void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameters ¶meters, |
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 | */ |
801 | void 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 | */ |
826 | QLowEnergyService *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 | */ |
879 | void QLowEnergyController::requestConnectionUpdate(const QLowEnergyConnectionParameters ¶meters) |
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 | */ |
895 | QLowEnergyController::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 | */ |
904 | QString 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 | */ |
917 | QLowEnergyController::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 | */ |
944 | int 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 | */ |
963 | void QLowEnergyController::() |
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 | |
981 | QT_END_NAMESPACE |
982 | |
983 | #include "moc_qlowenergycontroller.cpp" |
984 | |