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 &params) {
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 &params,
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 &params)
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