11#include "bluez/bluez_data_p.h"
12#include "bluez/hcimanager_p.h"
13#include "bluez/objectmanager_p.h"
14#include "bluez/remotedevicemanager_p.h"
15#include "bluez/bluez5_helper_p.h"
16#include "bluez/bluetoothmanagement_p.h"
18#include <QtCore/QFileInfo>
19#include <QtCore/QLoggingCategory>
20#include <QtCore/QSettings>
21#include <QtCore/QTimer>
22#include <QtBluetooth/QBluetoothLocalDevice>
23#include <QtBluetooth/QBluetoothSocket>
24#include <QtBluetooth/QLowEnergyCharacteristicData>
25#include <QtBluetooth/QLowEnergyDescriptorData>
26#include <QtBluetooth/QLowEnergyService>
27#include <QtBluetooth/QLowEnergyServiceData>
34#include <sys/socket.h>
40#define GATT_PRIMARY_SERVICE quint16(0x2800
)
41#define GATT_SECONDARY_SERVICE quint16(0x2801
)
42#define GATT_INCLUDED_SERVICE quint16(0x2802
)
43#define GATT_CHARACTERISTIC quint16(0x2803
)
46#define ERROR_RESPONSE_HEADER_SIZE 5
47#define FIND_INFO_REQUEST_HEADER_SIZE 5
48#define GRP_TYPE_REQ_HEADER_SIZE 7
49#define READ_BY_TYPE_REQ_HEADER_SIZE 7
50#define READ_REQUEST_HEADER_SIZE 3
51#define READ_BLOB_REQUEST_HEADER_SIZE 5
52#define WRITE_REQUEST_HEADER_SIZE 3
53#define PREPARE_WRITE_HEADER_SIZE 5
54#define EXECUTE_WRITE_HEADER_SIZE 2
55#define MTU_EXCHANGE_HEADER_SIZE 3
57#define APPEND_VALUE true
58#define NEW_VALUE false
62Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
64using namespace QBluetooth;
72 const char *data = response.constData();
73 if (response.size() != 5
76 qCWarning(QT_BT_BLUEZ) << QLatin1String(
"Not a valid error response");
81 quint16 handle = bt_get_le16(&data[2]);
87 errorString = QStringLiteral(
"invalid handle");
break;
89 errorString = QStringLiteral(
"not readable attribute - permissions");
break;
91 errorString = QStringLiteral(
"not writable attribute - permissions");
break;
93 errorString = QStringLiteral(
"PDU invalid");
break;
95 errorString = QStringLiteral(
"needs authentication - permissions");
break;
97 errorString = QStringLiteral(
"server does not support request");
break;
99 errorString = QStringLiteral(
"offset past end of attribute");
break;
101 errorString = QStringLiteral(
"need authorization - permissions");
break;
103 errorString = QStringLiteral(
"run out of prepare queue space");
break;
105 errorString = QStringLiteral(
"no attribute in given range found");
break;
107 errorString = QStringLiteral(
"attribute not read/written using read blob");
break;
109 errorString = QStringLiteral(
"need encryption key size - permissions");
break;
111 errorString = QStringLiteral(
"written value is invalid size");
break;
113 errorString = QStringLiteral(
"unlikely error");
break;
115 errorString = QStringLiteral(
"needs encryption - permissions");
break;
117 errorString = QStringLiteral(
"unsupported group type");
break;
119 errorString = QStringLiteral(
"insufficient resources to complete request");
break;
124 QStringLiteral(
"application error: %1").arg(
static_cast<quint8>(errorCode));
126 errorString = QStringLiteral(
"unknown error code");
130 qCDebug(QT_BT_BLUEZ) <<
"Error:" << errorCode <<
"Error description:" << errorString
131 <<
"last command:" << lastCommand <<
"handle:" << handle;
138 return uuid.minimumSize() == 2 ? 2 : 16;
149 quint16 uuid16 = uuid.toUInt16(&ok);
151 putBtData(uuid16, dst);
152 dst +=
sizeof(uuid16);
154 QUuid::Id128Bytes btOrder = uuid.toBytes(QSysInfo::LittleEndian);
155 memcpy(dst, btOrder.data,
sizeof(btOrder));
156 dst +=
sizeof(btOrder);
162 memcpy(dst, value.constData(), value.size());
166QLowEnergyControllerPrivateBluez::QLowEnergyControllerPrivateBluez()
168 requestPending(
false),
169 mtuSize(ATT_DEFAULT_LE_MTU),
170 securityLevelValue(-1),
171 encryptionChangePending(
false)
174 qRegisterMetaType<QList<QLowEnergyHandle> >();
177void QLowEnergyControllerPrivateBluez::
init()
180 hciManager = std::make_shared<HciManager>(localAdapter);
182 if (!hciManager->isValid()){
183 setError(QLowEnergyController::InvalidBluetoothAdapterError);
187 hciManager->monitorEvent(HciManager::HciEvent::EVT_ENCRYPT_CHANGE);
188 connect(hciManager.get(), SIGNAL(encryptionChangedEvent(QBluetoothAddress,
bool)),
189 this, SLOT(encryptionChangedEvent(QBluetoothAddress,
bool)));
190 hciManager->monitorEvent(HciManager::HciEvent::EVT_LE_META_EVENT);
191 hciManager->monitorAclPackets();
192 connect(hciManager.get(), &HciManager::connectionComplete,
this, [
this](quint16 handle) {
193 connectionHandle = handle;
194 qCDebug(QT_BT_BLUEZ) <<
"received connection complete event, handle:" << handle;
196 connect(hciManager.get(), &HciManager::connectionUpdate,
this,
197 [
this](quint16 handle,
const QLowEnergyConnectionParameters ¶ms) {
198 if (handle == connectionHandle)
199 emit q_ptr->connectionUpdated(params);
202 connect(hciManager.get(), &HciManager::signatureResolvingKeyReceived,
this,
203 [
this](quint16 handle,
bool remoteKey,
const QUuid::Id128Bytes &csrk) {
204 if (handle != connectionHandle)
206 if ((remoteKey && role == QLowEnergyController::CentralRole)
207 || (!remoteKey && role == QLowEnergyController::PeripheralRole)) {
210 qCDebug(QT_BT_BLUEZ) <<
"received new signature resolving key"
211 << QByteArray(
reinterpret_cast<
const char *>(csrk.data),
212 sizeof csrk).toHex();
213 signingData.insert(remoteDevice.toUInt64(), SigningData(csrk));
217 if (role == QLowEnergyController::CentralRole) {
218 if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(
"BLUETOOTH_GATT_TIMEOUT"))) {
220 int value = qEnvironmentVariableIntValue(
"BLUETOOTH_GATT_TIMEOUT", &ok);
222 gattRequestTimeout = value;
226 if (gattRequestTimeout > 0) {
227 qCWarning(QT_BT_BLUEZ) <<
"Enabling GATT request timeout behavior" << gattRequestTimeout;
228 requestTimer =
new QTimer(
this);
229 requestTimer->setSingleShot(
true);
230 requestTimer->setInterval(gattRequestTimeout);
231 connect(requestTimer, &QTimer::timeout,
232 this, &QLowEnergyControllerPrivateBluez::handleGattRequestTimeout);
237void QLowEnergyControllerPrivateBluez::handleGattRequestTimeout()
240 if (encryptionChangePending) {
242 qCWarning(QT_BT_BLUEZ) <<
"****** Encryption change event blocking further GATT requests";
246 if (!openRequests.isEmpty() && requestPending) {
247 const Request currentRequest = openRequests.dequeue();
248 requestPending =
false;
250 qCWarning(QT_BT_BLUEZ).nospace() <<
"****** Request type 0x" << currentRequest.command
251 <<
" to server/peripheral timed out";
252 qCWarning(QT_BT_BLUEZ) <<
"****** Looks like the characteristic or descriptor does NOT act in"
253 <<
"accordance to Bluetooth 4.x spec.";
254 qCWarning(QT_BT_BLUEZ) <<
"****** Please check server implementation."
255 <<
"Continuing under reservation.";
259 QLowEnergyHandle handle) {
262 errorPackage[1] =
static_cast<quint8>(
264 putBtData(handle, errorPackage.data() + 2);
275 case QBluezConst::AttCommand::ATT_OP_READ_BY_GROUP_REQUEST:
277 case QBluezConst::AttCommand::ATT_OP_READ_BY_TYPE_REQUEST:
281 processReply(currentRequest, createRequestErrorMessage(command, 0));
289 uint handleData = currentRequest.reference.toUInt();
290 const QLowEnergyHandle charHandle = (handleData & 0xffff);
291 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
292 processReply(currentRequest, createRequestErrorMessage(command,
293 descriptorHandle ? descriptorHandle : charHandle));
295 case QBluezConst::AttCommand::ATT_OP_FIND_INFORMATION_REQUEST:
296 processReply(currentRequest, createRequestErrorMessage(
297 command, currentRequest.reference2.toUInt()));
304 uint handleData = currentRequest.reference.toUInt();
305 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
306 processReply(currentRequest,
307 createRequestErrorMessage(command, attrHandle));
311 qCWarning(QT_BT_BLUEZ) <<
"Missing response for ATT peripheral command: "
312 << Qt::hex << command;
317 sendNextPendingRequest();
324 delete cmacCalculator;
325 cmacCalculator =
nullptr;
331 bool listen(
const QBluetoothAddress &localAdapter)
333 m_socket = ::socket(AF_BLUETOOTH, SOCK_SEQPACKET,
BTPROTO_L2CAP);
334 if (m_socket == -1) {
335 qCWarning(QT_BT_BLUEZ) <<
"socket creation failed:" << qt_error_string(errno);
343 memset(&addr, 0,
sizeof addr);
345 addr.l2_family = AF_BLUETOOTH;
348 convertAddress(localAdapter.toUInt64(), addr.l2_bdaddr.b);
349 if (::bind(m_socket,
reinterpret_cast<sockaddr *>(&addr),
sizeof addr) == -1) {
350 qCWarning(QT_BT_BLUEZ) <<
"bind() failed:" << qt_error_string(errno);
353 if (::listen(m_socket, 1)) {
354 qCWarning(QT_BT_BLUEZ) <<
"listen() failed:" << qt_error_string(errno);
368 const int socket = m_socket;
378void QLowEnergyControllerPrivateBluez::
startAdvertising(
const QLowEnergyAdvertisingParameters ¶ms,
379 const QLowEnergyAdvertisingData &advertisingData,
380 const QLowEnergyAdvertisingData &scanResponseData)
382 qCDebug(QT_BT_BLUEZ) <<
"Starting to advertise";
384 advertiser =
new QLeAdvertiserBluez(params, advertisingData, scanResponseData, hciManager,
386 connect(advertiser, &QLeAdvertiser::errorOccurred,
this,
387 &QLowEnergyControllerPrivateBluez::handleAdvertisingError);
389 setState(QLowEnergyController::AdvertisingState);
390 advertiser->startAdvertising();
391 if (params.mode() == QLowEnergyAdvertisingParameters::AdvNonConnInd
392 || params.mode() == QLowEnergyAdvertisingParameters::AdvScanInd) {
393 qCDebug(QT_BT_BLUEZ) <<
"Non-connectable advertising requested, "
394 "not listening for connections.";
399 if (!serverSocket.listen(localAdapter)) {
400 setError(QLowEnergyController::AdvertisingError);
401 setState(QLowEnergyController::UnconnectedState);
406 serverSocketNotifier =
new QSocketNotifier(socketFd, QSocketNotifier::Read,
this);
407 connect(serverSocketNotifier, &QSocketNotifier::activated,
this,
408 &QLowEnergyControllerPrivateBluez::handleConnectionRequest);
413 setState(QLowEnergyController::UnconnectedState);
414 advertiser->stopAdvertising();
417void QLowEnergyControllerPrivateBluez::requestConnectionUpdate(
const QLowEnergyConnectionParameters ¶ms)
423 if (role == QLowEnergyController::CentralRole)
424 hciManager->sendConnectionUpdateCommand(connectionHandle, params);
426 hciManager->sendConnectionParameterUpdateRequest(connectionHandle, params);
431 if (remoteDevice.isNull()) {
432 qCWarning(QT_BT_BLUEZ) <<
"Invalid/null remote device address";
433 setError(QLowEnergyController::UnknownRemoteDeviceError);
437 setState(QLowEnergyController::ConnectingState);
440 l2cpSocket =
nullptr;
443 createServicesForCentralIfRequired();
449 QList<quint16> activeHandles = hciManager->activeLowEnergyConnections();
450 if (!activeHandles.isEmpty()) {
451 qCWarning(QT_BT_BLUEZ) <<
"Cannot connect due to pending active LE connections";
453 if (!device1Manager) {
454 device1Manager =
new RemoteDeviceManager(localAdapter,
this);
455 connect(device1Manager, &RemoteDeviceManager::finished,
456 this, &QLowEnergyControllerPrivateBluez::activeConnectionTerminationDone);
459 QList<QBluetoothAddress> connectedAddresses;
460 for (
const auto handle: activeHandles) {
461 const QBluetoothAddress addr = hciManager->addressForConnectionHandle(handle);
463 connectedAddresses.push_back(addr);
465 device1Manager->scheduleJob(RemoteDeviceManager::JobType::JobDisconnectDevice, connectedAddresses);
467 establishL2cpClientSocket();
472
473
474void QLowEnergyControllerPrivateBluez::activeConnectionTerminationDone()
479 qCDebug(QT_BT_BLUEZ) <<
"RemoteDeviceManager finished attempting"
480 <<
"to close external connections";
482 QList<quint16> activeHandles = hciManager->activeLowEnergyConnections();
483 if (!activeHandles.isEmpty()) {
484 qCWarning(QT_BT_BLUEZ) <<
"Cannot close pending external BTLE connections. Aborting connect attempt";
485 setError(QLowEnergyController::ConnectionError);
486 setState(QLowEnergyController::UnconnectedState);
490 establishL2cpClientSocket();
495
496
497void QLowEnergyControllerPrivateBluez::establishL2cpClientSocket()
501 l2cpSocket =
new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol,
this);
502 connect(l2cpSocket, SIGNAL(connected()),
this, SLOT(l2cpConnected()));
503 connect(l2cpSocket, SIGNAL(disconnected()),
this, SLOT(l2cpDisconnected()));
504 connect(l2cpSocket, SIGNAL(errorOccurred(QBluetoothSocket::SocketError)),
this,
505 SLOT(l2cpErrorChanged(QBluetoothSocket::SocketError)));
506 connect(l2cpSocket, SIGNAL(readyRead()),
this, SLOT(l2cpReadyRead()));
508 quint32 addressTypeToUse = (addressType == QLowEnergyController::PublicAddress)
512 if (BluetoothManagement::instance()->isAddressRandom(remoteDevice)) {
517 qCDebug(QT_BT_BLUEZ) <<
"addresstypeToUse:"
519 ? QStringLiteral(
"Random") : QStringLiteral(
"Public"));
521 l2cpSocket->d_ptr->lowEnergySocketType = addressTypeToUse;
523 int sockfd = l2cpSocket->socketDescriptor();
525 qCWarning(QT_BT_BLUEZ) <<
"l2cp socket not initialised";
526 setError(QLowEnergyController::ConnectionError);
527 setState(QLowEnergyController::UnconnectedState);
532 memset(&addr, 0,
sizeof(addr));
533 addr.l2_family = AF_BLUETOOTH;
536 convertAddress(localAdapter.toUInt64(), addr.l2_bdaddr.b);
539 if (::bind(sockfd, (
struct sockaddr *)&addr,
sizeof(addr)) < 0) {
540 qCWarning(QT_BT_BLUEZ) << qt_error_string(errno);
541 setError(QLowEnergyController::ConnectionError);
542 setState(QLowEnergyController::UnconnectedState);
549 QIODevice::ReadWrite | QIODevice::Unbuffered);
550 loadSigningDataIfNecessary(LocalSigningKey);
553void QLowEnergyControllerPrivateBluez::createServicesForCentralIfRequired()
556 int value = qEnvironmentVariableIntValue(
"QT_DEFAULT_CENTRAL_SERVICES", &ok);
557 if (Q_UNLIKELY(ok && value == 0))
561 if (localServices.contains(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::GenericAccess)))
564 qCDebug(QT_BT_BLUEZ) <<
"Creating default GAP/GATT services";
568 QLowEnergyServiceData gapServiceData;
569 gapServiceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
570 gapServiceData.setUuid(QBluetoothUuid::ServiceClassUuid::GenericAccess);
572 QLowEnergyCharacteristicData gapDeviceName;
573 gapDeviceName.setUuid(QBluetoothUuid::CharacteristicType::DeviceName);
574 gapDeviceName.setProperties(QLowEnergyCharacteristic::Read);
576 QBluetoothLocalDevice mainAdapter;
577 gapDeviceName.setValue(mainAdapter.name().toLatin1());
579 QLowEnergyCharacteristicData gapAppearance;
580 gapAppearance.setUuid(QBluetoothUuid::CharacteristicType::Appearance);
581 gapAppearance.setProperties(QLowEnergyCharacteristic::Read);
582 gapAppearance.setValue(QByteArray::fromHex(
"80"));
584 QLowEnergyCharacteristicData gapPrivacyFlag;
585 gapPrivacyFlag.setUuid(QBluetoothUuid::CharacteristicType::PeripheralPrivacyFlag);
586 gapPrivacyFlag.setProperties(QLowEnergyCharacteristic::Read);
587 gapPrivacyFlag.setValue(QByteArray::fromHex(
"00"));
589 gapServiceData.addCharacteristic(gapDeviceName);
590 gapServiceData.addCharacteristic(gapAppearance);
591 gapServiceData.addCharacteristic(gapPrivacyFlag);
593 Q_Q(QLowEnergyController);
594 QLowEnergyService *service = addServiceHelper(gapServiceData);
596 service->setParent(q);
598 QLowEnergyServiceData gattServiceData;
599 gattServiceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
600 gattServiceData.setUuid(QBluetoothUuid::ServiceClassUuid::GenericAttribute);
602 QLowEnergyCharacteristicData serviceChangedChar;
603 serviceChangedChar.setUuid(QBluetoothUuid::CharacteristicType::ServiceChanged);
604 serviceChangedChar.setProperties(QLowEnergyCharacteristic::Indicate);
606 serviceChangedChar.setValue(QByteArray::fromHex(
"0104"));
608 const QLowEnergyDescriptorData clientConfig(
609 QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
611 serviceChangedChar.addDescriptor(clientConfig);
612 gattServiceData.addCharacteristic(serviceChangedChar);
614 service = addServiceHelper(gattServiceData);
616 service->setParent(q);
619void QLowEnergyControllerPrivateBluez::l2cpConnected()
621 Q_Q(QLowEnergyController);
623 securityLevelValue = securityLevel();
626 setState(QLowEnergyController::ConnectedState);
632 setState(QLowEnergyController::ClosingState);
640 qWarning(QT_BT_BLUEZ) <<
"Unexpected closure of device. Cleaning up internal states.";
645void QLowEnergyControllerPrivateBluez::l2cpDisconnected()
647 Q_Q(QLowEnergyController);
649 if (role == QLowEnergyController::PeripheralRole) {
650 storeClientConfigurations();
651 remoteDevice.clear();
656 setState(QLowEnergyController::UnconnectedState);
657 emit q->disconnected();
660void QLowEnergyControllerPrivateBluez::l2cpErrorChanged(QBluetoothSocket::SocketError e)
663 case QBluetoothSocket::SocketError::HostNotFoundError:
664 setError(QLowEnergyController::UnknownRemoteDeviceError);
665 qCDebug(QT_BT_BLUEZ) <<
"The passed remote device address cannot be found";
667 case QBluetoothSocket::SocketError::NetworkError:
668 setError(QLowEnergyController::NetworkError);
669 qCDebug(QT_BT_BLUEZ) <<
"Network IO error while talking to LE device";
671 case QBluetoothSocket::SocketError::RemoteHostClosedError:
672 setError(QLowEnergyController::RemoteHostClosedError);
673 qCDebug(QT_BT_BLUEZ) <<
"Remote host closed the connection";
675 case QBluetoothSocket::SocketError::UnknownSocketError:
676 case QBluetoothSocket::SocketError::UnsupportedProtocolError:
677 case QBluetoothSocket::SocketError::OperationError:
678 case QBluetoothSocket::SocketError::ServiceNotFoundError:
682 qCDebug(QT_BT_BLUEZ) <<
"Unknown l2cp socket error: " << e << l2cpSocket->errorString();
683 setError(QLowEnergyController::UnknownError);
689 setState(QLowEnergyController::UnconnectedState);
693void QLowEnergyControllerPrivateBluez::resetController()
695 openRequests.clear();
696 openPrepareWriteRequests.clear();
697 scheduledIndications.clear();
698 indicationInFlight =
false;
699 requestPending =
false;
700 encryptionChangePending =
false;
701 receivedMtuExchangeRequest =
false;
702 mtuSize = ATT_DEFAULT_LE_MTU;
703 securityLevelValue = -1;
704 connectionHandle = 0;
706 if (role == QLowEnergyController::PeripheralRole) {
709 advertiser->stopAdvertising();
711 advertiser =
nullptr;
713 localAttributes.clear();
717void QLowEnergyControllerPrivateBluez::restartRequestTimer()
722 if (gattRequestTimeout > 0)
723 requestTimer->start(gattRequestTimeout);
726void QLowEnergyControllerPrivateBluez::l2cpReadyRead()
728 const QByteArray incomingPacket = l2cpSocket->readAll();
729 qCDebug(QT_BT_BLUEZ) <<
"Received size:" << incomingPacket.size() <<
"data:"
730 << incomingPacket.toHex();
731 if (incomingPacket.isEmpty())
738 processUnsolicitedReply(incomingPacket);
747 processUnsolicitedReply(incomingPacket);
752 case QBluezConst::AttCommand::ATT_OP_EXCHANGE_MTU_REQUEST:
753 handleExchangeMtuRequest(incomingPacket);
755 case QBluezConst::AttCommand::ATT_OP_FIND_INFORMATION_REQUEST:
756 handleFindInformationRequest(incomingPacket);
758 case QBluezConst::AttCommand::ATT_OP_FIND_BY_TYPE_VALUE_REQUEST:
759 handleFindByTypeValueRequest(incomingPacket);
761 case QBluezConst::AttCommand::ATT_OP_READ_BY_TYPE_REQUEST:
762 handleReadByTypeRequest(incomingPacket);
764 case QBluezConst::AttCommand::ATT_OP_READ_REQUEST:
765 handleReadRequest(incomingPacket);
767 case QBluezConst::AttCommand::ATT_OP_READ_BLOB_REQUEST:
768 handleReadBlobRequest(incomingPacket);
770 case QBluezConst::AttCommand::ATT_OP_READ_MULTIPLE_REQUEST:
771 handleReadMultipleRequest(incomingPacket);
773 case QBluezConst::AttCommand::ATT_OP_READ_BY_GROUP_REQUEST:
774 handleReadByGroupTypeRequest(incomingPacket);
776 case QBluezConst::AttCommand::ATT_OP_WRITE_REQUEST:
777 case QBluezConst::AttCommand::ATT_OP_WRITE_COMMAND:
778 case QBluezConst::AttCommand::ATT_OP_SIGNED_WRITE_COMMAND:
779 handleWriteRequestOrCommand(incomingPacket);
781 case QBluezConst::AttCommand::ATT_OP_PREPARE_WRITE_REQUEST:
782 handlePrepareWriteRequest(incomingPacket);
784 case QBluezConst::AttCommand::ATT_OP_EXECUTE_WRITE_REQUEST:
785 handleExecuteWriteRequest(incomingPacket);
788 if (indicationInFlight) {
789 indicationInFlight =
false;
790 sendNextIndication();
792 qCWarning(QT_BT_BLUEZ) <<
"received unexpected handle value confirmation";
798 requestPending =
false;
802 if (openRequests.isEmpty()) {
803 qCWarning(QT_BT_BLUEZ) <<
"Received unexpected packet from peer, disconnecting.";
808 const Request request = openRequests.dequeue();
809 processReply(request, incomingPacket);
811 sendNextPendingRequest();
815
816
817
818
819
820
821
822
823void QLowEnergyControllerPrivateBluez::encryptionChangedEvent(
824 const QBluetoothAddress &address,
bool wasSuccess)
826 if (!encryptionChangePending)
829 if (remoteDevice != address)
832 securityLevelValue = securityLevel();
839 Q_ASSERT(!openRequests.isEmpty());
840 Request failedRequest = openRequests.takeFirst();
844 uint ref = failedRequest.reference.toUInt();
845 const QLowEnergyHandle charHandle = (ref & 0xffff);
846 const QLowEnergyHandle descriptorHandle = ((ref >> 16) & 0xffff);
848 QSharedPointer<QLowEnergyServicePrivate> service
849 = serviceForHandle(charHandle);
850 if (!service.isNull() && service->characteristicList.contains(charHandle)) {
851 if (!descriptorHandle)
852 service->setError(QLowEnergyService::CharacteristicWriteError);
854 service->setError(QLowEnergyService::DescriptorWriteError);
857 uint handleData = failedRequest.reference.toUInt();
858 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
859 const QByteArray newValue = failedRequest.reference2.toByteArray();
864 sendExecuteWriteRequest(attrHandle, newValue,
true);
868 encryptionChangePending =
false;
869 sendNextPendingRequest();
872void QLowEnergyControllerPrivateBluez::sendPacket(
const QByteArray &packet)
874 qint64 result = l2cpSocket->write(packet.constData(),
880 qCDebug(QT_BT_BLUEZ) <<
"Cannot write L2CP packet:" << Qt::hex
882 << l2cpSocket->errorString();
883 setError(QLowEnergyController::NetworkError);
884 }
else if (result < packet.size()) {
885 qCWarning(QT_BT_BLUEZ) <<
"L2CP write request incomplete:"
886 << result <<
"of" << packet.size();
891void QLowEnergyControllerPrivateBluez::sendNextPendingRequest()
893 if (openRequests.isEmpty() || requestPending || encryptionChangePending)
896 const Request &request = openRequests.head();
900 requestPending =
true;
901 restartRequestTimer();
902 sendPacket(request.payload);
907 const char *data, quint16 elementLength)
911 Q_ASSERT(elementLength >= 5);
913 QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
914 charData->properties =
915 (QLowEnergyCharacteristic::PropertyTypes)(data[2] & 0xff);
916 charData->valueHandle = bt_get_le16(&data[3]);
919 if (elementLength == 7)
920 charData->uuid = QBluetoothUuid(bt_get_le16(&data[5]));
921 else if (elementLength == 21)
922 charData->uuid = QUuid::fromBytes(&data[5], QSysInfo::LittleEndian);
924 qCDebug(QT_BT_BLUEZ) <<
"Found handle:" << Qt::hex << attributeHandle
925 <<
"properties:" << charData->properties
926 <<
"value handle:" << charData->valueHandle
927 <<
"uuid:" << charData->uuid.toString();
929 return attributeHandle;
933 QList<QBluetoothUuid> *foundServices,
934 const char *data, quint16 elementLength)
936 Q_ASSERT(foundServices);
938 Q_ASSERT(elementLength >= 6);
940 QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
953 if (elementLength == 8)
954 foundServices->append(QBluetoothUuid(bt_get_le16(&data[6])));
955 else if (elementLength == 22)
956 foundServices->append(QUuid::fromBytes(&data[6], QSysInfo::LittleEndian));
958 qCDebug(QT_BT_BLUEZ) <<
"Found included service: " << Qt::hex
959 << attributeHandle <<
"uuid:" << *foundServices;
961 return attributeHandle;
967 qCDebug(QT_BT_BLUEZ,
"%s malformed data: %s", qt_getEnumName(cmd),
968 response.toHex().constData());
971void QLowEnergyControllerPrivateBluez::processReply(
972 const Request &request,
const QByteArray &response)
974 Q_Q(QLowEnergyController);
978 Q_ASSERT(!response.isEmpty());
982 bool isErrorResponse =
false;
985 if (!dumpErrorInformation(response))
988 isErrorResponse =
true;
995 quint16 oldMtuSize = mtuSize;
996 if (isErrorResponse) {
997 mtuSize = ATT_DEFAULT_LE_MTU;
999 if (response.size() < 3) {
1000 reportMalformedData(command, response);
1003 const char *data = response.constData();
1004 quint16 mtu = bt_get_le16(&data[1]);
1006 if (mtuSize < ATT_DEFAULT_LE_MTU)
1007 mtuSize = ATT_DEFAULT_LE_MTU;
1009 qCDebug(QT_BT_BLUEZ) <<
"Server MTU:" << mtu <<
"resulting mtu:" << mtuSize;
1011 if (oldMtuSize != mtuSize)
1012 emit q->mtuChanged(mtuSize);
1019 const quint16 type = request.reference.toUInt();
1021 if (isErrorResponse) {
1023 setState(QLowEnergyController::DiscoveredState);
1024 q->discoveryFinished();
1033 if (response.size() < 2 || response[1] < 4) {
1034 reportMalformedData(command, response);
1038 QLowEnergyHandle start = 0, end = 0;
1039 const quint16 elementLength = response.constData()[1];
1040 const quint16 numElements = (response.size() - 2) / elementLength;
1042 const char *data = response.constData();
1043 for (
int i = 0; i < numElements; i++) {
1044 start = bt_get_le16(&data[offset]);
1045 end = bt_get_le16(&data[offset+2]);
1047 QBluetoothUuid uuid;
1048 if (elementLength == 6)
1049 uuid = QBluetoothUuid(bt_get_le16(&data[offset+4]));
1050 else if (elementLength == 20)
1051 uuid = QUuid::fromBytes(&data[offset+4], QSysInfo::LittleEndian);
1054 offset += elementLength;
1057 qCDebug(QT_BT_BLUEZ) <<
"Found uuid:" << uuid <<
"start handle:" << Qt::hex
1058 << start <<
"end handle:" << end;
1062 priv->startHandle = start;
1063 priv->endHandle = end;
1065 priv->type &= ~QLowEnergyService::PrimaryService;
1068 QSharedPointer<QLowEnergyServicePrivate> pointer(priv);
1070 serviceList.insert(uuid, pointer);
1071 emit q->serviceDiscovered(uuid);
1074 if (end != 0xFFFF) {
1075 sendReadByGroupRequest(end+1, 0xFFFF, type);
1078 setState(QLowEnergyController::DiscoveredState);
1079 emit q->discoveryFinished();
1090 QSharedPointer<QLowEnergyServicePrivate> p =
1091 request.reference.value<QSharedPointer<QLowEnergyServicePrivate> >();
1092 const quint16 attributeType = request.reference2.toUInt();
1094 if (isErrorResponse) {
1099 if (!p->characteristicList.isEmpty()) {
1100 readServiceValues(p->uuid,
true);
1104 p->setState(QLowEnergyService::RemoteServiceDiscovered);
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1129 if (response.size() < 2 || response[1] < minimumElementLength) {
1130 reportMalformedData(command, response);
1134 QLowEnergyHandle lastHandle;
1135 const quint16 elementLength = response.constData()[1];
1136 const quint16 numElements = (response.size() - 2) / elementLength;
1138 const char *data = response.constData();
1139 for (
int i = 0; i < numElements; i++) {
1142 lastHandle = parseReadByTypeCharDiscovery(
1143 &characteristic, &data[offset], elementLength);
1144 p->characteristicList[lastHandle] = characteristic;
1145 offset += elementLength;
1147 QList<QBluetoothUuid> includedServices;
1148 lastHandle = parseReadByTypeIncludeDiscovery(
1149 &includedServices, &data[offset], elementLength);
1150 p->includedServices = includedServices;
1151 for (
const QBluetoothUuid &uuid : std::as_const(includedServices)) {
1152 if (serviceList.contains(uuid))
1153 serviceList[uuid]->type |= QLowEnergyService::IncludedService;
1158 if (lastHandle + 1 < p->endHandle) {
1159 sendReadByTypeRequest(p, lastHandle + 1, attributeType);
1164 readServiceValues(p->uuid,
true);
1172 uint handleData = request.reference.toUInt();
1173 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1174 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1176 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1177 Q_ASSERT(!service.isNull());
1178 bool isServiceDiscoveryRun
1179 = !(service->state == QLowEnergyService::RemoteServiceDiscovered);
1181 if (isErrorResponse) {
1182 Q_ASSERT(!encryptionChangePending);
1184 encryptionChangePending = increaseEncryptLevelfRequired(err);
1185 if (encryptionChangePending) {
1188 openRequests.prepend(request);
1190 }
else if (!isServiceDiscoveryRun) {
1192 if (!descriptorHandle)
1193 service->setError(QLowEnergyService::CharacteristicReadError);
1195 service->setError(QLowEnergyService::DescriptorReadError);
1198 if (!descriptorHandle)
1199 updateValueOfCharacteristic(charHandle, response.mid(1),
NEW_VALUE);
1201 updateValueOfDescriptor(charHandle, descriptorHandle,
1204 if (response.size() == mtuSize) {
1205 qCDebug(QT_BT_BLUEZ) <<
"Switching to blob reads for"
1206 << charHandle << descriptorHandle
1207 << service->characteristicList[charHandle].uuid.toString();
1209 readServiceValuesByOffset(handleData, mtuSize-1,
1210 request.reference2.toBool());
1212 }
else if (!isServiceDiscoveryRun) {
1214 if (!descriptorHandle) {
1215 QLowEnergyCharacteristic ch(service, charHandle);
1216 emit service->characteristicRead(ch, response.mid(1));
1218 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1219 emit service->descriptorRead(descriptor, response.mid(1));
1225 if (request.reference2.toBool() && isServiceDiscoveryRun) {
1231 if (!descriptorHandle)
1232 discoverServiceDescriptors(service->uuid);
1234 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1242 uint handleData = request.reference.toUInt();
1243 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1244 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1246 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1247 Q_ASSERT(!service.isNull());
1250
1251
1252
1253
1254
1255 if (!isErrorResponse) {
1257 if (!descriptorHandle)
1258 length = updateValueOfCharacteristic(charHandle, response.mid(1),
APPEND_VALUE);
1260 length = updateValueOfDescriptor(charHandle, descriptorHandle,
1263 if (response.size() == mtuSize) {
1264 readServiceValuesByOffset(handleData, length,
1265 request.reference2.toBool());
1267 }
else if (service->state == QLowEnergyService::RemoteServiceDiscovered) {
1269 if (!descriptorHandle) {
1270 QLowEnergyCharacteristic ch(service, charHandle);
1271 emit service->characteristicRead(ch, ch.value());
1273 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1274 emit service->descriptorRead(descriptor, descriptor.value());
1279 qWarning() <<
"READ BLOB for char:" << charHandle
1280 <<
"descriptor:" << descriptorHandle <<
"on service"
1281 << service->uuid.toString() <<
"failed (service discovery run:"
1282 << (service->state == QLowEnergyService::RemoteServiceDiscovered) <<
")";
1285 if (request.reference2.toBool()) {
1289 if (!descriptorHandle)
1290 discoverServiceDescriptors(service->uuid);
1292 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1302
1303
1304
1305
1307 QList<QLowEnergyHandle> keys = request.reference.value<QList<QLowEnergyHandle> >();
1308 if (keys.isEmpty()) {
1309 qCWarning(QT_BT_BLUEZ) <<
"Descriptor discovery for unknown characteristic received";
1312 QLowEnergyHandle charHandle = keys.first();
1314 QSharedPointer<QLowEnergyServicePrivate> p =
1315 serviceForHandle(charHandle);
1316 Q_ASSERT(!p.isNull());
1318 if (isErrorResponse) {
1319 if (keys.size() == 1) {
1321 readServiceValues(p->uuid,
false);
1325 discoverNextDescriptor(p, keys, keys.first());
1331 if (response.size() < 6) {
1332 reportMalformedData(command, response);
1336 const quint8 format = response[1];
1337 quint16 elementLength;
1340 elementLength = 2 + 2;
1343 elementLength = 2 + 16;
1346 qCWarning(QT_BT_BLUEZ) <<
"Unknown format in FIND_INFORMATION_RESPONSE";
1350 const quint16 numElements = (response.size() - 2) / elementLength;
1353 QLowEnergyHandle descriptorHandle {};
1354 QBluetoothUuid uuid;
1355 const char *data = response.constData();
1356 for (
int i = 0; i < numElements; i++) {
1357 descriptorHandle = bt_get_le16(&data[offset]);
1360 uuid = QBluetoothUuid(bt_get_le16(&data[offset+2]));
1361 else if (format == 0x02)
1362 uuid = QUuid::fromBytes(&data[offset+2], QSysInfo::LittleEndian);
1364 offset += elementLength;
1369 quint16 shortUuid = uuid.toUInt16(&ok);
1372 qCDebug(QT_BT_BLUEZ) <<
"Suppressing primary/characteristic" << Qt::hex << shortUuid;
1377 if (descriptorHandle == p->characteristicList[charHandle].valueHandle) {
1378 qCDebug(QT_BT_BLUEZ) <<
"Suppressing char handle" << Qt::hex << descriptorHandle;
1384 p->characteristicList[charHandle].descriptorList.insert(
1385 descriptorHandle, data);
1387 qCDebug(QT_BT_BLUEZ) <<
"Descriptor found, uuid:"
1389 <<
"descriptor handle:" << Qt::hex << descriptorHandle;
1392 const QLowEnergyHandle nextPotentialHandle = descriptorHandle + 1;
1393 if (keys.size() == 1) {
1402 if ((p->endHandle != 0xffff && nextPotentialHandle >= p->endHandle + 1)
1403 || (descriptorHandle == 0xffff)) {
1407 readServiceValues(p->uuid,
false);
1409 discoverNextDescriptor(p, keys, nextPotentialHandle);
1412 if (nextPotentialHandle >= keys[1])
1414 discoverNextDescriptor(p, keys, nextPotentialHandle);
1422 uint ref = request.reference.toUInt();
1423 const QLowEnergyHandle charHandle = (ref & 0xffff);
1424 const QLowEnergyHandle descriptorHandle = ((ref >> 16) & 0xffff);
1426 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1427 if (service.isNull() || !service->characteristicList.contains(charHandle))
1430 if (isErrorResponse) {
1431 Q_ASSERT(!encryptionChangePending);
1433 encryptionChangePending = increaseEncryptLevelfRequired(err);
1434 if (encryptionChangePending) {
1435 openRequests.prepend(request);
1439 if (!descriptorHandle)
1440 service->setError(QLowEnergyService::CharacteristicWriteError);
1442 service->setError(QLowEnergyService::DescriptorWriteError);
1446 const QByteArray newValue = request.reference2.toByteArray();
1447 if (!descriptorHandle) {
1448 QLowEnergyCharacteristic ch(service, charHandle);
1449 if (ch.properties() & QLowEnergyCharacteristic::Read)
1450 updateValueOfCharacteristic(charHandle, newValue,
NEW_VALUE);
1451 emit service->characteristicWritten(ch, newValue);
1453 updateValueOfDescriptor(charHandle, descriptorHandle, newValue,
NEW_VALUE);
1454 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1455 emit service->descriptorWritten(descriptor, newValue);
1463 uint handleData = request.reference.toUInt();
1464 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
1465 const QByteArray newValue = request.reference2.toByteArray();
1466 const int writtenPayload = ((handleData >> 16) & 0xffff);
1468 if (isErrorResponse) {
1469 Q_ASSERT(!encryptionChangePending);
1471 encryptionChangePending = increaseEncryptLevelfRequired(err);
1472 if (encryptionChangePending) {
1473 openRequests.prepend(request);
1477 sendExecuteWriteRequest(attrHandle, newValue,
true);
1479 if (writtenPayload < newValue.size()) {
1480 sendNextPrepareWriteRequest(attrHandle, newValue, writtenPayload);
1482 sendExecuteWriteRequest(attrHandle, newValue,
false);
1492 uint handleData = request.reference.toUInt();
1493 const QLowEnergyHandle attrHandle = handleData & 0xffff;
1494 bool wasCancellation = !((handleData >> 16) & 0xffff);
1495 const QByteArray newValue = request.reference2.toByteArray();
1498 const QLowEnergyDescriptor descriptor = descriptorForHandle(attrHandle);
1499 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(attrHandle);
1500 Q_ASSERT(!service.isNull());
1502 if (isErrorResponse || wasCancellation) {
1504 if (descriptor.isValid())
1505 service->setError(QLowEnergyService::DescriptorWriteError);
1507 service->setError(QLowEnergyService::CharacteristicWriteError);
1509 if (descriptor.isValid()) {
1510 updateValueOfDescriptor(descriptor.characteristicHandle(),
1512 emit service->descriptorWritten(descriptor, newValue);
1514 QLowEnergyCharacteristic ch(service, attrHandle);
1515 if (ch.properties() & QLowEnergyCharacteristic::Read)
1516 updateValueOfCharacteristic(attrHandle, newValue,
NEW_VALUE);
1517 emit service->characteristicWritten(ch, newValue);
1522 qCDebug(QT_BT_BLUEZ) <<
"Unknown packet: " << response.toHex();
1532void QLowEnergyControllerPrivateBluez::sendReadByGroupRequest(
1533 QLowEnergyHandle start, QLowEnergyHandle end, quint16 type)
1539 putBtData(start, &packet[1]);
1540 putBtData(end, &packet[3]);
1541 putBtData(type, &packet[5]);
1545 qCDebug(QT_BT_BLUEZ) <<
"Sending read_by_group_type request, startHandle:" << Qt::hex
1546 << start <<
"endHandle:" << end << type;
1549 request.payload = data;
1551 request.reference = type;
1552 openRequests.enqueue(request);
1554 sendNextPendingRequest();
1558 QLowEnergyService::DiscoveryMode mode)
1560 if (!serviceList.contains(service)) {
1561 qCWarning(QT_BT_BLUEZ) <<
"Discovery of unknown service" << service.toString()
1566 QSharedPointer<QLowEnergyServicePrivate> serviceData = serviceList.value(service);
1567 serviceData->mode = mode;
1568 serviceData->characteristicList.clear();
1572void QLowEnergyControllerPrivateBluez::sendReadByTypeRequest(
1573 QSharedPointer<QLowEnergyServicePrivate> serviceData,
1574 QLowEnergyHandle nextHandle, quint16 attributeType)
1579 putBtData(nextHandle, &packet[1]);
1580 putBtData(serviceData->endHandle, &packet[3]);
1581 putBtData(attributeType, &packet[5]);
1585 qCDebug(QT_BT_BLUEZ) <<
"Sending read_by_type request, startHandle:" << Qt::hex
1586 << nextHandle <<
"endHandle:" << serviceData->endHandle
1587 <<
"type:" << attributeType <<
"packet:" << data.toHex();
1590 request.payload = data;
1592 request.reference = QVariant::fromValue(serviceData);
1593 request.reference2 = attributeType;
1594 openRequests.enqueue(request);
1596 sendNextPendingRequest();
1600
1601
1602
1603
1604
1605
1606
1607
1608void QLowEnergyControllerPrivateBluez::readServiceValues(
1609 const QBluetoothUuid &serviceUuid,
bool readCharacteristics)
1612 if (QT_BT_BLUEZ().isDebugEnabled()) {
1613 if (readCharacteristics)
1614 qCDebug(QT_BT_BLUEZ) <<
"Reading all characteristic values for"
1615 << serviceUuid.toString();
1617 qCDebug(QT_BT_BLUEZ) <<
"Reading all descriptor values for"
1618 << serviceUuid.toString();
1621 QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid);
1623 if (service->mode == QLowEnergyService::SkipValueDiscovery) {
1624 if (readCharacteristics) {
1626 discoverServiceDescriptors(service->uuid);
1628 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1635 QPair<QLowEnergyHandle, quint32> pair;
1638 QList<QPair<QLowEnergyHandle, quint32> > targetHandles;
1640 CharacteristicDataMap::const_iterator charIt = service->characteristicList.constBegin();
1641 for ( ; charIt != service->characteristicList.constEnd(); ++charIt) {
1642 const QLowEnergyHandle charHandle = charIt.key();
1645 if (readCharacteristics) {
1649 if (!(charDetails.properties & QLowEnergyCharacteristic::Read))
1652 pair.first = charDetails.valueHandle;
1653 pair.second = charHandle;
1654 targetHandles.append(pair);
1658 DescriptorDataMap::const_iterator descIt = charDetails.descriptorList.constBegin();
1659 for ( ; descIt != charDetails.descriptorList.constEnd(); ++descIt) {
1660 const QLowEnergyHandle descriptorHandle = descIt.key();
1662 pair.first = descriptorHandle;
1663 pair.second = (charHandle | (descriptorHandle << 16));
1664 targetHandles.append(pair);
1670 if (targetHandles.isEmpty()) {
1671 if (readCharacteristics) {
1674 discoverServiceDescriptors(service->uuid);
1677 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1682 for (qsizetype i = 0; i < targetHandles.size(); i++) {
1683 pair = targetHandles.at(i);
1685 putBtData(pair.first, &packet[1]);
1691 request.payload = data;
1693 request.reference = pair.second;
1695 request.reference2 = QVariant((
bool)(i + 1 == targetHandles.size()));
1696 openRequests.enqueue(request);
1699 sendNextPendingRequest();
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712void QLowEnergyControllerPrivateBluez::readServiceValuesByOffset(
1713 uint handleData, quint16 offset,
bool isLastValue)
1715 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1716 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1721 QLowEnergyHandle handleToRead = charHandle;
1722 if (descriptorHandle) {
1723 handleToRead = descriptorHandle;
1724 qCDebug(QT_BT_BLUEZ) <<
"Reading descriptor via blob request"
1725 << Qt::hex << descriptorHandle;
1728 QSharedPointer<QLowEnergyServicePrivate> service =
1729 serviceForHandle(charHandle);
1730 if (!service.isNull()
1731 && service->characteristicList.contains(charHandle)) {
1732 handleToRead = service->characteristicList[charHandle].valueHandle;
1733 qCDebug(QT_BT_BLUEZ) <<
"Reading characteristic via blob request"
1734 << Qt::hex << handleToRead;
1740 putBtData(handleToRead, data.data() + 1);
1741 putBtData(offset, data.data() + 3);
1744 request.payload = data;
1746 request.reference = handleData;
1747 request.reference2 = isLastValue;
1748 openRequests.prepend(request);
1751void QLowEnergyControllerPrivateBluez::discoverServiceDescriptors(
1752 const QBluetoothUuid &serviceUuid)
1754 qCDebug(QT_BT_BLUEZ) <<
"Discovering descriptor values for"
1755 << serviceUuid.toString();
1756 QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid);
1758 if (service->characteristicList.isEmpty()) {
1760 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1765 QList<QLowEnergyHandle> keys = service->characteristicList.keys();
1766 std::sort(keys.begin(), keys.end());
1768 discoverNextDescriptor(service, keys, keys[0]);
1771void QLowEnergyControllerPrivateBluez::processUnsolicitedReply(
const QByteArray &payload)
1773 Q_ASSERT(!payload.isEmpty());
1775 const char *data = payload.constData();
1777 bool isNotification = (command
1780 if (payload.size() < 3) {
1781 reportMalformedData(command, payload);
1785 const QLowEnergyHandle changedHandle = bt_get_le16(&data[1]);
1787 if (QT_BT_BLUEZ().isDebugEnabled()) {
1789 qCDebug(QT_BT_BLUEZ) <<
"Change notification for handle" << Qt::hex << changedHandle;
1791 qCDebug(QT_BT_BLUEZ) <<
"Change indication for handle" << Qt::hex << changedHandle;
1794 const QLowEnergyCharacteristic ch = characteristicForHandle(changedHandle);
1795 if (ch.isValid() && ch.handle() == changedHandle) {
1796 if (ch.properties() & QLowEnergyCharacteristic::Read)
1797 updateValueOfCharacteristic(ch.attributeHandle(), payload.mid(3),
NEW_VALUE);
1798 emit ch.d_ptr->characteristicChanged(ch, payload.mid(3));
1800 qCWarning(QT_BT_BLUEZ) <<
"Cannot find matching characteristic for "
1801 "notification/indication";
1805void QLowEnergyControllerPrivateBluez::exchangeMTU()
1807 qCDebug(QT_BT_BLUEZ) <<
"Exchanging MTU";
1811 putBtData(ATT_MAX_LE_MTU, &packet[1]);
1817 request.payload = data;
1819 openRequests.enqueue(request);
1821 sendNextPendingRequest();
1824int QLowEnergyControllerPrivateBluez::securityLevel()
const
1826 int socket = l2cpSocket->socketDescriptor();
1828 qCWarning(QT_BT_BLUEZ) <<
"Invalid l2cp socket, aborting getting of sec level";
1832 struct bt_security secData;
1833 socklen_t length =
sizeof(secData);
1834 memset(&secData, 0, length);
1837 qCDebug(QT_BT_BLUEZ) <<
"Current l2cp sec level:" << secData.level;
1838 return secData.level;
1841 if (errno != ENOPROTOOPT)
1846 length =
sizeof(optval);
1856 qCDebug(QT_BT_BLUEZ) <<
"Current l2cp sec level (old):" << level;
1863bool QLowEnergyControllerPrivateBluez::setSecurityLevel(
int level)
1868 int socket = l2cpSocket->socketDescriptor();
1870 qCWarning(QT_BT_BLUEZ) <<
"Invalid l2cp socket, aborting setting of sec level";
1874 struct bt_security secData;
1875 socklen_t length =
sizeof(secData);
1876 memset(&secData, 0, length);
1877 secData.level = level;
1880 qCDebug(QT_BT_BLUEZ) <<
"Setting new l2cp sec level:" << secData.level;
1884 if (errno != ENOPROTOOPT)
1903 qCDebug(QT_BT_BLUEZ) <<
"Old l2cp sec level:" << optval;
1910void QLowEnergyControllerPrivateBluez::discoverNextDescriptor(
1911 QSharedPointer<QLowEnergyServicePrivate> serviceData,
1912 const QList<QLowEnergyHandle> pendingCharHandles,
1913 const QLowEnergyHandle startingHandle)
1915 Q_ASSERT(!pendingCharHandles.isEmpty());
1916 Q_ASSERT(!serviceData.isNull());
1918 qCDebug(QT_BT_BLUEZ) <<
"Sending find_info request" << Qt::hex
1919 << pendingCharHandles << startingHandle;
1924 const QLowEnergyHandle charStartHandle = startingHandle;
1925 QLowEnergyHandle charEndHandle = 0;
1926 if (pendingCharHandles.size() == 1)
1927 charEndHandle = serviceData->endHandle;
1929 charEndHandle = pendingCharHandles[1] - 1;
1931 putBtData(charStartHandle, &packet[1]);
1932 putBtData(charEndHandle, &packet[3]);
1938 request.payload = data;
1940 request.reference = QVariant::fromValue<QList<QLowEnergyHandle> >(pendingCharHandles);
1941 request.reference2 = startingHandle;
1942 openRequests.enqueue(request);
1944 sendNextPendingRequest();
1947void QLowEnergyControllerPrivateBluez::sendNextPrepareWriteRequest(
1948 const QLowEnergyHandle handle,
const QByteArray &newValue,
1952 QLowEnergyHandle targetHandle = 0;
1953 const QLowEnergyDescriptor descriptor = descriptorForHandle(handle);
1954 if (descriptor.isValid())
1955 targetHandle = descriptor.handle();
1957 targetHandle = characteristicForHandle(handle).handle();
1959 if (!targetHandle) {
1960 qCWarning(QT_BT_BLUEZ) <<
"sendNextPrepareWriteRequest cancelled due to invalid handle"
1967 putBtData(targetHandle, &packet[1]);
1968 putBtData(offset, &packet[3]);
1970 qCDebug(QT_BT_BLUEZ) <<
"Writing long characteristic (prepare):"
1971 << Qt::hex << handle;
1975 const qsizetype requiredPayload = (
std::min)(newValue.size() - offset, maxAvailablePayload);
1978 Q_ASSERT((offset + requiredPayload) <= newValue.size());
1979 Q_ASSERT(dataSize <= mtuSize);
1981 QByteArray data(dataSize, Qt::Uninitialized);
1987 request.payload = data;
1989 request.reference = (handle | ((offset + requiredPayload) << 16));
1990 request.reference2 = newValue;
1991 openRequests.enqueue(request);
1995
1996
1997
1998
1999
2000
2001
2002void QLowEnergyControllerPrivateBluez::sendExecuteWriteRequest(
2003 const QLowEnergyHandle attrHandle,
const QByteArray &newValue,
2016 qCDebug(QT_BT_BLUEZ) <<
"Sending Execute Write Request for long characteristic value"
2017 << Qt::hex << attrHandle;
2020 request.payload = data;
2022 request.reference = (attrHandle | ((isCancelation ? 0x00 : 0x01) << 16));
2023 request.reference2 = newValue;
2024 openRequests.prepend(request);
2029
2030
2031
2032
2033
2035 const QSharedPointer<QLowEnergyServicePrivate> service,
2036 const QLowEnergyHandle charHandle,
2037 const QByteArray &newValue,
2038 QLowEnergyService::WriteMode mode)
2040 Q_ASSERT(!service.isNull());
2042 if (!service->characteristicList.contains(charHandle))
2046 if (role == QLowEnergyController::PeripheralRole)
2047 writeCharacteristicForPeripheral(charData, newValue);
2049 writeCharacteristicForCentral(service, charHandle, charData.valueHandle, newValue, mode);
2053 const QSharedPointer<QLowEnergyServicePrivate> service,
2054 const QLowEnergyHandle charHandle,
2055 const QLowEnergyHandle descriptorHandle,
2056 const QByteArray &newValue)
2058 Q_ASSERT(!service.isNull());
2060 if (role == QLowEnergyController::PeripheralRole)
2061 writeDescriptorForPeripheral(service, charHandle, descriptorHandle, newValue);
2063 writeDescriptorForCentral(charHandle, descriptorHandle, newValue);
2067
2068
2069
2070
2072 const QSharedPointer<QLowEnergyServicePrivate> service,
2073 const QLowEnergyHandle charHandle)
2075 Q_ASSERT(!service.isNull());
2076 if (!service->characteristicList.contains(charHandle))
2080 = service->characteristicList[charHandle];
2081 if (!(charDetails.properties & QLowEnergyCharacteristic::Read)) {
2085 qCWarning(QT_BT_BLUEZ) <<
"Reading non-readable char" << charHandle;
2090 putBtData(charDetails.valueHandle, &packet[1]);
2095 qCDebug(QT_BT_BLUEZ) <<
"Targeted reading characteristic" << Qt::hex << charHandle;
2098 request.payload = data;
2100 request.reference = charHandle;
2103 request.reference2 =
false;
2104 openRequests.enqueue(request);
2106 sendNextPendingRequest();
2110 const QSharedPointer<QLowEnergyServicePrivate> service,
2111 const QLowEnergyHandle charHandle,
2112 const QLowEnergyHandle descriptorHandle)
2114 Q_ASSERT(!service.isNull());
2115 if (!service->characteristicList.contains(charHandle))
2119 = service->characteristicList[charHandle];
2120 if (!charDetails.descriptorList.contains(descriptorHandle))
2125 putBtData(descriptorHandle, &packet[1]);
2130 qCDebug(QT_BT_BLUEZ) <<
"Targeted reading descriptor" << Qt::hex << descriptorHandle;
2133 request.payload = data;
2135 request.reference = (charHandle | (descriptorHandle << 16));
2138 request.reference2 =
false;
2139 openRequests.enqueue(request);
2141 sendNextPendingRequest();
2145
2146
2147
2148bool QLowEnergyControllerPrivateBluez::increaseEncryptLevelfRequired(
2154 switch (errorCode) {
2158 if (!hciManager->isValid())
2160 if (!hciManager->monitorEvent(HciManager::HciEvent::EVT_ENCRYPT_CHANGE))
2163 qCDebug(QT_BT_BLUEZ) <<
"Requesting encrypted link";
2165 restartRequestTimer();
2177void QLowEnergyControllerPrivateBluez::handleAdvertisingError()
2179 qCWarning(QT_BT_BLUEZ) <<
"received advertising error";
2180 setError(QLowEnergyController::AdvertisingError);
2181 setState(QLowEnergyController::UnconnectedState);
2184bool QLowEnergyControllerPrivateBluez::checkPacketSize(
const QByteArray &packet,
int minSize,
2189 if (Q_LIKELY(packet.size() >= minSize && packet.size() <= maxSize))
2191 qCWarning(QT_BT_BLUEZ) <<
"client request of type" << packet.at(0)
2192 <<
"has unexpected packet size" << packet.size();
2193 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2194 QBluezConst::AttError::ATT_ERROR_INVALID_PDU);
2198bool QLowEnergyControllerPrivateBluez::checkHandle(
const QByteArray &packet, QLowEnergyHandle handle)
2200 if (handle != 0 && handle <= lastLocalHandle)
2202 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2203 QBluezConst::AttError::ATT_ERROR_INVALID_HANDLE);
2208 QLowEnergyHandle startingHandle,
2209 QLowEnergyHandle endingHandle)
2211 if (startingHandle == 0 || startingHandle > endingHandle) {
2212 qCDebug(QT_BT_BLUEZ) <<
"handle range invalid";
2213 sendErrorResponse(request, startingHandle, QBluezConst::AttError::ATT_ERROR_INVALID_HANDLE);
2219void QLowEnergyControllerPrivateBluez::handleExchangeMtuRequest(
const QByteArray &packet)
2223 if (!checkPacketSize(packet, 3))
2225 if (receivedMtuExchangeRequest) {
2226 qCDebug(QT_BT_BLUEZ) <<
"Client sent extraneous MTU exchange packet";
2227 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2228 QBluezConst::AttError::ATT_ERROR_REQUEST_NOT_SUPPORTED);
2231 receivedMtuExchangeRequest =
true;
2236 putBtData(ATT_MAX_LE_MTU, reply.data() + 1);
2240 const quint16 clientRxMtu = bt_get_le16(packet.constData() + 1);
2241 mtuSize = std::clamp(clientRxMtu, ATT_DEFAULT_LE_MTU, ATT_MAX_LE_MTU);
2242 qCDebug(QT_BT_BLUEZ) <<
"MTU request from client:" << clientRxMtu
2243 <<
"effective client RX MTU:" << mtuSize;
2244 qCDebug(QT_BT_BLUEZ) <<
"Sending server RX MTU" << ATT_MAX_LE_MTU;
2247void QLowEnergyControllerPrivateBluez::handleFindInformationRequest(
const QByteArray &packet)
2251 if (!checkPacketSize(packet, 5))
2253 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2254 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2255 qCDebug(QT_BT_BLUEZ) <<
"client sends find information request; start:" << startingHandle
2256 <<
"end:" << endingHandle;
2257 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2261 QList<Attribute> results = getAttributes(startingHandle, endingHandle);
2262 if (results.isEmpty()) {
2263 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2264 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2267 ensureUniformUuidSizes(results);
2269 QByteArray responsePrefix(2, Qt::Uninitialized);
2270 const int uuidSize = getUuidSize(results.first().type);
2273 responsePrefix[1] = uuidSize == 2 ? 0x1 : 0x2;
2274 const int elementSize =
sizeof(QLowEnergyHandle) + uuidSize;
2275 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2276 putDataAndIncrement(attr.handle, data);
2277 putDataAndIncrement(attr.type, data);
2279 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2283void QLowEnergyControllerPrivateBluez::handleFindByTypeValueRequest(
const QByteArray &packet)
2287 if (!checkPacketSize(packet, 7, mtuSize))
2289 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2290 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2291 const quint16 type = bt_get_le16(packet.constData() + 5);
2292 const QByteArray value = QByteArray::fromRawData(packet.constData() + 7, packet.size() - 7);
2293 qCDebug(QT_BT_BLUEZ) <<
"client sends find by type value request; start:" << startingHandle
2294 <<
"end:" << endingHandle <<
"type:" << type
2295 <<
"value:" << value.toHex();
2296 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2300 const auto predicate = [value,
this, type](
const Attribute &attr) {
2301 return attr.type == QBluetoothUuid(type) && attr.value == value
2302 && checkReadPermissions(attr) == QBluezConst::AttError::ATT_ERROR_NO_ERROR;
2304 const QList<Attribute> results = getAttributes(startingHandle, endingHandle, predicate);
2305 if (results.isEmpty()) {
2306 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2307 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2311 QByteArray responsePrefix(
2313 const int elemSize = 2 *
sizeof(QLowEnergyHandle);
2314 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2315 putDataAndIncrement(attr.handle, data);
2316 putDataAndIncrement(attr.groupEndHandle, data);
2318 sendListResponse(responsePrefix, elemSize, results, elemWriter);
2321void QLowEnergyControllerPrivateBluez::handleReadByTypeRequest(
const QByteArray &packet)
2325 if (!checkPacketSize(packet, 7, 21))
2327 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2328 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2329 const void *
const typeStart = packet.constData() + 5;
2330 const bool is16BitUuid = packet.size() == 7;
2331 const bool is128BitUuid = packet.size() == 21;
2332 QBluetoothUuid type;
2334 type = QBluetoothUuid(bt_get_le16(typeStart));
2335 }
else if (is128BitUuid) {
2336 type = QUuid::fromBytes(typeStart, QSysInfo::LittleEndian);
2338 qCWarning(QT_BT_BLUEZ) <<
"read by type request has invalid packet size" << packet.size();
2339 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2340 QBluezConst::AttError::ATT_ERROR_INVALID_PDU);
2343 qCDebug(QT_BT_BLUEZ) <<
"client sends read by type request, start:" << startingHandle
2344 <<
"end:" << endingHandle <<
"type:" << type;
2345 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2350 QList<Attribute> results =
2351 getAttributes(startingHandle, endingHandle,
2352 [type](
const Attribute &attr) {
return attr.type == type; });
2353 ensureUniformValueSizes(results);
2355 if (results.isEmpty()) {
2356 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2357 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2363 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2364 results.first().handle, error);
2368 const qsizetype elementSize =
sizeof(QLowEnergyHandle) + results.first().value.size();
2369 QByteArray responsePrefix(2, Qt::Uninitialized);
2371 responsePrefix[1] = elementSize;
2372 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2373 putDataAndIncrement(attr.handle, data);
2374 putDataAndIncrement(attr.value, data);
2376 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2379void QLowEnergyControllerPrivateBluez::handleReadRequest(
const QByteArray &packet)
2383 if (!checkPacketSize(packet, 3))
2385 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2386 qCDebug(QT_BT_BLUEZ) <<
"client sends read request; handle:" << handle;
2388 if (!checkHandle(packet, handle))
2390 const Attribute &attribute = localAttributes.at(handle);
2393 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2398 const qsizetype sentValueLength = (std::min)(attribute.value.size(), qsizetype(mtuSize) - 1);
2399 QByteArray response(1 + sentValueLength, Qt::Uninitialized);
2401 using namespace std;
2402 memcpy(response.data() + 1, attribute.value.constData(), sentValueLength);
2403 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2404 sendPacket(response);
2407void QLowEnergyControllerPrivateBluez::handleReadBlobRequest(
const QByteArray &packet)
2411 if (!checkPacketSize(packet, 5))
2413 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2414 const quint16 valueOffset = bt_get_le16(packet.constData() + 3);
2415 qCDebug(QT_BT_BLUEZ) <<
"client sends read blob request; handle:" << handle
2416 <<
"offset:" << valueOffset;
2418 if (!checkHandle(packet, handle))
2420 const Attribute &attribute = localAttributes.at(handle);
2423 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2427 if (valueOffset > attribute.value.size()) {
2428 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2429 QBluezConst::AttError::ATT_ERROR_INVALID_OFFSET);
2432 if (attribute.value.size() <= mtuSize - 3) {
2433 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2434 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_LONG);
2439 const qsizetype sentValueLength = (std::min)(attribute.value.size() - valueOffset,
2440 qsizetype(mtuSize) - 1);
2442 QByteArray response(1 + sentValueLength, Qt::Uninitialized);
2444 using namespace std;
2445 memcpy(response.data() + 1, attribute.value.constData() + valueOffset, sentValueLength);
2446 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2447 sendPacket(response);
2450void QLowEnergyControllerPrivateBluez::handleReadMultipleRequest(
const QByteArray &packet)
2454 if (!checkPacketSize(packet, 5, mtuSize))
2456 QList<QLowEnergyHandle> handles((packet.size() - 1) /
sizeof(QLowEnergyHandle));
2457 auto *packetPtr =
reinterpret_cast<
const QLowEnergyHandle *>(packet.constData() + 1);
2458 for (qsizetype i = 0; i < handles.size(); ++i, ++packetPtr)
2459 handles[i] = bt_get_le16(packetPtr);
2460 qCDebug(QT_BT_BLUEZ) <<
"client sends read multiple request for handles" << handles;
2462 const auto it = std::find_if(handles.constBegin(), handles.constEnd(),
2463 [
this](QLowEnergyHandle handle) {
return handle >= lastLocalHandle; });
2464 if (it != handles.constEnd()) {
2465 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), *it,
2466 QBluezConst::AttError::ATT_ERROR_INVALID_HANDLE);
2469 const QList<Attribute> results = getAttributes(handles.first(), handles.last());
2470 QByteArray response(
2472 for (
const Attribute &attr : results) {
2473 const QBluezConst::AttError error = checkReadPermissions(attr);
2474 if (error != QBluezConst::AttError::ATT_ERROR_NO_ERROR) {
2475 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), attr.handle,
2482 response += attr.value.left(mtuSize - response.size());
2485 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2486 sendPacket(response);
2489void QLowEnergyControllerPrivateBluez::handleReadByGroupTypeRequest(
const QByteArray &packet)
2493 if (!checkPacketSize(packet, 7, 21))
2495 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2496 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2497 const bool is16BitUuid = packet.size() == 7;
2498 const bool is128BitUuid = packet.size() == 21;
2499 const void *
const typeStart = packet.constData() + 5;
2500 QBluetoothUuid type;
2502 type = QBluetoothUuid(bt_get_le16(typeStart));
2503 }
else if (is128BitUuid) {
2504 type = QUuid::fromBytes(typeStart, QSysInfo::LittleEndian);
2506 qCWarning(QT_BT_BLUEZ) <<
"read by group type request has invalid packet size"
2508 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2509 QBluezConst::AttError::ATT_ERROR_INVALID_PDU);
2512 qCDebug(QT_BT_BLUEZ) <<
"client sends read by group type request, start:" << startingHandle
2513 <<
"end:" << endingHandle <<
"type:" << type;
2515 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2520 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2521 QBluezConst::AttError::ATT_ERROR_UNSUPPRTED_GROUP_TYPE);
2525 QList<Attribute> results =
2526 getAttributes(startingHandle, endingHandle,
2527 [type](
const Attribute &attr) {
return attr.type == type; });
2528 if (results.isEmpty()) {
2529 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2530 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2535 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2536 results.first().handle, error);
2540 ensureUniformValueSizes(results);
2542 const qsizetype elementSize = 2 *
sizeof(QLowEnergyHandle) + results.first().value.size();
2543 QByteArray responsePrefix(2, Qt::Uninitialized);
2545 responsePrefix[1] = elementSize;
2546 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2547 putDataAndIncrement(attr.handle, data);
2548 putDataAndIncrement(attr.groupEndHandle, data);
2549 putDataAndIncrement(attr.value, data);
2551 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2554void QLowEnergyControllerPrivateBluez::updateLocalAttributeValue(
2555 QLowEnergyHandle handle,
2556 const QByteArray &value,
2557 QLowEnergyCharacteristic &characteristic,
2558 QLowEnergyDescriptor &descriptor)
2560 localAttributes[handle].value = value;
2561 for (
const auto &service : std::as_const(localServices)) {
2562 if (handle < service->startHandle || handle > service->endHandle)
2564 for (
auto charIt = service->characteristicList.begin();
2565 charIt != service->characteristicList.end(); ++charIt) {
2566 QLowEnergyServicePrivate::CharData &charData = charIt.value();
2567 if (handle == charIt.key() + 1) {
2568 charData.value = value;
2569 characteristic = QLowEnergyCharacteristic(service, charIt.key());
2572 for (
auto descIt = charData.descriptorList.begin();
2573 descIt != charData.descriptorList.end(); ++descIt) {
2574 if (handle == descIt.key()) {
2575 descIt.value().value = value;
2576 descriptor = QLowEnergyDescriptor(service, charIt.key(), handle);
2582 qFatal(
"local services map inconsistent with local attribute map");
2588void QLowEnergyControllerPrivateBluez::writeCharacteristicForPeripheral(
2590 const QByteArray &newValue)
2592 const QLowEnergyHandle valueHandle = charData.valueHandle;
2593 Q_ASSERT(valueHandle <= lastLocalHandle);
2594 Attribute &attribute = localAttributes[valueHandle];
2596 qCWarning(QT_BT_BLUEZ) <<
"ignoring value of invalid length" << newValue.size()
2597 <<
"for attribute" << valueHandle;
2600 attribute.value = newValue;
2601 charData.value = newValue;
2602 const bool hasNotifyProperty = attribute.properties & QLowEnergyCharacteristic::Notify;
2603 const bool hasIndicateProperty
2604 = attribute.properties & QLowEnergyCharacteristic::Indicate;
2605 if (!hasNotifyProperty && !hasIndicateProperty)
2607 for (
const QLowEnergyServicePrivate::DescData &desc : std::as_const(charData.descriptorList)) {
2608 if (desc.uuid != QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)
2612 const bool isConnected = state == QLowEnergyController::ConnectedState;
2614 Q_ASSERT(desc.value.size() == 2);
2615 quint16 configValue = bt_get_le16(desc.value.constData());
2616 if (isNotificationEnabled(configValue) && hasNotifyProperty) {
2617 sendNotification(valueHandle);
2618 }
else if (isIndicationEnabled(configValue) && hasIndicateProperty) {
2619 if (indicationInFlight)
2620 scheduledIndications << valueHandle;
2622 sendIndication(valueHandle);
2627 for (
auto it = clientConfigData.begin(); it != clientConfigData.end(); ++it) {
2628 if (isConnected && it.key() == remoteDevice.toUInt64())
2630 QList<ClientConfigurationData> &configDataList = it.value();
2631 for (ClientConfigurationData &configData : configDataList) {
2632 if (configData.charValueHandle != valueHandle)
2634 if ((isNotificationEnabled(configData.configValue) && hasNotifyProperty)
2635 || (isIndicationEnabled(configData.configValue) && hasIndicateProperty)) {
2636 configData.charValueWasUpdated =
true;
2645void QLowEnergyControllerPrivateBluez::writeCharacteristicForCentral(
const QSharedPointer<QLowEnergyServicePrivate> &service,
2646 QLowEnergyHandle charHandle,
2647 QLowEnergyHandle valueHandle,
2648 const QByteArray &newValue,
2649 QLowEnergyService::WriteMode mode)
2652 putBtData(valueHandle, packet.data() + 1);
2653 memcpy(packet.data() + 3, newValue.constData(), newValue.size());
2654 bool writeWithResponse =
false;
2656 case QLowEnergyService::WriteWithResponse:
2658 sendNextPrepareWriteRequest(charHandle, newValue, 0);
2659 sendNextPendingRequest();
2664 writeWithResponse =
true;
2666 case QLowEnergyService::WriteWithoutResponse:
2669 case QLowEnergyService::WriteSigned:
2672 qCWarning(QT_BT_BLUEZ) <<
"signed write not possible: requires bond between devices";
2673 service->setError(QLowEnergyService::CharacteristicWriteError);
2677 qCWarning(QT_BT_BLUEZ) <<
"signed write not possible: not allowed on encrypted link";
2678 service->setError(QLowEnergyService::CharacteristicWriteError);
2681 const auto signingDataIt = signingData.find(remoteDevice.toUInt64());
2682 if (signingDataIt == signingData.end()) {
2683 qCWarning(QT_BT_BLUEZ) <<
"signed write not possible: no signature key found";
2684 service->setError(QLowEnergyService::CharacteristicWriteError);
2687 ++signingDataIt.value().counter;
2688 packet = LeCmacCalculator::createFullMessage(packet, signingDataIt.value().counter);
2689 const quint64 mac = LeCmacCalculator().calculateMac(packet, signingDataIt.value().key);
2690 packet.resize(packet.size() +
sizeof mac);
2691 putBtData(mac, packet.data() + packet.size() -
sizeof mac);
2692 storeSignCounter(LocalSigningKey);
2696 qCDebug(QT_BT_BLUEZ) <<
"Writing characteristic" << Qt::hex << charHandle
2697 <<
"(size:" << packet.size() <<
"with response:"
2698 << (mode == QLowEnergyService::WriteWithResponse)
2699 <<
"signed:" << (mode == QLowEnergyService::WriteSigned) <<
")";
2704 if (!writeWithResponse) {
2710 request.payload = packet;
2712 request.reference = charHandle;
2713 request.reference2 = newValue;
2714 openRequests.enqueue(request);
2716 sendNextPendingRequest();
2719void QLowEnergyControllerPrivateBluez::writeDescriptorForPeripheral(
2720 const QSharedPointer<QLowEnergyServicePrivate> &service,
2721 const QLowEnergyHandle charHandle,
2722 const QLowEnergyHandle descriptorHandle,
2723 const QByteArray &newValue)
2725 Q_ASSERT(descriptorHandle <= lastLocalHandle);
2726 Attribute &attribute = localAttributes[descriptorHandle];
2728 qCWarning(QT_BT_BLUEZ) <<
"invalid value of size" << newValue.size()
2729 <<
"for attribute" << descriptorHandle;
2732 attribute.value = newValue;
2733 service->characteristicList[charHandle].descriptorList[descriptorHandle].value = newValue;
2736void QLowEnergyControllerPrivateBluez::writeDescriptorForCentral(
2737 const QLowEnergyHandle charHandle,
2738 const QLowEnergyHandle descriptorHandle,
2739 const QByteArray &newValue)
2742 sendNextPrepareWriteRequest(descriptorHandle, newValue, 0);
2743 sendNextPendingRequest();
2749 putBtData(descriptorHandle, &packet[1]);
2752 QByteArray data(size, Qt::Uninitialized);
2756 qCDebug(QT_BT_BLUEZ) <<
"Writing descriptor" << Qt::hex << descriptorHandle
2757 <<
"(size:" << size <<
")";
2760 request.payload = data;
2762 request.reference = (charHandle | (descriptorHandle << 16));
2763 request.reference2 = newValue;
2764 openRequests.enqueue(request);
2766 sendNextPendingRequest();
2769void QLowEnergyControllerPrivateBluez::handleWriteRequestOrCommand(
const QByteArray &packet)
2777 if (!checkPacketSize(packet, isSigned ? 15 : 3, mtuSize))
2779 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2780 qCDebug(QT_BT_BLUEZ) <<
"client sends" << (isSigned ?
"signed" :
"") <<
"write"
2781 << (isRequest ?
"request" :
"command") <<
"for handle" << handle;
2783 if (!checkHandle(packet, handle))
2786 Attribute &attribute = localAttributes[handle];
2787 const QLowEnergyCharacteristic::PropertyType type = isRequest
2788 ? QLowEnergyCharacteristic::Write : isSigned
2789 ? QLowEnergyCharacteristic::WriteSigned : QLowEnergyCharacteristic::WriteNoResponse;
2792 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2800 qCWarning(QT_BT_BLUEZ) <<
"Ignoring signed write from non-bonded device.";
2804 qCWarning(QT_BT_BLUEZ) <<
"Ignoring signed write on encrypted link.";
2807 const auto signingDataIt = signingData.find(remoteDevice.toUInt64());
2808 if (signingDataIt == signingData.constEnd()) {
2809 qCWarning(QT_BT_BLUEZ) <<
"No CSRK found for peer device, ignoring signed write";
2813 const quint32 signCounter = getBtData<quint32>(packet.data() + packet.size() - 12);
2814 if (signCounter < signingDataIt.value().counter + 1) {
2815 qCWarning(QT_BT_BLUEZ) <<
"Client's' sign counter" << signCounter
2816 <<
"not greater than local sign counter"
2817 << signingDataIt.value().counter
2818 <<
"; ignoring signed write command.";
2822 const quint64 macFromClient = getBtData<quint64>(packet.data() + packet.size() - 8);
2823 const bool signatureCorrect = verifyMac(packet.left(packet.size() - 12),
2824 signingDataIt.value().key, signCounter, macFromClient);
2825 if (!signatureCorrect) {
2826 qCWarning(QT_BT_BLUEZ) <<
"Signed Write packet has wrong signature, disconnecting";
2831 signingDataIt.value().counter = signCounter;
2832 storeSignCounter(RemoteSigningKey);
2833 valueLength = packet.size() - 15;
2835 valueLength = packet.size() - 3;
2839 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2840 QBluezConst::AttError::ATT_ERROR_INVAL_ATTR_VALUE_LEN);
2846 QByteArray value = packet.mid(3, valueLength);
2848 value += attribute.value.mid(valueLength, attribute
.maxLength - valueLength);
2850 QLowEnergyCharacteristic characteristic;
2851 QLowEnergyDescriptor descriptor;
2852 updateLocalAttributeValue(handle, value, characteristic, descriptor);
2855 const QByteArray response =
2857 sendPacket(response);
2860 if (characteristic.isValid()) {
2861 emit characteristic.d_ptr->characteristicChanged(characteristic, value);
2863 Q_ASSERT(descriptor.isValid());
2864 emit descriptor.d_ptr->descriptorWritten(descriptor, value);
2868void QLowEnergyControllerPrivateBluez::handlePrepareWriteRequest(
const QByteArray &packet)
2872 if (!checkPacketSize(packet, 5, mtuSize))
2874 const quint16 handle = bt_get_le16(packet.constData() + 1);
2875 qCDebug(QT_BT_BLUEZ) <<
"client sends prepare write request for handle" << handle;
2877 if (!checkHandle(packet, handle))
2879 const Attribute &attribute = localAttributes.at(handle);
2881 checkPermissions(attribute, QLowEnergyCharacteristic::Write);
2883 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2887 if (openPrepareWriteRequests.size() >= maxPrepareQueueSize) {
2888 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2889 QBluezConst::AttError::ATT_ERROR_PREPARE_QUEUE_FULL);
2894 openPrepareWriteRequests << WriteRequest(handle, bt_get_le16(packet.constData() + 3),
2897 QByteArray response = packet;
2899 sendPacket(response);
2902void QLowEnergyControllerPrivateBluez::handleExecuteWriteRequest(
const QByteArray &packet)
2906 if (!checkPacketSize(packet, 2))
2908 const bool cancel = packet.at(1) == 0;
2909 qCDebug(QT_BT_BLUEZ) <<
"client sends execute write request; flag is"
2910 << (cancel ?
"cancel" :
"flush");
2912 QList<WriteRequest> requests = openPrepareWriteRequests;
2913 openPrepareWriteRequests.clear();
2914 QList<QLowEnergyCharacteristic> characteristics;
2915 QList<QLowEnergyDescriptor> descriptors;
2917 for (
const WriteRequest &request : std::as_const(requests)) {
2918 Attribute &attribute = localAttributes[request.handle];
2919 if (request.valueOffset > attribute.value.size()) {
2920 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2921 request.handle, QBluezConst::AttError::ATT_ERROR_INVALID_OFFSET);
2924 const QByteArray newValue = attribute.value.left(request.valueOffset) + request.value;
2925 if (newValue.size() > attribute.maxLength) {
2926 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2928 QBluezConst::AttError::ATT_ERROR_INVAL_ATTR_VALUE_LEN);
2931 QLowEnergyCharacteristic characteristic;
2932 QLowEnergyDescriptor descriptor;
2935 updateLocalAttributeValue(request.handle, newValue, characteristic, descriptor);
2936 if (characteristic.isValid()) {
2937 characteristics << characteristic;
2938 }
else if (descriptor.isValid()) {
2939 Q_ASSERT(descriptor.isValid());
2940 descriptors << descriptor;
2945 sendPacket(QByteArray(
2946 1,
static_cast<quint8>(QBluezConst::AttCommand::ATT_OP_EXECUTE_WRITE_RESPONSE)));
2948 for (
const QLowEnergyCharacteristic &characteristic : std::as_const(characteristics))
2949 emit characteristic.d_ptr->characteristicChanged(characteristic, characteristic.value());
2950 for (
const QLowEnergyDescriptor &descriptor : std::as_const(descriptors))
2951 emit descriptor.d_ptr->descriptorWritten(descriptor, descriptor.value());
2964 packet[1] =
static_cast<quint8>(request);
2965 putBtData(handle, packet.data() + 2);
2966 packet[4] =
static_cast<quint8>(code);
2967 qCWarning(QT_BT_BLUEZ) <<
"sending error response; request:"
2968 << request <<
"handle:" << handle
2973void QLowEnergyControllerPrivateBluez::sendListResponse(
const QByteArray &packetStart,
2975 const QList<Attribute> &attributes,
2976 const ElemWriter &elemWriter)
2978 const qsizetype offset = packetStart.size();
2979 const qsizetype elemCount = (std::min)(attributes.size(), (mtuSize - offset) / elemSize);
2980 const qsizetype totalPacketSize = offset + elemCount * elemSize;
2981 QByteArray response(totalPacketSize, Qt::Uninitialized);
2982 using namespace std;
2983 memcpy(response.data(), packetStart.constData(), offset);
2984 char *data = response.data() + offset;
2985 for_each(attributes.constBegin(), attributes.constBegin() + elemCount,
2986 [&data, elemWriter](
const Attribute &attr) { elemWriter(attr, data); });
2987 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2988 sendPacket(response);
2991void QLowEnergyControllerPrivateBluez::sendNotification(QLowEnergyHandle handle)
2993 sendNotificationOrIndication(QBluezConst::AttCommand::ATT_OP_HANDLE_VAL_NOTIFICATION, handle);
2996void QLowEnergyControllerPrivateBluez::sendIndication(QLowEnergyHandle handle)
2998 Q_ASSERT(!indicationInFlight);
2999 indicationInFlight =
true;
3000 sendNotificationOrIndication(QBluezConst::AttCommand::ATT_OP_HANDLE_VAL_INDICATION, handle);
3004 QLowEnergyHandle handle)
3006 Q_ASSERT(handle <= lastLocalHandle);
3007 const Attribute &attribute = localAttributes.at(handle);
3008 const qsizetype maxValueLength = (std::min)(attribute.value.size(), qsizetype(mtuSize) - 3);
3009 QByteArray packet(3 + maxValueLength, Qt::Uninitialized);
3010 packet[0] =
static_cast<quint8>(opCode);
3011 putBtData(handle, packet.data() + 1);
3012 using namespace std;
3013 memcpy(packet.data() + 3, attribute.value.constData(), maxValueLength);
3014 qCDebug(QT_BT_BLUEZ) <<
"sending notification/indication:" << packet.toHex();
3018void QLowEnergyControllerPrivateBluez::sendNextIndication()
3020 if (!scheduledIndications.isEmpty())
3021 sendIndication(scheduledIndications.takeFirst());
3026 const QString peerAddressString = peerAddress.toString();
3029 QStringLiteral(
"/"),
3030 QDBusConnection::systemBus());
3031 QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects();
3032 reply.waitForFinished();
3033 if (reply.isError())
3037 for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
3038 const InterfaceList &ifaceList = it.value();
3040 for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) {
3041 const QString &iface = jt.key();
3042 const QVariantMap &ifaceValues = jt.value();
3044 if (iface == QStringLiteral(
"org.bluez.Device1")) {
3045 if (ifaceValues.value(QStringLiteral(
"Address")).toString() == peerAddressString)
3046 return ifaceValues.value(QStringLiteral(
"Alias")).toString();
3053void QLowEnergyControllerPrivateBluez::handleConnectionRequest()
3055 if (state != QLowEnergyController::AdvertisingState) {
3056 qCWarning(QT_BT_BLUEZ) <<
"Incoming connection request in unexpected state" << state;
3059 Q_ASSERT(serverSocketNotifier);
3060 serverSocketNotifier->setEnabled(
false);
3062 socklen_t clientAddrSize =
sizeof clientAddr;
3063 const int clientSocket = accept(serverSocketNotifier->socket(),
3064 reinterpret_cast<sockaddr *>(&clientAddr), &clientAddrSize);
3065 if (clientSocket == -1) {
3067 qCWarning(QT_BT_BLUEZ) <<
"accept() failed:" << qt_error_string(errno);
3068 serverSocketNotifier->setEnabled(
true);
3072 remoteDevice = QBluetoothAddress(convertAddress(clientAddr.l2_bdaddr.b));
3073 remoteName = nameOfRemoteCentral(remoteDevice);
3074 qCDebug(QT_BT_BLUEZ) <<
"GATT connection from device" << remoteDevice << remoteName;
3076 if (connectionHandle == 0)
3077 qCWarning(QT_BT_BLUEZ) <<
"Received client connection, but no connection complete event";
3080 disconnect(l2cpSocket);
3081 if (l2cpSocket->isOpen())
3082 l2cpSocket->close();
3084 l2cpSocket->deleteLater();
3085 l2cpSocket =
nullptr;
3087 closeServerSocket();
3090 l2cpSocket =
new QBluetoothSocket(
3091 rawSocketPrivate, QBluetoothServiceInfo::L2capProtocol,
this);
3092 connect(l2cpSocket, &QBluetoothSocket::disconnected,
3093 this, &QLowEnergyControllerPrivateBluez::l2cpDisconnected);
3094 connect(l2cpSocket, &QBluetoothSocket::errorOccurred,
this,
3095 &QLowEnergyControllerPrivateBluez::l2cpErrorChanged);
3096 connect(l2cpSocket, &QIODevice::readyRead,
this, &QLowEnergyControllerPrivateBluez::l2cpReadyRead);
3097 l2cpSocket->d_ptr->lowEnergySocketType = addressType == QLowEnergyController::PublicAddress
3099 l2cpSocket->setSocketDescriptor(clientSocket, QBluetoothServiceInfo::L2capProtocol,
3100 QBluetoothSocket::SocketState::ConnectedState, QIODevice::ReadWrite | QIODevice::Unbuffered);
3101 restoreClientConfigurations();
3102 loadSigningDataIfNecessary(RemoteSigningKey);
3104 Q_Q(QLowEnergyController);
3105 setState(QLowEnergyController::ConnectedState);
3106 emit q->connected();
3109void QLowEnergyControllerPrivateBluez::closeServerSocket()
3111 if (!serverSocketNotifier)
3113 serverSocketNotifier->disconnect();
3114 close(serverSocketNotifier->socket());
3115 serverSocketNotifier->deleteLater();
3116 serverSocketNotifier =
nullptr;
3119bool QLowEnergyControllerPrivateBluez::isBonded()
const
3123 return QBluetoothLocalDevice(localAdapter).pairingStatus(remoteDevice)
3124 != QBluetoothLocalDevice::Unpaired;
3127QList<QLowEnergyControllerPrivateBluez::TempClientConfigurationData>
3128QLowEnergyControllerPrivateBluez::gatherClientConfigData()
3130 QList<TempClientConfigurationData> data;
3131 for (
const auto &service : std::as_const(localServices)) {
3132 for (
auto charIt = service->characteristicList.begin();
3133 charIt != service->characteristicList.end(); ++charIt) {
3134 QLowEnergyServicePrivate::CharData &charData = charIt.value();
3135 for (
auto descIt = charData.descriptorList.begin();
3136 descIt != charData.descriptorList.end(); ++descIt) {
3137 QLowEnergyServicePrivate::DescData &descData = descIt.value();
3138 if (descData.uuid == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration) {
3139 data << TempClientConfigurationData(&descData, charData.valueHandle,
3149void QLowEnergyControllerPrivateBluez::storeClientConfigurations()
3152 clientConfigData.remove(remoteDevice.toUInt64());
3155 QList<ClientConfigurationData> clientConfigs;
3156 const QList<TempClientConfigurationData> &tempConfigList = gatherClientConfigData();
3157 for (
const auto &tempConfigData : tempConfigList) {
3158 Q_ASSERT(tempConfigData.descData->value.size() == 2);
3159 const quint16 value = bt_get_le16(tempConfigData.descData->value.constData());
3161 clientConfigs << ClientConfigurationData(tempConfigData.charValueHandle,
3162 tempConfigData.configHandle, value);
3165 clientConfigData.insert(remoteDevice.toUInt64(), clientConfigs);
3168void QLowEnergyControllerPrivateBluez::restoreClientConfigurations()
3170 const QList<TempClientConfigurationData> &tempConfigList = gatherClientConfigData();
3171 const QList<ClientConfigurationData> &restoredClientConfigs = isBonded()
3172 ? clientConfigData.value(remoteDevice.toUInt64())
3173 : QList<ClientConfigurationData>();
3174 QList<QLowEnergyHandle> notifications;
3175 for (
const auto &tempConfigData : tempConfigList) {
3176 bool wasRestored =
false;
3177 for (
const auto &restoredData : restoredClientConfigs) {
3178 if (restoredData.charValueHandle == tempConfigData.charValueHandle) {
3179 Q_ASSERT(tempConfigData.descData->value.size() == 2);
3180 putBtData(restoredData.configValue, tempConfigData.descData->value.data());
3182 if (restoredData.charValueWasUpdated) {
3183 if (isNotificationEnabled(restoredData.configValue))
3184 notifications << restoredData.charValueHandle;
3185 else if (isIndicationEnabled(restoredData.configValue))
3186 scheduledIndications << restoredData.charValueHandle;
3192 tempConfigData.descData->value = QByteArray(2, 0);
3193 Q_ASSERT(lastLocalHandle >= tempConfigData.configHandle);
3194 Q_ASSERT(tempConfigData.configHandle > tempConfigData.charValueHandle);
3195 localAttributes[tempConfigData.configHandle].value = tempConfigData.descData->value;
3198 for (
const QLowEnergyHandle handle : std::as_const(notifications))
3199 sendNotification(handle);
3200 sendNextIndication();
3203void QLowEnergyControllerPrivateBluez::loadSigningDataIfNecessary(SigningKeyType keyType)
3205 const auto signingDataIt = signingData.constFind(remoteDevice.toUInt64());
3206 if (signingDataIt != signingData.constEnd())
3208 const QString settingsFilePath = keySettingsFilePath();
3209 if (!QFileInfo(settingsFilePath).exists()) {
3210 qCDebug(QT_BT_BLUEZ) <<
"No settings found for peer device.";
3213 QSettings settings(settingsFilePath, QSettings::IniFormat);
3214 const QString group = signingKeySettingsGroup(keyType);
3215 settings.beginGroup(group);
3216 const QByteArray keyString = settings.value(QLatin1String(
"Key")).toByteArray();
3217 if (keyString.isEmpty()) {
3218 qCDebug(QT_BT_BLUEZ) <<
"Group" << group <<
"not found in settings file";
3221 const QByteArray keyData = QByteArray::fromHex(keyString);
3222 if (keyData.size() != qsizetype(
sizeof(
BluezUint128))) {
3223 qCWarning(QT_BT_BLUEZ) <<
"Signing key in settings file has invalid size"
3224 << keyString.size();
3227 qCDebug(QT_BT_BLUEZ) <<
"CSRK of peer device is" << keyString;
3228 const quint32 counter = settings.value(QLatin1String(
"Counter"), 0).toUInt();
3229 using namespace std;
3231 memcpy(csrk.data, keyData.constData(), keyData.size());
3232 signingData.insert(remoteDevice.toUInt64(), SigningData(csrk, counter - 1));
3235void QLowEnergyControllerPrivateBluez::storeSignCounter(SigningKeyType keyType)
const
3237 const auto signingDataIt = signingData.constFind(remoteDevice.toUInt64());
3238 if (signingDataIt == signingData.constEnd())
3240 const QString settingsFilePath = keySettingsFilePath();
3241 if (!QFileInfo(settingsFilePath).exists())
3243 QSettings settings(settingsFilePath, QSettings::IniFormat);
3244 if (!settings.isWritable())
3246 settings.beginGroup(signingKeySettingsGroup(keyType));
3247 const QString counterKey = QLatin1String(
"Counter");
3248 if (!settings.allKeys().contains(counterKey))
3250 const quint32 counterValue = signingDataIt.value().counter + 1;
3251 if (counterValue == settings.value(counterKey).toUInt())
3253 settings.setValue(counterKey, counterValue);
3256QString QLowEnergyControllerPrivateBluez::signingKeySettingsGroup(SigningKeyType keyType)
const
3258 return QLatin1String(keyType == LocalSigningKey ?
"LocalSignatureKey" :
"RemoteSignatureKey");
3261QString QLowEnergyControllerPrivateBluez::keySettingsFilePath()
const
3263 return QString::fromLatin1(
"/var/lib/bluetooth/%1/%2/info")
3264 .arg(localAdapter.toString(), remoteDevice.toString());
3269 QByteArray ba(
sizeof(uuid), Qt::Uninitialized);
3270 char *ptr = ba.data();
3271 putDataAndIncrement(uuid, ptr);
3272 ba.resize(ptr - ba.constData());
3277 QLowEnergyHandle startHandle)
3283 localAttributes.resize(lastLocalHandle + 1);
3285 serviceAttribute.handle = startHandle;
3286 serviceAttribute.type = QBluetoothUuid(
static_cast<quint16>(service.type()));
3287 serviceAttribute.properties = QLowEnergyCharacteristic::Read;
3288 serviceAttribute.value = uuidToByteArray(service.uuid());
3289 QLowEnergyHandle currentHandle = startHandle;
3290 const QList<QLowEnergyService *> includedServices = service.includedServices();
3291 for (
const QLowEnergyService *
const service : includedServices) {
3292 Attribute attribute;
3293 attribute.handle = ++currentHandle;
3295 attribute.properties = QLowEnergyCharacteristic::Read;
3296 const bool includeUuidInValue = service->serviceUuid().minimumSize() == 2;
3297 attribute.value.resize((2 + includeUuidInValue) *
sizeof(QLowEnergyHandle));
3298 char *valueData = attribute.value.data();
3299 putDataAndIncrement(service->d_ptr->startHandle, valueData);
3300 putDataAndIncrement(service->d_ptr->endHandle, valueData);
3301 if (includeUuidInValue)
3302 putDataAndIncrement(service->serviceUuid(), valueData);
3303 localAttributes[attribute.handle] = attribute;
3305 const QList<QLowEnergyCharacteristicData> characteristics = service.characteristics();
3306 for (
const QLowEnergyCharacteristicData &cd : characteristics) {
3307 Attribute attribute;
3310 attribute.handle = ++currentHandle;
3311 attribute.groupEndHandle = attribute.handle + 1 + cd.descriptors().size();
3313 attribute.properties = QLowEnergyCharacteristic::Read;
3314 attribute.value.resize(1 +
sizeof(QLowEnergyHandle) + cd.uuid().minimumSize());
3315 char *valueData = attribute.value.data();
3316 putDataAndIncrement(
static_cast<quint8>(cd.properties()), valueData);
3317 putDataAndIncrement(QLowEnergyHandle(currentHandle + 1), valueData);
3318 putDataAndIncrement(cd.uuid(), valueData);
3319 localAttributes[attribute.handle] = attribute;
3322 attribute.handle = ++currentHandle;
3323 attribute.groupEndHandle = attribute.handle;
3324 attribute.type = cd.uuid();
3325 attribute.properties = cd.properties();
3326 attribute.readConstraints = cd.readConstraints();
3327 attribute.writeConstraints = cd.writeConstraints();
3328 attribute.value = cd.value();
3329 attribute.minLength = cd.minimumValueLength();
3330 attribute.maxLength = cd.maximumValueLength();
3331 localAttributes[attribute.handle] = attribute;
3333 const QList<QLowEnergyDescriptorData> descriptors = cd.descriptors();
3334 for (
const QLowEnergyDescriptorData &dd : descriptors) {
3335 attribute.handle = ++currentHandle;
3336 attribute.groupEndHandle = attribute.handle;
3337 attribute.type = dd.uuid();
3338 attribute.properties = QLowEnergyCharacteristic::PropertyTypes();
3339 attribute.readConstraints = AttAccessConstraints();
3340 attribute.writeConstraints = AttAccessConstraints();
3341 attribute.minLength = 0;
3342 attribute.maxLength = INT_MAX;
3345 if (attribute.type == QBluetoothUuid::DescriptorType::CharacteristicExtendedProperties) {
3346 attribute.properties = QLowEnergyCharacteristic::Read;
3347 attribute.minLength = attribute.maxLength = 2;
3348 }
else if (attribute.type == QBluetoothUuid::DescriptorType::CharacteristicPresentationFormat) {
3349 attribute.properties = QLowEnergyCharacteristic::Read;
3350 attribute.minLength = attribute.maxLength = 7;
3351 }
else if (attribute.type == QBluetoothUuid::DescriptorType::CharacteristicAggregateFormat) {
3352 attribute.properties = QLowEnergyCharacteristic::Read;
3353 attribute.minLength = 4;
3354 }
else if (attribute.type == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration
3355 || attribute.type == QBluetoothUuid::DescriptorType::ServerCharacteristicConfiguration) {
3356 attribute.properties = QLowEnergyCharacteristic::Read
3357 | QLowEnergyCharacteristic::Write
3358 | QLowEnergyCharacteristic::WriteNoResponse
3359 | QLowEnergyCharacteristic::WriteSigned;
3360 attribute.writeConstraints = dd.writeConstraints();
3361 attribute.minLength = attribute.maxLength = 2;
3363 if (dd.isReadable())
3364 attribute.properties |= QLowEnergyCharacteristic::Read;
3365 if (dd.isWritable()) {
3366 attribute.properties |= QLowEnergyCharacteristic::Write;
3367 attribute.properties |= QLowEnergyCharacteristic::WriteNoResponse;
3368 attribute.properties |= QLowEnergyCharacteristic::WriteSigned;
3370 attribute.readConstraints = dd.readConstraints();
3371 attribute.writeConstraints = dd.writeConstraints();
3374 attribute.value = dd.value();
3375 if (attribute.value.size() < attribute.minLength
3376 || attribute.value.size() > attribute.maxLength) {
3377 qCWarning(QT_BT_BLUEZ) <<
"attribute of type" << attribute.type
3378 <<
"has invalid length of" << attribute.value.size()
3380 attribute.value = QByteArray(attribute.minLength, 0);
3382 localAttributes[attribute.handle] = attribute;
3385 serviceAttribute.groupEndHandle = currentHandle;
3386 localAttributes[serviceAttribute.handle] = serviceAttribute;
3389int QLowEnergyControllerPrivateBluez::
mtu()
const
3394void QLowEnergyControllerPrivateBluez::ensureUniformAttributes(
3395 QList<Attribute> &attributes,
const std::function<
int(
const Attribute &)> &getSize)
3397 if (attributes.isEmpty())
3399 const int firstSize = getSize(attributes.first());
3400 const auto it = std::find_if(attributes.begin() + 1, attributes.end(),
3401 [firstSize, getSize](
const Attribute &attr) {
return getSize(attr) != firstSize; });
3402 if (it != attributes.end())
3403 attributes.erase(it, attributes.end());
3407void QLowEnergyControllerPrivateBluez::ensureUniformUuidSizes(QList<Attribute> &attributes)
3409 ensureUniformAttributes(attributes,
3410 [](
const Attribute &attr) {
return getUuidSize(attr.type); });
3413void QLowEnergyControllerPrivateBluez::ensureUniformValueSizes(QList<Attribute> &attributes)
3415 ensureUniformAttributes(attributes,
3416 [](
const Attribute &attr) {
return attr.value.size(); });
3419QList<QLowEnergyControllerPrivateBluez::Attribute>
3420QLowEnergyControllerPrivateBluez::getAttributes(QLowEnergyHandle startHandle,
3421 QLowEnergyHandle endHandle,
3422 const AttributePredicate &attributePredicate)
3424 QList<Attribute> results;
3425 if (startHandle > lastLocalHandle)
3427 if (lastLocalHandle == 0)
3429 Q_ASSERT(startHandle <= endHandle);
3430 const QLowEnergyHandle firstHandle = qMin(startHandle, lastLocalHandle);
3431 const QLowEnergyHandle lastHandle = qMin(endHandle, lastLocalHandle);
3432 for (QLowEnergyHandle i = firstHandle; i <= lastHandle; ++i) {
3433 const Attribute &attr = localAttributes.at(i);
3434 if (attributePredicate(attr))
3441QLowEnergyControllerPrivateBluez::checkPermissions(
const Attribute &attr,
3442 QLowEnergyCharacteristic::PropertyType type)
3444 const bool isReadAccess = type == QLowEnergyCharacteristic::Read;
3445 const bool isWriteCommand = type == QLowEnergyCharacteristic::WriteNoResponse;
3446 const bool isWriteAccess = type == QLowEnergyCharacteristic::Write
3447 || type == QLowEnergyCharacteristic::WriteSigned
3449 Q_ASSERT(isReadAccess || isWriteAccess);
3450 if (!(attr.properties & type)) {
3456 const bool unsignedWriteOk = isWriteCommand
3457 && (attr.properties & QLowEnergyCharacteristic::WriteSigned)
3459 if (!unsignedWriteOk)
3462 const AttAccessConstraints constraints = isReadAccess
3463 ? attr.readConstraints : attr.writeConstraints;
3464 if (constraints.testFlag(AttAccessConstraint::AttAuthorizationRequired))
3467 if (constraints.testFlag(AttAccessConstraint::AttEncryptionRequired)
3470 if (constraints.testFlag(AttAccessConstraint::AttAuthenticationRequired)
3480 return checkPermissions(attr, QLowEnergyCharacteristic::Read);
3484QLowEnergyControllerPrivateBluez::checkReadPermissions(QList<Attribute> &attributes)
3486 if (attributes.isEmpty())
3498 std::find_if(attributes.begin() + 1, attributes.end(), [
this](
const Attribute &attr) {
3501 if (it != attributes.end())
3502 attributes.erase(it, attributes.end());
3506bool QLowEnergyControllerPrivateBluez::verifyMac(
const QByteArray &message,
BluezUint128 csrk,
3507 quint32 signCounter, quint64 expectedMac)
3509 if (!cmacCalculator)
3510 cmacCalculator =
new LeCmacCalculator;
3511 return cmacCalculator->verify(LeCmacCalculator::createFullMessage(message, signCounter), csrk,
3517#include "moc_qlowenergycontroller_bluez_p.cpp"
QMap< QDBusObjectPath, InterfaceList > ManagedObjectList
QUuid::Id128Bytes BluezUint128
#define ATTRIBUTE_CHANNEL_ID
#define BT_SECURITY_MEDIUM
bool isMonitoringEnabled() const
static BluetoothManagement * instance()
QBluetoothSocketPrivateBluez()
void discoverServiceDetails(const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode) override
void discoverServices() override
void readCharacteristic(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle) override
void writeCharacteristic(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QByteArray &newValue, QLowEnergyService::WriteMode mode) override
Writes long (prepare write request), short (write request) and writeWithoutResponse characteristic va...
void writeDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle, const QByteArray &newValue) override
void stopAdvertising() override
void startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, const QLowEnergyAdvertisingData &advertisingData, const QLowEnergyAdvertisingData &scanResponseData) override
void connectToDevice() override
~QLowEnergyControllerPrivateBluez() override
void disconnectFromDevice() override
void readDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle) override
void addToGenericAttributeList(const QLowEnergyServiceData &service, QLowEnergyHandle startHandle) override
void invalidateServices()
void setController(QLowEnergyControllerPrivate *control)
bool listen(const QBluetoothAddress &localAdapter)
@ ATT_ERROR_INVAL_ATTR_VALUE_LEN
@ ATT_ERROR_INVALID_OFFSET
@ ATT_ERROR_INSUF_AUTHORIZATION
@ ATT_ERROR_READ_NOT_PERM
@ ATT_ERROR_REQUEST_NOT_SUPPORTED
@ ATT_ERROR_INSUF_ENCRYPTION
@ ATT_ERROR_INVALID_HANDLE
@ ATT_ERROR_INSUF_ENCR_KEY_SIZE
@ ATT_ERROR_WRITE_NOT_PERM
@ ATT_ERROR_ATTRIBUTE_NOT_LONG
@ ATT_ERROR_APPLICATION_END
@ ATT_ERROR_REQUEST_STALLED
@ ATT_ERROR_INSUF_AUTHENTICATION
@ ATT_ERROR_ATTRIBUTE_NOT_FOUND
@ ATT_ERROR_PREPARE_QUEUE_FULL
@ ATT_ERROR_APPLICATION_START
@ ATT_ERROR_INSUF_RESOURCES
@ ATT_ERROR_UNSUPPRTED_GROUP_TYPE
@ ATT_OP_FIND_BY_TYPE_VALUE_RESPONSE
@ ATT_OP_PREPARE_WRITE_RESPONSE
@ ATT_OP_EXECUTE_WRITE_REQUEST
@ ATT_OP_READ_BLOB_REQUEST
@ ATT_OP_EXECUTE_WRITE_RESPONSE
@ ATT_OP_FIND_INFORMATION_RESPONSE
@ ATT_OP_SIGNED_WRITE_COMMAND
@ ATT_OP_EXCHANGE_MTU_REQUEST
@ ATT_OP_HANDLE_VAL_NOTIFICATION
@ ATT_OP_READ_BY_TYPE_REQUEST
@ ATT_OP_EXCHANGE_MTU_RESPONSE
@ ATT_OP_READ_BY_GROUP_REQUEST
@ ATT_OP_HANDLE_VAL_CONFIRMATION
@ ATT_OP_HANDLE_VAL_INDICATION
@ ATT_OP_READ_BY_TYPE_RESPONSE
@ ATT_OP_FIND_INFORMATION_REQUEST
@ ATT_OP_PREPARE_WRITE_REQUEST
@ ATT_OP_READ_BLOB_RESPONSE
@ ATT_OP_READ_MULTIPLE_RESPONSE
@ ATT_OP_READ_BY_GROUP_RESPONSE
void registerQLowEnergyControllerMetaType()
void putDataAndIncrement(const QBluetoothUuid &uuid, char *&dst)
#define GATT_CHARACTERISTIC
#define GATT_SECONDARY_SERVICE
static bool isNotificationEnabled(quint16 clientConfigValue)
QLowEnergyHandle parseReadByTypeIncludeDiscovery(QList< QBluetoothUuid > *foundServices, const char *data, quint16 elementLength)
#define GATT_PRIMARY_SERVICE
static int getUuidSize(const QBluetoothUuid &uuid)
#define WRITE_REQUEST_HEADER_SIZE
static Q_DECL_COLD_FUNCTION void reportMalformedData(QBluezConst::AttCommand cmd, const QByteArray &response)
constexpr quint16 ATT_MAX_LE_MTU
QLowEnergyHandle parseReadByTypeCharDiscovery(QLowEnergyServicePrivate::CharData *charData, const char *data, quint16 elementLength)
#define GRP_TYPE_REQ_HEADER_SIZE
static bool dumpErrorInformation(const QByteArray &response)
#define READ_REQUEST_HEADER_SIZE
#define READ_BY_TYPE_REQ_HEADER_SIZE
#define EXECUTE_WRITE_HEADER_SIZE
const int maxPrepareQueueSize
static QString nameOfRemoteCentral(const QBluetoothAddress &peerAddress)
#define PREPARE_WRITE_HEADER_SIZE
#define GATT_INCLUDED_SERVICE
#define READ_BLOB_REQUEST_HEADER_SIZE
static void putDataAndIncrement(const T &src, char *&dst)
static bool isIndicationEnabled(quint16 clientConfigValue)
#define MTU_EXCHANGE_HEADER_SIZE
#define FIND_INFO_REQUEST_HEADER_SIZE
#define ERROR_RESPONSE_HEADER_SIZE
static QByteArray uuidToByteArray(const QBluetoothUuid &uuid)
constexpr quint16 ATT_DEFAULT_LE_MTU