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