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
158QT_BEGIN_NAMESPACE
159
160Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
161
162using namespace QBluetooth;
163
164const int maxPrepareQueueSize = 1024;
165
166static 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
182static 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
243static int getUuidSize(const QBluetoothUuid &uuid)
244{
245 return uuid.minimumSize() == 2 ? 2 : 16;
246}
247
248template<typename T> static void putDataAndIncrement(const T &src, char *&dst)
249{
250 putBtData(src, dst);
251 dst += sizeof(T);
252}
253template<> 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}
266template<> 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
273QLowEnergyControllerPrivateBluez::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
284void 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
341void 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
420QLowEnergyControllerPrivateBluez::~QLowEnergyControllerPrivateBluez()
421{
422 closeServerSocket();
423 delete cmacCalculator;
424}
425
426class ServerSocket
427{
428public:
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
458 ~ServerSocket()
459 {
460 if (m_socket != -1)
461 close(m_socket);
462 }
463
464 int takeSocket()
465 {
466 const int socket = m_socket;
467 m_socket = -1;
468 return socket;
469 }
470
471private:
472 int m_socket = -1;
473};
474
475
476void 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
509void QLowEnergyControllerPrivateBluez::stopAdvertising()
510{
511 setState(QLowEnergyController::UnconnectedState);
512 advertiser->stopAdvertising();
513}
514
515void 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
527void 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 */
579void 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 */
602void 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
658void 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
724void QLowEnergyControllerPrivateBluez::l2cpConnected()
725{
726 Q_Q(QLowEnergyController);
727
728 securityLevelValue = securityLevel();
729 exchangeMTU();
730
731 setState(QLowEnergyController::ConnectedState);
732 emit q->connected();
733}
734
735void 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
750void 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
765void 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
798void 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
822void QLowEnergyControllerPrivateBluez::restartRequestTimer()
823{
824 if (!requestTimer)
825 return;
826
827 if (gattRequestTimeout > 0)
828 requestTimer->start(gattRequestTimeout);
829}
830
831void 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 */
929void 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
978void 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
997void 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
1011QLowEnergyHandle 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
1036QLowEnergyHandle 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
1062void 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
1600void QLowEnergyControllerPrivateBluez::discoverServices()
1601{
1602 sendReadByGroupRequest(0x0001, 0xFFFF, GATT_PRIMARY_SERVICE);
1603}
1604
1605void 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
1630void 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
1643void 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 */
1679void 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 */
1773void 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
1812void 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
1832void 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
1856void 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
1875int 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
1914bool 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
1961void 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
1998void 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 */
2053void 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 */
2085void 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
2103void 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 */
2122void 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
2160void 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 */
2199bool 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
2227void QLowEnergyControllerPrivateBluez::handleAdvertisingError()
2228{
2229 qCWarning(QT_BT_BLUEZ) << "received advertising error";
2230 setError(QLowEnergyController::AdvertisingError);
2231 setState(QLowEnergyController::UnconnectedState);
2232}
2233
2234bool 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
2247bool 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
2255bool 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
2266void 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
2293void 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
2326void 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
2361void 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
2414void 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(