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