1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Javier S. Pedro <maemo@javispedro.com>
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtBluetooth module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include <QtCore/QCoreApplication>
42#include <QtCore/QPointer>
43#include <QtBluetooth/QLowEnergyService>
44
45#include <algorithm>
46
47#include "qlowenergycontrollerbase_p.h"
48#include "qlowenergyserviceprivate_p.h"
49
50#ifdef Q_OS_DARWIN
51#include "qlowenergycontroller_darwin_p.h"
52#endif // Q_OS_DARWIN
53
54QT_BEGIN_NAMESPACE
55
56/*!
57 \class QLowEnergyService
58 \inmodule QtBluetooth
59 \brief The QLowEnergyService class represents an individual service
60 on a Bluetooth Low Energy Device.
61
62 \since 5.4
63
64 QLowEnergyService provides access to the details of Bluetooth Low Energy
65 services. The class facilitates the discovery and publification of
66 service details, permits reading and writing of the contained data
67 and notifies about data changes.
68
69 \section1 Service Structure
70
71 A Bluetooth Low Energy peripheral device can contain multiple services.
72 In turn each service may include further services. This class represents a
73 single service of the peripheral device and is created via
74 \l QLowEnergyController::createServiceObject(). The \l type() indicates
75 whether this service is a primary (top-level) service or whether the
76 service is part of another service. Each service may contain one or more
77 characteristics and each characteristic may contain descriptors. The
78 resulting structure may look like the following diagram:
79
80 \image peripheral-structure.png Structure of a generic peripheral
81
82 A characteristic is the principal information carrier. It has a
83 \l {QLowEnergyCharacteristic::value()}{value()} and
84 \l {QLowEnergyCharacteristic::value()}{properties()}
85 describing the access permissions for the value. The general purpose
86 of the contained descriptor is to further define the nature of the
87 characteristic. For example, it might specify how the value is meant to be
88 interpreted or whether it can notify the value consumer about value
89 changes.
90
91 \section1 Service Interaction
92
93 Once a service object was created for the first time, its details are yet to
94 be discovered. This is indicated by its current \l state() being \l DiscoveryRequired.
95 It is only possible to retrieve the \l serviceUuid() and \l serviceName().
96
97 The discovery of its included services, characteristics and descriptors
98 is triggered when calling \l discoverDetails(). During the discovery the
99 \l state() transitions from \l DiscoveryRequired via \l DiscoveringServices
100 to its final \l ServiceDiscovered state. This transition is advertised via
101 the \l stateChanged() signal. Once the details are known, all of the contained
102 characteristics, descriptors and included services are known and can be read
103 or written.
104
105 The values of characteristics and descriptors can be retrieved via
106 \l QLowEnergyCharacteristic and \l QLowEnergyDescriptor, respectively.
107 However, direct reading or writing of these attributes requires the service object.
108 The \l readCharacteristic() function attempts to re-read the value of a characteristic.
109 Although the initial service discovery may have obtained a value already this call may
110 be required in cases where the characteristic value constantly changes without
111 any notifications being provided. An example might be a time characteristic
112 that provides a continuous value. If the read attempt is successful, the
113 \l characteristicRead() signal is emitted. A failure to read the value triggers
114 the \l CharacteristicReadError.
115 The \l writeCharacteristic() function attempts to write a new value to the given
116 characteristic. If the write attempt is successful, the \l characteristicWritten()
117 signal is emitted. A failure to write triggers the \l CharacteristicWriteError.
118 Reading and writing of descriptors follows the same pattern.
119
120 Every attempt is made to read or write the value of a descriptor
121 or characteristic on the hardware. This means that meta information such as
122 \l QLowEnergyCharacteristic::properties() is generally ignored when reading and writing.
123 As an example, it is possible to call \l writeCharacteristic() despite the characteristic
124 being read-only based on its meta data description. The resulting write request is
125 forwarded to the connected device and it is up to the device to respond to the
126 potentially invalid request. In this case the result is the emission of the
127 \l CharacteristicWriteError in response to the returned device error. This behavior
128 simplifies interaction with devices which report wrong meta information.
129 If it was not possible to forward the request to the remote device the
130 \l OperationError is set. A potential reason could be that the to-be-written
131 characteristic object does not even belong the current service. In
132 summary, the two types of errors permit a quick distinction of local
133 and remote error cases.
134
135 All requests are serialised based on First-In First-Out principle.
136 For example, issuing a second write request, before the previous
137 write request has finished, is delayed until the first write request has finished.
138
139 \note Currently, it is not possible to send signed write or reliable write requests.
140
141 \target notifications
142
143 In some cases the peripheral generates value updates which
144 the central is interested in receiving. In order for a characteristic to support
145 such notifications it must have the \l QLowEnergyCharacteristic::Notify or
146 \l QLowEnergyCharacteristic::Indicate property and a descriptor of type
147 \l QBluetoothUuid::ClientCharacteristicConfiguration. Provided those conditions
148 are fulfilled notifications can be enabled as shown in the following code segment:
149
150 \snippet doc_src_qtbluetooth.cpp enable_btle_notifications
151
152 The example shows a battery level characteristic which updates the central
153 on every value change. The notifications are provided via
154 the \l characteristicChanged() signal. More details about this mechanism
155 are provided by the
156 \l {https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml}{Bluetooth Specification}.
157
158 \section1 Service Data Sharing
159
160 Each QLowEnergyService instance shares its internal states and information
161 with other QLowEnergyService instance of the same service. If one instance
162 initiates the discovery of the service details, all remaining instances
163 automatically follow. Therefore the following snippet always works:
164
165 \snippet doc_src_qtbluetooth.cpp data_share_qlowenergyservice
166
167 Other operations such as calls to \l readCharacteristic(), \l readDescriptor(), \l writeCharacteristic(),
168 \l writeDescriptor() or the invalidation of the service due to the
169 related \l QLowEnergyController disconnecting from the device are shared
170 the same way.
171
172 \sa QLowEnergyController, QLowEnergyCharacteristic, QLowEnergyDescriptor
173 */
174
175/*!
176 \enum QLowEnergyService::ServiceType
177
178 This enum describes the type of the service.
179
180 \value PrimaryService The service is a top-level/primary service.
181 If this type flag is not set, the service is considered
182 to be a secondary service. Each service may be included
183 by another service which is indicated by IncludedService.
184 \value IncludedService The service is included by another service.
185 On some platforms, this flag cannot be determined until
186 the service that includes the current service was
187 discovered.
188*/
189
190/*!
191 \enum QLowEnergyService::ServiceError
192
193 This enum describes all possible error conditions during the service's
194 existence. The \l error() function returns the last occurred error.
195
196 \value NoError No error has occurred.
197 \value OperationError An operation was attempted while the service was not ready.
198 An example might be the attempt to write to
199 the service while it was not yet in the
200 \l ServiceDiscovered \l state() or the service is invalid
201 due to a loss of connection to the peripheral device.
202 \value CharacteristicReadError An attempt to read a characteristic value failed. For example,
203 it might be triggered in response to a call to
204 \l readCharacteristic(). This value was introduced by Qt 5.5.
205 \value CharacteristicWriteError An attempt to write a new value to a characteristic
206 failed. For example, it might be triggered when attempting
207 to write to a read-only characteristic.
208 \value DescriptorReadError An attempt to read a descriptor value failed. For example,
209 it might be triggered in response to a call to
210 \l readDescriptor(). This value was introduced by Qt 5.5.
211 \value DescriptorWriteError An attempt to write a new value to a descriptor
212 failed. For example, it might be triggered when attempting
213 to write to a read-only descriptor.
214 \value UnknownError An unknown error occurred when interacting with the service.
215 This value was introduced by Qt 5.5.
216 */
217
218/*!
219 \enum QLowEnergyService::ServiceState
220
221 This enum describes the \l state() of the service object.
222
223 \value InvalidService A service can become invalid when it looses
224 the connection to the underlying device. Even though
225 the connection may be lost it retains its last information.
226 An invalid service cannot become valid anymore even if
227 the connection to the device is re-established.
228 \value DiscoveryRequired The service details are yet to be discovered by calling \l discoverDetails().
229 The only reliable pieces of information are its
230 \l serviceUuid() and \l serviceName().
231 \value DiscoveringServices The service details are being discovered.
232 \value ServiceDiscovered The service details have been discovered.
233 \value LocalService The service is associated with a controller object in the
234 \l{QLowEnergyController::PeripheralRole}{peripheral role}. Such
235 service objects do not change their state.
236 This value was introduced by Qt 5.7.
237 */
238
239/*!
240 \enum QLowEnergyService::WriteMode
241
242 This enum describes the mode to be used when writing a characteristic value.
243 The characteristic advertises its supported write modes via its
244 \l {QLowEnergyCharacteristic::properties()}{properties}.
245
246 \value WriteWithResponse If a characteristic is written using this mode, the peripheral
247 shall send a write confirmation. If the operation is
248 successful, the confirmation is emitted via the
249 \l characteristicWritten() signal. Otherwise the
250 \l CharacteristicWriteError is emitted.
251 A characteristic must have set the
252 \l QLowEnergyCharacteristic::Write property to support this
253 write mode.
254
255 \value WriteWithoutResponse If a characteristic is written using this mode, the remote peripheral
256 shall not send a write confirmation. The operation's success
257 cannot be determined and the payload must not be longer than 20 bytes.
258 A characteristic must have set the
259 \l QLowEnergyCharacteristic::WriteNoResponse property to support this
260 write mode. Its adavantage is a quicker
261 write operation as it may happen in between other
262 device interactions.
263
264 \value WriteSigned If a characteristic is written using this mode, the remote peripheral
265 shall not send a write confirmation. The operation's success
266 cannot be determined and the payload must not be longer than 8 bytes.
267 A bond must exist between the two devices and the link must not be
268 encrypted.
269 A characteristic must have set the
270 \l QLowEnergyCharacteristic::WriteSigned property to support this
271 write mode.
272 This value was introduced in Qt 5.7 and is currently only
273 supported on Android and on Linux with BlueZ 5 and a kernel version
274 3.7 or newer.
275 */
276
277/*!
278 \fn void QLowEnergyService::stateChanged(QLowEnergyService::ServiceState newState)
279
280 This signal is emitted when the service's state changes. The \a newState can also be
281 retrieved via \l state().
282
283 \sa state()
284 */
285
286/*!
287 \fn void QLowEnergyService::error(QLowEnergyService::ServiceError newError)
288
289 This signal is emitted when an error occurrs. The \a newError parameter
290 describes the error that occurred.
291 */
292
293/*!
294 \fn void QLowEnergyService::characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
295
296 This signal is emitted when the read request for \a characteristic successfully returned
297 its \a value. The signal might be triggered by calling \l characteristicRead(). If
298 the read operation is not successful, the \l error() signal is emitted using the
299 \l CharacteristicReadError flag.
300
301 \note This signal is only emitted for Central Role related use cases.
302
303 \sa readCharacteristic()
304 \since 5.5
305 */
306
307/*!
308 \fn void QLowEnergyService::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
309
310 This signal is emitted when the value of \a characteristic
311 is successfully changed to \a newValue. The change must have been triggered
312 by calling \l writeCharacteristic(). If the write operation is not successful,
313 the \l error() signal is emitted using the \l CharacteristicWriteError flag.
314
315 The reception of the written signal can be considered as a sign that the target device
316 received the to-be-written value and reports back the status of write request.
317
318 \note If \l writeCharacteristic() is called using the \l WriteWithoutResponse mode,
319 this signal and the \l error() are never emitted.
320
321 \note This signal is only emitted for Central Role related use cases.
322
323 \sa writeCharacteristic()
324 */
325
326/*!
327 \fn void QLowEnergyService::characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
328
329 If the associated controller object is in the \l {QLowEnergyController::CentralRole}{central}
330 role, this signal is emitted when the value of \a characteristic is changed by an event on the
331 peripheral/device side. In that case, the signal emission implies that change notifications must
332 have been activated via the characteristic's
333 \l {QBluetoothUuid::ClientCharacteristicConfiguration}{ClientCharacteristicConfiguration}
334 descriptor prior to the change event on the peripheral. More details on how this might be
335 done can be found further \l{notifications}{above}.
336
337 If the controller is in the \l {QLowEnergyController::PeripheralRole}{peripheral} role, that is,
338 the service object was created via \l QLowEnergyController::addService, the signal is emitted
339 when a GATT client has written the value of the characteristic using a write request or command.
340
341 The \a newValue parameter contains the updated value of the \a characteristic.
342
343 */
344
345/*!
346 \fn void QLowEnergyService::descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value)
347
348 This signal is emitted when the read request for \a descriptor successfully returned
349 its \a value. The signal might be triggered by calling \l descriptorRead(). If
350 the read operation is not successful, the \l error() signal is emitted using the
351 \l DescriptorReadError flag.
352
353 \note This signal is only emitted for Central Role related use cases.
354
355 \sa readDescriptor()
356 \since 5.5
357 */
358
359/*!
360 \fn void QLowEnergyService::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue)
361
362 This signal is emitted when the value of \a descriptor
363 is successfully changed to \a newValue. If the associated controller object is in the
364 \l {QLowEnergyController::CentralRole}{central} role, the change must have been caused
365 by calling \l writeDescriptor(). Otherwise, the signal is the result of a write request or
366 command from a GATT client to the respective descriptor.
367
368 \sa writeDescriptor()
369 */
370
371/*!
372 \internal
373
374 QLowEnergyControllerPrivate creates instances of this class.
375 The user gets access to class instances via
376 \l QLowEnergyController::services().
377 */
378QLowEnergyService::QLowEnergyService(QSharedPointer<QLowEnergyServicePrivate> p,
379 QObject *parent)
380 : QObject(parent),
381 d_ptr(p)
382{
383 qRegisterMetaType<QLowEnergyService::ServiceState>();
384 qRegisterMetaType<QLowEnergyService::ServiceError>();
385 qRegisterMetaType<QLowEnergyService::ServiceType>();
386 qRegisterMetaType<QLowEnergyService::WriteMode>();
387
388 connect(p.data(), &QLowEnergyServicePrivate::error,
389 this, QOverload<QLowEnergyService::ServiceError>::of(&QLowEnergyService::error));
390 connect(p.data(), &QLowEnergyServicePrivate::stateChanged,
391 this, &QLowEnergyService::stateChanged);
392 connect(p.data(), &QLowEnergyServicePrivate::characteristicChanged,
393 this, &QLowEnergyService::characteristicChanged);
394 connect(p.data(), &QLowEnergyServicePrivate::characteristicWritten,
395 this, &QLowEnergyService::characteristicWritten);
396 connect(p.data(), &QLowEnergyServicePrivate::descriptorWritten,
397 this, &QLowEnergyService::descriptorWritten);
398 connect(p.data(), &QLowEnergyServicePrivate::characteristicRead,
399 this, &QLowEnergyService::characteristicRead);
400 connect(p.data(), &QLowEnergyServicePrivate::descriptorRead,
401 this, &QLowEnergyService::descriptorRead);
402}
403
404/*!
405 Destroys the \l QLowEnergyService instance.
406 */
407QLowEnergyService::~QLowEnergyService()
408{
409}
410
411/*!
412 Returns the UUIDs of all services which are included by the
413 current service.
414
415 The returned list is empty if this service instance's \l discoverDetails()
416 was not yet called or there are no known characteristics.
417
418 It is possible that an included service contains yet another service. Such
419 second level includes have to be obtained via their relevant first level
420 QLowEnergyService instance. Technically, this could create
421 a circular dependency.
422
423 \l {QLowEnergyController::createServiceObject()} should be used to obtain
424 service instances for each of the UUIDs.
425
426 \sa {QLowEnergyController::}{createServiceObject()}
427 */
428QList<QBluetoothUuid> QLowEnergyService::includedServices() const
429{
430 return d_ptr->includedServices;
431}
432
433/*!
434 Returns the current state of the service.
435
436 If the device's service was instantiated for the first time, the object's
437 state is \l DiscoveryRequired. The state of all service objects which
438 point to the same service on the peripheral device are always equal.
439 This is caused by the shared nature of the internal object data.
440 Therefore any service object instance created after
441 the first one has a state equal to already existing instances.
442
443 A service becomes invalid if the \l QLowEnergyController disconnects
444 from the remote device. An invalid service retains its internal state
445 at the time of the disconnect event. This implies that once the service
446 details are discovered they can even be retrieved from an invalid
447 service. This permits scenarios where the device connection is established,
448 the service details are retrieved and the device immediately disconnected
449 to permit the next device to connect to the peripheral device.
450
451 However, under normal circumstances the connection should remain to avoid
452 repeated discovery of services and their details. The discovery may take
453 a while and the client can subscribe to ongoing change notifications.
454
455 \sa stateChanged()
456 */
457QLowEnergyService::ServiceState QLowEnergyService::state() const
458{
459 return d_ptr->state;
460}
461
462/*!
463 Returns the type of the service.
464
465 \note The type attribute cannot be relied upon until the service has
466 reached the \l ServiceDiscovered state. This field is initialised
467 with \l PrimaryService.
468
469 \note On Android, it is not possible to determine whether a service
470 is a primary or secondary service. Therefore all services
471 have the \l PrimaryService flag set.
472
473 */
474QLowEnergyService::ServiceTypes QLowEnergyService::type() const
475{
476 return d_ptr->type;
477}
478
479/*!
480 Returns the matching characteristic for \a uuid; otherwise an invalid
481 characteristic.
482
483 The returned characteristic is invalid if this service instance's \l discoverDetails()
484 was not yet called or there are no characteristics with a matching \a uuid.
485
486 \sa characteristics()
487*/
488QLowEnergyCharacteristic QLowEnergyService::characteristic(const QBluetoothUuid &uuid) const
489{
490 CharacteristicDataMap::const_iterator charIt = d_ptr->characteristicList.constBegin();
491 for ( ; charIt != d_ptr->characteristicList.constEnd(); ++charIt) {
492 const QLowEnergyHandle charHandle = charIt.key();
493 const QLowEnergyServicePrivate::CharData &charDetails = charIt.value();
494
495 if (charDetails.uuid == uuid)
496 return QLowEnergyCharacteristic(d_ptr, charHandle);
497 }
498
499 return QLowEnergyCharacteristic();
500}
501
502/*!
503 Returns all characteristics associated with this \c QLowEnergyService instance.
504
505 The returned list is empty if this service instance's \l discoverDetails()
506 was not yet called or there are no known characteristics.
507
508 \sa characteristic(), state(), discoverDetails()
509*/
510
511QList<QLowEnergyCharacteristic> QLowEnergyService::characteristics() const
512{
513 QList<QLowEnergyCharacteristic> results;
514 QList<QLowEnergyHandle> handles = d_ptr->characteristicList.keys();
515 std::sort(handles.begin(), handles.end());
516
517 for (const QLowEnergyHandle &handle : qAsConst(handles)) {
518 QLowEnergyCharacteristic characteristic(d_ptr, handle);
519 results.append(characteristic);
520 }
521 return results;
522}
523
524
525/*!
526 Returns the UUID of the service; otherwise a null UUID.
527 */
528QBluetoothUuid QLowEnergyService::serviceUuid() const
529{
530 return d_ptr->uuid;
531}
532
533
534/*!
535 Returns the name of the service; otherwise an empty string.
536
537 The returned name can only be retrieved if \l serviceUuid()
538 is a \l {https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx}{well-known UUID}.
539*/
540QString QLowEnergyService::serviceName() const
541{
542 bool ok = false;
543 quint16 clsId = d_ptr->uuid.toUInt16(&ok);
544 if (ok) {
545 QBluetoothUuid::ServiceClassUuid id
546 = static_cast<QBluetoothUuid::ServiceClassUuid>(clsId);
547 const QString name = QBluetoothUuid::serviceClassToString(id);
548 if (!name.isEmpty())
549 return name;
550 }
551 return qApp ?
552 qApp->translate("QBluetoothServiceDiscoveryAgent", "Unknown Service") :
553 QStringLiteral("Unknown Service");
554}
555
556
557/*!
558 Initiates the discovery of the services, characteristics
559 and descriptors contained by the service. The discovery process is indicated
560 via the \l stateChanged() signal.
561
562 \sa state()
563 */
564void QLowEnergyService::discoverDetails()
565{
566 Q_D(QLowEnergyService);
567
568 if (!d->controller || d->state == QLowEnergyService::InvalidService) {
569 d->setError(QLowEnergyService::OperationError);
570 return;
571 }
572
573 if (d->state != QLowEnergyService::DiscoveryRequired)
574 return;
575
576 d->setState(QLowEnergyService::DiscoveringServices);
577
578 d->controller->discoverServiceDetails(d->uuid);
579}
580
581/*!
582 Returns the last occurred error or \l NoError.
583 */
584QLowEnergyService::ServiceError QLowEnergyService::error() const
585{
586 return d_ptr->lastError;
587}
588
589/*!
590 Returns \c true if \a characteristic belongs to this service;
591 otherwise \c false.
592
593 A characteristic belongs to a service if \l characteristics()
594 contains the \a characteristic.
595 */
596bool QLowEnergyService::contains(const QLowEnergyCharacteristic &characteristic) const
597{
598 if (characteristic.d_ptr.isNull() || !characteristic.data)
599 return false;
600
601 if (d_ptr == characteristic.d_ptr
602 && d_ptr->characteristicList.contains(characteristic.attributeHandle())) {
603 return true;
604 }
605
606 return false;
607}
608
609
610/*!
611 Reads the value of \a characteristic. If the operation is successful, the
612 \l characteristicRead() signal is emitted; otherwise the \l CharacteristicReadError
613 is set. In general, a \a characteristic is readable, if its
614 \l QLowEnergyCharacteristic::Read property is set.
615
616 All descriptor and characteristic requests towards the same remote device are
617 serialised. A queue is employed when issuing multiple requests at the same time.
618 The queue does not eliminate duplicated read requests for the same characteristic.
619
620 A characteristic can only be read if the service is in the \l ServiceDiscovered state
621 and belongs to the service. If one of these conditions is
622 not true the \l QLowEnergyService::OperationError is set.
623
624 \note Calling this function despite \l QLowEnergyCharacteristic::properties() reporting a non-readable property
625 always attempts to read the characteristic's value on the hardware. If the hardware
626 returns with an error the \l CharacteristicReadError is set.
627
628 \sa characteristicRead(), writeCharacteristic()
629
630 \since 5.5
631 */
632void QLowEnergyService::readCharacteristic(
633 const QLowEnergyCharacteristic &characteristic)
634{
635 Q_D(QLowEnergyService);
636
637 if (d->controller == nullptr || state() != ServiceDiscovered || !contains(characteristic)) {
638 d->setError(QLowEnergyService::OperationError);
639 return;
640 }
641
642 d->controller->readCharacteristic(characteristic.d_ptr,
643 characteristic.attributeHandle());
644}
645
646/*!
647 Writes \a newValue as value for the \a characteristic. The exact semantics depend on
648 the role that the associated controller object is in.
649
650 \b {Central role}
651
652 The call results in a write request or command to a remote peripheral.
653 If the operation is successful,
654 the \l characteristicWritten() signal is emitted; otherwise the \l CharacteristicWriteError
655 is set. Calling this function does not trigger the a \l characteristicChanged()
656 signal unless the peripheral itself changes the value again after the current write request.
657
658 The \a mode parameter determines whether the remote device should send a write
659 confirmation. The to-be-written \a characteristic must support the relevant
660 write mode. The characteristic's supported write modes are indicated by its
661 \l QLowEnergyCharacteristic::Write and \l QLowEnergyCharacteristic::WriteNoResponse
662 properties.
663
664 All descriptor and characteristic write requests towards the same remote device are
665 serialised. A queue is employed when issuing multiple write requests at the same time.
666 The queue does not eliminate duplicated write requests for the same characteristic.
667 For example, if the same descriptor is set to the value A and immediately afterwards
668 to B, the two write request are executed in the given order.
669
670 \note Currently, it is not possible to use signed or reliable writes as defined by the
671 Bluetooth specification.
672
673 A characteristic can only be written if this service is in the \l ServiceDiscovered state
674 and belongs to the service. If one of these conditions is
675 not true the \l QLowEnergyService::OperationError is set.
676
677 \note Calling this function despite \l QLowEnergyCharacteristic::properties() reporting
678 a non-writable property always attempts to write to the hardware.
679 Similarly, a \l WriteWithoutResponse is sent to the hardware too although the
680 characteristic may only support \l WriteWithResponse. If the hardware returns
681 with an error the \l CharacteristicWriteError is set.
682
683 \b {Peripheral role}
684
685 The call results in the value of the characteristic getting updated in the local database.
686
687 If a client is currently connected and it has enabled notifications or indications for
688 the characteristic, the respective information will be sent.
689 If a device has enabled notifications or indications for the characteristic and that device
690 is currently not connected, but a bond exists between it and the local device, then
691 the notification or indication will be sent on the next reconnection.
692
693 If there is a constraint on the length of the characteristic value and \a newValue
694 does not adhere to that constraint, the behavior is unspecified.
695
696 \note The \a mode argument is ignored in peripheral mode.
697
698 \sa QLowEnergyService::characteristicWritten(), QLowEnergyService::readCharacteristic()
699
700 */
701void QLowEnergyService::writeCharacteristic(
702 const QLowEnergyCharacteristic &characteristic,
703 const QByteArray &newValue, QLowEnergyService::WriteMode mode)
704{
705 //TODO check behavior when writing to WriteSigned characteristic
706 Q_D(QLowEnergyService);
707
708 if (d->controller == nullptr
709 || (d->controller->role == QLowEnergyController::CentralRole
710 && state() != ServiceDiscovered)
711 || !contains(characteristic)) {
712 d->setError(QLowEnergyService::OperationError);
713 return;
714 }
715
716 // don't write if properties don't permit it
717 d->controller->writeCharacteristic(characteristic.d_ptr,
718 characteristic.attributeHandle(),
719 newValue,
720 mode);
721}
722
723/*!
724 Returns \c true if \a descriptor belongs to this service; otherwise \c false.
725 */
726bool QLowEnergyService::contains(const QLowEnergyDescriptor &descriptor) const
727{
728 if (descriptor.d_ptr.isNull() || !descriptor.data)
729 return false;
730
731 const QLowEnergyHandle charHandle = descriptor.characteristicHandle();
732 if (!charHandle)
733 return false;
734
735 if (d_ptr == descriptor.d_ptr
736 && d_ptr->characteristicList.contains(charHandle)
737 && d_ptr->characteristicList[charHandle].descriptorList.contains(descriptor.handle()))
738 {
739 return true;
740 }
741
742 return false;
743}
744
745/*!
746 Reads the value of \a descriptor. If the operation is successful, the
747 \l descriptorRead() signal is emitted; otherwise the \l DescriptorReadError
748 is set.
749
750 All descriptor and characteristic requests towards the same remote device are
751 serialised. A queue is employed when issuing multiple requests at the same time.
752 The queue does not eliminate duplicated read requests for the same descriptor.
753
754 A descriptor can only be read if the service is in the \l ServiceDiscovered state
755 and the descriptor belongs to the service. If one of these conditions is
756 not true the \l QLowEnergyService::OperationError is set.
757
758 \sa descriptorRead(), writeDescriptor()
759
760 \since 5.5
761 */
762void QLowEnergyService::readDescriptor(
763 const QLowEnergyDescriptor &descriptor)
764{
765 Q_D(QLowEnergyService);
766
767 if (d->controller == nullptr || state() != ServiceDiscovered || !contains(descriptor)) {
768 d->setError(QLowEnergyService::OperationError);
769 return;
770 }
771
772 d->controller->readDescriptor(descriptor.d_ptr,
773 descriptor.characteristicHandle(),
774 descriptor.handle());
775}
776
777/*!
778 Writes \a newValue as value for \a descriptor. The exact semantics depend on
779 the role that the associated controller object is in.
780
781 \b {Central role}
782
783 A call to this function results in a write request to the remote device.
784 If the operation is successful, the \l descriptorWritten() signal is emitted; otherwise
785 the \l DescriptorWriteError is emitted.
786
787 All descriptor and characteristic requests towards the same remote device are
788 serialised. A queue is employed when issuing multiple write requests at the same time.
789 The queue does not eliminate duplicated write requests for the same descriptor.
790 For example, if the same descriptor is set to the value A and immediately afterwards
791 to B, the two write request are executed in the given order.
792
793 A descriptor can only be written if this service is in the \l ServiceDiscovered state,
794 belongs to the service. If one of these conditions is
795 not true the \l QLowEnergyService::OperationError is set.
796
797 \b {Peripheral Role}
798
799 The value is written to the local service database. If the contents of \a newValue are not
800 valid for \a descriptor, the behavior is unspecified.
801
802 \sa descriptorWritten(), readDescriptor()
803 */
804void QLowEnergyService::writeDescriptor(const QLowEnergyDescriptor &descriptor,
805 const QByteArray &newValue)
806{
807 Q_D(QLowEnergyService);
808
809 if (d->controller == nullptr
810 || (d->controller->role == QLowEnergyController::CentralRole
811 && state() != ServiceDiscovered)
812 || !contains(descriptor)) {
813 d->setError(QLowEnergyService::OperationError);
814 return;
815 }
816#ifdef Q_OS_DARWIN
817 if (descriptor.uuid() == QBluetoothUuid::ClientCharacteristicConfiguration) {
818 // We have to identify a special case - ClientCharacteristicConfiguration
819 // since with CoreBluetooth:
820 //
821 // "You cannot use this method to write the value of a client configuration descriptor
822 // (represented by the CBUUIDClientCharacteristicConfigurationString constant),
823 // which describes how notification or indications are configured for a
824 // characteristic’s value with respect to a client. If you want to manage
825 // notifications or indications for a characteristic’s value, you must
826 // use the setNotifyValue:forCharacteristic: method instead."
827 auto controller = static_cast<QLowEnergyControllerPrivateDarwin *>(d->controller.data());
828 return controller->setNotifyValue(descriptor.d_ptr, descriptor.characteristicHandle(), newValue);
829 }
830#endif // Q_OS_DARWIN
831
832 d->controller->writeDescriptor(descriptor.d_ptr,
833 descriptor.characteristicHandle(),
834 descriptor.handle(),
835 newValue);
836}
837
838QT_END_NAMESPACE
839