1/***************************************************************************
2**
3** Copyright (C) 2016 BlackBerry Limited. All rights reserved.
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 "qlowenergycharacteristic.h"
41#include "qlowenergyserviceprivate_p.h"
42#include <QHash>
43
44QT_BEGIN_NAMESPACE
45
46/*!
47 \class QLowEnergyCharacteristic
48 \inmodule QtBluetooth
49 \brief The QLowEnergyCharacteristic class stores information about a Bluetooth
50 Low Energy service characteristic.
51
52 \since 5.4
53
54 QLowEnergyCharacteristic provides information about a Bluetooth Low Energy
55 service characteristic's \l name(), \l uuid(), \l value(), \l properties(),
56 \l handle() and \l descriptors(). To obtain the characteristic's specification
57 and information, it is necessary to connect to the device using the
58 \l QLowEnergyService and \l QLowEnergyController classes.
59
60 The characteristic value may be written via the \l QLowEnergyService instance
61 that manages the service to which this characteristic belongs. The
62 \l {QLowEnergyService::writeCharacteristic()} function writes the new value.
63 The \l {QLowEnergyService::characteristicWritten()} signal is emitted upon success.
64 The \l value() of this object is automatically updated accordingly.
65
66 Characteristics may contain none, one or more descriptors. They can be individually
67 retrieved using the \l descriptor() function. The \l descriptors() function returns
68 all descriptors as a list. The general purpose of a descriptor is to add contextual
69 information to the characteristic. For example, the descriptor might provide
70 format or range information specifying how the characteristic's value is to be\
71 interpreted.
72
73 \sa QLowEnergyService, QLowEnergyDescriptor
74*/
75
76/*!
77 \enum QLowEnergyCharacteristic::PropertyType
78
79 This enum describes the properties of a characteristic.
80
81 \value Unknown The type is not known.
82 \value Broadcasting Allow for the broadcasting of Generic Attributes (GATT) characteristic values.
83 \value Read Allow the characteristic values to be read.
84 \value WriteNoResponse Allow characteristic values without responses to be written.
85 \value Write Allow for characteristic values to be written.
86 \value Notify Permits notification of characteristic values.
87 \value Indicate Permits indications of characteristic values.
88 \value WriteSigned Permits signed writes of the GATT characteristic values.
89 \value ExtendedProperty Additional characteristic properties are defined in the characteristic's
90 extended properties descriptor.
91
92 \sa properties()
93*/
94
95struct QLowEnergyCharacteristicPrivate
96{
97 QLowEnergyHandle handle;
98};
99
100/*!
101 Construct a new QLowEnergyCharacteristic. A default-constructed instance of
102 this class is always invalid.
103
104 \sa isValid()
105*/
106QLowEnergyCharacteristic::QLowEnergyCharacteristic():
107 d_ptr(nullptr)
108{
109
110}
111
112/*!
113 Construct a new QLowEnergyCharacteristic that is a copy of \a other.
114
115 The two copies continue to share the same underlying data which does not detach
116 upon write.
117*/
118QLowEnergyCharacteristic::QLowEnergyCharacteristic(const QLowEnergyCharacteristic &other):
119 d_ptr(other.d_ptr)
120{
121 if (other.data) {
122 data = new QLowEnergyCharacteristicPrivate();
123 data->handle = other.data->handle;
124 }
125}
126
127/*!
128 \internal
129*/
130QLowEnergyCharacteristic::QLowEnergyCharacteristic(
131 QSharedPointer<QLowEnergyServicePrivate> p, QLowEnergyHandle handle):
132 d_ptr(p)
133{
134 data = new QLowEnergyCharacteristicPrivate();
135 data->handle = handle;
136}
137
138/*!
139 Destroys the QLowEnergyCharacteristic object.
140*/
141QLowEnergyCharacteristic::~QLowEnergyCharacteristic()
142{
143 delete data;
144}
145
146/*!
147 Returns the human-readable name of the characteristic.
148
149 The name is based on the characteristic's \l uuid() which must have been
150 standardized. The complete list of characteristic types can be found
151 under \l {https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicsHome.aspx}{Bluetooth.org Characteristics}.
152
153 The returned string is empty if the \l uuid() is unknown.
154
155 \sa QBluetoothUuid::characteristicToString()
156*/
157QString QLowEnergyCharacteristic::name() const
158{
159 return QBluetoothUuid::characteristicToString(
160 static_cast<QBluetoothUuid::CharacteristicType>(uuid().toUInt16()));
161}
162
163/*!
164 Returns the UUID of the characteristic if \l isValid() returns \c true; otherwise a
165 \l {QUuid::isNull()}{null} UUID.
166*/
167QBluetoothUuid QLowEnergyCharacteristic::uuid() const
168{
169 if (d_ptr.isNull() || !data
170 || !d_ptr->characteristicList.contains(data->handle))
171 return QBluetoothUuid();
172
173 return d_ptr->characteristicList[data->handle].uuid;
174}
175
176/*!
177 Returns the properties of the characteristic.
178
179 The properties define the access permissions for the characteristic.
180*/
181QLowEnergyCharacteristic::PropertyTypes QLowEnergyCharacteristic::properties() const
182{
183 if (d_ptr.isNull() || !data
184 || !d_ptr->characteristicList.contains(data->handle))
185 return QLowEnergyCharacteristic::Unknown;
186
187 return d_ptr->characteristicList[data->handle].properties;
188}
189
190/*!
191 Returns the cached value of the characteristic.
192
193 If the characteristic's \l properties() permit writing of new values,
194 the value can be updated using \l QLowEnergyService::writeCharacteristic().
195
196 The cache is updated during the associated service's
197 \l {QLowEnergyService::discoverDetails()} {detail discovery}, a successful
198 \l {QLowEnergyService::readCharacteristic()}{read}/\l {QLowEnergyService::writeCharacteristic()}{write}
199 operation or when an update notification is received.
200
201 The returned \l QByteArray always remains empty if the characteristic does not
202 have the \l {QLowEnergyCharacteristic::Read}{read permission}. In such cases only
203 the \l QLowEnergyService::characteristicChanged() or
204 \l QLowEnergyService::characteristicWritten() may provice information about the
205 value of this characteristic.
206*/
207QByteArray QLowEnergyCharacteristic::value() const
208{
209 if (d_ptr.isNull() || !data
210 || !d_ptr->characteristicList.contains(data->handle))
211 return QByteArray();
212
213 return d_ptr->characteristicList[data->handle].value;
214}
215
216/*!
217 Returns the handle of the characteristic's value attribute;
218 or \c 0 if the handle cannot be accessed on the platform or
219 if the characteristic is invalid.
220
221 \note On \macos and iOS handles can differ from 0, but these
222 values have no special meaning outside of internal/private API.
223*/
224QLowEnergyHandle QLowEnergyCharacteristic::handle() const
225{
226 if (d_ptr.isNull() || !data
227 || !d_ptr->characteristicList.contains(data->handle))
228 return 0;
229
230 return d_ptr->characteristicList[data->handle].valueHandle;
231}
232
233/*!
234 Makes a copy of \a other and assigns it to this \l QLowEnergyCharacteristic object.
235 The two copies continue to share the same service and controller details.
236*/
237QLowEnergyCharacteristic &QLowEnergyCharacteristic::operator=(const QLowEnergyCharacteristic &other)
238{
239 d_ptr = other.d_ptr;
240
241 if (!other.data) {
242 if (data) {
243 delete data;
244 data = nullptr;
245 }
246 } else {
247 if (!data)
248 data = new QLowEnergyCharacteristicPrivate();
249
250 data->handle = other.data->handle;
251 }
252 return *this;
253}
254
255/*!
256 Returns \c true if \a other is equal to this QLowEnergyCharacteristic; otherwise \c false.
257
258 Two \l QLowEnergyCharacteristic instances are considered to be equal if they refer to
259 the same characteristic on the same remote Bluetooth Low Energy device or both instances
260 have been default-constructed.
261 */
262bool QLowEnergyCharacteristic::operator==(const QLowEnergyCharacteristic &other) const
263{
264 if (d_ptr != other.d_ptr)
265 return false;
266
267 if ((data && !other.data) || (!data && other.data))
268 return false;
269
270 if (!data)
271 return true;
272
273 if (data->handle != other.data->handle)
274 return false;
275
276 return true;
277}
278
279/*!
280 Returns \c true if \a other is not equal to this QLowEnergyCharacteristic; otherwise \c false.
281
282 Two QLowEnergyCharcteristic instances are considered to be equal if they refer to
283 the same characteristic on the same remote Bluetooth Low Energy device or both instances
284 have been default-constructed.
285 */
286
287bool QLowEnergyCharacteristic::operator!=(const QLowEnergyCharacteristic &other) const
288{
289 return !(*this == other);
290}
291
292/*!
293 Returns \c true if the QLowEnergyCharacteristic object is valid, otherwise returns \c false.
294
295 An invalid characteristic object is not associated with any service (default-constructed)
296 or the associated service is no longer valid due to a disconnect from
297 the underlying Bluetooth Low Energy device, for example. Once the object is invalid
298 it cannot become valid anymore.
299
300 \note If a \l QLowEnergyCharacteristic instance turns invalid due to a disconnect
301 from the underlying device, the information encapsulated by the current
302 instance remains as it was at the time of the disconnect. Therefore it can be
303 retrieved after the disconnect event.
304*/
305bool QLowEnergyCharacteristic::isValid() const
306{
307 if (d_ptr.isNull() || !data)
308 return false;
309
310 if (d_ptr->state == QLowEnergyService::InvalidService)
311 return false;
312
313 return true;
314}
315
316/*!
317 \internal
318
319 Returns the handle of the characteristic or
320 \c 0 if the handle cannot be accessed on the platform or if the
321 characteristic is invalid.
322
323 \note On \macos and iOS handles can differ from 0, but these
324 values have no special meaning outside of internal/private API.
325
326 \sa isValid()
327 */
328QLowEnergyHandle QLowEnergyCharacteristic::attributeHandle() const
329{
330 if (d_ptr.isNull() || !data)
331 return 0;
332
333 return data->handle;
334}
335
336
337/*!
338 Returns the descriptor for \a uuid or an invalid \c QLowEnergyDescriptor instance.
339
340 \sa descriptors()
341*/
342QLowEnergyDescriptor QLowEnergyCharacteristic::descriptor(const QBluetoothUuid &uuid) const
343{
344 if (d_ptr.isNull() || !data)
345 return QLowEnergyDescriptor();
346
347 CharacteristicDataMap::const_iterator charIt = d_ptr->characteristicList.constFind(data->handle);
348 if (charIt != d_ptr->characteristicList.constEnd()) {
349 const QLowEnergyServicePrivate::CharData &charDetails = charIt.value();
350
351 DescriptorDataMap::const_iterator descIt = charDetails.descriptorList.constBegin();
352 for ( ; descIt != charDetails.descriptorList.constEnd(); ++descIt) {
353 const QLowEnergyHandle descHandle = descIt.key();
354 const QLowEnergyServicePrivate::DescData &descDetails = descIt.value();
355
356 if (descDetails.uuid == uuid)
357 return QLowEnergyDescriptor(d_ptr, data->handle, descHandle);
358 }
359 }
360
361 return QLowEnergyDescriptor();
362}
363
364/*!
365 Returns the list of descriptors belonging to this characteristic; otherwise
366 an empty list.
367
368 \sa descriptor()
369*/
370QList<QLowEnergyDescriptor> QLowEnergyCharacteristic::descriptors() const
371{
372 QList<QLowEnergyDescriptor> result;
373
374 if (d_ptr.isNull() || !data
375 || !d_ptr->characteristicList.contains(data->handle))
376 return result;
377
378 QList<QLowEnergyHandle> descriptorKeys = d_ptr->characteristicList[data->handle].
379 descriptorList.keys();
380
381 std::sort(descriptorKeys.begin(), descriptorKeys.end());
382
383 for (const QLowEnergyHandle descHandle : qAsConst(descriptorKeys)) {
384 QLowEnergyDescriptor descriptor(d_ptr, data->handle, descHandle);
385 result.append(descriptor);
386 }
387
388 return result;
389}
390
391QT_END_NAMESPACE
392