1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 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 "lecmaccalculator_p.h"
42 #include "qlowenergycontroller_bluez_p.h"
43 #include "qbluetoothsocketbase_p.h"
44 #include "qbluetoothsocket_bluez_p.h"
45 #include "qleadvertiser_p.h"
46 #include "bluez/bluez_data_p.h"
47 #include "bluez/hcimanager_p.h"
48 #include "bluez/objectmanager_p.h"
49 #include "bluez/remotedevicemanager_p.h"
50 #include "bluez/bluez5_helper_p.h"
51 #include "bluez/bluetoothmanagement_p.h"
52
53 // Bluez 4
54 #include "bluez/adapter_p.h"
55 #include "bluez/device_p.h"
56 #include "bluez/manager_p.h"
57
58 #include <QtCore/QFileInfo>
59 #include <QtCore/QLoggingCategory>
60 #include <QtCore/QSettings>
61 #include <QtCore/QTimer>
62 #include <QtBluetooth/QBluetoothLocalDevice>
63 #include <QtBluetooth/QBluetoothSocket>
64 #include <QtBluetooth/QLowEnergyCharacteristicData>
65 #include <QtBluetooth/QLowEnergyDescriptorData>
66 #include <QtBluetooth/QLowEnergyService>
67 #include <QtBluetooth/QLowEnergyServiceData>
68
69 #include <algorithm>
70 #include <climits>
71 #include <cstring>
72 #include <errno.h>
73 #include <sys/types.h>
74 #include <sys/socket.h>
75 #include <unistd.h>
76
77 #define ATT_DEFAULT_LE_MTU 23
78 #define ATT_MAX_LE_MTU 0x200
79
80 #define GATT_PRIMARY_SERVICE quint16(0x2800)
81 #define GATT_SECONDARY_SERVICE quint16(0x2801)
82 #define GATT_INCLUDED_SERVICE quint16(0x2802)
83 #define GATT_CHARACTERISTIC quint16(0x2803)
84
85 // GATT commands
86 #define ATT_OP_ERROR_RESPONSE 0x1
87 #define ATT_OP_EXCHANGE_MTU_REQUEST 0x2 //send own mtu
88 #define ATT_OP_EXCHANGE_MTU_RESPONSE 0x3 //receive server MTU
89 #define ATT_OP_FIND_INFORMATION_REQUEST 0x4 //discover individual attribute info
90 #define ATT_OP_FIND_INFORMATION_RESPONSE 0x5
91 #define ATT_OP_FIND_BY_TYPE_VALUE_REQUEST 0x6
92 #define ATT_OP_FIND_BY_TYPE_VALUE_RESPONSE 0x7
93 #define ATT_OP_READ_BY_TYPE_REQUEST 0x8 //discover characteristics
94 #define ATT_OP_READ_BY_TYPE_RESPONSE 0x9
95 #define ATT_OP_READ_REQUEST 0xA //read characteristic & descriptor values
96 #define ATT_OP_READ_RESPONSE 0xB
97 #define ATT_OP_READ_BLOB_REQUEST 0xC //read values longer than MTU-1
98 #define ATT_OP_READ_BLOB_RESPONSE 0xD
99 #define ATT_OP_READ_MULTIPLE_REQUEST 0xE
100 #define ATT_OP_READ_MULTIPLE_RESPONSE 0xF
101 #define ATT_OP_READ_BY_GROUP_REQUEST 0x10 //discover services
102 #define ATT_OP_READ_BY_GROUP_RESPONSE 0x11
103 #define ATT_OP_WRITE_REQUEST 0x12 //write characteristic with response
104 #define ATT_OP_WRITE_RESPONSE 0x13
105 #define ATT_OP_PREPARE_WRITE_REQUEST 0x16 //write values longer than MTU-3 -> queueing
106 #define ATT_OP_PREPARE_WRITE_RESPONSE 0x17
107 #define ATT_OP_EXECUTE_WRITE_REQUEST 0x18 //write values longer than MTU-3 -> execute queue
108 #define ATT_OP_EXECUTE_WRITE_RESPONSE 0x19
109 #define ATT_OP_HANDLE_VAL_NOTIFICATION 0x1b //informs about value change
110 #define ATT_OP_HANDLE_VAL_INDICATION 0x1d //informs about value change -> requires reply
111 #define ATT_OP_HANDLE_VAL_CONFIRMATION 0x1e //answer for ATT_OP_HANDLE_VAL_INDICATION
112 #define ATT_OP_WRITE_COMMAND 0x52 //write characteristic without response
113 #define ATT_OP_SIGNED_WRITE_COMMAND 0xD2
114
115 //GATT command sizes in bytes
116 #define ERROR_RESPONSE_HEADER_SIZE 5
117 #define FIND_INFO_REQUEST_HEADER_SIZE 5
118 #define GRP_TYPE_REQ_HEADER_SIZE 7
119 #define READ_BY_TYPE_REQ_HEADER_SIZE 7
120 #define READ_REQUEST_HEADER_SIZE 3
121 #define READ_BLOB_REQUEST_HEADER_SIZE 5
122 #define WRITE_REQUEST_HEADER_SIZE 3 // same size for WRITE_COMMAND header
123 #define PREPARE_WRITE_HEADER_SIZE 5
124 #define EXECUTE_WRITE_HEADER_SIZE 2
125 #define MTU_EXCHANGE_HEADER_SIZE 3
126
127 // GATT error codes
128 #define ATT_ERROR_INVALID_HANDLE 0x01
129 #define ATT_ERROR_READ_NOT_PERM 0x02
130 #define ATT_ERROR_WRITE_NOT_PERM 0x03
131 #define ATT_ERROR_INVALID_PDU 0x04
132 #define ATT_ERROR_INSUF_AUTHENTICATION 0x05
133 #define ATT_ERROR_REQUEST_NOT_SUPPORTED 0x06
134 #define ATT_ERROR_INVALID_OFFSET 0x07
135 #define ATT_ERROR_INSUF_AUTHORIZATION 0x08
136 #define ATT_ERROR_PREPARE_QUEUE_FULL 0x09
137 #define ATT_ERROR_ATTRIBUTE_NOT_FOUND 0x0A
138 #define ATT_ERROR_ATTRIBUTE_NOT_LONG 0x0B
139 #define ATT_ERROR_INSUF_ENCR_KEY_SIZE 0x0C
140 #define ATT_ERROR_INVAL_ATTR_VALUE_LEN 0x0D
141 #define ATT_ERROR_UNLIKELY 0x0E
142 #define ATT_ERROR_INSUF_ENCRYPTION 0x0F
143 #define ATT_ERROR_UNSUPPRTED_GROUP_TYPE 0x10
144 #define ATT_ERROR_INSUF_RESOURCES 0x11
145 #define ATT_ERROR_APPLICATION_START 0x80
146 //------------------------------------------
147 // The error codes in this block are
148 // implementation specific errors
149
150 #define ATT_ERROR_REQUEST_STALLED 0x81
151
152 //------------------------------------------
153 #define ATT_ERROR_APPLICATION_END 0x9f
154
155 #define APPEND_VALUE true
156 #define NEW_VALUE false
157
158 QT_BEGIN_NAMESPACE
159
160 Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
161
162 using namespace QBluetooth;
163
164 const int maxPrepareQueueSize = 1024;
165
convert_uuid128(const quint128 * p)166 static inline QBluetoothUuid convert_uuid128(const quint128 *p)
167 {
168 quint128 dst_hostOrder, dst_bigEndian;
169
170 // Bluetooth LE data comes as little endian
171 // uuids are constructed using high endian
172 btoh128(p, &dst_hostOrder);
173 hton128(&dst_hostOrder, &dst_bigEndian);
174
175 // convert to Qt's own data type
176 quint128 qtdst;
177 memcpy(&qtdst, &dst_bigEndian, sizeof(quint128));
178
179 return QBluetoothUuid(qtdst);
180 }
181
dumpErrorInformation(const QByteArray & response)182 static void dumpErrorInformation(const QByteArray &response)
183 {
184 const char *data = response.constData();
185 if (response.size() != 5 || data[0] != ATT_OP_ERROR_RESPONSE) {
186 qCWarning(QT_BT_BLUEZ) << QLatin1String("Not a valid error response");
187 return;
188 }
189
190 quint8 lastCommand = data[1];
191 quint16 handle = bt_get_le16(&data[2]);
192 quint8 errorCode = data[4];
193
194 QString errorString;
195 switch (errorCode) {
196 case ATT_ERROR_INVALID_HANDLE:
197 errorString = QStringLiteral("invalid handle"); break;
198 case ATT_ERROR_READ_NOT_PERM:
199 errorString = QStringLiteral("not readable attribute - permissions"); break;
200 case ATT_ERROR_WRITE_NOT_PERM:
201 errorString = QStringLiteral("not writable attribute - permissions"); break;
202 case ATT_ERROR_INVALID_PDU:
203 errorString = QStringLiteral("PDU invalid"); break;
204 case ATT_ERROR_INSUF_AUTHENTICATION:
205 errorString = QStringLiteral("needs authentication - permissions"); break;
206 case ATT_ERROR_REQUEST_NOT_SUPPORTED:
207 errorString = QStringLiteral("server does not support request"); break;
208 case ATT_ERROR_INVALID_OFFSET:
209 errorString = QStringLiteral("offset past end of attribute"); break;
210 case ATT_ERROR_INSUF_AUTHORIZATION:
211 errorString = QStringLiteral("need authorization - permissions"); break;
212 case ATT_ERROR_PREPARE_QUEUE_FULL:
213 errorString = QStringLiteral("run out of prepare queue space"); break;
214 case ATT_ERROR_ATTRIBUTE_NOT_FOUND:
215 errorString = QStringLiteral("no attribute in given range found"); break;
216 case ATT_ERROR_ATTRIBUTE_NOT_LONG:
217 errorString = QStringLiteral("attribute not read/written using read blob"); break;
218 case ATT_ERROR_INSUF_ENCR_KEY_SIZE:
219 errorString = QStringLiteral("need encryption key size - permissions"); break;
220 case ATT_ERROR_INVAL_ATTR_VALUE_LEN:
221 errorString = QStringLiteral("written value is invalid size"); break;
222 case ATT_ERROR_UNLIKELY:
223 errorString = QStringLiteral("unlikely error"); break;
224 case ATT_ERROR_INSUF_ENCRYPTION:
225 errorString = QStringLiteral("needs encryption - permissions"); break;
226 case ATT_ERROR_UNSUPPRTED_GROUP_TYPE:
227 errorString = QStringLiteral("unsupported group type"); break;
228 case ATT_ERROR_INSUF_RESOURCES:
229 errorString = QStringLiteral("insufficient resources to complete request"); break;
230 default:
231 if (errorCode >= ATT_ERROR_APPLICATION_START && errorCode <= ATT_ERROR_APPLICATION_END)
232 errorString = QStringLiteral("application error: %1").arg(errorCode);
233 else
234 errorString = QStringLiteral("unknown error code");
235 break;
236 }
237
238 qCDebug(QT_BT_BLUEZ) << "Error1:" << errorString
239 << "last command:" << hex << lastCommand
240 << "handle:" << handle;
241 }
242
getUuidSize(const QBluetoothUuid & uuid)243 static int getUuidSize(const QBluetoothUuid &uuid)
244 {
245 return uuid.minimumSize() == 2 ? 2 : 16;
246 }
247
putDataAndIncrement(const T & src,char * & dst)248 template<typename T> static void putDataAndIncrement(const T &src, char *&dst)
249 {
250 putBtData(src, dst);
251 dst += sizeof(T);
252 }
putDataAndIncrement(const QBluetoothUuid & uuid,char * & dst)253 template<> void putDataAndIncrement(const QBluetoothUuid &uuid, char *&dst)
254 {
255 const int uuidSize = getUuidSize(uuid);
256 if (uuidSize == 2) {
257 putBtData(uuid.toUInt16(), dst);
258 } else {
259 quint128 hostOrder;
260 quint128 qtUuidOrder = uuid.toUInt128();
261 ntoh128(&qtUuidOrder, &hostOrder);
262 putBtData(hostOrder, dst);
263 }
264 dst += uuidSize;
265 }
putDataAndIncrement(const QByteArray & value,char * & dst)266 template<> void putDataAndIncrement(const QByteArray &value, char *&dst)
267 {
268 using namespace std;
269 memcpy(dst, value.constData(), value.count());
270 dst += value.count();
271 }
272
QLowEnergyControllerPrivateBluez()273 QLowEnergyControllerPrivateBluez::QLowEnergyControllerPrivateBluez()
274 : QLowEnergyControllerPrivate(),
275 requestPending(false),
276 mtuSize(ATT_DEFAULT_LE_MTU),
277 securityLevelValue(-1),
278 encryptionChangePending(false)
279 {
280 registerQLowEnergyControllerMetaType();
281 qRegisterMetaType<QList<QLowEnergyHandle> >();
282 }
283
init()284 void QLowEnergyControllerPrivateBluez::init()
285 {
286 hciManager = new HciManager(localAdapter, this);
287 if (!hciManager->isValid())
288 return;
289
290 hciManager->monitorEvent(HciManager::EncryptChangeEvent);
291 connect(hciManager, SIGNAL(encryptionChangedEvent(QBluetoothAddress,bool)),
292 this, SLOT(encryptionChangedEvent(QBluetoothAddress,bool)));
293 hciManager->monitorEvent(HciManager::LeMetaEvent);
294 hciManager->monitorAclPackets();
295 connect(hciManager, &HciManager::connectionComplete, [this](quint16 handle) {
296 connectionHandle = handle;
297 qCDebug(QT_BT_BLUEZ) << "received connection complete event, handle:" << handle;
298 });
299 connect(hciManager, &HciManager::connectionUpdate,
300 [this](quint16 handle, const QLowEnergyConnectionParameters ¶ms) {
301 if (handle == connectionHandle)
302 emit q_ptr->connectionUpdated(params);
303 }
304 );
305 connect(hciManager, &HciManager::signatureResolvingKeyReceived,
306 [this](quint16 handle, bool remoteKey, const quint128 &csrk) {
307 if (handle != connectionHandle)
308 return;
309 if ((remoteKey && role == QLowEnergyController::CentralRole)
310 || (!remoteKey && role == QLowEnergyController::PeripheralRole)) {
311 return;
312 }
313 qCDebug(QT_BT_BLUEZ) << "received new signature resolving key"
314 << QByteArray(reinterpret_cast<const char *>(csrk.data),
315 sizeof csrk).toHex();
316 signingData.insert(remoteDevice.toUInt64(), SigningData(csrk));
317 }
318 );
319
320 if (role == QLowEnergyController::CentralRole) {
321 if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("BLUETOOTH_GATT_TIMEOUT"))) {
322 bool ok = false;
323 int value = qEnvironmentVariableIntValue("BLUETOOTH_GATT_TIMEOUT", &ok);
324 if (ok)
325 gattRequestTimeout = value;
326 }
327
328 // permit disabling of timeout behavior via environment variable
329 if (gattRequestTimeout > 0) {
330 qCWarning(QT_BT_BLUEZ) << "Enabling GATT request timeout behavior" << gattRequestTimeout;
331 requestTimer = new QTimer(this);
332 requestTimer->setSingleShot(true);
333 requestTimer->setInterval(gattRequestTimeout);
334 connect(requestTimer, &QTimer::timeout,
335 this, &QLowEnergyControllerPrivateBluez::handleGattRequestTimeout);
336 qRegisterMetaTypeStreamOperators<QBluetoothUuid>();
337 }
338 }
339 }
340
handleGattRequestTimeout()341 void QLowEnergyControllerPrivateBluez::handleGattRequestTimeout()
342 {
343 // antyhing open that might require cancellation or a warning?
344 if (encryptionChangePending) {
345 // We cannot really recover for now but the warning is essential for debugging
346 qCWarning(QT_BT_BLUEZ) << "****** Encryption change event blocking further GATT requests";
347 return;
348 }
349
350 if (!openRequests.isEmpty() && requestPending) {
351 const Request currentRequest = openRequests.dequeue();
352 requestPending = false; // reset pending flag
353
354 qCWarning(QT_BT_BLUEZ).nospace() << "****** Request type 0x" << hex << currentRequest.command
355 << " to server/peripheral timed out";
356 qCWarning(QT_BT_BLUEZ) << "****** Looks like the characteristic or descriptor does NOT act in"
357 << "accordance to Bluetooth 4.x spec.";
358 qCWarning(QT_BT_BLUEZ) << "****** Please check server implementation."
359 << "Continuing under reservation.";
360
361 quint8 command = currentRequest.command;
362 const auto createRequestErrorMessage = [](quint8 opcodeWithError,
363 QLowEnergyHandle handle) {
364 QByteArray errorPackage(ERROR_RESPONSE_HEADER_SIZE, Qt::Uninitialized);
365 errorPackage[0] = ATT_OP_ERROR_RESPONSE;
366 errorPackage[1] = opcodeWithError; // e.g. ATT_OP_READ_REQUEST
367 putBtData(handle, errorPackage.data() + 2); //
368 errorPackage[4] = ATT_ERROR_REQUEST_STALLED;
369
370 return errorPackage;
371 };
372
373 switch (command) {
374 case ATT_OP_EXCHANGE_MTU_REQUEST: // MTU change request
375 // never received reply to MTU request
376 // it is safe to skip and go to next request
377 break;
378 case ATT_OP_READ_BY_GROUP_REQUEST: // primary or secondary service discovery
379 case ATT_OP_READ_BY_TYPE_REQUEST: // characteristic or included service discovery
380 // jump back into usual response handling with custom error code
381 // 2nd param "0" as required by spec
382 processReply(currentRequest, createRequestErrorMessage(command, 0));
383 break;
384 case ATT_OP_READ_REQUEST: // read descriptor or characteristic value
385 case ATT_OP_READ_BLOB_REQUEST: // read long descriptor or characteristic
386 case ATT_OP_WRITE_REQUEST: // write descriptor or characteristic
387 {
388 uint handleData = currentRequest.reference.toUInt();
389 const QLowEnergyHandle charHandle = (handleData & 0xffff);
390 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
391 processReply(currentRequest, createRequestErrorMessage(command,
392 descriptorHandle ? descriptorHandle : charHandle));
393 }
394 break;
395 case ATT_OP_FIND_INFORMATION_REQUEST: // get descriptor information
396 processReply(currentRequest, createRequestErrorMessage(
397 command, currentRequest.reference2.toUInt()));
398 break;
399 case ATT_OP_PREPARE_WRITE_REQUEST: // prepare to write long desc or char
400 case ATT_OP_EXECUTE_WRITE_REQUEST: // execute long write of desc or char
401 {
402 uint handleData = currentRequest.reference.toUInt();
403 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
404 processReply(currentRequest,
405 createRequestErrorMessage(command, attrHandle));
406 }
407 break;
408 default:
409 // not a command used by central role implementation
410 qCWarning(QT_BT_BLUEZ) << "Missing response for ATT peripheral command: "
411 << hex << command;
412 break;
413 }
414
415 // spin openRequest queue further
416 sendNextPendingRequest();
417 }
418 }
419
~QLowEnergyControllerPrivateBluez()420 QLowEnergyControllerPrivateBluez::~QLowEnergyControllerPrivateBluez()
421 {
422 closeServerSocket();
423 delete cmacCalculator;
424 }
425
426 class ServerSocket
427 {
428 public:
listen(const QBluetoothAddress & localAdapter)429 bool listen(const QBluetoothAddress &localAdapter)
430 {
431 m_socket = ::socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
432 if (m_socket == -1) {
433 qCWarning(QT_BT_BLUEZ) << "socket creation failed:" << qt_error_string(errno);
434 return false;
435 }
436 sockaddr_l2 addr;
437
438 // memset should be in std namespace for C++ compilers, but we also need to support
439 // broken ones that put it in the global one.
440 using namespace std;
441 memset(&addr, 0, sizeof addr);
442
443 addr.l2_family = AF_BLUETOOTH;
444 addr.l2_cid = htobs(ATTRIBUTE_CHANNEL_ID);
445 addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
446 convertAddress(localAdapter.toUInt64(), addr.l2_bdaddr.b);
447 if (::bind(m_socket, reinterpret_cast<sockaddr *>(&addr), sizeof addr) == -1) {
448 qCWarning(QT_BT_BLUEZ) << "bind() failed:" << qt_error_string(errno);
449 return false;
450 }
451 if (::listen(m_socket, 1)) {
452 qCWarning(QT_BT_BLUEZ) << "listen() failed:" << qt_error_string(errno);
453 return false;
454 }
455 return true;
456 }
457
~ServerSocket()458 ~ServerSocket()
459 {
460 if (m_socket != -1)
461 close(m_socket);
462 }
463
takeSocket()464 int takeSocket()
465 {
466 const int socket = m_socket;
467 m_socket = -1;
468 return socket;
469 }
470
471 private:
472 int m_socket = -1;
473 };
474
475
startAdvertising(const QLowEnergyAdvertisingParameters & params,const QLowEnergyAdvertisingData & advertisingData,const QLowEnergyAdvertisingData & scanResponseData)476 void QLowEnergyControllerPrivateBluez::startAdvertising(const QLowEnergyAdvertisingParameters ¶ms,
477 const QLowEnergyAdvertisingData &advertisingData,
478 const QLowEnergyAdvertisingData &scanResponseData)
479 {
480 qCDebug(QT_BT_BLUEZ) << "Starting to advertise";
481 if (!advertiser) {
482 advertiser = new QLeAdvertiserBluez(params, advertisingData, scanResponseData, *hciManager,
483 this);
484 connect(advertiser, &QLeAdvertiser::errorOccurred, this,
485 &QLowEnergyControllerPrivateBluez::handleAdvertisingError);
486 }
487 setState(QLowEnergyController::AdvertisingState);
488 advertiser->startAdvertising();
489 if (params.mode() == QLowEnergyAdvertisingParameters::AdvNonConnInd
490 || params.mode() == QLowEnergyAdvertisingParameters::AdvScanInd) {
491 qCDebug(QT_BT_BLUEZ) << "Non-connectable advertising requested, "
492 "not listening for connections.";
493 return;
494 }
495
496 ServerSocket serverSocket;
497 if (!serverSocket.listen(localAdapter)) {
498 setError(QLowEnergyController::AdvertisingError);
499 setState(QLowEnergyController::UnconnectedState);
500 return;
501 }
502
503 const int socketFd = serverSocket.takeSocket();
504 serverSocketNotifier = new QSocketNotifier(socketFd, QSocketNotifier::Read, this);
505 connect(serverSocketNotifier, &QSocketNotifier::activated, this,
506 &QLowEnergyControllerPrivateBluez::handleConnectionRequest);
507 }
508
stopAdvertising()509 void QLowEnergyControllerPrivateBluez::stopAdvertising()
510 {
511 setState(QLowEnergyController::UnconnectedState);
512 advertiser->stopAdvertising();
513 }
514
requestConnectionUpdate(const QLowEnergyConnectionParameters & params)515 void QLowEnergyControllerPrivateBluez::requestConnectionUpdate(const QLowEnergyConnectionParameters ¶ms)
516 {
517 // The spec says that the connection update command can be used by both slave and master
518 // devices, but BlueZ allows it only for master devices. So for slave devices, we have to use a
519 // connection parameter update request, which we need to wrap in an ACL command, as BlueZ
520 // does not allow user-space sockets for the signaling channel.
521 if (role == QLowEnergyController::CentralRole)
522 hciManager->sendConnectionUpdateCommand(connectionHandle, params);
523 else
524 hciManager->sendConnectionParameterUpdateRequest(connectionHandle, params);
525 }
526
connectToDevice()527 void QLowEnergyControllerPrivateBluez::connectToDevice()
528 {
529 if (remoteDevice.isNull()) {
530 qCWarning(QT_BT_BLUEZ) << "Invalid/null remote device address";
531 setError(QLowEnergyController::UnknownRemoteDeviceError);
532 return;
533 }
534
535 setState(QLowEnergyController::ConnectingState);
536 if (l2cpSocket) {
537 delete l2cpSocket;
538 l2cpSocket = nullptr;
539 }
540
541 createServicesForCentralIfRequired();
542
543 // check for active running connections
544 // BlueZ 5.37+ (maybe even earlier versions) can have pending BTLE connections
545 // Only one active L2CP socket to CID 0x4 possible at a time
546 // this check is not performed for BlueZ 4 based platforms as bluetoothd
547 // does not support BTLE management
548
549 if (!isBluez5()) {
550 establishL2cpClientSocket();
551 return;
552 }
553
554 QVector<quint16> activeHandles = hciManager->activeLowEnergyConnections();
555 if (!activeHandles.isEmpty()) {
556 qCWarning(QT_BT_BLUEZ) << "Cannot connect due to pending active LE connections";
557
558 if (!device1Manager) {
559 device1Manager = new RemoteDeviceManager(localAdapter, this);
560 connect(device1Manager, &RemoteDeviceManager::finished,
561 this, &QLowEnergyControllerPrivateBluez::activeConnectionTerminationDone);
562 }
563
564 QVector<QBluetoothAddress> connectedAddresses;
565 for (const auto handle: activeHandles) {
566 const QBluetoothAddress addr = hciManager->addressForConnectionHandle(handle);
567 if (!addr.isNull())
568 connectedAddresses.push_back(addr);
569 }
570 device1Manager->scheduleJob(RemoteDeviceManager::JobType::JobDisconnectDevice, connectedAddresses);
571 } else {
572 establishL2cpClientSocket();
573 }
574 }
575
576 /*!
577 * Handles outcome of attempts to close external connections.
578 */
activeConnectionTerminationDone()579 void QLowEnergyControllerPrivateBluez::activeConnectionTerminationDone()
580 {
581 if (!device1Manager)
582 return;
583
584 qCDebug(QT_BT_BLUEZ) << "RemoteDeviceManager finished attempting"
585 << "to close external connections";
586
587 QVector<quint16> activeHandles = hciManager->activeLowEnergyConnections();
588 if (!activeHandles.isEmpty()) {
589 qCWarning(QT_BT_BLUEZ) << "Cannot close pending external BTLE connections. Aborting connect attempt";
590 setError(QLowEnergyController::ConnectionError);
591 setState(QLowEnergyController::UnconnectedState);
592 l2cpDisconnected();
593 return;
594 } else {
595 establishL2cpClientSocket();
596 }
597 }
598
599 /*!
600 * Establishes the L2CP client socket.
601 */
establishL2cpClientSocket()602 void QLowEnergyControllerPrivateBluez::establishL2cpClientSocket()
603 {
604 //we are already in Connecting state
605
606 l2cpSocket = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol, this);
607 connect(l2cpSocket, SIGNAL(connected()), this, SLOT(l2cpConnected()));
608 connect(l2cpSocket, SIGNAL(disconnected()), this, SLOT(l2cpDisconnected()));
609 connect(l2cpSocket, SIGNAL(error(QBluetoothSocket::SocketError)),
610 this, SLOT(l2cpErrorChanged(QBluetoothSocket::SocketError)));
611 connect(l2cpSocket, SIGNAL(readyRead()), this, SLOT(l2cpReadyRead()));
612
613 quint32 addressTypeToUse = (addressType == QLowEnergyController::PublicAddress)
614 ? BDADDR_LE_PUBLIC : BDADDR_LE_RANDOM;
615 if (BluetoothManagement::instance()->isMonitoringEnabled()) {
616 // if monitoring is possible and it's private then we force it to the relevant option
617 if (BluetoothManagement::instance()->isAddressRandom(remoteDevice)) {
618 addressTypeToUse = BDADDR_LE_RANDOM;
619 }
620 }
621
622 qCDebug(QT_BT_BLUEZ) << "addresstypeToUse:"
623 << (addressTypeToUse == BDADDR_LE_RANDOM
624 ? QStringLiteral("Random") : QStringLiteral("Public"));
625
626 l2cpSocket->d_ptr->lowEnergySocketType = addressTypeToUse;
627
628 int sockfd = l2cpSocket->socketDescriptor();
629 if (sockfd < 0) {
630 qCWarning(QT_BT_BLUEZ) << "l2cp socket not initialised";
631 setError(QLowEnergyController::ConnectionError);
632 setState(QLowEnergyController::UnconnectedState);
633 return;
634 }
635
636 struct sockaddr_l2 addr;
637 memset(&addr, 0, sizeof(addr));
638 addr.l2_family = AF_BLUETOOTH;
639 addr.l2_cid = htobs(ATTRIBUTE_CHANNEL_ID);
640 addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
641 convertAddress(localAdapter.toUInt64(), addr.l2_bdaddr.b);
642
643 // bind the socket to the local device
644 if (::bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
645 qCWarning(QT_BT_BLUEZ) << qt_error_string(errno);
646 setError(QLowEnergyController::ConnectionError);
647 setState(QLowEnergyController::UnconnectedState);
648 return;
649 }
650
651 // connect
652 // Unbuffered mode required to separate each GATT packet
653 l2cpSocket->connectToService(remoteDevice, ATTRIBUTE_CHANNEL_ID,
654 QIODevice::ReadWrite | QIODevice::Unbuffered);
655 loadSigningDataIfNecessary(LocalSigningKey);
656 }
657
createServicesForCentralIfRequired()658 void QLowEnergyControllerPrivateBluez::createServicesForCentralIfRequired()
659 {
660 bool ok = false;
661 int value = qEnvironmentVariableIntValue("QT_DEFAULT_CENTRAL_SERVICES", &ok);
662 if (Q_UNLIKELY(ok && value == 0))
663 return; //nothing to do
664
665 //do not add the services each time we start a connection
666 if (localServices.contains(QBluetoothUuid(QBluetoothUuid::GenericAccess)))
667 return;
668
669 qCDebug(QT_BT_BLUEZ) << "Creating default GAP/GATT services";
670
671 //populate Generic Access service
672 //for now the values are static
673 QLowEnergyServiceData gapServiceData;
674 gapServiceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
675 gapServiceData.setUuid(QBluetoothUuid::GenericAccess);
676
677 QLowEnergyCharacteristicData gapDeviceName;
678 gapDeviceName.setUuid(QBluetoothUuid::DeviceName);
679 gapDeviceName.setProperties(QLowEnergyCharacteristic::Read);
680
681 QBluetoothLocalDevice mainAdapter;
682 gapDeviceName.setValue(mainAdapter.name().toLatin1()); //static name
683
684 QLowEnergyCharacteristicData gapAppearance;
685 gapAppearance.setUuid(QBluetoothUuid::Appearance);
686 gapAppearance.setProperties(QLowEnergyCharacteristic::Read);
687 gapAppearance.setValue(QByteArray::fromHex("80")); // Generic Computer (0x80)
688
689 QLowEnergyCharacteristicData gapPrivacyFlag;
690 gapPrivacyFlag.setUuid(QBluetoothUuid::PeripheralPrivacyFlag);
691 gapPrivacyFlag.setProperties(QLowEnergyCharacteristic::Read);
692 gapPrivacyFlag.setValue(QByteArray::fromHex("00")); // disable privacy
693
694 gapServiceData.addCharacteristic(gapDeviceName);
695 gapServiceData.addCharacteristic(gapAppearance);
696 gapServiceData.addCharacteristic(gapPrivacyFlag);
697
698 Q_Q(QLowEnergyController);
699 QLowEnergyService *service = addServiceHelper(gapServiceData);
700 if (service)
701 service->setParent(q);
702
703 QLowEnergyServiceData gattServiceData;
704 gattServiceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
705 gattServiceData.setUuid(QBluetoothUuid::GenericAttribute);
706
707 QLowEnergyCharacteristicData serviceChangedChar;
708 serviceChangedChar.setUuid(QBluetoothUuid::ServiceChanged);
709 serviceChangedChar.setProperties(QLowEnergyCharacteristic::Indicate);
710 //arbitrary range of 2 bit handle range (1-4
711 serviceChangedChar.setValue(QByteArray::fromHex("0104"));
712
713 const QLowEnergyDescriptorData clientConfig(
714 QBluetoothUuid::ClientCharacteristicConfiguration,
715 QByteArray(2, 0));
716 serviceChangedChar.addDescriptor(clientConfig);
717 gattServiceData.addCharacteristic(serviceChangedChar);
718
719 service = addServiceHelper(gattServiceData);
720 if (service)
721 service->setParent(q);
722 }
723
l2cpConnected()724 void QLowEnergyControllerPrivateBluez::l2cpConnected()
725 {
726 Q_Q(QLowEnergyController);
727
728 securityLevelValue = securityLevel();
729 exchangeMTU();
730
731 setState(QLowEnergyController::ConnectedState);
732 emit q->connected();
733 }
734
disconnectFromDevice()735 void QLowEnergyControllerPrivateBluez::disconnectFromDevice()
736 {
737 setState(QLowEnergyController::ClosingState);
738 if (l2cpSocket)
739 l2cpSocket->close();
740 resetController();
741
742 // this may happen when RemoteDeviceManager::JobType::JobDisconnectDevice
743 // is pending.
744 if (!l2cpSocket) {
745 qWarning(QT_BT_BLUEZ) << "Unexpected closure of device. Cleaning up internal states.";
746 l2cpDisconnected();
747 }
748 }
749
l2cpDisconnected()750 void QLowEnergyControllerPrivateBluez::l2cpDisconnected()
751 {
752 Q_Q(QLowEnergyController);
753
754 if (role == QLowEnergyController::PeripheralRole) {
755 storeClientConfigurations();
756 remoteDevice.clear();
757 remoteName.clear();
758 }
759 invalidateServices();
760 resetController();
761 setState(QLowEnergyController::UnconnectedState);
762 emit q->disconnected();
763 }
764
l2cpErrorChanged(QBluetoothSocket::SocketError e)765 void QLowEnergyControllerPrivateBluez::l2cpErrorChanged(QBluetoothSocket::SocketError e)
766 {
767 switch (e) {
768 case QBluetoothSocket::HostNotFoundError:
769 setError(QLowEnergyController::UnknownRemoteDeviceError);
770 qCDebug(QT_BT_BLUEZ) << "The passed remote device address cannot be found";
771 break;
772 case QBluetoothSocket::NetworkError:
773 setError(QLowEnergyController::NetworkError);
774 qCDebug(QT_BT_BLUEZ) << "Network IO error while talking to LE device";
775 break;
776 case QBluetoothSocket::RemoteHostClosedError:
777 setError(QLowEnergyController::RemoteHostClosedError);
778 qCDebug(QT_BT_BLUEZ) << "Remote host closed the connection";
779 break;
780 case QBluetoothSocket::UnknownSocketError:
781 case QBluetoothSocket::UnsupportedProtocolError:
782 case QBluetoothSocket::OperationError:
783 case QBluetoothSocket::ServiceNotFoundError:
784 default:
785 // these errors shouldn't happen -> as it means
786 // the code in this file has bugs
787 qCDebug(QT_BT_BLUEZ) << "Unknown l2cp socket error: " << e << l2cpSocket->errorString();
788 setError(QLowEnergyController::UnknownError);
789 break;
790 }
791
792 invalidateServices();
793 resetController();
794 setState(QLowEnergyController::UnconnectedState);
795 }
796
797
resetController()798 void QLowEnergyControllerPrivateBluez::resetController()
799 {
800 openRequests.clear();
801 openPrepareWriteRequests.clear();
802 scheduledIndications.clear();
803 indicationInFlight = false;
804 requestPending = false;
805 encryptionChangePending = false;
806 receivedMtuExchangeRequest = false;
807 mtuSize = ATT_DEFAULT_LE_MTU;
808 securityLevelValue = -1;
809 connectionHandle = 0;
810
811 if (role == QLowEnergyController::PeripheralRole) {
812 // public API behavior requires stop of advertisement
813 if (advertiser) {
814 advertiser->stopAdvertising();
815 delete advertiser;
816 advertiser = nullptr;
817 }
818 localAttributes.clear();
819 }
820 }
821
restartRequestTimer()822 void QLowEnergyControllerPrivateBluez::restartRequestTimer()
823 {
824 if (!requestTimer)
825 return;
826
827 if (gattRequestTimeout > 0)
828 requestTimer->start(gattRequestTimeout);
829 }
830
l2cpReadyRead()831 void QLowEnergyControllerPrivateBluez::l2cpReadyRead()
832 {
833 const QByteArray incomingPacket = l2cpSocket->readAll();
834 qCDebug(QT_BT_BLUEZ) << "Received size:" << incomingPacket.size() << "data:"
835 << incomingPacket.toHex();
836 if (incomingPacket.isEmpty())
837 return;
838
839 const quint8 command = incomingPacket.constData()[0];
840 switch (command) {
841 case ATT_OP_HANDLE_VAL_NOTIFICATION:
842 {
843 processUnsolicitedReply(incomingPacket);
844 return;
845 }
846 case ATT_OP_HANDLE_VAL_INDICATION:
847 {
848 //send confirmation
849 QByteArray packet;
850 packet.append(static_cast<char>(ATT_OP_HANDLE_VAL_CONFIRMATION));
851 sendPacket(packet);
852
853 processUnsolicitedReply(incomingPacket);
854 return;
855 }
856 //--------------------------------------------------
857 // Peripheral side packet handling
858 case ATT_OP_EXCHANGE_MTU_REQUEST:
859 handleExchangeMtuRequest(incomingPacket);
860 return;
861 case ATT_OP_FIND_INFORMATION_REQUEST:
862 handleFindInformationRequest(incomingPacket);
863 return;
864 case ATT_OP_FIND_BY_TYPE_VALUE_REQUEST:
865 handleFindByTypeValueRequest(incomingPacket);
866 return;
867 case ATT_OP_READ_BY_TYPE_REQUEST:
868 handleReadByTypeRequest(incomingPacket);
869 return;
870 case ATT_OP_READ_REQUEST:
871 handleReadRequest(incomingPacket);
872 return;
873 case ATT_OP_READ_BLOB_REQUEST:
874 handleReadBlobRequest(incomingPacket);
875 return;
876 case ATT_OP_READ_MULTIPLE_REQUEST:
877 handleReadMultipleRequest(incomingPacket);
878 return;
879 case ATT_OP_READ_BY_GROUP_REQUEST:
880 handleReadByGroupTypeRequest(incomingPacket);
881 return;
882 case ATT_OP_WRITE_REQUEST:
883 case ATT_OP_WRITE_COMMAND:
884 case ATT_OP_SIGNED_WRITE_COMMAND:
885 handleWriteRequestOrCommand(incomingPacket);
886 return;
887 case ATT_OP_PREPARE_WRITE_REQUEST:
888 handlePrepareWriteRequest(incomingPacket);
889 return;
890 case ATT_OP_EXECUTE_WRITE_REQUEST:
891 handleExecuteWriteRequest(incomingPacket);
892 return;
893 case ATT_OP_HANDLE_VAL_CONFIRMATION:
894 if (indicationInFlight) {
895 indicationInFlight = false;
896 sendNextIndication();
897 } else {
898 qCWarning(QT_BT_BLUEZ) << "received unexpected handle value confirmation";
899 }
900 return;
901 //--------------------------------------------------
902 default:
903 //only solicited replies finish pending requests
904 requestPending = false;
905 break;
906 }
907
908 if (openRequests.isEmpty()) {
909 qCWarning(QT_BT_BLUEZ) << "Received unexpected packet from peer, disconnecting.";
910 disconnectFromDevice();
911 return;
912 }
913
914 const Request request = openRequests.dequeue();
915 processReply(request, incomingPacket);
916
917 sendNextPendingRequest();
918 }
919
920 /*!
921 * Called when the request for socket encryption has been
922 * processed by the kernel. Such requests take time as the kernel
923 * has to renegotiate the link parameters with the remote device.
924 *
925 * Therefore any such request delays the pending ATT commands until this
926 * callback is called. The first pending request in the queue is the request
927 * that triggered the encryption request.
928 */
encryptionChangedEvent(const QBluetoothAddress & address,bool wasSuccess)929 void QLowEnergyControllerPrivateBluez::encryptionChangedEvent(
930 const QBluetoothAddress &address, bool wasSuccess)
931 {
932 if (!encryptionChangePending) // somebody else caused change event
933 return;
934
935 if (remoteDevice != address)
936 return;
937
938 securityLevelValue = securityLevel();
939
940 // On success continue to process ATT command queue
941 if (!wasSuccess) {
942 // We could not increase the security of the link
943 // The next request was requeued due to security error
944 // skip it to avoid endless loop of security negotiations
945 Q_ASSERT(!openRequests.isEmpty());
946 Request failedRequest = openRequests.takeFirst();
947
948 if (failedRequest.command == ATT_OP_WRITE_REQUEST) {
949 // Failing write requests trigger some sort of response
950 uint ref = failedRequest.reference.toUInt();
951 const QLowEnergyHandle charHandle = (ref & 0xffff);
952 const QLowEnergyHandle descriptorHandle = ((ref >> 16) & 0xffff);
953
954 QSharedPointer<QLowEnergyServicePrivate> service
955 = serviceForHandle(charHandle);
956 if (!service.isNull() && service->characteristicList.contains(charHandle)) {
957 if (!descriptorHandle)
958 service->setError(QLowEnergyService::CharacteristicWriteError);
959 else
960 service->setError(QLowEnergyService::DescriptorWriteError);
961 }
962 } else if (failedRequest.command == ATT_OP_PREPARE_WRITE_REQUEST) {
963 uint handleData = failedRequest.reference.toUInt();
964 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
965 const QByteArray newValue = failedRequest.reference2.toByteArray();
966
967 // Prepare command failed, cancel pending prepare queue on
968 // the device. The appropriate (Descriptor|Characteristic)WriteError
969 // is emitted too once the execute write request comes through
970 sendExecuteWriteRequest(attrHandle, newValue, true);
971 }
972 }
973
974 encryptionChangePending = false;
975 sendNextPendingRequest();
976 }
977
sendPacket(const QByteArray & packet)978 void QLowEnergyControllerPrivateBluez::sendPacket(const QByteArray &packet)
979 {
980 qint64 result = l2cpSocket->write(packet.constData(),
981 packet.size());
982 // We ignore result == 0 which is likely to be caused by EAGAIN.
983 // This packet is effectively discarded but the controller can still recover
984
985 if (result == -1) {
986 qCDebug(QT_BT_BLUEZ) << "Cannot write L2CP packet:" << hex
987 << packet.toHex()
988 << l2cpSocket->errorString();
989 setError(QLowEnergyController::NetworkError);
990 } else if (result < packet.size()) {
991 qCWarning(QT_BT_BLUEZ) << "L2CP write request incomplete:"
992 << result << "of" << packet.size();
993 }
994
995 }
996
sendNextPendingRequest()997 void QLowEnergyControllerPrivateBluez::sendNextPendingRequest()
998 {
999 if (openRequests.isEmpty() || requestPending || encryptionChangePending)
1000 return;
1001
1002 const Request &request = openRequests.head();
1003 // qCDebug(QT_BT_BLUEZ) << "Sending request, type:" << hex << request.command
1004 // << request.payload.toHex();
1005
1006 requestPending = true;
1007 restartRequestTimer();
1008 sendPacket(request.payload);
1009 }
1010
parseReadByTypeCharDiscovery(QLowEnergyServicePrivate::CharData * charData,const char * data,quint16 elementLength)1011 QLowEnergyHandle parseReadByTypeCharDiscovery(
1012 QLowEnergyServicePrivate::CharData *charData,
1013 const char *data, quint16 elementLength)
1014 {
1015 Q_ASSERT(charData);
1016 Q_ASSERT(data);
1017
1018 QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
1019 charData->properties =
1020 (QLowEnergyCharacteristic::PropertyTypes)(data[2] & 0xff);
1021 charData->valueHandle = bt_get_le16(&data[3]);
1022
1023 if (elementLength == 7) // 16 bit uuid
1024 charData->uuid = QBluetoothUuid(bt_get_le16(&data[5]));
1025 else
1026 charData->uuid = convert_uuid128((quint128 *)&data[5]);
1027
1028 qCDebug(QT_BT_BLUEZ) << "Found handle:" << hex << attributeHandle
1029 << "properties:" << charData->properties
1030 << "value handle:" << charData->valueHandle
1031 << "uuid:" << charData->uuid.toString();
1032
1033 return attributeHandle;
1034 }
1035
parseReadByTypeIncludeDiscovery(QList<QBluetoothUuid> * foundServices,const char * data,quint16 elementLength)1036 QLowEnergyHandle parseReadByTypeIncludeDiscovery(
1037 QList<QBluetoothUuid> *foundServices,
1038 const char *data, quint16 elementLength)
1039 {
1040 Q_ASSERT(foundServices);
1041 Q_ASSERT(data);
1042
1043 QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
1044
1045 // the next 2 elements are not required as we have discovered
1046 // all (primary/secondary) services already. Now we are only
1047 // interested in their relationship to each other
1048 // data[2] -> included service start handle
1049 // data[4] -> included service end handle
1050
1051 if (elementLength == 8) //16 bit uuid
1052 foundServices->append(QBluetoothUuid(bt_get_le16(&data[6])));
1053 else
1054 foundServices->append(convert_uuid128((quint128 *) &data[6]));
1055
1056 qCDebug(QT_BT_BLUEZ) << "Found included service: " << hex
1057 << attributeHandle << "uuid:" << *foundServices;
1058
1059 return attributeHandle;
1060 }
1061
processReply(const Request & request,const QByteArray & response)1062 void QLowEnergyControllerPrivateBluez::processReply(
1063 const Request &request, const QByteArray &response)
1064 {
1065 Q_Q(QLowEnergyController);
1066
1067 quint8 command = response.constData()[0];
1068
1069 bool isErrorResponse = false;
1070 // if error occurred 2. byte is previous request type
1071 if (command == ATT_OP_ERROR_RESPONSE) {
1072 dumpErrorInformation(response);
1073 command = response.constData()[1];
1074 isErrorResponse = true;
1075 }
1076
1077 switch (command) {
1078 case ATT_OP_EXCHANGE_MTU_REQUEST: // in case of error
1079 case ATT_OP_EXCHANGE_MTU_RESPONSE:
1080 {
1081 Q_ASSERT(request.command == ATT_OP_EXCHANGE_MTU_REQUEST);
1082 if (isErrorResponse) {
1083 mtuSize = ATT_DEFAULT_LE_MTU;
1084 break;
1085 }
1086
1087 const char *data = response.constData();
1088 quint16 mtu = bt_get_le16(&data[1]);
1089 mtuSize = mtu;
1090 if (mtuSize < ATT_DEFAULT_LE_MTU)
1091 mtuSize = ATT_DEFAULT_LE_MTU;
1092
1093 qCDebug(QT_BT_BLUEZ) << "Server MTU:" << mtu << "resulting mtu:" << mtuSize;
1094 }
1095 break;
1096 case ATT_OP_READ_BY_GROUP_REQUEST: // in case of error
1097 case ATT_OP_READ_BY_GROUP_RESPONSE:
1098 {
1099 // Discovering services
1100 Q_ASSERT(request.command == ATT_OP_READ_BY_GROUP_REQUEST);
1101
1102 const quint16 type = request.reference.toUInt();
1103
1104 if (isErrorResponse) {
1105 if (type == GATT_SECONDARY_SERVICE) {
1106 setState(QLowEnergyController::DiscoveredState);
1107 q->discoveryFinished();
1108 } else { // search for secondary services
1109 sendReadByGroupRequest(0x0001, 0xFFFF, GATT_SECONDARY_SERVICE);
1110 }
1111 break;
1112 }
1113
1114 QLowEnergyHandle start = 0, end = 0;
1115 const quint16 elementLength = response.constData()[1];
1116 const quint16 numElements = (response.size() - 2) / elementLength;
1117 quint16 offset = 2;
1118 const char *data = response.constData();
1119 for (int i = 0; i < numElements; i++) {
1120 start = bt_get_le16(&data[offset]);
1121 end = bt_get_le16(&data[offset+2]);
1122
1123 QBluetoothUuid uuid;
1124 if (elementLength == 6) //16 bit uuid
1125 uuid = QBluetoothUuid(bt_get_le16(&data[offset+4]));
1126 else if (elementLength == 20) //128 bit uuid
1127 uuid = convert_uuid128((quint128 *)&data[offset+4]);
1128 //else -> do nothing
1129
1130 offset += elementLength;
1131
1132
1133 qCDebug(QT_BT_BLUEZ) << "Found uuid:" << uuid << "start handle:" << hex
1134 << start << "end handle:" << end;
1135
1136 QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate();
1137 priv->uuid = uuid;
1138 priv->startHandle = start;
1139 priv->endHandle = end;
1140 if (type != GATT_PRIMARY_SERVICE) //unset PrimaryService bit
1141 priv->type &= ~QLowEnergyService::PrimaryService;
1142 priv->setController(this);
1143
1144 QSharedPointer<QLowEnergyServicePrivate> pointer(priv);
1145
1146 serviceList.insert(uuid, pointer);
1147 emit q->serviceDiscovered(uuid);
1148 }
1149
1150 if (end != 0xFFFF) {
1151 sendReadByGroupRequest(end+1, 0xFFFF, type);
1152 } else {
1153 if (type == GATT_SECONDARY_SERVICE) {
1154 setState(QLowEnergyController::DiscoveredState);
1155 emit q->discoveryFinished();
1156 } else { // search for secondary services
1157 sendReadByGroupRequest(0x0001, 0xFFFF, GATT_SECONDARY_SERVICE);
1158 }
1159 }
1160 }
1161 break;
1162 case ATT_OP_READ_BY_TYPE_REQUEST: //in case of error
1163 case ATT_OP_READ_BY_TYPE_RESPONSE:
1164 {
1165 // Discovering characteristics
1166 Q_ASSERT(request.command == ATT_OP_READ_BY_TYPE_REQUEST);
1167
1168 QSharedPointer<QLowEnergyServicePrivate> p =
1169 request.reference.value<QSharedPointer<QLowEnergyServicePrivate> >();
1170 const quint16 attributeType = request.reference2.toUInt();
1171
1172 if (isErrorResponse) {
1173 if (attributeType == GATT_CHARACTERISTIC) {
1174 // we reached end of service handle
1175 // just finished up characteristic discovery
1176 // continue with values of characteristics
1177 if (!p->characteristicList.isEmpty()) {
1178 readServiceValues(p->uuid, true);
1179 } else {
1180 // discovery finished since the service doesn't have any
1181 // characteristics
1182 p->setState(QLowEnergyService::ServiceDiscovered);
1183 }
1184 } else if (attributeType == GATT_INCLUDED_SERVICE) {
1185 // finished up include discovery
1186 // continue with characteristic discovery
1187 sendReadByTypeRequest(p, p->startHandle, GATT_CHARACTERISTIC);
1188 }
1189 break;
1190 }
1191
1192 /* packet format:
1193 * if GATT_CHARACTERISTIC discovery
1194 * <opcode><elementLength>
1195 * [<handle><property><charHandle><uuid>]+
1196 *
1197 * if GATT_INCLUDE discovery
1198 * <opcode><elementLength>
1199 * [<handle><startHandle_included><endHandle_included><uuid>]+
1200 *
1201 * The uuid can be 16 or 128 bit.
1202 */
1203 QLowEnergyHandle lastHandle;
1204 const quint16 elementLength = response.constData()[1];
1205 const quint16 numElements = (response.size() - 2) / elementLength;
1206 quint16 offset = 2;
1207 const char *data = response.constData();
1208 for (int i = 0; i < numElements; i++) {
1209 if (attributeType == GATT_CHARACTERISTIC) {
1210 QLowEnergyServicePrivate::CharData characteristic;
1211 lastHandle = parseReadByTypeCharDiscovery(
1212 &characteristic, &data[offset], elementLength);
1213 p->characteristicList[lastHandle] = characteristic;
1214 offset += elementLength;
1215 } else if (attributeType == GATT_INCLUDED_SERVICE) {
1216 QList<QBluetoothUuid> includedServices;
1217 lastHandle = parseReadByTypeIncludeDiscovery(
1218 &includedServices, &data[offset], elementLength);
1219 p->includedServices = includedServices;
1220 for (const QBluetoothUuid &uuid : qAsConst(includedServices)) {
1221 if (serviceList.contains(uuid))
1222 serviceList[uuid]->type |= QLowEnergyService::IncludedService;
1223 }
1224 }
1225 }
1226
1227 if (lastHandle + 1 < p->endHandle) { // more chars to discover
1228 sendReadByTypeRequest(p, lastHandle + 1, attributeType);
1229 } else {
1230 if (attributeType == GATT_INCLUDED_SERVICE)
1231 sendReadByTypeRequest(p, p->startHandle, GATT_CHARACTERISTIC);
1232 else
1233 readServiceValues(p->uuid, true);
1234 }
1235 }
1236 break;
1237 case ATT_OP_READ_REQUEST: //error case
1238 case ATT_OP_READ_RESPONSE:
1239 {
1240 //Reading characteristics and descriptors
1241 Q_ASSERT(request.command == ATT_OP_READ_REQUEST);
1242
1243 uint handleData = request.reference.toUInt();
1244 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1245 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1246
1247 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1248 Q_ASSERT(!service.isNull());
1249 bool isServiceDiscoveryRun
1250 = !(service->state == QLowEnergyService::ServiceDiscovered);
1251
1252 if (isErrorResponse) {
1253 Q_ASSERT(!encryptionChangePending);
1254 encryptionChangePending = increaseEncryptLevelfRequired(response.constData()[4]);
1255 if (encryptionChangePending) {
1256 // Just requested a security level change.
1257 // Retry the same command again once the change has happened
1258 openRequests.prepend(request);
1259 break;
1260 } else if (!isServiceDiscoveryRun) {
1261 // not encryption problem -> abort readCharacteristic()/readDescriptor() run
1262 if (!descriptorHandle)
1263 service->setError(QLowEnergyService::CharacteristicReadError);
1264 else
1265 service->setError(QLowEnergyService::DescriptorReadError);
1266 }
1267 } else {
1268 if (!descriptorHandle)
1269 updateValueOfCharacteristic(charHandle, response.mid(1), NEW_VALUE);
1270 else
1271 updateValueOfDescriptor(charHandle, descriptorHandle,
1272 response.mid(1), NEW_VALUE);
1273
1274 if (response.size() == mtuSize) {
1275 qCDebug(QT_BT_BLUEZ) << "Switching to blob reads for"
1276 << charHandle << descriptorHandle
1277 << service->characteristicList[charHandle].uuid.toString();
1278 // Potentially more data -> switch to blob reads
1279 readServiceValuesByOffset(handleData, mtuSize-1,
1280 request.reference2.toBool());
1281 break;
1282 } else if (!isServiceDiscoveryRun) {
1283 // readCharacteristic() or readDescriptor() ongoing
1284 if (!descriptorHandle) {
1285 QLowEnergyCharacteristic ch(service, charHandle);
1286 emit service->characteristicRead(ch, response.mid(1));
1287 } else {
1288 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1289 emit service->descriptorRead(descriptor, response.mid(1));
1290 }
1291 break;
1292 }
1293 }
1294
1295 if (request.reference2.toBool() && isServiceDiscoveryRun) {
1296 // we only run into this code path during the initial service discovery
1297 // and not when processing readCharacteristics() after service discovery
1298
1299 //last characteristic -> progress to descriptor discovery
1300 //last descriptor -> service discovery is done
1301 if (!descriptorHandle)
1302 discoverServiceDescriptors(service->uuid);
1303 else
1304 service->setState(QLowEnergyService::ServiceDiscovered);
1305 }
1306 }
1307 break;
1308 case ATT_OP_READ_BLOB_REQUEST: //error case
1309 case ATT_OP_READ_BLOB_RESPONSE:
1310 {
1311 //Reading characteristic or descriptor with value longer value than MTU
1312 Q_ASSERT(request.command == ATT_OP_READ_BLOB_REQUEST);
1313
1314 uint handleData = request.reference.toUInt();
1315 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1316 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1317
1318 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1319 Q_ASSERT(!service.isNull());
1320
1321 /*
1322 * READ_BLOB does not require encryption setup code. BLOB commands
1323 * are only issued after read request if the read request is too long
1324 * for single MTU. The preceding read request would have triggered
1325 * the setup of the encryption already.
1326 */
1327 if (!isErrorResponse) {
1328 quint16 length = 0;
1329 if (!descriptorHandle)
1330 length = updateValueOfCharacteristic(charHandle, response.mid(1), APPEND_VALUE);
1331 else
1332 length = updateValueOfDescriptor(charHandle, descriptorHandle,
1333 response.mid(1), APPEND_VALUE);
1334
1335 if (response.size() == mtuSize) {
1336 readServiceValuesByOffset(handleData, length,
1337 request.reference2.toBool());
1338 break;
1339 } else if (service->state == QLowEnergyService::ServiceDiscovered) {
1340 // readCharacteristic() or readDescriptor() ongoing
1341 if (!descriptorHandle) {
1342 QLowEnergyCharacteristic ch(service, charHandle);
1343 emit service->characteristicRead(ch, ch.value());
1344 } else {
1345 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1346 emit service->descriptorRead(descriptor, descriptor.value());
1347 }
1348 break;
1349 }
1350 } else {
1351 qWarning() << "READ BLOB for char:" << charHandle
1352 << "descriptor:" << descriptorHandle << "on service"
1353 << service->uuid.toString() << "failed (service discovery run:"
1354 << (service->state == QLowEnergyService::ServiceDiscovered) << ")";
1355 }
1356
1357 if (request.reference2.toBool()) {
1358 //last overlong characteristic -> progress to descriptor discovery
1359 //last overlong descriptor -> service discovery is done
1360
1361 if (!descriptorHandle)
1362 discoverServiceDescriptors(service->uuid);
1363 else
1364 service->setState(QLowEnergyService::ServiceDiscovered);
1365 }
1366
1367 }
1368 break;
1369 case ATT_OP_FIND_INFORMATION_REQUEST: //error case
1370 case ATT_OP_FIND_INFORMATION_RESPONSE:
1371 {
1372 //Discovering descriptors
1373 Q_ASSERT(request.command == ATT_OP_FIND_INFORMATION_REQUEST);
1374
1375 /* packet format:
1376 * <opcode><format>[<handle><descriptor_uuid>]+
1377 *
1378 * The uuid can be 16 or 128 bit which is indicated by format.
1379 */
1380
1381 QList<QLowEnergyHandle> keys = request.reference.value<QList<QLowEnergyHandle> >();
1382 if (keys.isEmpty()) {
1383 qCWarning(QT_BT_BLUEZ) << "Descriptor discovery for unknown characteristic received";
1384 break;
1385 }
1386 QLowEnergyHandle charHandle = keys.first();
1387
1388 QSharedPointer<QLowEnergyServicePrivate> p =
1389 serviceForHandle(charHandle);
1390 Q_ASSERT(!p.isNull());
1391
1392 if (isErrorResponse) {
1393 if (keys.count() == 1) {
1394 // no more descriptors to discover
1395 readServiceValues(p->uuid, false); //read descriptor values
1396 } else {
1397 // hop to the next descriptor
1398 keys.removeFirst();
1399 discoverNextDescriptor(p, keys, keys.first());
1400 }
1401 break;
1402 }
1403
1404 const quint8 format = response[1];
1405 quint16 elementLength;
1406 switch (format) {
1407 case 0x01:
1408 elementLength = 2 + 2; //sizeof(QLowEnergyHandle) + 16bit uuid
1409 break;
1410 case 0x02:
1411 elementLength = 2 + 16; //sizeof(QLowEnergyHandle) + 128bit uuid
1412 break;
1413 default:
1414 qCWarning(QT_BT_BLUEZ) << "Unknown format in FIND_INFORMATION_RESPONSE";
1415 return;
1416 }
1417
1418 const quint16 numElements = (response.size() - 2) / elementLength;
1419
1420 quint16 offset = 2;
1421 QLowEnergyHandle descriptorHandle;
1422 QBluetoothUuid uuid;
1423 const char *data = response.constData();
1424 for (int i = 0; i < numElements; i++) {
1425 descriptorHandle = bt_get_le16(&data[offset]);
1426
1427 if (format == 0x01)
1428 uuid = QBluetoothUuid(bt_get_le16(&data[offset+2]));
1429 else if (format == 0x02)
1430 uuid = convert_uuid128((quint128 *)&data[offset+2]);
1431
1432 offset += elementLength;
1433
1434 // ignore all attributes which are not of type descriptor
1435 // examples are the characteristics value or
1436 bool ok = false;
1437 quint16 shortUuid = uuid.toUInt16(&ok);
1438 if (ok && shortUuid >= QLowEnergyServicePrivate::PrimaryService
1439 && shortUuid <= QLowEnergyServicePrivate::Characteristic){
1440 qCDebug(QT_BT_BLUEZ) << "Suppressing primary/characteristic" << hex << shortUuid;
1441 continue;
1442 }
1443
1444 // ignore value handle
1445 if (descriptorHandle == p->characteristicList[charHandle].valueHandle) {
1446 qCDebug(QT_BT_BLUEZ) << "Suppressing char handle" << hex << descriptorHandle;
1447 continue;
1448 }
1449
1450 QLowEnergyServicePrivate::DescData data;
1451 data.uuid = uuid;
1452 p->characteristicList[charHandle].descriptorList.insert(
1453 descriptorHandle, data);
1454
1455 qCDebug(QT_BT_BLUEZ) << "Descriptor found, uuid:"
1456 << uuid.toString()
1457 << "descriptor handle:" << hex << descriptorHandle;
1458 }
1459
1460 const QLowEnergyHandle nextPotentialHandle = descriptorHandle + 1;
1461 if (keys.count() == 1) {
1462 // Reached last characteristic of service
1463
1464 // The endhandle of a service is always the last handle of
1465 // the current service. We must either continue until we have reached
1466 // the starting handle of the next service (endHandle+1) or
1467 // the last physical handle address (0xffff). Note that
1468 // the endHandle of the last service on the device is 0xffff.
1469
1470 if ((p->endHandle != 0xffff && nextPotentialHandle >= p->endHandle + 1)
1471 || (descriptorHandle == 0xffff)) {
1472 keys.removeFirst();
1473 // last descriptor of last characteristic found
1474 // continue with reading descriptor values
1475 readServiceValues(p->uuid, false);
1476 } else {
1477 discoverNextDescriptor(p, keys, nextPotentialHandle);
1478 }
1479 } else {
1480 if (nextPotentialHandle >= keys[1]) //reached next char
1481 keys.removeFirst();
1482 discoverNextDescriptor(p, keys, nextPotentialHandle);
1483 }
1484 }
1485 break;
1486 case ATT_OP_WRITE_REQUEST: //error case
1487 case ATT_OP_WRITE_RESPONSE:
1488 {
1489 //Write command response
1490 Q_ASSERT(request.command == ATT_OP_WRITE_REQUEST);
1491
1492 uint ref = request.reference.toUInt();
1493 const QLowEnergyHandle charHandle = (ref & 0xffff);
1494 const QLowEnergyHandle descriptorHandle = ((ref >> 16) & 0xffff);
1495
1496 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1497 if (service.isNull() || !service->characteristicList.contains(charHandle))
1498 break;
1499
1500 if (isErrorResponse) {
1501 Q_ASSERT(!encryptionChangePending);
1502 encryptionChangePending = increaseEncryptLevelfRequired(response.constData()[4]);
1503 if (encryptionChangePending) {
1504 openRequests.prepend(request);
1505 break;
1506 }
1507
1508 if (!descriptorHandle)
1509 service->setError(QLowEnergyService::CharacteristicWriteError);
1510 else
1511 service->setError(QLowEnergyService::DescriptorWriteError);
1512 break;
1513 }
1514
1515 const QByteArray newValue = request.reference2.toByteArray();
1516 if (!descriptorHandle) {
1517 QLowEnergyCharacteristic ch(service, charHandle);
1518 if (ch.properties() & QLowEnergyCharacteristic::Read)
1519 updateValueOfCharacteristic(charHandle, newValue, NEW_VALUE);
1520 emit service->characteristicWritten(ch, newValue);
1521 } else {
1522 updateValueOfDescriptor(charHandle, descriptorHandle, newValue, NEW_VALUE);
1523 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1524 emit service->descriptorWritten(descriptor, newValue);
1525 }
1526 }
1527 break;
1528 case ATT_OP_PREPARE_WRITE_REQUEST: //error case
1529 case ATT_OP_PREPARE_WRITE_RESPONSE:
1530 {
1531 //Prepare write command response
1532 Q_ASSERT(request.command == ATT_OP_PREPARE_WRITE_REQUEST);
1533
1534 uint handleData = request.reference.toUInt();
1535 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
1536 const QByteArray newValue = request.reference2.toByteArray();
1537 const int writtenPayload = ((handleData >> 16) & 0xffff);
1538
1539 if (isErrorResponse) {
1540 Q_ASSERT(!encryptionChangePending);
1541 encryptionChangePending = increaseEncryptLevelfRequired(response.constData()[4]);
1542 if (encryptionChangePending) {
1543 openRequests.prepend(request);
1544 break;
1545 }
1546 //emits error on cancellation and aborts existing prepare reuqests
1547 sendExecuteWriteRequest(attrHandle, newValue, true);
1548 } else {
1549 if (writtenPayload < newValue.size()) {
1550 sendNextPrepareWriteRequest(attrHandle, newValue, writtenPayload);
1551 } else {
1552 sendExecuteWriteRequest(attrHandle, newValue, false);
1553 }
1554 }
1555 }
1556 break;
1557 case ATT_OP_EXECUTE_WRITE_REQUEST: //error case
1558 case ATT_OP_EXECUTE_WRITE_RESPONSE:
1559 {
1560 // right now used in connection with long characteristic/descriptor value writes
1561 // not catering for reliable writes
1562 Q_ASSERT(request.command == ATT_OP_EXECUTE_WRITE_REQUEST);
1563
1564 uint handleData = request.reference.toUInt();
1565 const QLowEnergyHandle attrHandle = handleData & 0xffff;
1566 bool wasCancellation = !((handleData >> 16) & 0xffff);
1567 const QByteArray newValue = request.reference2.toByteArray();
1568
1569 // is it a descriptor or characteristic?
1570 const QLowEnergyDescriptor descriptor = descriptorForHandle(attrHandle);
1571 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(attrHandle);
1572 Q_ASSERT(!service.isNull());
1573
1574 if (isErrorResponse || wasCancellation) {
1575 // charHandle == 0 -> cancellation
1576 if (descriptor.isValid())
1577 service->setError(QLowEnergyService::DescriptorWriteError);
1578 else
1579 service->setError(QLowEnergyService::CharacteristicWriteError);
1580 } else {
1581 if (descriptor.isValid()) {
1582 updateValueOfDescriptor(descriptor.characteristicHandle(),
1583 attrHandle, newValue, NEW_VALUE);
1584 emit service->descriptorWritten(descriptor, newValue);
1585 } else {
1586 QLowEnergyCharacteristic ch(service, attrHandle);
1587 if (ch.properties() & QLowEnergyCharacteristic::Read)
1588 updateValueOfCharacteristic(attrHandle, newValue, NEW_VALUE);
1589 emit service->characteristicWritten(ch, newValue);
1590 }
1591 }
1592 }
1593 break;
1594 default:
1595 qCDebug(QT_BT_BLUEZ) << "Unknown packet: " << response.toHex();
1596 break;
1597 }
1598 }
1599
discoverServices()1600 void QLowEnergyControllerPrivateBluez::discoverServices()
1601 {
1602 sendReadByGroupRequest(0x0001, 0xFFFF, GATT_PRIMARY_SERVICE);
1603 }
1604
sendReadByGroupRequest(QLowEnergyHandle start,QLowEnergyHandle end,quint16 type)1605 void QLowEnergyControllerPrivateBluez::sendReadByGroupRequest(
1606 QLowEnergyHandle start, QLowEnergyHandle end, quint16 type)
1607 {
1608 //call for primary and secondary services
1609 quint8 packet[GRP_TYPE_REQ_HEADER_SIZE];
1610
1611 packet[0] = ATT_OP_READ_BY_GROUP_REQUEST;
1612 putBtData(start, &packet[1]);
1613 putBtData(end, &packet[3]);
1614 putBtData(type, &packet[5]);
1615
1616 QByteArray data(GRP_TYPE_REQ_HEADER_SIZE, Qt::Uninitialized);
1617 memcpy(data.data(), packet, GRP_TYPE_REQ_HEADER_SIZE);
1618 qCDebug(QT_BT_BLUEZ) << "Sending read_by_group_type request, startHandle:" << hex
1619 << start << "endHandle:" << end << type;
1620
1621 Request request;
1622 request.payload = data;
1623 request.command = ATT_OP_READ_BY_GROUP_REQUEST;
1624 request.reference = type;
1625 openRequests.enqueue(request);
1626
1627 sendNextPendingRequest();
1628 }
1629
discoverServiceDetails(const QBluetoothUuid & service)1630 void QLowEnergyControllerPrivateBluez::discoverServiceDetails(const QBluetoothUuid &service)
1631 {
1632 if (!serviceList.contains(service)) {
1633 qCWarning(QT_BT_BLUEZ) << "Discovery of unknown service" << service.toString()
1634 << "not possible";
1635 return;
1636 }
1637
1638 QSharedPointer<QLowEnergyServicePrivate> serviceData = serviceList.value(service);
1639 serviceData->characteristicList.clear();
1640 sendReadByTypeRequest(serviceData, serviceData->startHandle, GATT_INCLUDED_SERVICE);
1641 }
1642
sendReadByTypeRequest(QSharedPointer<QLowEnergyServicePrivate> serviceData,QLowEnergyHandle nextHandle,quint16 attributeType)1643 void QLowEnergyControllerPrivateBluez::sendReadByTypeRequest(
1644 QSharedPointer<QLowEnergyServicePrivate> serviceData,
1645 QLowEnergyHandle nextHandle, quint16 attributeType)
1646 {
1647 quint8 packet[READ_BY_TYPE_REQ_HEADER_SIZE];
1648
1649 packet[0] = ATT_OP_READ_BY_TYPE_REQUEST;
1650 putBtData(nextHandle, &packet[1]);
1651 putBtData(serviceData->endHandle, &packet[3]);
1652 putBtData(attributeType, &packet[5]);
1653
1654 QByteArray data(READ_BY_TYPE_REQ_HEADER_SIZE, Qt::Uninitialized);
1655 memcpy(data.data(), packet, READ_BY_TYPE_REQ_HEADER_SIZE);
1656 qCDebug(QT_BT_BLUEZ) << "Sending read_by_type request, startHandle:" << hex
1657 << nextHandle << "endHandle:" << serviceData->endHandle
1658 << "type:" << attributeType << "packet:" << data.toHex();
1659
1660 Request request;
1661 request.payload = data;
1662 request.command = ATT_OP_READ_BY_TYPE_REQUEST;
1663 request.reference = QVariant::fromValue(serviceData);
1664 request.reference2 = attributeType;
1665 openRequests.enqueue(request);
1666
1667 sendNextPendingRequest();
1668 }
1669
1670 /*!
1671 \internal
1672
1673 Reads all values of specific characteristic and descriptor. This function is
1674 used during the initial service discovery process.
1675
1676 \a readCharacteristics determines whether we intend to read a characteristic;
1677 otherwise we read a descriptor.
1678 */
readServiceValues(const QBluetoothUuid & serviceUuid,bool readCharacteristics)1679 void QLowEnergyControllerPrivateBluez::readServiceValues(
1680 const QBluetoothUuid &serviceUuid, bool readCharacteristics)
1681 {
1682 quint8 packet[READ_REQUEST_HEADER_SIZE];
1683 if (QT_BT_BLUEZ().isDebugEnabled()) {
1684 if (readCharacteristics)
1685 qCDebug(QT_BT_BLUEZ) << "Reading all characteristic values for"
1686 << serviceUuid.toString();
1687 else
1688 qCDebug(QT_BT_BLUEZ) << "Reading all descriptor values for"
1689 << serviceUuid.toString();
1690 }
1691
1692 QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid);
1693
1694 // pair.first -> target attribute
1695 // pair.second -> context information for read request
1696 QPair<QLowEnergyHandle, quint32> pair;
1697
1698 // Create list of attribute handles which need to be read
1699 QList<QPair<QLowEnergyHandle, quint32> > targetHandles;
1700
1701 CharacteristicDataMap::const_iterator charIt = service->characteristicList.constBegin();
1702 for ( ; charIt != service->characteristicList.constEnd(); ++charIt) {
1703 const QLowEnergyHandle charHandle = charIt.key();
1704 const QLowEnergyServicePrivate::CharData &charDetails = charIt.value();
1705
1706 if (readCharacteristics) {
1707 // Collect handles of all characteristic value attributes
1708
1709 // Don't try to read writeOnly characteristic
1710 if (!(charDetails.properties & QLowEnergyCharacteristic::Read))
1711 continue;
1712
1713 pair.first = charDetails.valueHandle;
1714 pair.second = charHandle;
1715 targetHandles.append(pair);
1716
1717 } else {
1718 // Collect handles of all descriptor attributes
1719 DescriptorDataMap::const_iterator descIt = charDetails.descriptorList.constBegin();
1720 for ( ; descIt != charDetails.descriptorList.constEnd(); ++descIt) {
1721 const QLowEnergyHandle descriptorHandle = descIt.key();
1722
1723 pair.first = descriptorHandle;
1724 pair.second = (charHandle | (descriptorHandle << 16));
1725 targetHandles.append(pair);
1726 }
1727 }
1728 }
1729
1730
1731 if (targetHandles.isEmpty()) {
1732 if (readCharacteristics) {
1733 // none of the characteristics is readable
1734 // -> continue with descriptor discovery
1735 discoverServiceDescriptors(service->uuid);
1736 } else {
1737 // characteristic w/o descriptors
1738 service->setState(QLowEnergyService::ServiceDiscovered);
1739 }
1740 return;
1741 }
1742
1743 for (int i = 0; i < targetHandles.count(); i++) {
1744 pair = targetHandles.at(i);
1745 packet[0] = ATT_OP_READ_REQUEST;
1746 putBtData(pair.first, &packet[1]);
1747
1748 QByteArray data(READ_REQUEST_HEADER_SIZE, Qt::Uninitialized);
1749 memcpy(data.data(), packet, READ_REQUEST_HEADER_SIZE);
1750
1751 Request request;
1752 request.payload = data;
1753 request.command = ATT_OP_READ_REQUEST;
1754 request.reference = pair.second;
1755 // last entry?
1756 request.reference2 = QVariant((bool)(i + 1 == targetHandles.count()));
1757 openRequests.enqueue(request);
1758 }
1759
1760 sendNextPendingRequest();
1761 }
1762
1763 /*!
1764 \internal
1765
1766 This function is used when reading a handle value that is
1767 longer than the mtuSize.
1768
1769 The BLOB read request is prepended to the list of
1770 open requests to finish the current value read up before
1771 starting the next read request.
1772 */
readServiceValuesByOffset(uint handleData,quint16 offset,bool isLastValue)1773 void QLowEnergyControllerPrivateBluez::readServiceValuesByOffset(
1774 uint handleData, quint16 offset, bool isLastValue)
1775 {
1776 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1777 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1778
1779 QByteArray data(READ_BLOB_REQUEST_HEADER_SIZE, Qt::Uninitialized);
1780 data[0] = ATT_OP_READ_BLOB_REQUEST;
1781
1782 QLowEnergyHandle handleToRead = charHandle;
1783 if (descriptorHandle) {
1784 handleToRead = descriptorHandle;
1785 qCDebug(QT_BT_BLUEZ) << "Reading descriptor via blob request"
1786 << hex << descriptorHandle;
1787 } else {
1788 //charHandle is not the char's value handle
1789 QSharedPointer<QLowEnergyServicePrivate> service =
1790 serviceForHandle(charHandle);
1791 if (!service.isNull()
1792 && service->characteristicList.contains(charHandle)) {
1793 handleToRead = service->characteristicList[charHandle].valueHandle;
1794 qCDebug(QT_BT_BLUEZ) << "Reading characteristic via blob request"
1795 << hex << handleToRead;
1796 } else {
1797 Q_ASSERT(false);
1798 }
1799 }
1800
1801 putBtData(handleToRead, data.data() + 1);
1802 putBtData(offset, data.data() + 3);
1803
1804 Request request;
1805 request.payload = data;
1806 request.command = ATT_OP_READ_BLOB_REQUEST;
1807 request.reference = handleData;
1808 request.reference2 = isLastValue;
1809 openRequests.prepend(request);
1810 }
1811
discoverServiceDescriptors(const QBluetoothUuid & serviceUuid)1812 void QLowEnergyControllerPrivateBluez::discoverServiceDescriptors(
1813 const QBluetoothUuid &serviceUuid)
1814 {
1815 qCDebug(QT_BT_BLUEZ) << "Discovering descriptor values for"
1816 << serviceUuid.toString();
1817 QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid);
1818
1819 if (service->characteristicList.isEmpty()) { // service has no characteristics
1820 // implies that characteristic & descriptor discovery can be skipped
1821 service->setState(QLowEnergyService::ServiceDiscovered);
1822 return;
1823 }
1824
1825 // start handle of all known characteristics
1826 QList<QLowEnergyHandle> keys = service->characteristicList.keys();
1827 std::sort(keys.begin(), keys.end());
1828
1829 discoverNextDescriptor(service, keys, keys[0]);
1830 }
1831
processUnsolicitedReply(const QByteArray & payload)1832 void QLowEnergyControllerPrivateBluez::processUnsolicitedReply(const QByteArray &payload)
1833 {
1834 const char *data = payload.constData();
1835 bool isNotification = (data[0] == ATT_OP_HANDLE_VAL_NOTIFICATION);
1836 const QLowEnergyHandle changedHandle = bt_get_le16(&data[1]);
1837
1838 if (QT_BT_BLUEZ().isDebugEnabled()) {
1839 if (isNotification)
1840 qCDebug(QT_BT_BLUEZ) << "Change notification for handle" << hex << changedHandle;
1841 else
1842 qCDebug(QT_BT_BLUEZ) << "Change indication for handle" << hex << changedHandle;
1843 }
1844
1845 const QLowEnergyCharacteristic ch = characteristicForHandle(changedHandle);
1846 if (ch.isValid() && ch.handle() == changedHandle) {
1847 if (ch.properties() & QLowEnergyCharacteristic::Read)
1848 updateValueOfCharacteristic(ch.attributeHandle(), payload.mid(3), NEW_VALUE);
1849 emit ch.d_ptr->characteristicChanged(ch, payload.mid(3));
1850 } else {
1851 qCWarning(QT_BT_BLUEZ) << "Cannot find matching characteristic for "
1852 "notification/indication";
1853 }
1854 }
1855
exchangeMTU()1856 void QLowEnergyControllerPrivateBluez::exchangeMTU()
1857 {
1858 qCDebug(QT_BT_BLUEZ) << "Exchanging MTU";
1859
1860 quint8 packet[MTU_EXCHANGE_HEADER_SIZE];
1861 packet[0] = ATT_OP_EXCHANGE_MTU_REQUEST;
1862 putBtData(quint16(ATT_MAX_LE_MTU), &packet[1]);
1863
1864 QByteArray data(MTU_EXCHANGE_HEADER_SIZE, Qt::Uninitialized);
1865 memcpy(data.data(), packet, MTU_EXCHANGE_HEADER_SIZE);
1866
1867 Request request;
1868 request.payload = data;
1869 request.command = ATT_OP_EXCHANGE_MTU_REQUEST;
1870 openRequests.enqueue(request);
1871
1872 sendNextPendingRequest();
1873 }
1874
securityLevel() const1875 int QLowEnergyControllerPrivateBluez::securityLevel() const
1876 {
1877 int socket = l2cpSocket->socketDescriptor();
1878 if (socket < 0) {
1879 qCWarning(QT_BT_BLUEZ) << "Invalid l2cp socket, aborting getting of sec level";
1880 return -1;
1881 }
1882
1883 struct bt_security secData;
1884 socklen_t length = sizeof(secData);
1885 memset(&secData, 0, length);
1886
1887 if (getsockopt(socket, SOL_BLUETOOTH, BT_SECURITY, &secData, &length) == 0) {
1888 qCDebug(QT_BT_BLUEZ) << "Current l2cp sec level:" << secData.level;
1889 return secData.level;
1890 }
1891
1892 if (errno != ENOPROTOOPT) //older kernel, fall back to L2CAP_LM option
1893 return -1;
1894
1895 // cater for older kernels
1896 int optval;
1897 length = sizeof(optval);
1898 if (getsockopt(socket, SOL_L2CAP, L2CAP_LM, &optval, &length) == 0) {
1899 int level = BT_SECURITY_SDP;
1900 if (optval & L2CAP_LM_AUTH)
1901 level = BT_SECURITY_LOW;
1902 if (optval & L2CAP_LM_ENCRYPT)
1903 level = BT_SECURITY_MEDIUM;
1904 if (optval & L2CAP_LM_SECURE)
1905 level = BT_SECURITY_HIGH;
1906
1907 qDebug() << "Current l2cp sec level (old):" << level;
1908 return level;
1909 }
1910
1911 return -1;
1912 }
1913
setSecurityLevel(int level)1914 bool QLowEnergyControllerPrivateBluez::setSecurityLevel(int level)
1915 {
1916 if (level > BT_SECURITY_HIGH || level < BT_SECURITY_LOW)
1917 return false;
1918
1919 int socket = l2cpSocket->socketDescriptor();
1920 if (socket < 0) {
1921 qCWarning(QT_BT_BLUEZ) << "Invalid l2cp socket, aborting setting of sec level";
1922 return false;
1923 }
1924
1925 struct bt_security secData;
1926 socklen_t length = sizeof(secData);
1927 memset(&secData, 0, length);
1928 secData.level = level;
1929
1930 if (setsockopt(socket, SOL_BLUETOOTH, BT_SECURITY, &secData, length) == 0) {
1931 qCDebug(QT_BT_BLUEZ) << "Setting new l2cp sec level:" << secData.level;
1932 return true;
1933 }
1934
1935 if (errno != ENOPROTOOPT) //older kernel
1936 return false;
1937
1938 int optval = 0;
1939 switch (level) { // fall through intendeds
1940 case BT_SECURITY_HIGH:
1941 optval |= L2CAP_LM_SECURE;
1942 Q_FALLTHROUGH();
1943 case BT_SECURITY_MEDIUM:
1944 optval |= L2CAP_LM_ENCRYPT;
1945 Q_FALLTHROUGH();
1946 case BT_SECURITY_LOW:
1947 optval |= L2CAP_LM_AUTH;
1948 break;
1949 default:
1950 return false;
1951 }
1952
1953 if (setsockopt(socket, SOL_L2CAP, L2CAP_LM, &optval, sizeof(optval)) == 0) {
1954 qDebug(QT_BT_BLUEZ) << "Old l2cp sec level:" << optval;
1955 return true;
1956 }
1957
1958 return false;
1959 }
1960
discoverNextDescriptor(QSharedPointer<QLowEnergyServicePrivate> serviceData,const QList<QLowEnergyHandle> pendingCharHandles,const QLowEnergyHandle startingHandle)1961 void QLowEnergyControllerPrivateBluez::discoverNextDescriptor(
1962 QSharedPointer<QLowEnergyServicePrivate> serviceData,
1963 const QList<QLowEnergyHandle> pendingCharHandles,
1964 const QLowEnergyHandle startingHandle)
1965 {
1966 Q_ASSERT(!pendingCharHandles.isEmpty());
1967 Q_ASSERT(!serviceData.isNull());
1968
1969 qCDebug(QT_BT_BLUEZ) << "Sending find_info request" << hex
1970 << pendingCharHandles << startingHandle;
1971
1972 quint8 packet[FIND_INFO_REQUEST_HEADER_SIZE];
1973 packet[0] = ATT_OP_FIND_INFORMATION_REQUEST;
1974
1975 const QLowEnergyHandle charStartHandle = startingHandle;
1976 QLowEnergyHandle charEndHandle = 0;
1977 if (pendingCharHandles.count() == 1) //single characteristic
1978 charEndHandle = serviceData->endHandle;
1979 else
1980 charEndHandle = pendingCharHandles[1] - 1;
1981
1982 putBtData(charStartHandle, &packet[1]);
1983 putBtData(charEndHandle, &packet[3]);
1984
1985 QByteArray data(FIND_INFO_REQUEST_HEADER_SIZE, Qt::Uninitialized);
1986 memcpy(data.data(), packet, FIND_INFO_REQUEST_HEADER_SIZE);
1987
1988 Request request;
1989 request.payload = data;
1990 request.command = ATT_OP_FIND_INFORMATION_REQUEST;
1991 request.reference = QVariant::fromValue<QList<QLowEnergyHandle> >(pendingCharHandles);
1992 request.reference2 = startingHandle;
1993 openRequests.enqueue(request);
1994
1995 sendNextPendingRequest();
1996 }
1997
sendNextPrepareWriteRequest(const QLowEnergyHandle handle,const QByteArray & newValue,quint16 offset)1998 void QLowEnergyControllerPrivateBluez::sendNextPrepareWriteRequest(
1999 const QLowEnergyHandle handle, const QByteArray &newValue,
2000 quint16 offset)
2001 {
2002 // is it a descriptor or characteristic?
2003 QLowEnergyHandle targetHandle = 0;
2004 const QLowEnergyDescriptor descriptor = descriptorForHandle(handle);
2005 if (descriptor.isValid())
2006 targetHandle = descriptor.handle();
2007 else
2008 targetHandle = characteristicForHandle(handle).handle();
2009
2010 if (!targetHandle) {
2011 qCWarning(QT_BT_BLUEZ) << "sendNextPrepareWriteRequest cancelled due to invalid handle"
2012 << handle;
2013 return;
2014 }
2015
2016 quint8 packet[PREPARE_WRITE_HEADER_SIZE];
2017 packet[0] = ATT_OP_PREPARE_WRITE_REQUEST;
2018 putBtData(targetHandle, &packet[1]); // attribute handle
2019 putBtData(offset, &packet[3]); // offset into newValue
2020
2021 qCDebug(QT_BT_BLUEZ) << "Writing long characteristic (prepare):"
2022 << hex << handle;
2023
2024
2025 const int maxAvailablePayload = mtuSize - PREPARE_WRITE_HEADER_SIZE;
2026 const int requiredPayload = qMin(newValue.size() - offset, maxAvailablePayload);
2027 const int dataSize = PREPARE_WRITE_HEADER_SIZE + requiredPayload;
2028
2029 Q_ASSERT((offset + requiredPayload) <= newValue.size());
2030 Q_ASSERT(dataSize <= mtuSize);
2031
2032 QByteArray data(dataSize, Qt::Uninitialized);
2033 memcpy(data.data(), packet, PREPARE_WRITE_HEADER_SIZE);
2034 memcpy(&(data.data()[PREPARE_WRITE_HEADER_SIZE]), &(newValue.constData()[offset]),
2035 requiredPayload);
2036
2037 Request request;
2038 request.payload = data;
2039 request.command = ATT_OP_PREPARE_WRITE_REQUEST;
2040 request.reference = (handle | ((offset + requiredPayload) << 16));
2041 request.reference2 = newValue;
2042 openRequests.enqueue(request);
2043 }
2044
2045 /*!
2046 Sends an "Execute Write Request" for a long characteristic or descriptor write.
2047 This cannot be used for executes in relation to reliable write requests.
2048
2049 A cancellation removes all pending prepare write request on the GATT server.
2050 Otherwise this function sends an execute request for all pending prepare
2051 write requests.
2052 */
sendExecuteWriteRequest(const QLowEnergyHandle attrHandle,const QByteArray & newValue,bool isCancelation)2053 void QLowEnergyControllerPrivateBluez::sendExecuteWriteRequest(
2054 const QLowEnergyHandle attrHandle, const QByteArray &newValue,
2055 bool isCancelation)
2056 {
2057 quint8 packet[EXECUTE_WRITE_HEADER_SIZE];
2058 packet[0] = ATT_OP_EXECUTE_WRITE_REQUEST;
2059 if (isCancelation)
2060 packet[1] = 0x00; // cancel pending write prepare requests
2061 else
2062 packet[1] = 0x01; // execute pending write prepare requests
2063
2064 QByteArray data(EXECUTE_WRITE_HEADER_SIZE, Qt::Uninitialized);
2065 memcpy(data.data(), packet, EXECUTE_WRITE_HEADER_SIZE);
2066
2067 qCDebug(QT_BT_BLUEZ) << "Sending Execute Write Request for long characteristic value"
2068 << hex << attrHandle;
2069
2070 Request request;
2071 request.payload = data;
2072 request.command = ATT_OP_EXECUTE_WRITE_REQUEST;
2073 request.reference = (attrHandle | ((isCancelation ? 0x00 : 0x01) << 16));
2074 request.reference2 = newValue;
2075 openRequests.prepend(request);
2076 }
2077
2078
2079 /*!
2080 Writes long (prepare write request), short (write request)
2081 and writeWithoutResponse characteristic values.
2082
2083 TODO Reliable/prepare write across multiple characteristics is not supported
2084 */
writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,const QLowEnergyHandle charHandle,const QByteArray & newValue,QLowEnergyService::WriteMode mode)2085 void QLowEnergyControllerPrivateBluez::writeCharacteristic(
2086 const QSharedPointer<QLowEnergyServicePrivate> service,
2087 const QLowEnergyHandle charHandle,
2088 const QByteArray &newValue,
2089 QLowEnergyService::WriteMode mode)
2090 {
2091 Q_ASSERT(!service.isNull());
2092
2093 if (!service->characteristicList.contains(charHandle))
2094 return;
2095
2096 QLowEnergyServicePrivate::CharData &charData = service->characteristicList[charHandle];
2097 if (role == QLowEnergyController::PeripheralRole)
2098 writeCharacteristicForPeripheral(charData, newValue);
2099 else
2100 writeCharacteristicForCentral(service, charHandle, charData.valueHandle, newValue, mode);
2101 }
2102
writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,const QLowEnergyHandle charHandle,const QLowEnergyHandle descriptorHandle,const QByteArray & newValue)2103 void QLowEnergyControllerPrivateBluez::writeDescriptor(
2104 const QSharedPointer<QLowEnergyServicePrivate> service,
2105 const QLowEnergyHandle charHandle,
2106 const QLowEnergyHandle descriptorHandle,
2107 const QByteArray &newValue)
2108 {
2109 Q_ASSERT(!service.isNull());
2110
2111 if (role == QLowEnergyController::PeripheralRole)
2112 writeDescriptorForPeripheral(service, charHandle, descriptorHandle, newValue);
2113 else
2114 writeDescriptorForCentral(charHandle, descriptorHandle, newValue);
2115 }
2116
2117 /*!
2118 \internal
2119
2120 Reads the value of one specific characteristic.
2121 */
readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,const QLowEnergyHandle charHandle)2122 void QLowEnergyControllerPrivateBluez::readCharacteristic(
2123 const QSharedPointer<QLowEnergyServicePrivate> service,
2124 const QLowEnergyHandle charHandle)
2125 {
2126 Q_ASSERT(!service.isNull());
2127 if (!service->characteristicList.contains(charHandle))
2128 return;
2129
2130 const QLowEnergyServicePrivate::CharData &charDetails
2131 = service->characteristicList[charHandle];
2132 if (!(charDetails.properties & QLowEnergyCharacteristic::Read)) {
2133 // if this succeeds the device has a bug, char is advertised as
2134 // non-readable. We try to be permissive and let the remote
2135 // device answer to the read attempt
2136 qCWarning(QT_BT_BLUEZ) << "Reading non-readable char" << charHandle;
2137 }
2138
2139 quint8 packet[READ_REQUEST_HEADER_SIZE];
2140 packet[0] = ATT_OP_READ_REQUEST;
2141 putBtData(charDetails.valueHandle, &packet[1]);
2142
2143 QByteArray data(READ_REQUEST_HEADER_SIZE, Qt::Uninitialized);
2144 memcpy(data.data(), packet, READ_REQUEST_HEADER_SIZE);
2145
2146 qCDebug(QT_BT_BLUEZ) << "Targeted reading characteristic" << hex << charHandle;
2147
2148 Request request;
2149 request.payload = data;
2150 request.command = ATT_OP_READ_REQUEST;
2151 request.reference = charHandle;
2152 // reference2 not really required but false prevents service discovery
2153 // code from running in ATT_OP_READ_RESPONSE handler
2154 request.reference2 = false;
2155 openRequests.enqueue(request);
2156
2157 sendNextPendingRequest();
2158 }
2159
readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,const QLowEnergyHandle charHandle,const QLowEnergyHandle descriptorHandle)2160 void QLowEnergyControllerPrivateBluez::readDescriptor(
2161 const QSharedPointer<QLowEnergyServicePrivate> service,
2162 const QLowEnergyHandle charHandle,
2163 const QLowEnergyHandle descriptorHandle)
2164 {
2165 Q_ASSERT(!service.isNull());
2166 if (!service->characteristicList.contains(charHandle))
2167 return;
2168
2169 const QLowEnergyServicePrivate::CharData &charDetails
2170 = service->characteristicList[charHandle];
2171 if (!charDetails.descriptorList.contains(descriptorHandle))
2172 return;
2173
2174 quint8 packet[READ_REQUEST_HEADER_SIZE];
2175 packet[0] = ATT_OP_READ_REQUEST;
2176 putBtData(descriptorHandle, &packet[1]);
2177
2178 QByteArray data(READ_REQUEST_HEADER_SIZE, Qt::Uninitialized);
2179 memcpy(data.data(), packet, READ_REQUEST_HEADER_SIZE);
2180
2181 qCDebug(QT_BT_BLUEZ) << "Targeted reading descriptor" << hex << descriptorHandle;
2182
2183 Request request;
2184 request.payload = data;
2185 request.command = ATT_OP_READ_REQUEST;
2186 request.reference = (charHandle | (descriptorHandle << 16));
2187 // reference2 not really required but false prevents service discovery
2188 // code from running in ATT_OP_READ_RESPONSE handler
2189 request.reference2 = false;
2190 openRequests.enqueue(request);
2191
2192 sendNextPendingRequest();
2193 }
2194
2195 /*!
2196 * Returns true if the encryption change was successfully requested.
2197 * The request is triggered if we got a related ATT error.
2198 */
increaseEncryptLevelfRequired(quint8 errorCode)2199 bool QLowEnergyControllerPrivateBluez::increaseEncryptLevelfRequired(quint8 errorCode)
2200 {
2201 if (securityLevelValue == BT_SECURITY_HIGH)
2202 return false;
2203
2204 switch (errorCode) {
2205 case ATT_ERROR_INSUF_ENCRYPTION:
2206 case ATT_ERROR_INSUF_AUTHENTICATION:
2207 case ATT_ERROR_INSUF_ENCR_KEY_SIZE:
2208 if (!hciManager->isValid())
2209 return false;
2210 if (!hciManager->monitorEvent(HciManager::EncryptChangeEvent))
2211 return false;
2212 if (securityLevelValue != BT_SECURITY_HIGH) {
2213 qCDebug(QT_BT_BLUEZ) << "Requesting encrypted link";
2214 if (setSecurityLevel(BT_SECURITY_HIGH)) {
2215 restartRequestTimer();
2216 return true;
2217 }
2218 }
2219 break;
2220 default:
2221 break;
2222 }
2223
2224 return false;
2225 }
2226
handleAdvertisingError()2227 void QLowEnergyControllerPrivateBluez::handleAdvertisingError()
2228 {
2229 qCWarning(QT_BT_BLUEZ) << "received advertising error";
2230 setError(QLowEnergyController::AdvertisingError);
2231 setState(QLowEnergyController::UnconnectedState);
2232 }
2233
checkPacketSize(const QByteArray & packet,int minSize,int maxSize)2234 bool QLowEnergyControllerPrivateBluez::checkPacketSize(const QByteArray &packet, int minSize,
2235 int maxSize)
2236 {
2237 if (maxSize == -1)
2238 maxSize = minSize;
2239 if (Q_LIKELY(packet.count() >= minSize && packet.count() <= maxSize))
2240 return true;
2241 qCWarning(QT_BT_BLUEZ) << "client request of type" << packet.at(0)
2242 << "has unexpected packet size" << packet.count();
2243 sendErrorResponse(packet.at(0), 0, ATT_ERROR_INVALID_PDU);
2244 return false;
2245 }
2246
checkHandle(const QByteArray & packet,QLowEnergyHandle handle)2247 bool QLowEnergyControllerPrivateBluez::checkHandle(const QByteArray &packet, QLowEnergyHandle handle)
2248 {
2249 if (handle != 0 && handle <= lastLocalHandle)
2250 return true;
2251 sendErrorResponse(packet.at(0), handle, ATT_ERROR_INVALID_HANDLE);
2252 return false;
2253 }
2254
checkHandlePair(quint8 request,QLowEnergyHandle startingHandle,QLowEnergyHandle endingHandle)2255 bool QLowEnergyControllerPrivateBluez::checkHandlePair(quint8 request, QLowEnergyHandle startingHandle,
2256 QLowEnergyHandle endingHandle)
2257 {
2258 if (startingHandle == 0 || startingHandle > endingHandle) {
2259 qCDebug(QT_BT_BLUEZ) << "handle range invalid";
2260 sendErrorResponse(request, startingHandle, ATT_ERROR_INVALID_HANDLE);
2261 return false;
2262 }
2263 return true;
2264 }
2265
handleExchangeMtuRequest(const QByteArray & packet)2266 void QLowEnergyControllerPrivateBluez::handleExchangeMtuRequest(const QByteArray &packet)
2267 {
2268 // Spec v4.2, Vol 3, Part F, 3.4.2
2269
2270 if (!checkPacketSize(packet, 3))
2271 return;
2272 if (receivedMtuExchangeRequest) { // Client must only send this once per connection.
2273 qCDebug(QT_BT_BLUEZ) << "Client sent extraneous MTU exchange packet";
2274 sendErrorResponse(packet.at(0), 0, ATT_ERROR_REQUEST_NOT_SUPPORTED);
2275 return;
2276 }
2277 receivedMtuExchangeRequest = true;
2278
2279 // Send reply.
2280 QByteArray reply(MTU_EXCHANGE_HEADER_SIZE, Qt::Uninitialized);
2281 reply[0] = ATT_OP_EXCHANGE_MTU_RESPONSE;
2282 putBtData(static_cast<quint16>(ATT_MAX_LE_MTU), reply.data() + 1);
2283 sendPacket(reply);
2284
2285 // Apply requested MTU.
2286 const quint16 clientRxMtu = bt_get_le16(packet.constData() + 1);
2287 mtuSize = qMax<quint16>(ATT_DEFAULT_LE_MTU, qMin<quint16>(clientRxMtu, ATT_MAX_LE_MTU));
2288 qCDebug(QT_BT_BLUEZ) << "MTU request from client:" << clientRxMtu
2289 << "effective client RX MTU:" << mtuSize;
2290 qCDebug(QT_BT_BLUEZ) << "Sending server RX MTU" << ATT_MAX_LE_MTU;
2291 }
2292
handleFindInformationRequest(const QByteArray & packet)2293 void QLowEnergyControllerPrivateBluez::handleFindInformationRequest(const QByteArray &packet)
2294 {
2295 // Spec v4.2, Vol 3, Part F, 3.4.3.1-2
2296
2297 if (!checkPacketSize(packet, 5))
2298 return;
2299 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2300 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2301 qCDebug(QT_BT_BLUEZ) << "client sends find information request; start:" << startingHandle
2302 << "end:" << endingHandle;
2303 if (!checkHandlePair(packet.at(0), startingHandle, endingHandle))
2304 return;
2305
2306 QVector<Attribute> results = getAttributes(startingHandle, endingHandle);
2307 if (results.isEmpty()) {
2308 sendErrorResponse(packet.at(0), startingHandle, ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2309 return;
2310 }
2311 ensureUniformUuidSizes(results);
2312
2313 QByteArray responsePrefix(2, Qt::Uninitialized);
2314 const int uuidSize = getUuidSize(results.first().type);
2315 responsePrefix[0] = ATT_OP_FIND_INFORMATION_RESPONSE;
2316 responsePrefix[1] = uuidSize == 2 ? 0x1 : 0x2;
2317 const int elementSize = sizeof(QLowEnergyHandle) + uuidSize;
2318 const auto elemWriter = [](const Attribute &attr, char *&data) {
2319 putDataAndIncrement(attr.handle, data);
2320 putDataAndIncrement(attr.type, data);
2321 };
2322 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2323
2324 }
2325
handleFindByTypeValueRequest(const QByteArray & packet)2326 void QLowEnergyControllerPrivateBluez::handleFindByTypeValueRequest(const QByteArray &packet)
2327 {
2328 // Spec v4.2, Vol 3, Part F, 3.4.3.3-4
2329
2330 if (!checkPacketSize(packet, 7, mtuSize))
2331 return;
2332 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2333 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2334 const quint16 type = bt_get_le16(packet.constData() + 5);
2335 const QByteArray value = QByteArray::fromRawData(packet.constData() + 7, packet.count() - 7);
2336 qCDebug(QT_BT_BLUEZ) << "client sends find by type value request; start:" << startingHandle
2337 << "end:" << endingHandle << "type:" << type
2338 << "value:" << value.toHex();
2339 if (!checkHandlePair(packet.at(0), startingHandle, endingHandle))
2340 return;
2341
2342 const auto predicate = [value, this, type](const Attribute &attr) {
2343 return attr.type == QBluetoothUuid(type) && attr.value == value
2344 && checkReadPermissions(attr) == 0;
2345 };
2346 const QVector<Attribute> results = getAttributes(startingHandle, endingHandle, predicate);
2347 if (results.isEmpty()) {
2348 sendErrorResponse(packet.at(0), startingHandle, ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2349 return;
2350 }
2351
2352 QByteArray responsePrefix(1, ATT_OP_FIND_BY_TYPE_VALUE_RESPONSE);
2353 const int elemSize = 2 * sizeof(QLowEnergyHandle);
2354 const auto elemWriter = [](const Attribute &attr, char *&data) {
2355 putDataAndIncrement(attr.handle, data);
2356 putDataAndIncrement(attr.groupEndHandle, data);
2357 };
2358 sendListResponse(responsePrefix, elemSize, results, elemWriter);
2359 }
2360
handleReadByTypeRequest(const QByteArray & packet)2361 void QLowEnergyControllerPrivateBluez::handleReadByTypeRequest(const QByteArray &packet)
2362 {
2363 // Spec v4.2, Vol 3, Part F, 3.4.4.1-2
2364
2365 if (!checkPacketSize(packet, 7, 21))
2366 return;
2367 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2368 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2369 const void * const typeStart = packet.constData() + 5;
2370 const bool is16BitUuid = packet.count() == 7;
2371 const bool is128BitUuid = packet.count() == 21;
2372 QBluetoothUuid type;
2373 if (is16BitUuid) {
2374 type = QBluetoothUuid(bt_get_le16(typeStart));
2375 } else if (is128BitUuid) {
2376 type = QBluetoothUuid(convert_uuid128(reinterpret_cast<const quint128 *>(typeStart)));
2377 } else {
2378 qCWarning(QT_BT_BLUEZ) << "read by type request has invalid packet size" << packet.count();
2379 sendErrorResponse(packet.at(0), 0, ATT_ERROR_INVALID_PDU);
2380 return;
2381 }
2382 qCDebug(QT_BT_BLUEZ) << "client sends read by type request, start:" << startingHandle
2383 << "end:" << endingHandle << "type:" << type;
2384 if (!checkHandlePair(packet.at(0), startingHandle, endingHandle))
2385 return;
2386
2387 // Get all attributes with matching type.
2388 QVector<Attribute> results = getAttributes(startingHandle, endingHandle,
2389 [type](const Attribute &attr) { return attr.type == type; });
2390 ensureUniformValueSizes(results);
2391
2392 if (results.isEmpty()) {
2393 sendErrorResponse(packet.at(0), startingHandle, ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2394 return;
2395 }
2396
2397 const int error = checkReadPermissions(results);
2398 if (error) {
2399 sendErrorResponse(packet.at(0), results.first().handle, error);
2400 return;
2401 }
2402
2403 const int elementSize = sizeof(QLowEnergyHandle) + results.first().value.count();
2404 QByteArray responsePrefix(2, Qt::Uninitialized);
2405 responsePrefix[0] = ATT_OP_READ_BY_TYPE_RESPONSE;
2406 responsePrefix[1] = elementSize;
2407 const auto elemWriter = [](const Attribute &attr, char *&data) {
2408 putDataAndIncrement(attr.handle, data);
2409 putDataAndIncrement(attr.value, data);
2410 };
2411 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2412 }
2413
handleReadRequest(const QByteArray & packet)2414 void QLowEnergyControllerPrivateBluez::handleReadRequest(const QByteArray &packet)
2415 {
2416 // Spec v4.2, Vol 3, Part F, 3.4.4.3-4
2417
2418 if (!checkPacketSize(packet, 3))
2419 return;
2420 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2421 qCDebug(QT_BT_BLUEZ) << "client sends read request; handle:" << handle;
2422
2423 if (!checkHandle(packet, handle))
2424 return;
2425 const Attribute &attribute = localAttributes.at(handle);
2426 const int permissionsError = checkReadPermissions(attribute);
2427 if (permissionsError) {
2428 sendErrorResponse(packet.at(0), handle, permissionsError);
2429 return;
2430 }
2431
2432 const int sentValueLength = qMin(attribute.value.count(), mtuSize - 1);
2433 QByteArray response(1 + sentValueLength, Qt::Uninitialized);
2434 response[0] = ATT_OP_READ_RESPONSE;
2435 using namespace std;
2436 memcpy(response.data() + 1, attribute.value.constData(), sentValueLength);
2437 qCDebug(QT_BT_BLUEZ) << "sending response:" << response.toHex();
2438 sendPacket(response);
2439 }
2440
handleReadBlobRequest(const QByteArray & packet)2441 void QLowEnergyControllerPrivateBluez::handleReadBlobRequest(const QByteArray &packet)
2442 {
2443 // Spec v4.2, Vol 3, Part F, 3.4.4.5-6
2444
2445 if (!checkPacketSize(packet, 5))
2446 return;
2447 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2448 const quint16 valueOffset = bt_get_le16(packet.constData() + 3);
2449 qCDebug(QT_BT_BLUEZ) << "client sends read blob request; handle:" << handle
2450 << "offset:" << valueOffset;
2451
2452 if (!checkHandle(packet, handle))
2453 return;
2454 const Attribute &attribute = localAttributes.at(handle);
2455 const int permissionsError = checkReadPermissions(attribute);
2456 if (permissionsError) {
2457 sendErrorResponse(packet.at(0), handle, permissionsError);
2458 return;
2459 }
2460 if (valueOffset > attribute.value.count()) {
2461 sendErrorResponse(packet.at(0), handle, ATT_ERROR_INVALID_OFFSET);
2462 return;
2463 }
2464 if (attribute.value.count() <= mtuSize - 3) {
2465 sendErrorResponse(packet.at(0), handle, ATT_ERROR_ATTRIBUTE_NOT_LONG);
2466 return;
2467 }
2468
2469 // Yes, this value can be zero.
2470 const int sentValueLength = qMin(attribute.value.count() - valueOffset, mtuSize - 1);
2471
2472 QByteArray response(1 + sentValueLength, Qt::Uninitialized);
2473 response[0] = ATT_OP_READ_BLOB_RESPONSE;
2474 using namespace std;
2475 memcpy(response.data() + 1, attribute.value.constData() + valueOffset, sentValueLength);
2476 qCDebug(QT_BT_BLUEZ) << "sending response:" << response.toHex();
2477 sendPacket(response);
2478 }
2479
handleReadMultipleRequest(const QByteArray & packet)2480 void QLowEnergyControllerPrivateBluez::handleReadMultipleRequest(const QByteArray &packet)
2481 {
2482 // Spec v4.2, Vol 3, Part F, 3.4.4.7-8
2483
2484 if (!checkPacketSize(packet, 5, mtuSize))
2485 return;
2486 QVector<QLowEnergyHandle> handles((packet.count() - 1) / sizeof(QLowEnergyHandle));
2487 auto *packetPtr = reinterpret_cast<const QLowEnergyHandle *>(packet.constData() + 1);
2488 for (int i = 0; i < handles.count(); ++i, ++packetPtr)
2489 handles[i] = bt_get_le16(packetPtr);
2490 qCDebug(QT_BT_BLUEZ) << "client sends read multiple request for handles" << handles;
2491
2492 const auto it = std::find_if(handles.constBegin(), handles.constEnd(),
2493 [this](QLowEnergyHandle handle) { return handle >= lastLocalHandle; });
2494 if (it != handles.constEnd()) {
2495 sendErrorResponse(packet.at(0), *it, ATT_ERROR_INVALID_HANDLE);
2496 return;
2497 }
2498 const QVector<Attribute> results = getAttributes(handles.first(), handles.last());
2499 QByteArray response(1, ATT_OP_READ_MULTIPLE_RESPONSE);
2500 for (const Attribute &attr : results) {
2501 const int error = checkReadPermissions(attr);
2502 if (error) {
2503 sendErrorResponse(packet.at(0), attr.handle, error);
2504 return;
2505 }
2506
2507 // Note: We do not abort if no more values fit into the packet, because we still have to
2508 // report possible permission errors for the other handles.
2509 response += attr.value.left(mtuSize - response.count());
2510 }
2511
2512 qCDebug(QT_BT_BLUEZ) << "sending response:" << response.toHex();
2513 sendPacket(response);
2514 }
2515
handleReadByGroupTypeRequest(const QByteArray & packet)2516 void QLowEnergyControllerPrivateBluez::handleReadByGroupTypeRequest(const QByteArray &packet)
2517 {
2518 // Spec v4.2, Vol 3, Part F, 3.4.4.9-10
2519
2520 if (!checkPacketSize(packet, 7, 21))
2521 return;
2522 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2523 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2524 const bool is16BitUuid = packet.count() == 7;
2525 const bool is128BitUuid = packet.count() == 21;
2526 const void * const typeStart = packet.constData() + 5;
2527 QBluetoothUuid type;
2528 if (is16BitUuid) {
2529 type = QBluetoothUuid(bt_get_le16(typeStart));
2530 } else if (is128BitUuid) {
2531 type = QBluetoothUuid(convert_uuid128(reinterpret_cast<const quint128 *>(typeStart)));
2532 } else {
2533 qCWarning(QT_BT_BLUEZ) << "read by group type request has invalid packet size"
2534 << packet.count();
2535 sendErrorResponse(packet.at(0), 0, ATT_ERROR_INVALID_PDU);
2536 return;
2537 }
2538 qCDebug(QT_BT_BLUEZ) << "client sends read by group type request, start:" << startingHandle
2539 << "end:" << endingHandle << "type:" << type;
2540
2541 if (!checkHandlePair(packet.at(0), startingHandle, endingHandle))
2542 return;
2543 if (type != QBluetoothUuid(static_cast<quint16>(GATT_PRIMARY_SERVICE))
2544 && type != QBluetoothUuid(static_cast<quint16>(GATT_SECONDARY_SERVICE))) {
2545 sendErrorResponse(packet.at(0), startingHandle, ATT_ERROR_UNSUPPRTED_GROUP_TYPE);
2546 return;
2547 }
2548
2549 QVector<Attribute> results = getAttributes(startingHandle, endingHandle,
2550 [type](const Attribute &attr) { return attr.type == type; });
2551 if (results.isEmpty()) {
2552 sendErrorResponse(packet.at(0), startingHandle, ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2553 return;
2554 }
2555 const int error = checkReadPermissions(results);
2556 if (error) {
2557 sendErrorResponse(packet.at(0), results.first().handle, error);
2558 return;
2559 }
2560
2561 ensureUniformValueSizes(results);
2562
2563 const int elementSize = 2 * sizeof(QLowEnergyHandle) + results.first().value.count();
2564 QByteArray responsePrefix(2, Qt::Uninitialized);
2565 responsePrefix[0] = ATT_OP_READ_BY_GROUP_RESPONSE;
2566 responsePrefix[1] = elementSize;
2567 const auto elemWriter = [](const Attribute &attr, char *&data) {
2568 putDataAndIncrement(attr.handle, data);
2569 putDataAndIncrement(attr.groupEndHandle, data);
2570 putDataAndIncrement(attr.value, data);
2571 };
2572 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2573 }
2574
updateLocalAttributeValue(QLowEnergyHandle handle,const QByteArray & value,QLowEnergyCharacteristic & characteristic,QLowEnergyDescriptor & descriptor)2575 void QLowEnergyControllerPrivateBluez::updateLocalAttributeValue(
2576 QLowEnergyHandle handle,
2577 const QByteArray &value,
2578 QLowEnergyCharacteristic &characteristic,
2579 QLowEnergyDescriptor &descriptor)
2580 {
2581 localAttributes[handle].value = value;
2582 for (const auto &service : qAsConst(localServices)) {
2583 if (handle < service->startHandle || handle > service->endHandle)
2584 continue;
2585 for (auto charIt = service->characteristicList.begin();
2586 charIt != service->characteristicList.end(); ++charIt) {
2587 QLowEnergyServicePrivate::CharData &charData = charIt.value();
2588 if (handle == charIt.key() + 1) { // Char value decl comes right after char decl.
2589 charData.value = value;
2590 characteristic = QLowEnergyCharacteristic(service, charIt.key());
2591 return;
2592 }
2593 for (auto descIt = charData.descriptorList.begin();
2594 descIt != charData.descriptorList.end(); ++descIt) {
2595 if (handle == descIt.key()) {
2596 descIt.value().value = value;
2597 descriptor = QLowEnergyDescriptor(service, charIt.key(), handle);
2598 return;
2599 }
2600 }
2601 }
2602 }
2603 qFatal("local services map inconsistent with local attribute map");
2604 }
2605
isNotificationEnabled(quint16 clientConfigValue)2606 static bool isNotificationEnabled(quint16 clientConfigValue) { return clientConfigValue & 0x1; }
isIndicationEnabled(quint16 clientConfigValue)2607 static bool isIndicationEnabled(quint16 clientConfigValue) { return clientConfigValue & 0x2; }
2608
writeCharacteristicForPeripheral(QLowEnergyServicePrivate::CharData & charData,const QByteArray & newValue)2609 void QLowEnergyControllerPrivateBluez::writeCharacteristicForPeripheral(
2610 QLowEnergyServicePrivate::CharData &charData,
2611 const QByteArray &newValue)
2612 {
2613 const QLowEnergyHandle valueHandle = charData.valueHandle;
2614 Q_ASSERT(valueHandle <= lastLocalHandle);
2615 Attribute &attribute = localAttributes[valueHandle];
2616 if (newValue.count() < attribute.minLength || newValue.count() > attribute.maxLength) {
2617 qCWarning(QT_BT_BLUEZ) << "ignoring value of invalid length" << newValue.count()
2618 << "for attribute" << valueHandle;
2619 return;
2620 }
2621 attribute.value = newValue;
2622 charData.value = newValue;
2623 const bool hasNotifyProperty = attribute.properties & QLowEnergyCharacteristic::Notify;
2624 const bool hasIndicateProperty
2625 = attribute.properties & QLowEnergyCharacteristic::Indicate;
2626 if (!hasNotifyProperty && !hasIndicateProperty)
2627 return;
2628 for (const QLowEnergyServicePrivate::DescData &desc : qAsConst(charData.descriptorList)) {
2629 if (desc.uuid != QBluetoothUuid::ClientCharacteristicConfiguration)
2630 continue;
2631
2632 // Notify/indicate currently connected client.
2633 const bool isConnected = state == QLowEnergyController::ConnectedState;
2634 if (isConnected) {
2635 Q_ASSERT(desc.value.count() == 2);
2636 quint16 configValue = bt_get_le16(desc.value.constData());
2637 if (isNotificationEnabled(configValue) && hasNotifyProperty) {
2638 sendNotification(valueHandle);
2639 } else if (isIndicationEnabled(configValue) && hasIndicateProperty) {
2640 if (indicationInFlight)
2641 scheduledIndications << valueHandle;
2642 else
2643 sendIndication(valueHandle);
2644 }
2645 }
2646
2647 // Prepare notification/indication of unconnected, bonded clients.
2648 for (auto it = clientConfigData.begin(); it != clientConfigData.end(); ++it) {
2649 if (isConnected && it.key() == remoteDevice.toUInt64())
2650 continue;
2651 QVector<ClientConfigurationData> &configDataList = it.value();
2652 for (ClientConfigurationData &configData : configDataList) {
2653 if (configData.charValueHandle != valueHandle)
2654 continue;
2655 if ((isNotificationEnabled(configData.configValue) && hasNotifyProperty)
2656 || (isIndicationEnabled(configData.configValue) && hasIndicateProperty)) {
2657 configData.charValueWasUpdated = true;
2658 break;
2659 }
2660 }
2661 }
2662 break;
2663 }
2664 }
2665
writeCharacteristicForCentral(const QSharedPointer<QLowEnergyServicePrivate> & service,QLowEnergyHandle charHandle,QLowEnergyHandle valueHandle,const QByteArray & newValue,QLowEnergyService::WriteMode mode)2666 void QLowEnergyControllerPrivateBluez::writeCharacteristicForCentral(const QSharedPointer<QLowEnergyServicePrivate> &service,
2667 QLowEnergyHandle charHandle,
2668 QLowEnergyHandle valueHandle,
2669 const QByteArray &newValue,
2670 QLowEnergyService::WriteMode mode)
2671 {
2672 QByteArray packet(WRITE_REQUEST_HEADER_SIZE + newValue.count(), Qt::Uninitialized);
2673 putBtData(valueHandle, packet.data() + 1);
2674 memcpy(packet.data() + 3, newValue.constData(), newValue.count());
2675 bool writeWithResponse = false;
2676 switch (mode) {
2677 case QLowEnergyService::WriteWithResponse:
2678 if (newValue.size() > (mtuSize - WRITE_REQUEST_HEADER_SIZE)) {
2679 sendNextPrepareWriteRequest(charHandle, newValue, 0);
2680 sendNextPendingRequest();
2681 return;
2682 }
2683 // write value fits into single package
2684 packet[0] = ATT_OP_WRITE_REQUEST;
2685 writeWithResponse = true;
2686 break;
2687 case QLowEnergyService::WriteWithoutResponse:
2688 packet[0] = ATT_OP_WRITE_COMMAND;
2689 break;
2690 case QLowEnergyService::WriteSigned:
2691 packet[0] = ATT_OP_SIGNED_WRITE_COMMAND;
2692 if (!isBonded()) {
2693 qCWarning(QT_BT_BLUEZ) << "signed write not possible: requires bond between devices";
2694 service->setError(QLowEnergyService::CharacteristicWriteError);
2695 return;
2696 }
2697 if (securityLevel() >= BT_SECURITY_MEDIUM) {
2698 qCWarning(QT_BT_BLUEZ) << "signed write not possible: not allowed on encrypted link";
2699 service->setError(QLowEnergyService::CharacteristicWriteError);
2700 return;
2701 }
2702 const auto signingDataIt = signingData.find(remoteDevice.toUInt64());
2703 if (signingDataIt == signingData.end()) {
2704 qCWarning(QT_BT_BLUEZ) << "signed write not possible: no signature key found";
2705 service->setError(QLowEnergyService::CharacteristicWriteError);
2706 return;
2707 }
2708 ++signingDataIt.value().counter;
2709 packet = LeCmacCalculator::createFullMessage(packet, signingDataIt.value().counter);
2710 const quint64 mac = LeCmacCalculator().calculateMac(packet, signingDataIt.value().key);
2711 packet.resize(packet.count() + sizeof mac);
2712 putBtData(mac, packet.data() + packet.count() - sizeof mac);
2713 storeSignCounter(LocalSigningKey);
2714 break;
2715 }
2716
2717 qCDebug(QT_BT_BLUEZ) << "Writing characteristic" << hex << charHandle
2718 << "(size:" << packet.count() << "with response:"
2719 << (mode == QLowEnergyService::WriteWithResponse)
2720 << "signed:" << (mode == QLowEnergyService::WriteSigned) << ")";
2721
2722 // Advantage of write without response is the quick turnaround.
2723 // It can be sent at any time and does not produce responses.
2724 // Therefore we will not put them into the openRequest queue at all.
2725 if (!writeWithResponse) {
2726 sendPacket(packet);
2727 return;
2728 }
2729
2730 Request request;
2731 request.payload = packet;
2732 request.command = ATT_OP_WRITE_REQUEST;
2733 request.reference = charHandle;
2734 request.reference2 = newValue;
2735 openRequests.enqueue(request);
2736
2737 sendNextPendingRequest();
2738 }
2739
writeDescriptorForPeripheral(const QSharedPointer<QLowEnergyServicePrivate> & service,const QLowEnergyHandle charHandle,const QLowEnergyHandle descriptorHandle,const QByteArray & newValue)2740 void QLowEnergyControllerPrivateBluez::writeDescriptorForPeripheral(
2741 const QSharedPointer<QLowEnergyServicePrivate> &service,
2742 const QLowEnergyHandle charHandle,
2743 const QLowEnergyHandle descriptorHandle,
2744 const QByteArray &newValue)
2745 {
2746 Q_ASSERT(descriptorHandle <= lastLocalHandle);
2747 Attribute &attribute = localAttributes[descriptorHandle];
2748 if (newValue.count() < attribute.minLength || newValue.count() > attribute.maxLength) {
2749 qCWarning(QT_BT_BLUEZ) << "invalid value of size" << newValue.count()
2750 << "for attribute" << descriptorHandle;
2751 return;
2752 }
2753 attribute.value = newValue;
2754 service->characteristicList[charHandle].descriptorList[descriptorHandle].value = newValue;
2755 }
2756
writeDescriptorForCentral(const QLowEnergyHandle charHandle,const QLowEnergyHandle descriptorHandle,const QByteArray & newValue)2757 void QLowEnergyControllerPrivateBluez::writeDescriptorForCentral(
2758 const QLowEnergyHandle charHandle,
2759 const QLowEnergyHandle descriptorHandle,
2760 const QByteArray &newValue)
2761 {
2762 if (newValue.size() > (mtuSize - WRITE_REQUEST_HEADER_SIZE)) {
2763 sendNextPrepareWriteRequest(descriptorHandle, newValue, 0);
2764 sendNextPendingRequest();
2765 return;
2766 }
2767
2768 quint8 packet[WRITE_REQUEST_HEADER_SIZE];
2769 packet[0] = ATT_OP_WRITE_REQUEST;
2770 putBtData(descriptorHandle, &packet[1]);
2771
2772 const int size = WRITE_REQUEST_HEADER_SIZE + newValue.size();
2773 QByteArray data(size, Qt::Uninitialized);
2774 memcpy(data.data(), packet, WRITE_REQUEST_HEADER_SIZE);
2775 memcpy(&(data.data()[WRITE_REQUEST_HEADER_SIZE]), newValue.constData(), newValue.size());
2776
2777 qCDebug(QT_BT_BLUEZ) << "Writing descriptor" << hex << descriptorHandle
2778 << "(size:" << size << ")";
2779
2780 Request request;
2781 request.payload = data;
2782 request.command = ATT_OP_WRITE_REQUEST;
2783 request.reference = (charHandle | (descriptorHandle << 16));
2784 request.reference2 = newValue;
2785 openRequests.enqueue(request);
2786
2787 sendNextPendingRequest();
2788 }
2789
handleWriteRequestOrCommand(const QByteArray & packet)2790 void QLowEnergyControllerPrivateBluez::handleWriteRequestOrCommand(const QByteArray &packet)
2791 {
2792 // Spec v4.2, Vol 3, Part F, 3.4.5.1-3
2793
2794 const bool isRequest = packet.at(0) == ATT_OP_WRITE_REQUEST;
2795 const bool isSigned = quint8(packet.at(0)) == quint8(ATT_OP_SIGNED_WRITE_COMMAND);
2796 if (!checkPacketSize(packet, isSigned ? 15 : 3, mtuSize))
2797 return;
2798 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2799 qCDebug(QT_BT_BLUEZ) << "client sends" << (isSigned ? "signed" : "") << "write"
2800 << (isRequest ? "request" : "command") << "for handle" << handle;
2801
2802 if (!checkHandle(packet, handle))
2803 return;
2804
2805 Attribute &attribute = localAttributes[handle];
2806 const QLowEnergyCharacteristic::PropertyType type = isRequest
2807 ? QLowEnergyCharacteristic::Write : isSigned
2808 ? QLowEnergyCharacteristic::WriteSigned : QLowEnergyCharacteristic::WriteNoResponse;
2809 const int permissionsError = checkPermissions(attribute, type);
2810 if (permissionsError) {
2811 sendErrorResponse(packet.at(0), handle, permissionsError);
2812 return;
2813 }
2814
2815 int valueLength;
2816 if (isSigned) {
2817 if (!isBonded()) {
2818 qCWarning(QT_BT_BLUEZ) << "Ignoring signed write from non-bonded device.";
2819 return;
2820 }
2821 if (securityLevel() >= BT_SECURITY_MEDIUM) {
2822 qCWarning(QT_BT_BLUEZ) << "Ignoring signed write on encrypted link.";
2823 return;
2824 }
2825 const auto signingDataIt = signingData.find(remoteDevice.toUInt64());
2826 if (signingDataIt == signingData.constEnd()) {
2827 qCWarning(QT_BT_BLUEZ) << "No CSRK found for peer device, ignoring signed write";
2828 return;
2829 }
2830
2831 const quint32 signCounter = getBtData<quint32>(packet.data() + packet.count() - 12);
2832 if (signCounter < signingDataIt.value().counter + 1) {
2833 qCWarning(QT_BT_BLUEZ) << "Client's' sign counter" << signCounter
2834 << "not greater than local sign counter"
2835 << signingDataIt.value().counter
2836 << "; ignoring signed write command.";
2837 return;
2838 }
2839
2840 const quint64 macFromClient = getBtData<quint64>(packet.data() + packet.count() - 8);
2841 const bool signatureCorrect = verifyMac(packet.left(packet.count() - 12),
2842 signingDataIt.value().key, signCounter, macFromClient);
2843 if (!signatureCorrect) {
2844 qCWarning(QT_BT_BLUEZ) << "Signed Write packet has wrong signature, disconnecting";
2845 disconnectFromDevice(); // Recommended by spec v4.2, Vol 3, part C, 10.4.2
2846 return;
2847 }
2848
2849 signingDataIt.value().counter = signCounter;
2850 storeSignCounter(RemoteSigningKey);
2851 valueLength = packet.count() - 15;
2852 } else {
2853 valueLength = packet.count() - 3;
2854 }
2855
2856 if (valueLength > attribute.maxLength) {
2857 sendErrorResponse(packet.at(0), handle, ATT_ERROR_INVAL_ATTR_VALUE_LEN);
2858 return;
2859 }
2860
2861 // If the attribute value has a fixed size and the value in the packet is shorter,
2862 // then we overwrite only the start of the attribute value and keep the rest.
2863 QByteArray value = packet.mid(3, valueLength);
2864 if (attribute.minLength == attribute.maxLength && valueLength < attribute.minLength)
2865 value += attribute.value.mid(valueLength, attribute.maxLength - valueLength);
2866
2867 QLowEnergyCharacteristic characteristic;
2868 QLowEnergyDescriptor descriptor;
2869 updateLocalAttributeValue(handle, value, characteristic, descriptor);
2870
2871 if (isRequest) {
2872 const QByteArray response = QByteArray(1, ATT_OP_WRITE_RESPONSE);
2873 sendPacket(response);
2874 }
2875
2876 if (characteristic.isValid()) {
2877 emit characteristic.d_ptr->characteristicChanged(characteristic, value);
2878 } else {
2879 Q_ASSERT(descriptor.isValid());
2880 emit descriptor.d_ptr->descriptorWritten(descriptor, value);
2881 }
2882 }
2883
handlePrepareWriteRequest(const QByteArray & packet)2884 void QLowEnergyControllerPrivateBluez::handlePrepareWriteRequest(const QByteArray &packet)
2885 {
2886 // Spec v4.2, Vol 3, Part F, 3.4.6.1
2887
2888 if (!checkPacketSize(packet, 5, mtuSize))
2889 return;
2890 const quint16 handle = bt_get_le16(packet.constData() + 1);
2891 qCDebug(QT_BT_BLUEZ) << "client sends prepare write request for handle" << handle;
2892
2893 if (!checkHandle(packet, handle))
2894 return;
2895 const Attribute &attribute = localAttributes.at(handle);
2896 const int permissionsError = checkPermissions(attribute, QLowEnergyCharacteristic::Write);
2897 if (permissionsError) {
2898 sendErrorResponse(packet.at(0), handle, permissionsError);
2899 return;
2900 }
2901 if (openPrepareWriteRequests.count() >= maxPrepareQueueSize) {
2902 sendErrorResponse(packet.at(0), handle, ATT_ERROR_PREPARE_QUEUE_FULL);
2903 return;
2904 }
2905
2906 // The value is not checked here, but on the Execute request.
2907 openPrepareWriteRequests << WriteRequest(handle, bt_get_le16(packet.constData() + 3),
2908 packet.mid(5));
2909
2910 QByteArray response = packet;
2911 response[0] = ATT_OP_PREPARE_WRITE_RESPONSE;
2912 sendPacket(response);
2913 }
2914
handleExecuteWriteRequest(const QByteArray & packet)2915 void QLowEnergyControllerPrivateBluez::handleExecuteWriteRequest(const QByteArray &packet)
2916 {
2917 // Spec v4.2, Vol 3, Part F, 3.4.6.3
2918
2919 if (!checkPacketSize(packet, 2))
2920 return;
2921 const bool cancel = packet.at(1) == 0;
2922 qCDebug(QT_BT_BLUEZ) << "client sends execute write request; flag is"
2923 << (cancel ? "cancel" : "flush");
2924
2925 QVector<WriteRequest> requests = openPrepareWriteRequests;
2926 openPrepareWriteRequests.clear();
2927 QVector<QLowEnergyCharacteristic> characteristics;
2928 QVector<QLowEnergyDescriptor> descriptors;
2929 if (!cancel) {
2930 for (const WriteRequest &request : qAsConst(requests)) {
2931 Attribute &attribute = localAttributes[request.handle];
2932 if (request.valueOffset > attribute.value.count()) {
2933 sendErrorResponse(packet.at(0), request.handle, ATT_ERROR_INVALID_OFFSET);
2934 return;
2935 }
2936 const QByteArray newValue = attribute.value.left(request.valueOffset) + request.value;
2937 if (newValue.count() > attribute.maxLength) {
2938 sendErrorResponse(packet.at(0), request.handle, ATT_ERROR_INVAL_ATTR_VALUE_LEN);
2939 return;
2940 }
2941 QLowEnergyCharacteristic characteristic;
2942 QLowEnergyDescriptor descriptor;
2943 // TODO: Redundant attribute lookup for the case of the same handle appearing
2944 // more than once.
2945 updateLocalAttributeValue(request.handle, newValue, characteristic, descriptor);
2946 if (characteristic.isValid()) {
2947 characteristics << characteristic;
2948 } else if (descriptor.isValid()) {
2949 Q_ASSERT(descriptor.isValid());
2950 descriptors << descriptor;
2951 }
2952 }
2953 }
2954
2955 sendPacket(QByteArray(1, ATT_OP_EXECUTE_WRITE_RESPONSE));
2956
2957 for (const QLowEnergyCharacteristic &characteristic : qAsConst(characteristics))
2958 emit characteristic.d_ptr->characteristicChanged(characteristic, characteristic.value());
2959 for (const QLowEnergyDescriptor &descriptor : qAsConst(descriptors))
2960 emit descriptor.d_ptr->descriptorWritten(descriptor, descriptor.value());
2961 }
2962
sendErrorResponse(quint8 request,quint16 handle,quint8 code)2963 void QLowEnergyControllerPrivateBluez::sendErrorResponse(quint8 request, quint16 handle, quint8 code)
2964 {
2965 // An ATT command never receives an error response.
2966 if (request == ATT_OP_WRITE_COMMAND || request == ATT_OP_SIGNED_WRITE_COMMAND)
2967 return;
2968
2969 QByteArray packet(ERROR_RESPONSE_HEADER_SIZE, Qt::Uninitialized);
2970 packet[0] = ATT_OP_ERROR_RESPONSE;
2971 packet[1] = request;
2972 putBtData(handle, packet.data() + 2);
2973 packet[4] = code;
2974 qCWarning(QT_BT_BLUEZ) << "sending error response; request:" << request << "handle:" << handle
2975 << "code:" << code;
2976 sendPacket(packet);
2977 }
2978
sendListResponse(const QByteArray & packetStart,int elemSize,const QVector<Attribute> & attributes,const ElemWriter & elemWriter)2979 void QLowEnergyControllerPrivateBluez::sendListResponse(const QByteArray &packetStart, int elemSize,
2980 const QVector<Attribute> &attributes, const ElemWriter &elemWriter)
2981 {
2982 const int offset = packetStart.count();
2983 const int elemCount = qMin(attributes.count(), (mtuSize - offset) / elemSize);
2984 const int totalPacketSize = offset + elemCount * elemSize;
2985 QByteArray response(totalPacketSize, Qt::Uninitialized);
2986 using namespace std;
2987 memcpy(response.data(), packetStart.constData(), offset);
2988 char *data = response.data() + offset;
2989 for_each(attributes.constBegin(), attributes.constBegin() + elemCount,
2990 [&data, elemWriter](const Attribute &attr) { elemWriter(attr, data); });
2991 qCDebug(QT_BT_BLUEZ) << "sending response:" << response.toHex();
2992 sendPacket(response);
2993 }
2994
sendNotification(QLowEnergyHandle handle)2995 void QLowEnergyControllerPrivateBluez::sendNotification(QLowEnergyHandle handle)
2996 {
2997 sendNotificationOrIndication(ATT_OP_HANDLE_VAL_NOTIFICATION, handle);
2998 }
2999
sendIndication(QLowEnergyHandle handle)3000 void QLowEnergyControllerPrivateBluez::sendIndication(QLowEnergyHandle handle)
3001 {
3002 Q_ASSERT(!indicationInFlight);
3003 indicationInFlight = true;
3004 sendNotificationOrIndication(ATT_OP_HANDLE_VAL_INDICATION, handle);
3005 }
3006
sendNotificationOrIndication(quint8 opCode,QLowEnergyHandle handle)3007 void QLowEnergyControllerPrivateBluez::sendNotificationOrIndication(
3008 quint8 opCode,
3009 QLowEnergyHandle handle)
3010 {
3011 Q_ASSERT(handle <= lastLocalHandle);
3012 const Attribute &attribute = localAttributes.at(handle);
3013 const int maxValueLength = qMin(attribute.value.count(), mtuSize - 3);
3014 QByteArray packet(3 + maxValueLength, Qt::Uninitialized);
3015 packet[0] = opCode;
3016 putBtData(handle, packet.data() + 1);
3017 using namespace std;
3018 memcpy(packet.data() + 3, attribute.value.constData(), maxValueLength);
3019 qCDebug(QT_BT_BLUEZ) << "sending notification/indication:" << packet.toHex();
3020 sendPacket(packet);
3021 }
3022
sendNextIndication()3023 void QLowEnergyControllerPrivateBluez::sendNextIndication()
3024 {
3025 if (!scheduledIndications.isEmpty())
3026 sendIndication(scheduledIndications.takeFirst());
3027 }
3028
nameOfRemoteCentral(const QBluetoothAddress & peerAddress,const QBluetoothAddress & localAdapter)3029 static QString nameOfRemoteCentral(const QBluetoothAddress &peerAddress, const QBluetoothAddress &localAdapter)
3030 {
3031 const QString peerAddressString = peerAddress.toString();
3032 if (isBluez5()) {
3033 OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral("org.bluez"),
3034 QStringLiteral("/"),
3035 QDBusConnection::systemBus());
3036 QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects();
3037 reply.waitForFinished();
3038 if (reply.isError())
3039 return QString();
3040
3041 ManagedObjectList managedObjectList = reply.value();
3042 for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
3043 const InterfaceList &ifaceList = it.value();
3044
3045 for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) {
3046 const QString &iface = jt.key();
3047 const QVariantMap &ifaceValues = jt.value();
3048
3049 if (iface == QStringLiteral("org.bluez.Device1")) {
3050 if (ifaceValues.value(QStringLiteral("Address")).toString() == peerAddressString)
3051 return ifaceValues.value(QStringLiteral("Alias")).toString();
3052 }
3053 }
3054 }
3055 return QString();
3056 } else {
3057 OrgBluezManagerInterface manager(QStringLiteral("org.bluez"), QStringLiteral("/"),
3058 QDBusConnection::systemBus());
3059
3060 QDBusPendingReply<QDBusObjectPath> reply = manager.FindAdapter(localAdapter.toString());
3061 reply.waitForFinished();
3062 if (reply.isError())
3063 return QString();
3064
3065 OrgBluezAdapterInterface adapter(QStringLiteral("org.bluez"), reply.value().path(),
3066 QDBusConnection::systemBus());
3067
3068 QDBusPendingReply<QDBusObjectPath> deviceObjectPath = adapter.FindDevice(peerAddressString);
3069 deviceObjectPath.waitForFinished();
3070 if (deviceObjectPath.isError()) {
3071 if (deviceObjectPath.error().name() != QStringLiteral("org.bluez.Error.DoesNotExist"))
3072 return QString();
3073
3074 deviceObjectPath = adapter.CreateDevice(peerAddressString);
3075 deviceObjectPath.waitForFinished();
3076 if (deviceObjectPath.isError())
3077 return QString();
3078 }
3079
3080 OrgBluezDeviceInterface device(QStringLiteral("org.bluez"), deviceObjectPath.value().path(),
3081 QDBusConnection::systemBus());
3082
3083 QDBusPendingReply<QVariantMap> properties = device.GetProperties();
3084 properties.waitForFinished();
3085 if (properties.isError())
3086 return QString();
3087
3088 return properties.value().value(QStringLiteral("Alias")).toString();
3089 }
3090 }
3091
handleConnectionRequest()3092 void QLowEnergyControllerPrivateBluez::handleConnectionRequest()
3093 {
3094 if (state != QLowEnergyController::AdvertisingState) {
3095 qCWarning(QT_BT_BLUEZ) << "Incoming connection request in unexpected state" << state;
3096 return;
3097 }
3098 Q_ASSERT(serverSocketNotifier);
3099 serverSocketNotifier->setEnabled(false);
3100 sockaddr_l2 clientAddr;
3101 socklen_t clientAddrSize = sizeof clientAddr;
3102 const int clientSocket = accept(serverSocketNotifier->socket(),
3103 reinterpret_cast<sockaddr *>(&clientAddr), &clientAddrSize);
3104 if (clientSocket == -1) {
3105 // Not fatal in itself. The next one might succeed.
3106 qCWarning(QT_BT_BLUEZ) << "accept() failed:" << qt_error_string(errno);
3107 serverSocketNotifier->setEnabled(true);
3108 return;
3109 }
3110
3111 remoteDevice = QBluetoothAddress(convertAddress(clientAddr.l2_bdaddr.b));
3112 remoteName = nameOfRemoteCentral(remoteDevice, localAdapter);
3113 qCDebug(QT_BT_BLUEZ) << "GATT connection from device" << remoteDevice << remoteName;
3114
3115 if (connectionHandle == 0)
3116 qCWarning(QT_BT_BLUEZ) << "Received client connection, but no connection complete event";
3117
3118 if (l2cpSocket) {
3119 disconnect(l2cpSocket);
3120 if (l2cpSocket->isOpen())
3121 l2cpSocket->close();
3122
3123 l2cpSocket->deleteLater();
3124 l2cpSocket = nullptr;
3125 }
3126 closeServerSocket();
3127
3128 QBluetoothSocketPrivateBluez *rawSocketPrivate = new QBluetoothSocketPrivateBluez();
3129 l2cpSocket = new QBluetoothSocket(
3130 rawSocketPrivate, QBluetoothServiceInfo::L2capProtocol, this);
3131 connect(l2cpSocket, &QBluetoothSocket::disconnected,
3132 this, &QLowEnergyControllerPrivateBluez::l2cpDisconnected);
3133 connect(l2cpSocket, static_cast<void (QBluetoothSocket::*)(QBluetoothSocket::SocketError)>
3134 (&QBluetoothSocket::error), this, &QLowEnergyControllerPrivateBluez::l2cpErrorChanged);
3135 connect(l2cpSocket, &QIODevice::readyRead, this, &QLowEnergyControllerPrivateBluez::l2cpReadyRead);
3136 l2cpSocket->d_ptr->lowEnergySocketType = addressType == QLowEnergyController::PublicAddress
3137 ? BDADDR_LE_PUBLIC : BDADDR_LE_RANDOM;
3138 l2cpSocket->setSocketDescriptor(clientSocket, QBluetoothServiceInfo::L2capProtocol,
3139 QBluetoothSocket::ConnectedState, QIODevice::ReadWrite | QIODevice::Unbuffered);
3140 restoreClientConfigurations();
3141 loadSigningDataIfNecessary(RemoteSigningKey);
3142
3143 Q_Q(QLowEnergyController);
3144 setState(QLowEnergyController::ConnectedState);
3145 emit q->connected();
3146 }
3147
closeServerSocket()3148 void QLowEnergyControllerPrivateBluez::closeServerSocket()
3149 {
3150 if (!serverSocketNotifier)
3151 return;
3152 serverSocketNotifier->disconnect();
3153 close(serverSocketNotifier->socket());
3154 serverSocketNotifier->deleteLater();
3155 serverSocketNotifier = nullptr;
3156 }
3157
isBonded() const3158 bool QLowEnergyControllerPrivateBluez::isBonded() const
3159 {
3160 // Pairing does not necessarily imply bonding, but we don't know whether the
3161 // bonding flag was set in the original pairing request.
3162 return QBluetoothLocalDevice(localAdapter).pairingStatus(remoteDevice)
3163 != QBluetoothLocalDevice::Unpaired;
3164 }
3165
gatherClientConfigData()3166 QVector<QLowEnergyControllerPrivateBluez::TempClientConfigurationData> QLowEnergyControllerPrivateBluez::gatherClientConfigData()
3167 {
3168 QVector<TempClientConfigurationData> data;
3169 for (const auto &service : qAsConst(localServices)) {
3170 for (auto charIt = service->characteristicList.begin();
3171 charIt != service->characteristicList.end(); ++charIt) {
3172 QLowEnergyServicePrivate::CharData &charData = charIt.value();
3173 for (auto descIt = charData.descriptorList.begin();
3174 descIt != charData.descriptorList.end(); ++descIt) {
3175 QLowEnergyServicePrivate::DescData &descData = descIt.value();
3176 if (descData.uuid == QBluetoothUuid::ClientCharacteristicConfiguration) {
3177 data << TempClientConfigurationData(&descData, charData.valueHandle,
3178 descIt.key());
3179 break;
3180 }
3181 }
3182 }
3183 }
3184 return data;
3185 }
3186
storeClientConfigurations()3187 void QLowEnergyControllerPrivateBluez::storeClientConfigurations()
3188 {
3189 if (!isBonded()) {
3190 clientConfigData.remove(remoteDevice.toUInt64());
3191 return;
3192 }
3193 QVector<ClientConfigurationData> clientConfigs;
3194 const QVector<TempClientConfigurationData> &tempConfigList = gatherClientConfigData();
3195 for (const auto &tempConfigData : tempConfigList) {
3196 Q_ASSERT(tempConfigData.descData->value.count() == 2);
3197 const quint16 value = bt_get_le16(tempConfigData.descData->value.constData());
3198 if (value != 0) {
3199 clientConfigs << ClientConfigurationData(tempConfigData.charValueHandle,
3200 tempConfigData.configHandle, value);
3201 }
3202 }
3203 clientConfigData.insert(remoteDevice.toUInt64(), clientConfigs);
3204 }
3205
restoreClientConfigurations()3206 void QLowEnergyControllerPrivateBluez::restoreClientConfigurations()
3207 {
3208 const QVector<TempClientConfigurationData> &tempConfigList = gatherClientConfigData();
3209 const QVector<ClientConfigurationData> &restoredClientConfigs = isBonded()
3210 ? clientConfigData.value(remoteDevice.toUInt64()) : QVector<ClientConfigurationData>();
3211 QVector<QLowEnergyHandle> notifications;
3212 for (const auto &tempConfigData : tempConfigList) {
3213 bool wasRestored = false;
3214 for (const auto &restoredData : restoredClientConfigs) {
3215 if (restoredData.charValueHandle == tempConfigData.charValueHandle) {
3216 Q_ASSERT(tempConfigData.descData->value.count() == 2);
3217 putBtData(restoredData.configValue, tempConfigData.descData->value.data());
3218 wasRestored = true;
3219 if (restoredData.charValueWasUpdated) {
3220 if (isNotificationEnabled(restoredData.configValue))
3221 notifications << restoredData.charValueHandle;
3222 else if (isIndicationEnabled(restoredData.configValue))
3223 scheduledIndications << restoredData.charValueHandle;
3224 }
3225 break;
3226 }
3227 }
3228 if (!wasRestored)
3229 tempConfigData.descData->value = QByteArray(2, 0); // Default value.
3230 Q_ASSERT(lastLocalHandle >= tempConfigData.configHandle);
3231 Q_ASSERT(tempConfigData.configHandle > tempConfigData.charValueHandle);
3232 localAttributes[tempConfigData.configHandle].value = tempConfigData.descData->value;
3233 }
3234
3235 for (const QLowEnergyHandle handle : qAsConst(notifications))
3236 sendNotification(handle);
3237 sendNextIndication();
3238 }
3239
loadSigningDataIfNecessary(SigningKeyType keyType)3240 void QLowEnergyControllerPrivateBluez::loadSigningDataIfNecessary(SigningKeyType keyType)
3241 {
3242 const auto signingDataIt = signingData.constFind(remoteDevice.toUInt64());
3243 if (signingDataIt != signingData.constEnd())
3244 return; // We are up to date for this device.
3245 const QString settingsFilePath = keySettingsFilePath();
3246 if (!QFileInfo(settingsFilePath).exists()) {
3247 qCDebug(QT_BT_BLUEZ) << "No settings found for peer device.";
3248 return;
3249 }
3250 QSettings settings(settingsFilePath, QSettings::IniFormat);
3251 const QString group = signingKeySettingsGroup(keyType);
3252 settings.beginGroup(group);
3253 const QByteArray keyString = settings.value(QLatin1String("Key")).toByteArray();
3254 if (keyString.isEmpty()) {
3255 qCDebug(QT_BT_BLUEZ) << "Group" << group << "not found in settings file";
3256 return;
3257 }
3258 const QByteArray keyData = QByteArray::fromHex(keyString);
3259 if (keyData.count() != int(sizeof(quint128))) {
3260 qCWarning(QT_BT_BLUEZ) << "Signing key in settings file has invalid size"
3261 << keyString.count();
3262 return;
3263 }
3264 qCDebug(QT_BT_BLUEZ) << "CSRK of peer device is" << keyString;
3265 const quint32 counter = settings.value(QLatin1String("Counter"), 0).toUInt();
3266 quint128 csrk;
3267 using namespace std;
3268 memcpy(csrk.data, keyData.constData(), keyData.count());
3269 signingData.insert(remoteDevice.toUInt64(), SigningData(csrk, counter - 1));
3270 }
3271
storeSignCounter(SigningKeyType keyType) const3272 void QLowEnergyControllerPrivateBluez::storeSignCounter(SigningKeyType keyType) const
3273 {
3274 const auto signingDataIt = signingData.constFind(remoteDevice.toUInt64());
3275 if (signingDataIt == signingData.constEnd())
3276 return;
3277 const QString settingsFilePath = keySettingsFilePath();
3278 if (!QFileInfo(settingsFilePath).exists())
3279 return;
3280 QSettings settings(settingsFilePath, QSettings::IniFormat);
3281 if (!settings.isWritable())
3282 return;
3283 settings.beginGroup(signingKeySettingsGroup(keyType));
3284 const QString counterKey = QLatin1String("Counter");
3285 if (!settings.allKeys().contains(counterKey))
3286 return;
3287 const quint32 counterValue = signingDataIt.value().counter + 1;
3288 if (counterValue == settings.value(counterKey).toUInt())
3289 return;
3290 settings.setValue(counterKey, counterValue);
3291 }
3292
signingKeySettingsGroup(SigningKeyType keyType) const3293 QString QLowEnergyControllerPrivateBluez::signingKeySettingsGroup(SigningKeyType keyType) const
3294 {
3295 return QLatin1String(keyType == LocalSigningKey ? "LocalSignatureKey" : "RemoteSignatureKey");
3296 }
3297
keySettingsFilePath() const3298 QString QLowEnergyControllerPrivateBluez::keySettingsFilePath() const
3299 {
3300 return QString::fromLatin1("/var/lib/bluetooth/%1/%2/info")
3301 .arg(localAdapter.toString(), remoteDevice.toString());
3302 }
3303
uuidToByteArray(const QBluetoothUuid & uuid)3304 static QByteArray uuidToByteArray(const QBluetoothUuid &uuid)
3305 {
3306 QByteArray ba;
3307 if (uuid.minimumSize() == 2) {
3308 ba.resize(2);
3309 putBtData(uuid.toUInt16(), ba.data());
3310 } else {
3311 ba.resize(16);
3312 quint128 hostOrder;
3313 quint128 qtUuidOrder = uuid.toUInt128();
3314 ntoh128(&qtUuidOrder, &hostOrder);
3315 putBtData(hostOrder, ba.data());
3316 }
3317 return ba;
3318 }
3319
addToGenericAttributeList(const QLowEnergyServiceData & service,QLowEnergyHandle startHandle)3320 void QLowEnergyControllerPrivateBluez::addToGenericAttributeList(const QLowEnergyServiceData &service,
3321 QLowEnergyHandle startHandle)
3322 {
3323 // Construct generic attribute data for the service with handles as keys.
3324 // Otherwise a number of request handling functions will be awkward to write
3325 // as well as computationally inefficient.
3326
3327 localAttributes.resize(lastLocalHandle + 1);
3328 Attribute serviceAttribute;
3329 serviceAttribute.handle = startHandle;
3330 serviceAttribute.type = QBluetoothUuid(static_cast<quint16>(service.type()));
3331 serviceAttribute.properties = QLowEnergyCharacteristic::Read;
3332 serviceAttribute.value = uuidToByteArray(service.uuid());
3333 QLowEnergyHandle currentHandle = startHandle;
3334 const QList<QLowEnergyService *> includedServices = service.includedServices();
3335 for (const QLowEnergyService * const service : includedServices) {
3336 Attribute attribute;
3337 attribute.handle = ++currentHandle;
3338 attribute.type = QBluetoothUuid(GATT_INCLUDED_SERVICE);
3339 attribute.properties = QLowEnergyCharacteristic::Read;
3340 const bool includeUuidInValue = service->serviceUuid().minimumSize() == 2;
3341 attribute.value.resize((2 + includeUuidInValue) * sizeof(QLowEnergyHandle));
3342 char *valueData = attribute.value.data();
3343 putDataAndIncrement(service->d_ptr->startHandle, valueData);
3344 putDataAndIncrement(service->d_ptr->endHandle, valueData);
3345 if (includeUuidInValue)
3346 putDataAndIncrement(service->serviceUuid(), valueData);
3347 localAttributes[attribute.handle] = attribute;
3348 }
3349 const QList<QLowEnergyCharacteristicData> characteristics = service.characteristics();
3350 for (const QLowEnergyCharacteristicData &cd : characteristics) {
3351 Attribute attribute;
3352
3353 // Characteristic declaration;
3354 attribute.handle = ++currentHandle;
3355 attribute.groupEndHandle = attribute.handle + 1 + cd.descriptors().count();
3356 attribute.type = QBluetoothUuid(GATT_CHARACTERISTIC);
3357 attribute.properties = QLowEnergyCharacteristic::Read;
3358 attribute.value.resize(1 + sizeof(QLowEnergyHandle) + cd.uuid().minimumSize());
3359 char *valueData = attribute.value.data();
3360 putDataAndIncrement(static_cast<quint8>(cd.properties()), valueData);
3361 putDataAndIncrement(QLowEnergyHandle(currentHandle + 1), valueData);
3362 putDataAndIncrement(cd.uuid(), valueData);
3363 localAttributes[attribute.handle] = attribute;
3364
3365 // Characteristic value declaration.
3366 attribute.handle = ++currentHandle;
3367 attribute.groupEndHandle = attribute.handle;
3368 attribute.type = cd.uuid();
3369 attribute.properties = cd.properties();
3370 attribute.readConstraints = cd.readConstraints();
3371 attribute.writeConstraints = cd.writeConstraints();
3372 attribute.value = cd.value();
3373 attribute.minLength = cd.minimumValueLength();
3374 attribute.maxLength = cd.maximumValueLength();
3375 localAttributes[attribute.handle] = attribute;
3376
3377 const QList<QLowEnergyDescriptorData> descriptors = cd.descriptors();
3378 for (const QLowEnergyDescriptorData &dd : descriptors) {
3379 attribute.handle = ++currentHandle;
3380 attribute.groupEndHandle = attribute.handle;
3381 attribute.type = dd.uuid();
3382 attribute.properties = QLowEnergyCharacteristic::PropertyTypes();
3383 attribute.readConstraints = AttAccessConstraints();
3384 attribute.writeConstraints = AttAccessConstraints();
3385 attribute.minLength = 0;
3386 attribute.maxLength = INT_MAX;
3387
3388 // Spec v4.2, Vol. 3, Part G, 3.3.3.x
3389 if (attribute.type == QBluetoothUuid::CharacteristicExtendedProperties) {
3390 attribute.properties = QLowEnergyCharacteristic::Read;
3391 attribute.minLength = attribute.maxLength = 2;
3392 } else if (attribute.type == QBluetoothUuid::CharacteristicPresentationFormat) {
3393 attribute.properties = QLowEnergyCharacteristic::Read;
3394 attribute.minLength = attribute.maxLength = 7;
3395 } else if (attribute.type == QBluetoothUuid::CharacteristicAggregateFormat) {
3396 attribute.properties = QLowEnergyCharacteristic::Read;
3397 attribute.minLength = 4;
3398 } else if (attribute.type == QBluetoothUuid::ClientCharacteristicConfiguration
3399 || attribute.type == QBluetoothUuid::ServerCharacteristicConfiguration) {
3400 attribute.properties = QLowEnergyCharacteristic::Read
3401 | QLowEnergyCharacteristic::Write
3402 | QLowEnergyCharacteristic::WriteNoResponse
3403 | QLowEnergyCharacteristic::WriteSigned;
3404 attribute.writeConstraints = dd.writeConstraints();
3405 attribute.minLength = attribute.maxLength = 2;
3406 } else {
3407 if (dd.isReadable())
3408 attribute.properties |= QLowEnergyCharacteristic::Read;
3409 if (dd.isWritable()) {
3410 attribute.properties |= QLowEnergyCharacteristic::Write;
3411 attribute.properties |= QLowEnergyCharacteristic::WriteNoResponse;
3412 attribute.properties |= QLowEnergyCharacteristic::WriteSigned;
3413 }
3414 attribute.readConstraints = dd.readConstraints();
3415 attribute.writeConstraints = dd.writeConstraints();
3416 }
3417
3418 attribute.value = dd.value();
3419 if (attribute.value.count() < attribute.minLength
3420 || attribute.value.count() > attribute.maxLength) {
3421 qCWarning(QT_BT_BLUEZ) << "attribute of type" << attribute.type
3422 << "has invalid length of" << attribute.value.count()
3423 << "bytes";
3424 attribute.value = QByteArray(attribute.minLength, 0);
3425 }
3426 localAttributes[attribute.handle] = attribute;
3427 }
3428 }
3429 serviceAttribute.groupEndHandle = currentHandle;
3430 localAttributes[serviceAttribute.handle] = serviceAttribute;
3431 }
3432
ensureUniformAttributes(QVector<Attribute> & attributes,const std::function<int (const Attribute &)> & getSize)3433 void QLowEnergyControllerPrivateBluez::ensureUniformAttributes(QVector<Attribute> &attributes,
3434 const std::function<int (const Attribute &)> &getSize)
3435 {
3436 if (attributes.isEmpty())
3437 return;
3438 const int firstSize = getSize(attributes.first());
3439 const auto it = std::find_if(attributes.begin() + 1, attributes.end(),
3440 [firstSize, getSize](const Attribute &attr) { return getSize(attr) != firstSize; });
3441 if (it != attributes.end())
3442 attributes.erase(it, attributes.end());
3443
3444 }
3445
ensureUniformUuidSizes(QVector<Attribute> & attributes)3446 void QLowEnergyControllerPrivateBluez::ensureUniformUuidSizes(QVector<Attribute> &attributes)
3447 {
3448 ensureUniformAttributes(attributes,
3449 [](const Attribute &attr) { return getUuidSize(attr.type); });
3450 }
3451
ensureUniformValueSizes(QVector<Attribute> & attributes)3452 void QLowEnergyControllerPrivateBluez::ensureUniformValueSizes(QVector<Attribute> &attributes)
3453 {
3454 ensureUniformAttributes(attributes,
3455 [](const Attribute &attr) { return attr.value.count(); });
3456 }
3457
getAttributes(QLowEnergyHandle startHandle,QLowEnergyHandle endHandle,const AttributePredicate & attributePredicate)3458 QVector<QLowEnergyControllerPrivateBluez::Attribute> QLowEnergyControllerPrivateBluez::getAttributes(QLowEnergyHandle startHandle,
3459 QLowEnergyHandle endHandle, const AttributePredicate &attributePredicate)
3460 {
3461 QVector<Attribute> results;
3462 if (startHandle > lastLocalHandle)
3463 return results;
3464 if (lastLocalHandle == 0) // We have no services at all.
3465 return results;
3466 Q_ASSERT(startHandle <= endHandle); // Must have been checked before.
3467 const QLowEnergyHandle firstHandle = qMin(startHandle, lastLocalHandle);
3468 const QLowEnergyHandle lastHandle = qMin(endHandle, lastLocalHandle);
3469 for (QLowEnergyHandle i = firstHandle; i <= lastHandle; ++i) {
3470 const Attribute &attr = localAttributes.at(i);
3471 if (attributePredicate(attr))
3472 results << attr;
3473 }
3474 return results;
3475 }
3476
checkPermissions(const Attribute & attr,QLowEnergyCharacteristic::PropertyType type)3477 int QLowEnergyControllerPrivateBluez::checkPermissions(const Attribute &attr,
3478 QLowEnergyCharacteristic::PropertyType type)
3479 {
3480 const bool isReadAccess = type == QLowEnergyCharacteristic::Read;
3481 const bool isWriteCommand = type == QLowEnergyCharacteristic::WriteNoResponse;
3482 const bool isWriteAccess = type == QLowEnergyCharacteristic::Write
3483 || type == QLowEnergyCharacteristic::WriteSigned
3484 || isWriteCommand;
3485 Q_ASSERT(isReadAccess || isWriteAccess);
3486 if (!(attr.properties & type)) {
3487 if (isReadAccess)
3488 return ATT_ERROR_READ_NOT_PERM;
3489
3490 // The spec says: If an attribute requires a signed write, then a non-signed write command
3491 // can also be used if the link is encrypted.
3492 const bool unsignedWriteOk = isWriteCommand
3493 && (attr.properties & QLowEnergyCharacteristic::WriteSigned)
3494 && securityLevel() >= BT_SECURITY_MEDIUM;
3495 if (!unsignedWriteOk)
3496 return ATT_ERROR_WRITE_NOT_PERM;
3497 }
3498 const AttAccessConstraints constraints = isReadAccess
3499 ? attr.readConstraints : attr.writeConstraints;
3500 if (constraints.testFlag(AttAuthorizationRequired))
3501 return ATT_ERROR_INSUF_AUTHORIZATION; // TODO: emit signal (and offer authorization function)?
3502 if (constraints.testFlag(AttEncryptionRequired) && securityLevel() < BT_SECURITY_MEDIUM)
3503 return ATT_ERROR_INSUF_ENCRYPTION;
3504 if (constraints.testFlag(AttAuthenticationRequired) && securityLevel() < BT_SECURITY_HIGH)
3505 return ATT_ERROR_INSUF_AUTHENTICATION;
3506 if (false)
3507 return ATT_ERROR_INSUF_ENCR_KEY_SIZE;
3508 return 0;
3509 }
3510
checkReadPermissions(const Attribute & attr)3511 int QLowEnergyControllerPrivateBluez::checkReadPermissions(const Attribute &attr)
3512 {
3513 return checkPermissions(attr, QLowEnergyCharacteristic::Read);
3514 }
3515
checkReadPermissions(QVector<Attribute> & attributes)3516 int QLowEnergyControllerPrivateBluez::checkReadPermissions(QVector<Attribute> &attributes)
3517 {
3518 if (attributes.isEmpty())
3519 return 0;
3520
3521 // The logic prescribed in the spec is as follows:
3522 // 1) If the first in a list of matching attributes has a permissions error,
3523 // then that error is returned via an error response.
3524 // 2) If any other element of that list would cause a permissions error, then all
3525 // attributes from this one on are not part of the result set, but no error is returned.
3526 const int error = checkReadPermissions(attributes.first());
3527 if (error)
3528 return error;
3529 const auto it = std::find_if(attributes.begin() + 1, attributes.end(),
3530 [this](const Attribute &attr) { return checkReadPermissions(attr) != 0; });
3531 if (it != attributes.end())
3532 attributes.erase(it, attributes.end());
3533 return 0;
3534 }
3535
verifyMac(const QByteArray & message,const quint128 & csrk,quint32 signCounter,quint64 expectedMac)3536 bool QLowEnergyControllerPrivateBluez::verifyMac(const QByteArray &message, const quint128 &csrk,
3537 quint32 signCounter, quint64 expectedMac)
3538 {
3539 if (!cmacCalculator)
3540 cmacCalculator = new LeCmacCalculator;
3541 return cmacCalculator->verify(LeCmacCalculator::createFullMessage(message, signCounter), csrk,
3542 expectedMac);
3543 }
3544
3545 QT_END_NAMESPACE
3546