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());
167
168
169
170
171QLowEnergyControllerPrivateBluez::QLowEnergyControllerPrivateBluez()
173 requestPending(
false),
174 mtuSize(ATT_DEFAULT_LE_MTU),
175 securityLevelValue(-1),
176 encryptionChangePending(
false)
179 qRegisterMetaType<QList<QLowEnergyHandle> >();
182void QLowEnergyControllerPrivateBluez::
init()
185 hciManager = std::make_shared<HciManager>(localAdapter);
187 if (!hciManager->isValid()){
188 setError(QLowEnergyController::InvalidBluetoothAdapterError);
192 hciManager->monitorEvent(HciManager::HciEvent::EVT_ENCRYPT_CHANGE);
193 connect(hciManager.get(), SIGNAL(encryptionChangedEvent(QBluetoothAddress,
bool)),
194 this, SLOT(encryptionChangedEvent(QBluetoothAddress,
bool)));
195 hciManager->monitorEvent(HciManager::HciEvent::EVT_LE_META_EVENT);
196 hciManager->monitorAclPackets();
197 connect(hciManager.get(), &HciManager::connectionComplete,
this, [
this](quint16 handle) {
198 connectionHandle = handle;
199 qCDebug(QT_BT_BLUEZ) <<
"received connection complete event, handle:" << handle;
201 connect(hciManager.get(), &HciManager::connectionUpdate,
this,
202 [
this](quint16 handle,
const QLowEnergyConnectionParameters ¶ms) {
203 if (handle == connectionHandle)
204 emit q_ptr->connectionUpdated(params);
207 connect(hciManager.get(), &HciManager::signatureResolvingKeyReceived,
this,
208 [
this](quint16 handle,
bool remoteKey,
const QUuid::Id128Bytes &csrk) {
209 if (handle != connectionHandle)
211 if ((remoteKey && role == QLowEnergyController::CentralRole)
212 || (!remoteKey && role == QLowEnergyController::PeripheralRole)) {
215 qCDebug(QT_BT_BLUEZ) <<
"received new signature resolving key";
216 signingData.insert(remoteDevice.toUInt64(), SigningData(csrk));
220 if (role == QLowEnergyController::CentralRole) {
221 if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(
"BLUETOOTH_GATT_TIMEOUT"))) {
223 int value = qEnvironmentVariableIntValue(
"BLUETOOTH_GATT_TIMEOUT", &ok);
225 gattRequestTimeout = value;
229 if (gattRequestTimeout > 0) {
230 qCWarning(QT_BT_BLUEZ) <<
"Enabling GATT request timeout behavior" << gattRequestTimeout;
231 requestTimer =
new QTimer(
this);
232 requestTimer->setSingleShot(
true);
233 requestTimer->setInterval(gattRequestTimeout);
234 connect(requestTimer, &QTimer::timeout,
235 this, &QLowEnergyControllerPrivateBluez::handleGattRequestTimeout);
240void QLowEnergyControllerPrivateBluez::handleGattRequestTimeout()
243 if (encryptionChangePending) {
245 qCWarning(QT_BT_BLUEZ) <<
"****** Encryption change event blocking further GATT requests";
249 if (!openRequests.isEmpty() && requestPending) {
250 const Request currentRequest = openRequests.dequeue();
251 requestPending =
false;
253 qCWarning(QT_BT_BLUEZ).nospace() <<
"****** Request type 0x" << currentRequest.command
254 <<
" to server/peripheral timed out";
255 qCWarning(QT_BT_BLUEZ) <<
"****** Looks like the characteristic or descriptor does NOT act in"
256 <<
"accordance to Bluetooth 4.x spec.";
257 qCWarning(QT_BT_BLUEZ) <<
"****** Please check server implementation."
258 <<
"Continuing under reservation.";
262 QLowEnergyHandle handle) {
265 errorPackage[1] =
static_cast<quint8>(
267 putBtData(handle, errorPackage.data() + 2);
278 case QBluezConst::AttCommand::ATT_OP_READ_BY_GROUP_REQUEST:
280 case QBluezConst::AttCommand::ATT_OP_READ_BY_TYPE_REQUEST:
284 processReply(currentRequest, createRequestErrorMessage(command, 0));
292 uint handleData = currentRequest.reference.toUInt();
293 const QLowEnergyHandle charHandle = (handleData & 0xffff);
294 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
295 processReply(currentRequest, createRequestErrorMessage(command,
296 descriptorHandle ? descriptorHandle : charHandle));
298 case QBluezConst::AttCommand::ATT_OP_FIND_INFORMATION_REQUEST:
299 processReply(currentRequest, createRequestErrorMessage(
300 command, currentRequest.reference2.toUInt()));
307 uint handleData = currentRequest.reference.toUInt();
308 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
309 processReply(currentRequest,
310 createRequestErrorMessage(command, attrHandle));
314 qCWarning(QT_BT_BLUEZ) <<
"Missing response for ATT peripheral command: "
315 << Qt::hex << command;
320 sendNextPendingRequest();
327 delete cmacCalculator;
328 cmacCalculator =
nullptr;
334 bool listen(
const QBluetoothAddress &localAdapter)
336 m_socket = ::socket(AF_BLUETOOTH, SOCK_SEQPACKET,
BTPROTO_L2CAP);
337 if (m_socket == -1) {
338 qCWarning(QT_BT_BLUEZ) <<
"socket creation failed:" << qt_error_string(errno);
346 memset(&addr, 0,
sizeof addr);
348 addr.l2_family = AF_BLUETOOTH;
351 convertAddress(localAdapter.toUInt64(), addr.l2_bdaddr.b);
352 if (::bind(m_socket,
reinterpret_cast<sockaddr *>(&addr),
sizeof addr) == -1) {
353 qCWarning(QT_BT_BLUEZ) <<
"bind() failed:" << qt_error_string(errno);
356 if (::listen(m_socket, 1)) {
357 qCWarning(QT_BT_BLUEZ) <<
"listen() failed:" << qt_error_string(errno);
371 const int socket = m_socket;
381void QLowEnergyControllerPrivateBluez::
startAdvertising(
const QLowEnergyAdvertisingParameters ¶ms,
382 const QLowEnergyAdvertisingData &advertisingData,
383 const QLowEnergyAdvertisingData &scanResponseData)
385 qCDebug(QT_BT_BLUEZ) <<
"Starting to advertise";
387 advertiser =
new QLeAdvertiserBluez(params, advertisingData, scanResponseData, hciManager,
389 connect(advertiser, &QLeAdvertiser::errorOccurred,
this,
390 &QLowEnergyControllerPrivateBluez::handleAdvertisingError);
392 setState(QLowEnergyController::AdvertisingState);
393 advertiser->startAdvertising();
394 if (params.mode() == QLowEnergyAdvertisingParameters::AdvNonConnInd
395 || params.mode() == QLowEnergyAdvertisingParameters::AdvScanInd) {
396 qCDebug(QT_BT_BLUEZ) <<
"Non-connectable advertising requested, "
397 "not listening for connections.";
402 if (!serverSocket.listen(localAdapter)) {
403 setError(QLowEnergyController::AdvertisingError);
404 setState(QLowEnergyController::UnconnectedState);
409 serverSocketNotifier =
new QSocketNotifier(socketFd, QSocketNotifier::Read,
this);
410 connect(serverSocketNotifier, &QSocketNotifier::activated,
this,
411 &QLowEnergyControllerPrivateBluez::handleConnectionRequest);
416 setState(QLowEnergyController::UnconnectedState);
417 advertiser->stopAdvertising();
420void QLowEnergyControllerPrivateBluez::requestConnectionUpdate(
const QLowEnergyConnectionParameters ¶ms)
426 if (role == QLowEnergyController::CentralRole)
427 hciManager->sendConnectionUpdateCommand(connectionHandle, params);
429 hciManager->sendConnectionParameterUpdateRequest(connectionHandle, params);
434 if (remoteDevice.isNull()) {
435 qCWarning(QT_BT_BLUEZ) <<
"Invalid/null remote device address";
436 setError(QLowEnergyController::UnknownRemoteDeviceError);
440 setState(QLowEnergyController::ConnectingState);
443 l2cpSocket =
nullptr;
446 createServicesForCentralIfRequired();
452 const QList<quint16> activeHandles = hciManager->activeLowEnergyConnections();
453 if (!activeHandles.isEmpty()) {
454 qCWarning(QT_BT_BLUEZ) <<
"Cannot connect due to pending active LE connections";
456 if (!device1Manager) {
457 device1Manager =
new RemoteDeviceManager(localAdapter,
this);
458 connect(device1Manager, &RemoteDeviceManager::finished,
459 this, &QLowEnergyControllerPrivateBluez::activeConnectionTerminationDone);
462 QList<QBluetoothAddress> connectedAddresses;
463 for (
const auto handle: activeHandles) {
464 const QBluetoothAddress addr = hciManager->addressForConnectionHandle(handle);
466 connectedAddresses.push_back(addr);
468 device1Manager->scheduleJob(RemoteDeviceManager::JobType::JobDisconnectDevice, connectedAddresses);
470 establishL2cpClientSocket();
475
476
477void QLowEnergyControllerPrivateBluez::activeConnectionTerminationDone()
482 qCDebug(QT_BT_BLUEZ) <<
"RemoteDeviceManager finished attempting"
483 <<
"to close external connections";
485 QList<quint16> activeHandles = hciManager->activeLowEnergyConnections();
486 if (!activeHandles.isEmpty()) {
487 qCWarning(QT_BT_BLUEZ) <<
"Cannot close pending external BTLE connections. Aborting connect attempt";
488 setError(QLowEnergyController::ConnectionError);
489 setState(QLowEnergyController::UnconnectedState);
493 establishL2cpClientSocket();
498
499
500void QLowEnergyControllerPrivateBluez::establishL2cpClientSocket()
504 l2cpSocket =
new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol,
this);
505 connect(l2cpSocket, SIGNAL(connected()),
this, SLOT(l2cpConnected()));
506 connect(l2cpSocket, SIGNAL(disconnected()),
this, SLOT(l2cpDisconnected()));
507 connect(l2cpSocket, SIGNAL(errorOccurred(QBluetoothSocket::SocketError)),
this,
508 SLOT(l2cpErrorChanged(QBluetoothSocket::SocketError)));
509 connect(l2cpSocket, SIGNAL(readyRead()),
this, SLOT(l2cpReadyRead()));
511 quint32 addressTypeToUse = (addressType == QLowEnergyController::PublicAddress)
520 qCDebug(QT_BT_BLUEZ) <<
"addresstypeToUse:"
522 ? QStringLiteral(
"Random") : QStringLiteral(
"Public"));
524 l2cpSocket->d_ptr->lowEnergySocketType = addressTypeToUse;
526 int sockfd = l2cpSocket->socketDescriptor();
528 qCWarning(QT_BT_BLUEZ) <<
"l2cp socket not initialised";
529 setError(QLowEnergyController::ConnectionError);
530 setState(QLowEnergyController::UnconnectedState);
535 memset(&addr, 0,
sizeof(addr));
536 addr.l2_family = AF_BLUETOOTH;
539 convertAddress(localAdapter.toUInt64(), addr.l2_bdaddr.b);
542 if (::bind(sockfd, (
struct sockaddr *)&addr,
sizeof(addr)) < 0) {
543 qCWarning(QT_BT_BLUEZ) << qt_error_string(errno);
544 setError(QLowEnergyController::ConnectionError);
545 setState(QLowEnergyController::UnconnectedState);
552 QIODevice::ReadWrite | QIODevice::Unbuffered);
553 loadSigningDataIfNecessary(LocalSigningKey);
556void QLowEnergyControllerPrivateBluez::createServicesForCentralIfRequired()
559 int value = qEnvironmentVariableIntValue(
"QT_DEFAULT_CENTRAL_SERVICES", &ok);
560 if (Q_UNLIKELY(ok && value == 0))
564 if (localServices.contains(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::GenericAccess)))
567 qCDebug(QT_BT_BLUEZ) <<
"Creating default GAP/GATT services";
571 QLowEnergyServiceData gapServiceData;
572 gapServiceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
573 gapServiceData.setUuid(QBluetoothUuid::ServiceClassUuid::GenericAccess);
575 QLowEnergyCharacteristicData gapDeviceName;
576 gapDeviceName.setUuid(QBluetoothUuid::CharacteristicType::DeviceName);
577 gapDeviceName.setProperties(QLowEnergyCharacteristic::Read);
579 QBluetoothLocalDevice mainAdapter;
580 gapDeviceName.setValue(mainAdapter.name().toLatin1());
582 QLowEnergyCharacteristicData gapAppearance;
583 gapAppearance.setUuid(QBluetoothUuid::CharacteristicType::Appearance);
584 gapAppearance.setProperties(QLowEnergyCharacteristic::Read);
585 gapAppearance.setValue(QByteArray::fromHex(
"80"));
587 QLowEnergyCharacteristicData gapPrivacyFlag;
588 gapPrivacyFlag.setUuid(QBluetoothUuid::CharacteristicType::PeripheralPrivacyFlag);
589 gapPrivacyFlag.setProperties(QLowEnergyCharacteristic::Read);
590 gapPrivacyFlag.setValue(QByteArray::fromHex(
"00"));
592 gapServiceData.addCharacteristic(gapDeviceName);
593 gapServiceData.addCharacteristic(gapAppearance);
594 gapServiceData.addCharacteristic(gapPrivacyFlag);
596 Q_Q(QLowEnergyController);
597 QLowEnergyService *service = addServiceHelper(gapServiceData);
599 service->setParent(q);
601 QLowEnergyServiceData gattServiceData;
602 gattServiceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
603 gattServiceData.setUuid(QBluetoothUuid::ServiceClassUuid::GenericAttribute);
605 QLowEnergyCharacteristicData serviceChangedChar;
606 serviceChangedChar.setUuid(QBluetoothUuid::CharacteristicType::ServiceChanged);
607 serviceChangedChar.setProperties(QLowEnergyCharacteristic::Indicate);
609 serviceChangedChar.setValue(QByteArray::fromHex(
"0104"));
611 const QLowEnergyDescriptorData clientConfig(
612 QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
614 serviceChangedChar.addDescriptor(clientConfig);
615 gattServiceData.addCharacteristic(serviceChangedChar);
617 service = addServiceHelper(gattServiceData);
619 service->setParent(q);
622void QLowEnergyControllerPrivateBluez::l2cpConnected()
624 Q_Q(QLowEnergyController);
626 securityLevelValue = securityLevel();
629 setState(QLowEnergyController::ConnectedState);
635 setState(QLowEnergyController::ClosingState);
643 qWarning(QT_BT_BLUEZ) <<
"Unexpected closure of device. Cleaning up internal states.";
648void QLowEnergyControllerPrivateBluez::l2cpDisconnected()
650 Q_Q(QLowEnergyController);
652 if (role == QLowEnergyController::PeripheralRole) {
653 storeClientConfigurations();
654 remoteDevice.clear();
659 setState(QLowEnergyController::UnconnectedState);
660 emit q->disconnected();
663void QLowEnergyControllerPrivateBluez::l2cpErrorChanged(QBluetoothSocket::SocketError e)
666 case QBluetoothSocket::SocketError::HostNotFoundError:
667 setError(QLowEnergyController::UnknownRemoteDeviceError);
668 qCDebug(QT_BT_BLUEZ) <<
"The passed remote device address cannot be found";
670 case QBluetoothSocket::SocketError::NetworkError:
671 setError(QLowEnergyController::NetworkError);
672 qCDebug(QT_BT_BLUEZ) <<
"Network IO error while talking to LE device";
674 case QBluetoothSocket::SocketError::RemoteHostClosedError:
675 setError(QLowEnergyController::RemoteHostClosedError);
676 qCDebug(QT_BT_BLUEZ) <<
"Remote host closed the connection";
678 case QBluetoothSocket::SocketError::UnknownSocketError:
679 case QBluetoothSocket::SocketError::UnsupportedProtocolError:
680 case QBluetoothSocket::SocketError::OperationError:
681 case QBluetoothSocket::SocketError::ServiceNotFoundError:
685 qCDebug(QT_BT_BLUEZ) <<
"Unknown l2cp socket error: " << e << l2cpSocket->errorString();
686 setError(QLowEnergyController::UnknownError);
692 setState(QLowEnergyController::UnconnectedState);
696void QLowEnergyControllerPrivateBluez::resetController()
698 openRequests.clear();
699 openPrepareWriteRequests.clear();
700 scheduledIndications.clear();
701 indicationInFlight =
false;
702 requestPending =
false;
703 encryptionChangePending =
false;
704 receivedMtuExchangeRequest =
false;
705 mtuSize = ATT_DEFAULT_LE_MTU;
706 securityLevelValue = -1;
707 connectionHandle = 0;
709 if (role == QLowEnergyController::PeripheralRole) {
712 advertiser->stopAdvertising();
714 advertiser =
nullptr;
716 localAttributes.clear();
720void QLowEnergyControllerPrivateBluez::restartRequestTimer()
725 if (gattRequestTimeout > 0)
726 requestTimer->start(gattRequestTimeout);
729void QLowEnergyControllerPrivateBluez::l2cpReadyRead()
731 const QByteArray incomingPacket = l2cpSocket->readAll();
732 qCDebug(QT_BT_BLUEZ) <<
"Received size:" << incomingPacket.size() <<
"data:"
733 << incomingPacket.toHex();
734 if (incomingPacket.isEmpty())
741 processUnsolicitedReply(incomingPacket);
750 processUnsolicitedReply(incomingPacket);
755 case QBluezConst::AttCommand::ATT_OP_EXCHANGE_MTU_REQUEST:
756 handleExchangeMtuRequest(incomingPacket);
758 case QBluezConst::AttCommand::ATT_OP_FIND_INFORMATION_REQUEST:
759 handleFindInformationRequest(incomingPacket);
761 case QBluezConst::AttCommand::ATT_OP_FIND_BY_TYPE_VALUE_REQUEST:
762 handleFindByTypeValueRequest(incomingPacket);
764 case QBluezConst::AttCommand::ATT_OP_READ_BY_TYPE_REQUEST:
765 handleReadByTypeRequest(incomingPacket);
767 case QBluezConst::AttCommand::ATT_OP_READ_REQUEST:
768 handleReadRequest(incomingPacket);
770 case QBluezConst::AttCommand::ATT_OP_READ_BLOB_REQUEST:
771 handleReadBlobRequest(incomingPacket);
773 case QBluezConst::AttCommand::ATT_OP_READ_MULTIPLE_REQUEST:
774 handleReadMultipleRequest(incomingPacket);
776 case QBluezConst::AttCommand::ATT_OP_READ_BY_GROUP_REQUEST:
777 handleReadByGroupTypeRequest(incomingPacket);
779 case QBluezConst::AttCommand::ATT_OP_WRITE_REQUEST:
780 case QBluezConst::AttCommand::ATT_OP_WRITE_COMMAND:
781 case QBluezConst::AttCommand::ATT_OP_SIGNED_WRITE_COMMAND:
782 handleWriteRequestOrCommand(incomingPacket);
784 case QBluezConst::AttCommand::ATT_OP_PREPARE_WRITE_REQUEST:
785 handlePrepareWriteRequest(incomingPacket);
787 case QBluezConst::AttCommand::ATT_OP_EXECUTE_WRITE_REQUEST:
788 handleExecuteWriteRequest(incomingPacket);
791 if (indicationInFlight) {
792 indicationInFlight =
false;
793 sendNextIndication();
795 qCWarning(QT_BT_BLUEZ) <<
"received unexpected handle value confirmation";
801 requestPending =
false;
805 if (openRequests.isEmpty()) {
806 qCWarning(QT_BT_BLUEZ) <<
"Received unexpected packet from peer, disconnecting.";
811 const Request request = openRequests.dequeue();
812 processReply(request, incomingPacket);
814 sendNextPendingRequest();
818
819
820
821
822
823
824
825
826void QLowEnergyControllerPrivateBluez::encryptionChangedEvent(
827 const QBluetoothAddress &address,
bool wasSuccess)
829 if (!encryptionChangePending)
832 if (remoteDevice != address)
835 securityLevelValue = securityLevel();
842 Q_ASSERT(!openRequests.isEmpty());
843 Request failedRequest = openRequests.takeFirst();
847 uint ref = failedRequest.reference.toUInt();
848 const QLowEnergyHandle charHandle = (ref & 0xffff);
849 const QLowEnergyHandle descriptorHandle = ((ref >> 16) & 0xffff);
851 QSharedPointer<QLowEnergyServicePrivate> service
852 = serviceForHandle(charHandle);
853 if (!service.isNull() && service->characteristicList.contains(charHandle)) {
854 if (!descriptorHandle)
855 service->setError(QLowEnergyService::CharacteristicWriteError);
857 service->setError(QLowEnergyService::DescriptorWriteError);
860 uint handleData = failedRequest.reference.toUInt();
861 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
862 const QByteArray newValue = failedRequest.reference2.toByteArray();
867 sendExecuteWriteRequest(attrHandle, newValue,
true);
871 encryptionChangePending =
false;
872 sendNextPendingRequest();
875void QLowEnergyControllerPrivateBluez::sendPacket(
const QByteArray &packet)
877 qint64 result = l2cpSocket->write(packet.constData(),
883 qCDebug(QT_BT_BLUEZ) <<
"Cannot write L2CP packet:" << Qt::hex
885 << l2cpSocket->errorString();
886 setError(QLowEnergyController::NetworkError);
887 }
else if (result < packet.size()) {
888 qCWarning(QT_BT_BLUEZ) <<
"L2CP write request incomplete:"
889 << result <<
"of" << packet.size();
894void QLowEnergyControllerPrivateBluez::sendNextPendingRequest()
896 if (openRequests.isEmpty() || requestPending || encryptionChangePending)
899 const Request &request = openRequests.head();
903 requestPending =
true;
904 restartRequestTimer();
905 sendPacket(request.payload);
910 const char *data, quint16 elementLength)
914 Q_ASSERT(elementLength >= 5);
916 QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
917 charData->properties =
918 (QLowEnergyCharacteristic::PropertyTypes)(data[2] & 0xff);
919 charData->valueHandle = bt_get_le16(&data[3]);
922 if (elementLength == 7)
923 charData->uuid = QBluetoothUuid(bt_get_le16(&data[5]));
924 else if (elementLength == 21)
925 charData->uuid = QUuid::fromBytes(&data[5], QSysInfo::LittleEndian);
927 qCDebug(QT_BT_BLUEZ) <<
"Found handle:" << Qt::hex << attributeHandle
928 <<
"properties:" << charData->properties
929 <<
"value handle:" << charData->valueHandle
930 <<
"uuid:" << charData->uuid.toString();
932 return attributeHandle;
936 QList<QBluetoothUuid> *foundServices,
937 const char *data, quint16 elementLength)
939 Q_ASSERT(foundServices);
941 Q_ASSERT(elementLength >= 6);
943 QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
956 if (elementLength == 8)
957 foundServices->append(QBluetoothUuid(bt_get_le16(&data[6])));
958 else if (elementLength == 22)
959 foundServices->append(QUuid::fromBytes(&data[6], QSysInfo::LittleEndian));
961 qCDebug(QT_BT_BLUEZ) <<
"Found included service: " << Qt::hex
962 << attributeHandle <<
"uuid:" << *foundServices;
964 return attributeHandle;
970 qCDebug(QT_BT_BLUEZ,
"%s malformed data: %s", qt_getEnumName(cmd),
971 response.toHex().constData());
974void QLowEnergyControllerPrivateBluez::processReply(
975 const Request &request,
const QByteArray &response)
977 Q_Q(QLowEnergyController);
981 Q_ASSERT(!response.isEmpty());
985 bool isErrorResponse =
false;
988 if (!dumpErrorInformation(response))
991 isErrorResponse =
true;
998 quint16 oldMtuSize = mtuSize;
999 if (isErrorResponse) {
1000 mtuSize = ATT_DEFAULT_LE_MTU;
1002 if (response.size() < 3) {
1003 reportMalformedData(command, response);
1006 const char *data = response.constData();
1007 quint16 mtu = bt_get_le16(&data[1]);
1009 if (mtuSize < ATT_DEFAULT_LE_MTU)
1010 mtuSize = ATT_DEFAULT_LE_MTU;
1012 qCDebug(QT_BT_BLUEZ) <<
"Server MTU:" << mtu <<
"resulting mtu:" << mtuSize;
1014 if (oldMtuSize != mtuSize)
1015 emit q->mtuChanged(mtuSize);
1022 const quint16 type = request.reference.toUInt();
1024 if (isErrorResponse) {
1026 setState(QLowEnergyController::DiscoveredState);
1027 q->discoveryFinished();
1036 if (response.size() < 2 || response[1] < 4) {
1037 reportMalformedData(command, response);
1041 QLowEnergyHandle start = 0, end = 0;
1042 const quint16 elementLength = response.constData()[1];
1043 const quint16 numElements = (response.size() - 2) / elementLength;
1045 const char *data = response.constData();
1046 for (
int i = 0; i < numElements; i++) {
1047 start = bt_get_le16(&data[offset]);
1048 end = bt_get_le16(&data[offset+2]);
1050 QBluetoothUuid uuid;
1051 if (elementLength == 6)
1052 uuid = QBluetoothUuid(bt_get_le16(&data[offset+4]));
1053 else if (elementLength == 20)
1054 uuid = QUuid::fromBytes(&data[offset+4], QSysInfo::LittleEndian);
1057 offset += elementLength;
1060 qCDebug(QT_BT_BLUEZ) <<
"Found uuid:" << uuid <<
"start handle:" << Qt::hex
1061 << start <<
"end handle:" << end;
1065 priv->startHandle = start;
1066 priv->endHandle = end;
1068 priv->type &= ~QLowEnergyService::PrimaryService;
1071 QSharedPointer<QLowEnergyServicePrivate> pointer(priv);
1073 serviceList.insert(uuid, pointer);
1074 emit q->serviceDiscovered(uuid);
1077 if (end != 0xFFFF) {
1078 sendReadByGroupRequest(end+1, 0xFFFF, type);
1081 setState(QLowEnergyController::DiscoveredState);
1082 emit q->discoveryFinished();
1093 QSharedPointer<QLowEnergyServicePrivate> p =
1094 request.reference.value<QSharedPointer<QLowEnergyServicePrivate> >();
1095 const quint16 attributeType = request.reference2.toUInt();
1097 if (isErrorResponse) {
1102 if (!p->characteristicList.isEmpty()) {
1103 readServiceValues(p->uuid,
true);
1107 p->setState(QLowEnergyService::RemoteServiceDiscovered);
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1132 if (response.size() < 2 || response[1] < minimumElementLength) {
1133 reportMalformedData(command, response);
1137 QLowEnergyHandle lastHandle;
1138 const quint16 elementLength = response.constData()[1];
1139 const quint16 numElements = (response.size() - 2) / elementLength;
1141 const char *data = response.constData();
1142 for (
int i = 0; i < numElements; i++) {
1145 lastHandle = parseReadByTypeCharDiscovery(
1146 &characteristic, &data[offset], elementLength);
1147 p->characteristicList[lastHandle] = characteristic;
1148 offset += elementLength;
1150 QList<QBluetoothUuid> includedServices;
1151 lastHandle = parseReadByTypeIncludeDiscovery(
1152 &includedServices, &data[offset], elementLength);
1153 p->includedServices = includedServices;
1154 for (
const QBluetoothUuid &uuid : std::as_const(includedServices)) {
1155 if (serviceList.contains(uuid))
1156 serviceList[uuid]->type |= QLowEnergyService::IncludedService;
1161 if (lastHandle + 1 < p->endHandle) {
1162 sendReadByTypeRequest(p, lastHandle + 1, attributeType);
1167 readServiceValues(p->uuid,
true);
1175 uint handleData = request.reference.toUInt();
1176 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1177 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1179 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1180 Q_ASSERT(!service.isNull());
1181 bool isServiceDiscoveryRun
1182 = !(service->state == QLowEnergyService::RemoteServiceDiscovered);
1184 if (isErrorResponse) {
1185 Q_ASSERT(!encryptionChangePending);
1187 encryptionChangePending = increaseEncryptLevelfRequired(err);
1188 if (encryptionChangePending) {
1191 openRequests.prepend(request);
1193 }
else if (!isServiceDiscoveryRun) {
1195 if (!descriptorHandle)
1196 service->setError(QLowEnergyService::CharacteristicReadError);
1198 service->setError(QLowEnergyService::DescriptorReadError);
1201 if (!descriptorHandle)
1202 updateValueOfCharacteristic(charHandle, response.mid(1),
NEW_VALUE);
1204 updateValueOfDescriptor(charHandle, descriptorHandle,
1207 if (response.size() == mtuSize) {
1208 qCDebug(QT_BT_BLUEZ) <<
"Switching to blob reads for"
1209 << charHandle << descriptorHandle
1210 << service->characteristicList[charHandle].uuid.toString();
1212 readServiceValuesByOffset(handleData, mtuSize-1,
1213 request.reference2.toBool());
1215 }
else if (!isServiceDiscoveryRun) {
1217 if (!descriptorHandle) {
1218 QLowEnergyCharacteristic ch(service, charHandle);
1219 emit service->characteristicRead(ch, response.mid(1));
1221 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1222 emit service->descriptorRead(descriptor, response.mid(1));
1228 if (request.reference2.toBool() && isServiceDiscoveryRun) {
1234 if (!descriptorHandle)
1235 discoverServiceDescriptors(service->uuid);
1237 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1245 uint handleData = request.reference.toUInt();
1246 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1247 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1249 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1250 Q_ASSERT(!service.isNull());
1253
1254
1255
1256
1257
1258 if (!isErrorResponse) {
1260 if (!descriptorHandle)
1261 length = updateValueOfCharacteristic(charHandle, response.mid(1),
APPEND_VALUE);
1263 length = updateValueOfDescriptor(charHandle, descriptorHandle,
1266 if (response.size() == mtuSize) {
1267 readServiceValuesByOffset(handleData, length,
1268 request.reference2.toBool());
1270 }
else if (service->state == QLowEnergyService::RemoteServiceDiscovered) {
1272 if (!descriptorHandle) {
1273 QLowEnergyCharacteristic ch(service, charHandle);
1274 emit service->characteristicRead(ch, ch.value());
1276 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1277 emit service->descriptorRead(descriptor, descriptor.value());
1282 qWarning() <<
"READ BLOB for char:" << charHandle
1283 <<
"descriptor:" << descriptorHandle <<
"on service"
1284 << service->uuid.toString() <<
"failed (service discovery run:"
1285 << (service->state == QLowEnergyService::RemoteServiceDiscovered) <<
")";
1288 if (request.reference2.toBool()) {
1292 if (!descriptorHandle)
1293 discoverServiceDescriptors(service->uuid);
1295 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1305
1306
1307
1308
1310 QList<QLowEnergyHandle> keys = request.reference.value<QList<QLowEnergyHandle> >();
1311 if (keys.isEmpty()) {
1312 qCWarning(QT_BT_BLUEZ) <<
"Descriptor discovery for unknown characteristic received";
1315 QLowEnergyHandle charHandle = keys.first();
1317 QSharedPointer<QLowEnergyServicePrivate> p =
1318 serviceForHandle(charHandle);
1319 Q_ASSERT(!p.isNull());
1321 if (isErrorResponse) {
1322 if (keys.size() == 1) {
1324 readServiceValues(p->uuid,
false);
1328 discoverNextDescriptor(p, keys, keys.first());
1334 if (response.size() < 6) {
1335 reportMalformedData(command, response);
1339 const quint8 format = response[1];
1340 quint16 elementLength;
1343 elementLength = 2 + 2;
1346 elementLength = 2 + 16;
1349 qCWarning(QT_BT_BLUEZ) <<
"Unknown format in FIND_INFORMATION_RESPONSE";
1353 const quint16 numElements = (response.size() - 2) / elementLength;
1356 QLowEnergyHandle descriptorHandle {};
1357 QBluetoothUuid uuid;
1358 const char *data = response.constData();
1359 for (
int i = 0; i < numElements; i++) {
1360 descriptorHandle = bt_get_le16(&data[offset]);
1363 uuid = QBluetoothUuid(bt_get_le16(&data[offset+2]));
1364 else if (format == 0x02)
1365 uuid = QUuid::fromBytes(&data[offset+2], QSysInfo::LittleEndian);
1367 offset += elementLength;
1372 quint16 shortUuid = uuid.toUInt16(&ok);
1375 qCDebug(QT_BT_BLUEZ) <<
"Suppressing primary/characteristic" << Qt::hex << shortUuid;
1380 if (descriptorHandle == p->characteristicList[charHandle].valueHandle) {
1381 qCDebug(QT_BT_BLUEZ) <<
"Suppressing char handle" << Qt::hex << descriptorHandle;
1387 p->characteristicList[charHandle].descriptorList.insert(
1388 descriptorHandle, data);
1390 qCDebug(QT_BT_BLUEZ) <<
"Descriptor found, uuid:"
1392 <<
"descriptor handle:" << Qt::hex << descriptorHandle;
1395 const QLowEnergyHandle nextPotentialHandle = descriptorHandle + 1;
1396 if (keys.size() == 1) {
1405 if ((p->endHandle != 0xffff && nextPotentialHandle >= p->endHandle + 1)
1406 || (descriptorHandle == 0xffff)) {
1410 readServiceValues(p->uuid,
false);
1412 discoverNextDescriptor(p, keys, nextPotentialHandle);
1415 if (nextPotentialHandle >= keys[1])
1417 discoverNextDescriptor(p, keys, nextPotentialHandle);
1425 uint ref = request.reference.toUInt();
1426 const QLowEnergyHandle charHandle = (ref & 0xffff);
1427 const QLowEnergyHandle descriptorHandle = ((ref >> 16) & 0xffff);
1429 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1430 if (service.isNull() || !service->characteristicList.contains(charHandle))
1433 if (isErrorResponse) {
1434 Q_ASSERT(!encryptionChangePending);
1436 encryptionChangePending = increaseEncryptLevelfRequired(err);
1437 if (encryptionChangePending) {
1438 openRequests.prepend(request);
1442 if (!descriptorHandle)
1443 service->setError(QLowEnergyService::CharacteristicWriteError);
1445 service->setError(QLowEnergyService::DescriptorWriteError);
1449 const QByteArray newValue = request.reference2.toByteArray();
1450 if (!descriptorHandle) {
1451 QLowEnergyCharacteristic ch(service, charHandle);
1452 if (ch.properties() & QLowEnergyCharacteristic::Read)
1453 updateValueOfCharacteristic(charHandle, newValue,
NEW_VALUE);
1454 emit service->characteristicWritten(ch, newValue);
1456 updateValueOfDescriptor(charHandle, descriptorHandle, newValue,
NEW_VALUE);
1457 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1458 emit service->descriptorWritten(descriptor, newValue);
1466 uint handleData = request.reference.toUInt();
1467 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
1468 const QByteArray newValue = request.reference2.toByteArray();
1469 const int writtenPayload = ((handleData >> 16) & 0xffff);
1471 if (isErrorResponse) {
1472 Q_ASSERT(!encryptionChangePending);
1474 encryptionChangePending = increaseEncryptLevelfRequired(err);
1475 if (encryptionChangePending) {
1476 openRequests.prepend(request);
1480 sendExecuteWriteRequest(attrHandle, newValue,
true);
1482 if (writtenPayload < newValue.size()) {
1483 sendNextPrepareWriteRequest(attrHandle, newValue, writtenPayload);
1485 sendExecuteWriteRequest(attrHandle, newValue,
false);
1495 uint handleData = request.reference.toUInt();
1496 const QLowEnergyHandle attrHandle = handleData & 0xffff;
1497 bool wasCancellation = !((handleData >> 16) & 0xffff);
1498 const QByteArray newValue = request.reference2.toByteArray();
1501 const QLowEnergyDescriptor descriptor = descriptorForHandle(attrHandle);
1502 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(attrHandle);
1503 Q_ASSERT(!service.isNull());
1505 if (isErrorResponse || wasCancellation) {
1507 if (descriptor.isValid())
1508 service->setError(QLowEnergyService::DescriptorWriteError);
1510 service->setError(QLowEnergyService::CharacteristicWriteError);
1512 if (descriptor.isValid()) {
1513 updateValueOfDescriptor(descriptor.characteristicHandle(),
1515 emit service->descriptorWritten(descriptor, newValue);
1517 QLowEnergyCharacteristic ch(service, attrHandle);
1518 if (ch.properties() & QLowEnergyCharacteristic::Read)
1519 updateValueOfCharacteristic(attrHandle, newValue,
NEW_VALUE);
1520 emit service->characteristicWritten(ch, newValue);
1525 qCDebug(QT_BT_BLUEZ) <<
"Unknown packet: " << response.toHex();
1535void QLowEnergyControllerPrivateBluez::sendReadByGroupRequest(
1536 QLowEnergyHandle start, QLowEnergyHandle end, quint16 type)
1542 putBtData(start, &packet[1]);
1543 putBtData(end, &packet[3]);
1544 putBtData(type, &packet[5]);
1548 qCDebug(QT_BT_BLUEZ) <<
"Sending read_by_group_type request, startHandle:" << Qt::hex
1549 << start <<
"endHandle:" << end << type;
1552 request.payload = data;
1554 request.reference = type;
1555 openRequests.enqueue(request);
1557 sendNextPendingRequest();
1561 QLowEnergyService::DiscoveryMode mode)
1563 if (!serviceList.contains(service)) {
1564 qCWarning(QT_BT_BLUEZ) <<
"Discovery of unknown service" << service.toString()
1569 QSharedPointer<QLowEnergyServicePrivate> serviceData = serviceList.value(service);
1570 serviceData->mode = mode;
1571 serviceData->characteristicList.clear();
1575void QLowEnergyControllerPrivateBluez::sendReadByTypeRequest(
1576 QSharedPointer<QLowEnergyServicePrivate> serviceData,
1577 QLowEnergyHandle nextHandle, quint16 attributeType)
1582 putBtData(nextHandle, &packet[1]);
1583 putBtData(serviceData->endHandle, &packet[3]);
1584 putBtData(attributeType, &packet[5]);
1588 qCDebug(QT_BT_BLUEZ) <<
"Sending read_by_type request, startHandle:" << Qt::hex
1589 << nextHandle <<
"endHandle:" << serviceData->endHandle
1590 <<
"type:" << attributeType <<
"packet:" << data.toHex();
1593 request.payload = data;
1595 request.reference = QVariant::fromValue(serviceData);
1596 request.reference2 = attributeType;
1597 openRequests.enqueue(request);
1599 sendNextPendingRequest();
1603
1604
1605
1606
1607
1608
1609
1610
1611void QLowEnergyControllerPrivateBluez::readServiceValues(
1612 const QBluetoothUuid &serviceUuid,
bool readCharacteristics)
1615 if (QT_BT_BLUEZ().isDebugEnabled()) {
1616 if (readCharacteristics)
1617 qCDebug(QT_BT_BLUEZ) <<
"Reading all characteristic values for"
1618 << serviceUuid.toString();
1620 qCDebug(QT_BT_BLUEZ) <<
"Reading all descriptor values for"
1621 << serviceUuid.toString();
1624 QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid);
1626 if (service->mode == QLowEnergyService::SkipValueDiscovery) {
1627 if (readCharacteristics) {
1629 discoverServiceDescriptors(service->uuid);
1631 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1638 QPair<QLowEnergyHandle, quint32> pair;
1641 QList<QPair<QLowEnergyHandle, quint32> > targetHandles;
1643 CharacteristicDataMap::const_iterator charIt = service->characteristicList.constBegin();
1644 for ( ; charIt != service->characteristicList.constEnd(); ++charIt) {
1645 const QLowEnergyHandle charHandle = charIt.key();
1648 if (readCharacteristics) {
1652 if (!(charDetails.properties & QLowEnergyCharacteristic::Read))
1655 pair.first = charDetails.valueHandle;
1656 pair.second = charHandle;
1657 targetHandles.append(pair);
1661 DescriptorDataMap::const_iterator descIt = charDetails.descriptorList.constBegin();
1662 for ( ; descIt != charDetails.descriptorList.constEnd(); ++descIt) {
1663 const QLowEnergyHandle descriptorHandle = descIt.key();
1665 pair.first = descriptorHandle;
1666 pair.second = (charHandle | (descriptorHandle << 16));
1667 targetHandles.append(pair);
1673 if (targetHandles.isEmpty()) {
1674 if (readCharacteristics) {
1677 discoverServiceDescriptors(service->uuid);
1680 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1685 for (qsizetype i = 0; i < targetHandles.size(); i++) {
1686 pair = targetHandles.at(i);
1688 putBtData(pair.first, &packet[1]);
1694 request.payload = data;
1696 request.reference = pair.second;
1698 request.reference2 = QVariant((
bool)(i + 1 == targetHandles.size()));
1699 openRequests.enqueue(request);
1702 sendNextPendingRequest();
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715void QLowEnergyControllerPrivateBluez::readServiceValuesByOffset(
1716 uint handleData, quint16 offset,
bool isLastValue)
1718 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1719 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1724 QLowEnergyHandle handleToRead = charHandle;
1725 if (descriptorHandle) {
1726 handleToRead = descriptorHandle;
1727 qCDebug(QT_BT_BLUEZ) <<
"Reading descriptor via blob request"
1728 << Qt::hex << descriptorHandle;
1731 QSharedPointer<QLowEnergyServicePrivate> service =
1732 serviceForHandle(charHandle);
1733 if (!service.isNull()
1734 && service->characteristicList.contains(charHandle)) {
1735 handleToRead = service->characteristicList[charHandle].valueHandle;
1736 qCDebug(QT_BT_BLUEZ) <<
"Reading characteristic via blob request"
1737 << Qt::hex << handleToRead;
1743 putBtData(handleToRead, data.data() + 1);
1744 putBtData(offset, data.data() + 3);
1747 request.payload = data;
1749 request.reference = handleData;
1750 request.reference2 = isLastValue;
1751 openRequests.prepend(request);
1754void QLowEnergyControllerPrivateBluez::discoverServiceDescriptors(
1755 const QBluetoothUuid &serviceUuid)
1757 qCDebug(QT_BT_BLUEZ) <<
"Discovering descriptor values for"
1758 << serviceUuid.toString();
1759 QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid);
1761 if (service->characteristicList.isEmpty()) {
1763 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1768 QList<QLowEnergyHandle> keys = service->characteristicList.keys();
1769 std::sort(keys.begin(), keys.end());
1771 discoverNextDescriptor(service, keys, keys[0]);
1774void QLowEnergyControllerPrivateBluez::processUnsolicitedReply(
const QByteArray &payload)
1776 Q_ASSERT(!payload.isEmpty());
1778 const char *data = payload.constData();
1780 bool isNotification = (command
1783 if (payload.size() < 3) {
1784 reportMalformedData(command, payload);
1788 const QLowEnergyHandle changedHandle = bt_get_le16(&data[1]);
1790 if (QT_BT_BLUEZ().isDebugEnabled()) {
1792 qCDebug(QT_BT_BLUEZ) <<
"Change notification for handle" << Qt::hex << changedHandle;
1794 qCDebug(QT_BT_BLUEZ) <<
"Change indication for handle" << Qt::hex << changedHandle;
1797 const QLowEnergyCharacteristic ch = characteristicForHandle(changedHandle);
1798 if (ch.isValid() && ch.handle() == changedHandle) {
1799 if (ch.properties() & QLowEnergyCharacteristic::Read)
1800 updateValueOfCharacteristic(ch.attributeHandle(), payload.mid(3),
NEW_VALUE);
1801 emit ch.d_ptr->characteristicChanged(ch, payload.mid(3));
1803 qCWarning(QT_BT_BLUEZ) <<
"Cannot find matching characteristic for "
1804 "notification/indication";
1808void QLowEnergyControllerPrivateBluez::exchangeMTU()
1810 qCDebug(QT_BT_BLUEZ) <<
"Exchanging MTU";
1814 putBtData(ATT_MAX_LE_MTU, &packet[1]);
1820 request.payload = data;
1822 openRequests.enqueue(request);
1824 sendNextPendingRequest();
1827int QLowEnergyControllerPrivateBluez::securityLevel()
const
1829 int socket = l2cpSocket->socketDescriptor();
1831 qCWarning(QT_BT_BLUEZ) <<
"Invalid l2cp socket, aborting getting of sec level";
1835 struct bt_security secData;
1836 socklen_t length =
sizeof(secData);
1837 memset(&secData, 0, length);
1840 qCDebug(QT_BT_BLUEZ) <<
"Current l2cp sec level:" << secData.level;
1841 return secData.level;
1844 if (errno != ENOPROTOOPT)
1849 length =
sizeof(optval);
1859 qCDebug(QT_BT_BLUEZ) <<
"Current l2cp sec level (old):" << level;
1866bool QLowEnergyControllerPrivateBluez::setSecurityLevel(
int level)
1871 int socket = l2cpSocket->socketDescriptor();
1873 qCWarning(QT_BT_BLUEZ) <<
"Invalid l2cp socket, aborting setting of sec level";
1877 struct bt_security secData;
1878 socklen_t length =
sizeof(secData);
1879 memset(&secData, 0, length);
1880 secData.level = level;
1883 qCDebug(QT_BT_BLUEZ) <<
"Setting new l2cp sec level:" << secData.level;
1887 if (errno != ENOPROTOOPT)
1906 qCDebug(QT_BT_BLUEZ) <<
"Old l2cp sec level:" << optval;
1913void QLowEnergyControllerPrivateBluez::discoverNextDescriptor(
1914 QSharedPointer<QLowEnergyServicePrivate> serviceData,
1915 const QList<QLowEnergyHandle> pendingCharHandles,
1916 const QLowEnergyHandle startingHandle)
1918 Q_ASSERT(!pendingCharHandles.isEmpty());
1919 Q_ASSERT(!serviceData.isNull());
1921 qCDebug(QT_BT_BLUEZ) <<
"Sending find_info request" << Qt::hex
1922 << pendingCharHandles << startingHandle;
1927 const QLowEnergyHandle charStartHandle = startingHandle;
1928 QLowEnergyHandle charEndHandle = 0;
1929 if (pendingCharHandles.size() == 1)
1930 charEndHandle = serviceData->endHandle;
1932 charEndHandle = pendingCharHandles[1] - 1;
1934 putBtData(charStartHandle, &packet[1]);
1935 putBtData(charEndHandle, &packet[3]);
1941 request.payload = data;
1943 request.reference = QVariant::fromValue<QList<QLowEnergyHandle> >(pendingCharHandles);
1944 request.reference2 = startingHandle;
1945 openRequests.enqueue(request);
1947 sendNextPendingRequest();
1950void QLowEnergyControllerPrivateBluez::sendNextPrepareWriteRequest(
1951 const QLowEnergyHandle handle,
const QByteArray &newValue,
1955 QLowEnergyHandle targetHandle = 0;
1956 const QLowEnergyDescriptor descriptor = descriptorForHandle(handle);
1957 if (descriptor.isValid())
1958 targetHandle = descriptor.handle();
1960 targetHandle = characteristicForHandle(handle).handle();
1962 if (!targetHandle) {
1963 qCWarning(QT_BT_BLUEZ) <<
"sendNextPrepareWriteRequest cancelled due to invalid handle"
1970 putBtData(targetHandle, &packet[1]);
1971 putBtData(offset, &packet[3]);
1973 qCDebug(QT_BT_BLUEZ) <<
"Writing long characteristic (prepare):"
1974 << Qt::hex << handle;
1978 const qsizetype requiredPayload = (
std::min)(newValue.size() - offset, maxAvailablePayload);
1981 Q_ASSERT((offset + requiredPayload) <= newValue.size());
1982 Q_ASSERT(dataSize <= mtuSize);
1984 QByteArray data(dataSize, Qt::Uninitialized);
1990 request.payload = data;
1992 request.reference = (handle | ((offset + requiredPayload) << 16));
1993 request.reference2 = newValue;
1994 openRequests.enqueue(request);
1998
1999
2000
2001
2002
2003
2004
2005void QLowEnergyControllerPrivateBluez::sendExecuteWriteRequest(
2006 const QLowEnergyHandle attrHandle,
const QByteArray &newValue,
2019 qCDebug(QT_BT_BLUEZ) <<
"Sending Execute Write Request for long characteristic value"
2020 << Qt::hex << attrHandle;
2023 request.payload = data;
2025 request.reference = (attrHandle | ((isCancelation ? 0x00 : 0x01) << 16));
2026 request.reference2 = newValue;
2027 openRequests.prepend(request);
2032
2033
2034
2035
2036
2038 const QSharedPointer<QLowEnergyServicePrivate> service,
2039 const QLowEnergyHandle charHandle,
2040 const QByteArray &newValue,
2041 QLowEnergyService::WriteMode mode)
2043 Q_ASSERT(!service.isNull());
2045 if (!service->characteristicList.contains(charHandle))
2049 if (role == QLowEnergyController::PeripheralRole)
2050 writeCharacteristicForPeripheral(charData, newValue);
2052 writeCharacteristicForCentral(service, charHandle, charData.valueHandle, newValue, mode);
2056 const QSharedPointer<QLowEnergyServicePrivate> service,
2057 const QLowEnergyHandle charHandle,
2058 const QLowEnergyHandle descriptorHandle,
2059 const QByteArray &newValue)
2061 Q_ASSERT(!service.isNull());
2063 if (role == QLowEnergyController::PeripheralRole)
2064 writeDescriptorForPeripheral(service, charHandle, descriptorHandle, newValue);
2066 writeDescriptorForCentral(charHandle, descriptorHandle, newValue);
2070
2071
2072
2073
2075 const QSharedPointer<QLowEnergyServicePrivate> service,
2076 const QLowEnergyHandle charHandle)
2078 Q_ASSERT(!service.isNull());
2079 if (!service->characteristicList.contains(charHandle))
2083 = service->characteristicList[charHandle];
2084 if (!(charDetails.properties & QLowEnergyCharacteristic::Read)) {
2088 qCWarning(QT_BT_BLUEZ) <<
"Reading non-readable char" << charHandle;
2093 putBtData(charDetails.valueHandle, &packet[1]);
2098 qCDebug(QT_BT_BLUEZ) <<
"Targeted reading characteristic" << Qt::hex << charHandle;
2101 request.payload = data;
2103 request.reference = charHandle;
2106 request.reference2 =
false;
2107 openRequests.enqueue(request);
2109 sendNextPendingRequest();
2113 const QSharedPointer<QLowEnergyServicePrivate> service,
2114 const QLowEnergyHandle charHandle,
2115 const QLowEnergyHandle descriptorHandle)
2117 Q_ASSERT(!service.isNull());
2118 if (!service->characteristicList.contains(charHandle))
2122 = service->characteristicList[charHandle];
2123 if (!charDetails.descriptorList.contains(descriptorHandle))
2128 putBtData(descriptorHandle, &packet[1]);
2133 qCDebug(QT_BT_BLUEZ) <<
"Targeted reading descriptor" << Qt::hex << descriptorHandle;
2136 request.payload = data;
2138 request.reference = (charHandle | (descriptorHandle << 16));
2141 request.reference2 =
false;
2142 openRequests.enqueue(request);
2144 sendNextPendingRequest();
2148
2149
2150
2151bool QLowEnergyControllerPrivateBluez::increaseEncryptLevelfRequired(
2157 switch (errorCode) {
2161 if (!hciManager->isValid())
2163 if (!hciManager->monitorEvent(HciManager::HciEvent::EVT_ENCRYPT_CHANGE))
2166 qCDebug(QT_BT_BLUEZ) <<
"Requesting encrypted link";
2168 restartRequestTimer();
2180void QLowEnergyControllerPrivateBluez::handleAdvertisingError()
2182 qCWarning(QT_BT_BLUEZ) <<
"received advertising error";
2183 setError(QLowEnergyController::AdvertisingError);
2184 setState(QLowEnergyController::UnconnectedState);
2187bool QLowEnergyControllerPrivateBluez::checkPacketSize(
const QByteArray &packet,
int minSize,
2192 if (Q_LIKELY(packet.size() >= minSize && packet.size() <= maxSize))
2194 qCWarning(QT_BT_BLUEZ) <<
"client request of type" << packet.at(0)
2195 <<
"has unexpected packet size" << packet.size();
2196 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2197 QBluezConst::AttError::ATT_ERROR_INVALID_PDU);
2201bool QLowEnergyControllerPrivateBluez::checkHandle(
const QByteArray &packet, QLowEnergyHandle handle)
2203 if (handle != 0 && handle <= lastLocalHandle)
2205 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2206 QBluezConst::AttError::ATT_ERROR_INVALID_HANDLE);
2211 QLowEnergyHandle startingHandle,
2212 QLowEnergyHandle endingHandle)
2214 if (startingHandle == 0 || startingHandle > endingHandle) {
2215 qCDebug(QT_BT_BLUEZ) <<
"handle range invalid";
2216 sendErrorResponse(request, startingHandle, QBluezConst::AttError::ATT_ERROR_INVALID_HANDLE);
2222void QLowEnergyControllerPrivateBluez::handleExchangeMtuRequest(
const QByteArray &packet)
2226 if (!checkPacketSize(packet, 3))
2228 if (receivedMtuExchangeRequest) {
2229 qCDebug(QT_BT_BLUEZ) <<
"Client sent extraneous MTU exchange packet";
2230 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2231 QBluezConst::AttError::ATT_ERROR_REQUEST_NOT_SUPPORTED);
2234 receivedMtuExchangeRequest =
true;
2239 putBtData(ATT_MAX_LE_MTU, reply.data() + 1);
2243 const quint16 clientRxMtu = bt_get_le16(packet.constData() + 1);
2244 mtuSize = std::clamp(clientRxMtu, ATT_DEFAULT_LE_MTU, ATT_MAX_LE_MTU);
2245 qCDebug(QT_BT_BLUEZ) <<
"MTU request from client:" << clientRxMtu
2246 <<
"effective client RX MTU:" << mtuSize;
2247 qCDebug(QT_BT_BLUEZ) <<
"Sending server RX MTU" << ATT_MAX_LE_MTU;
2250void QLowEnergyControllerPrivateBluez::handleFindInformationRequest(
const QByteArray &packet)
2254 if (!checkPacketSize(packet, 5))
2256 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2257 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2258 qCDebug(QT_BT_BLUEZ) <<
"client sends find information request; start:" << startingHandle
2259 <<
"end:" << endingHandle;
2260 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2264 QList<Attribute> results = getAttributes(startingHandle, endingHandle);
2265 if (results.isEmpty()) {
2266 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2267 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2270 ensureUniformUuidSizes(results);
2272 QByteArray responsePrefix(2, Qt::Uninitialized);
2273 const int uuidSize = getUuidSize(results.first().type);
2276 responsePrefix[1] = uuidSize == 2 ? 0x1 : 0x2;
2277 const int elementSize =
sizeof(QLowEnergyHandle) + uuidSize;
2278 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2279 putDataAndIncrement(attr.handle, data);
2280 putDataAndIncrement(attr.type, data);
2282 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2286void QLowEnergyControllerPrivateBluez::handleFindByTypeValueRequest(
const QByteArray &packet)
2290 if (!checkPacketSize(packet, 7, mtuSize))
2292 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2293 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2294 const quint16 type = bt_get_le16(packet.constData() + 5);
2295 const QByteArray value = QByteArray::fromRawData(packet.constData() + 7, packet.size() - 7);
2296 qCDebug(QT_BT_BLUEZ) <<
"client sends find by type value request; start:" << startingHandle
2297 <<
"end:" << endingHandle <<
"type:" << type
2298 <<
"value:" << value.toHex();
2299 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2303 const auto predicate = [value,
this, type](
const Attribute &attr) {
2304 return attr.type == QBluetoothUuid(type) && attr.value == value
2305 && checkReadPermissions(attr) == QBluezConst::AttError::ATT_ERROR_NO_ERROR;
2307 const QList<Attribute> results = getAttributes(startingHandle, endingHandle, predicate);
2308 if (results.isEmpty()) {
2309 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2310 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2314 QByteArray responsePrefix(
2316 const int elemSize = 2 *
sizeof(QLowEnergyHandle);
2317 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2318 putDataAndIncrement(attr.handle, data);
2319 putDataAndIncrement(attr.groupEndHandle, data);
2321 sendListResponse(responsePrefix, elemSize, results, elemWriter);
2324void QLowEnergyControllerPrivateBluez::handleReadByTypeRequest(
const QByteArray &packet)
2328 if (!checkPacketSize(packet, 7, 21))
2330 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2331 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2332 const void *
const typeStart = packet.constData() + 5;
2333 const bool is16BitUuid = packet.size() == 7;
2334 const bool is128BitUuid = packet.size() == 21;
2335 QBluetoothUuid type;
2337 type = QBluetoothUuid(bt_get_le16(typeStart));
2338 }
else if (is128BitUuid) {
2339 type = QUuid::fromBytes(typeStart, QSysInfo::LittleEndian);
2341 qCWarning(QT_BT_BLUEZ) <<
"read by type request has invalid packet size" << packet.size();
2342 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2343 QBluezConst::AttError::ATT_ERROR_INVALID_PDU);
2346 qCDebug(QT_BT_BLUEZ) <<
"client sends read by type request, start:" << startingHandle
2347 <<
"end:" << endingHandle <<
"type:" << type;
2348 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2353 QList<Attribute> results =
2354 getAttributes(startingHandle, endingHandle,
2355 [type](
const Attribute &attr) {
return attr.type == type; });
2356 ensureUniformValueSizes(results);
2358 if (results.isEmpty()) {
2359 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2360 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2366 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2367 results.first().handle, error);
2371 const qsizetype elementSize =
sizeof(QLowEnergyHandle) + results.first().value.size();
2372 QByteArray responsePrefix(2, Qt::Uninitialized);
2374 responsePrefix[1] = elementSize;
2375 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2376 putDataAndIncrement(attr.handle, data);
2377 putDataAndIncrement(attr.value, data);
2379 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2382void QLowEnergyControllerPrivateBluez::handleReadRequest(
const QByteArray &packet)
2386 if (!checkPacketSize(packet, 3))
2388 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2389 qCDebug(QT_BT_BLUEZ) <<
"client sends read request; handle:" << handle;
2391 if (!checkHandle(packet, handle))
2393 const Attribute &attribute = localAttributes.at(handle);
2396 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2401 const qsizetype sentValueLength = (
std::min)(attribute.value.size(), qsizetype(mtuSize) - 1);
2402 QByteArray response(1 + sentValueLength, Qt::Uninitialized);
2404 using namespace std;
2405 memcpy(response.data() + 1, attribute.value.constData(), sentValueLength);
2406 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2407 sendPacket(response);
2410void QLowEnergyControllerPrivateBluez::handleReadBlobRequest(
const QByteArray &packet)
2414 if (!checkPacketSize(packet, 5))
2416 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2417 const quint16 valueOffset = bt_get_le16(packet.constData() + 3);
2418 qCDebug(QT_BT_BLUEZ) <<
"client sends read blob request; handle:" << handle
2419 <<
"offset:" << valueOffset;
2421 if (!checkHandle(packet, handle))
2423 const Attribute &attribute = localAttributes.at(handle);
2426 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2430 if (valueOffset > attribute.value.size()) {
2431 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2432 QBluezConst::AttError::ATT_ERROR_INVALID_OFFSET);
2435 if (attribute.value.size() <= mtuSize - 3) {
2436 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2437 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_LONG);
2442 const qsizetype sentValueLength = (
std::min)(attribute.value.size() - valueOffset,
2443 qsizetype(mtuSize) - 1);
2445 QByteArray response(1 + sentValueLength, Qt::Uninitialized);
2447 using namespace std;
2448 memcpy(response.data() + 1, attribute.value.constData() + valueOffset, sentValueLength);
2449 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2450 sendPacket(response);
2453void QLowEnergyControllerPrivateBluez::handleReadMultipleRequest(
const QByteArray &packet)
2457 if (!checkPacketSize(packet, 5, mtuSize))
2459 QList<QLowEnergyHandle> handles((packet.size() - 1) /
sizeof(QLowEnergyHandle));
2460 auto *packetPtr =
reinterpret_cast<
const QLowEnergyHandle *>(packet.constData() + 1);
2461 for (qsizetype i = 0; i < handles.size(); ++i, ++packetPtr)
2462 handles[i] = bt_get_le16(packetPtr);
2463 qCDebug(QT_BT_BLUEZ) <<
"client sends read multiple request for handles" << handles;
2465 const auto it =
std::find_if(handles.constBegin(), handles.constEnd(),
2466 [
this](QLowEnergyHandle handle) {
return handle >= lastLocalHandle; });
2467 if (it != handles.constEnd()) {
2468 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), *it,
2469 QBluezConst::AttError::ATT_ERROR_INVALID_HANDLE);
2472 const QList<Attribute> results = getAttributes(handles.first(), handles.last());
2473 QByteArray response(
2475 for (
const Attribute &attr : results) {
2476 const QBluezConst::AttError error = checkReadPermissions(attr);
2477 if (error != QBluezConst::AttError::ATT_ERROR_NO_ERROR) {
2478 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), attr.handle,
2485 response += attr.value.left(mtuSize - response.size());
2488 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2489 sendPacket(response);
2492void QLowEnergyControllerPrivateBluez::handleReadByGroupTypeRequest(
const QByteArray &packet)
2496 if (!checkPacketSize(packet, 7, 21))
2498 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2499 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2500 const bool is16BitUuid = packet.size() == 7;
2501 const bool is128BitUuid = packet.size() == 21;
2502 const void *
const typeStart = packet.constData() + 5;
2503 QBluetoothUuid type;
2505 type = QBluetoothUuid(bt_get_le16(typeStart));
2506 }
else if (is128BitUuid) {
2507 type = QUuid::fromBytes(typeStart, QSysInfo::LittleEndian);
2509 qCWarning(QT_BT_BLUEZ) <<
"read by group type request has invalid packet size"
2511 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2512 QBluezConst::AttError::ATT_ERROR_INVALID_PDU);
2515 qCDebug(QT_BT_BLUEZ) <<
"client sends read by group type request, start:" << startingHandle
2516 <<
"end:" << endingHandle <<
"type:" << type;
2518 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2523 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2524 QBluezConst::AttError::ATT_ERROR_UNSUPPRTED_GROUP_TYPE);
2528 QList<Attribute> results =
2529 getAttributes(startingHandle, endingHandle,
2530 [type](
const Attribute &attr) {
return attr.type == type; });
2531 if (results.isEmpty()) {
2532 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2533 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2538 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2539 results.first().handle, error);
2543 ensureUniformValueSizes(results);
2545 const qsizetype elementSize = 2 *
sizeof(QLowEnergyHandle) + results.first().value.size();
2546 QByteArray responsePrefix(2, Qt::Uninitialized);
2548 responsePrefix[1] = elementSize;
2549 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2550 putDataAndIncrement(attr.handle, data);
2551 putDataAndIncrement(attr.groupEndHandle, data);
2552 putDataAndIncrement(attr.value, data);
2554 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2557void QLowEnergyControllerPrivateBluez::updateLocalAttributeValue(
2558 QLowEnergyHandle handle,
2559 const QByteArray &value,
2560 QLowEnergyCharacteristic &characteristic,
2561 QLowEnergyDescriptor &descriptor)
2563 localAttributes[handle].value = value;
2564 for (
const auto &service : std::as_const(localServices)) {
2565 if (handle < service->startHandle || handle > service->endHandle)
2567 for (
auto charIt = service->characteristicList.begin();
2568 charIt != service->characteristicList.end(); ++charIt) {
2569 QLowEnergyServicePrivate::CharData &charData = charIt.value();
2570 if (handle == charIt.key() + 1) {
2571 charData.value = value;
2572 characteristic = QLowEnergyCharacteristic(service, charIt.key());
2575 for (
auto descIt = charData.descriptorList.begin();
2576 descIt != charData.descriptorList.end(); ++descIt) {
2577 if (handle == descIt.key()) {
2578 descIt.value().value = value;
2579 descriptor = QLowEnergyDescriptor(service, charIt.key(), handle);
2585 qFatal(
"local services map inconsistent with local attribute map");
2591void QLowEnergyControllerPrivateBluez::writeCharacteristicForPeripheral(
2593 const QByteArray &newValue)
2595 const QLowEnergyHandle valueHandle = charData.valueHandle;
2596 Q_ASSERT(valueHandle <= lastLocalHandle);
2597 Attribute &attribute = localAttributes[valueHandle];
2599 qCWarning(QT_BT_BLUEZ) <<
"ignoring value of invalid length" << newValue.size()
2600 <<
"for attribute" << valueHandle;
2603 attribute.value = newValue;
2604 charData.value = newValue;
2605 const bool hasNotifyProperty = attribute.properties & QLowEnergyCharacteristic::Notify;
2606 const bool hasIndicateProperty
2607 = attribute.properties & QLowEnergyCharacteristic::Indicate;
2608 if (!hasNotifyProperty && !hasIndicateProperty)
2610 for (
const QLowEnergyServicePrivate::DescData &desc : std::as_const(charData.descriptorList)) {
2611 if (desc.uuid != QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)
2615 const bool isConnected = state == QLowEnergyController::ConnectedState;
2617 Q_ASSERT(desc.value.size() == 2);
2618 quint16 configValue = bt_get_le16(desc.value.constData());
2619 if (isNotificationEnabled(configValue) && hasNotifyProperty) {
2620 sendNotification(valueHandle);
2621 }
else if (isIndicationEnabled(configValue) && hasIndicateProperty) {
2622 if (indicationInFlight)
2623 scheduledIndications << valueHandle;
2625 sendIndication(valueHandle);
2630 for (
auto it = clientConfigData.begin(); it != clientConfigData.end(); ++it) {
2631 if (isConnected && it.key() == remoteDevice.toUInt64())
2633 QList<ClientConfigurationData> &configDataList = it.value();
2634 for (ClientConfigurationData &configData : configDataList) {
2635 if (configData.charValueHandle != valueHandle)
2637 if ((isNotificationEnabled(configData.configValue) && hasNotifyProperty)
2638 || (isIndicationEnabled(configData.configValue) && hasIndicateProperty)) {
2639 configData.charValueWasUpdated =
true;
2648void QLowEnergyControllerPrivateBluez::writeCharacteristicForCentral(
const QSharedPointer<QLowEnergyServicePrivate> &service,
2649 QLowEnergyHandle charHandle,
2650 QLowEnergyHandle valueHandle,
2651 const QByteArray &newValue,
2652 QLowEnergyService::WriteMode mode)
2655 putBtData(valueHandle, packet.data() + 1);
2656 memcpy(packet.data() + 3, newValue.constData(), newValue.size());
2657 bool writeWithResponse =
false;
2659 case QLowEnergyService::WriteWithResponse:
2661 sendNextPrepareWriteRequest(charHandle, newValue, 0);
2662 sendNextPendingRequest();
2667 writeWithResponse =
true;
2669 case QLowEnergyService::WriteWithoutResponse:
2672 case QLowEnergyService::WriteSigned:
2675 qCWarning(QT_BT_BLUEZ) <<
"signed write not possible: requires bond between devices";
2676 service->setError(QLowEnergyService::CharacteristicWriteError);
2680 qCWarning(QT_BT_BLUEZ) <<
"signed write not possible: not allowed on encrypted link";
2681 service->setError(QLowEnergyService::CharacteristicWriteError);
2684 const auto signingDataIt = signingData.find(remoteDevice.toUInt64());
2685 if (signingDataIt == signingData.end()) {
2686 qCWarning(QT_BT_BLUEZ) <<
"signed write not possible: no signature key found";
2687 service->setError(QLowEnergyService::CharacteristicWriteError);
2690 ++signingDataIt.value().counter;
2691 packet = LeCmacCalculator::createFullMessage(packet, signingDataIt.value().counter);
2692 const quint64 mac = LeCmacCalculator().calculateMac(packet, signingDataIt.value().key);
2693 packet.resize(packet.size() +
sizeof mac);
2694 putBtData(mac, packet.data() + packet.size() -
sizeof mac);
2695 storeSignCounter(LocalSigningKey);
2699 qCDebug(QT_BT_BLUEZ) <<
"Writing characteristic" << Qt::hex << charHandle
2700 <<
"(size:" << packet.size() <<
"with response:"
2701 << (mode == QLowEnergyService::WriteWithResponse)
2702 <<
"signed:" << (mode == QLowEnergyService::WriteSigned) <<
")";
2707 if (!writeWithResponse) {
2713 request.payload = packet;
2715 request.reference = charHandle;
2716 request.reference2 = newValue;
2717 openRequests.enqueue(request);
2719 sendNextPendingRequest();
2722void QLowEnergyControllerPrivateBluez::writeDescriptorForPeripheral(
2723 const QSharedPointer<QLowEnergyServicePrivate> &service,
2724 const QLowEnergyHandle charHandle,
2725 const QLowEnergyHandle descriptorHandle,
2726 const QByteArray &newValue)
2728 Q_ASSERT(descriptorHandle <= lastLocalHandle);
2729 Attribute &attribute = localAttributes[descriptorHandle];
2731 qCWarning(QT_BT_BLUEZ) <<
"invalid value of size" << newValue.size()
2732 <<
"for attribute" << descriptorHandle;
2735 attribute.value = newValue;
2736 service->characteristicList[charHandle].descriptorList[descriptorHandle].value = newValue;
2739void QLowEnergyControllerPrivateBluez::writeDescriptorForCentral(
2740 const QLowEnergyHandle charHandle,
2741 const QLowEnergyHandle descriptorHandle,
2742 const QByteArray &newValue)
2745 sendNextPrepareWriteRequest(descriptorHandle, newValue, 0);
2746 sendNextPendingRequest();
2752 putBtData(descriptorHandle, &packet[1]);
2755 QByteArray data(size, Qt::Uninitialized);
2759 qCDebug(QT_BT_BLUEZ) <<
"Writing descriptor" << Qt::hex << descriptorHandle
2760 <<
"(size:" << size <<
")";
2763 request.payload = data;
2765 request.reference = (charHandle | (descriptorHandle << 16));
2766 request.reference2 = newValue;
2767 openRequests.enqueue(request);
2769 sendNextPendingRequest();
2772void QLowEnergyControllerPrivateBluez::handleWriteRequestOrCommand(
const QByteArray &packet)
2780 if (!checkPacketSize(packet, isSigned ? 15 : 3, mtuSize))
2782 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2783 qCDebug(QT_BT_BLUEZ) <<
"client sends" << (isSigned ?
"signed" :
"") <<
"write"
2784 << (isRequest ?
"request" :
"command") <<
"for handle" << handle;
2786 if (!checkHandle(packet, handle))
2789 Attribute &attribute = localAttributes[handle];
2790 const QLowEnergyCharacteristic::PropertyType type = isRequest
2791 ? QLowEnergyCharacteristic::Write : isSigned
2792 ? QLowEnergyCharacteristic::WriteSigned : QLowEnergyCharacteristic::WriteNoResponse;
2795 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2803 qCWarning(QT_BT_BLUEZ) <<
"Ignoring signed write from non-bonded device.";
2807 qCWarning(QT_BT_BLUEZ) <<
"Ignoring signed write on encrypted link.";
2810 const auto signingDataIt = signingData.find(remoteDevice.toUInt64());
2811 if (signingDataIt == signingData.constEnd()) {
2812 qCWarning(QT_BT_BLUEZ) <<
"No CSRK found for peer device, ignoring signed write";
2816 const quint32 signCounter = getBtData<quint32>(packet.data() + packet.size() - 12);
2817 if (signCounter < signingDataIt.value().counter + 1) {
2818 qCWarning(QT_BT_BLUEZ) <<
"Client's' sign counter" << signCounter
2819 <<
"not greater than local sign counter"
2820 << signingDataIt.value().counter
2821 <<
"; ignoring signed write command.";
2825 const quint64 macFromClient = getBtData<quint64>(packet.data() + packet.size() - 8);
2826 const bool signatureCorrect = verifyMac(packet.left(packet.size() - 12),
2827 signingDataIt.value().key, signCounter, macFromClient);
2828 if (!signatureCorrect) {
2829 qCWarning(QT_BT_BLUEZ) <<
"Signed Write packet has wrong signature, disconnecting";
2834 signingDataIt.value().counter = signCounter;
2835 storeSignCounter(RemoteSigningKey);
2836 valueLength = packet.size() - 15;
2838 valueLength = packet.size() - 3;
2842 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2843 QBluezConst::AttError::ATT_ERROR_INVAL_ATTR_VALUE_LEN);
2849 QByteArray value = packet.mid(3, valueLength);
2851 value += attribute.value.mid(valueLength, attribute
.maxLength - valueLength);
2853 QLowEnergyCharacteristic characteristic;
2854 QLowEnergyDescriptor descriptor;
2855 updateLocalAttributeValue(handle, value, characteristic, descriptor);
2858 const QByteArray response =
2860 sendPacket(response);
2863 if (characteristic.isValid()) {
2864 emit characteristic.d_ptr->characteristicChanged(characteristic, value);
2866 Q_ASSERT(descriptor.isValid());
2867 emit descriptor.d_ptr->descriptorWritten(descriptor, value);
2871void QLowEnergyControllerPrivateBluez::handlePrepareWriteRequest(
const QByteArray &packet)
2875 if (!checkPacketSize(packet, 5, mtuSize))
2877 const quint16 handle = bt_get_le16(packet.constData() + 1);
2878 qCDebug(QT_BT_BLUEZ) <<
"client sends prepare write request for handle" << handle;
2880 if (!checkHandle(packet, handle))
2882 const Attribute &attribute = localAttributes.at(handle);
2884 checkPermissions(attribute, QLowEnergyCharacteristic::Write);
2886 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2890 if (openPrepareWriteRequests.size() >= maxPrepareQueueSize) {
2891 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2892 QBluezConst::AttError::ATT_ERROR_PREPARE_QUEUE_FULL);
2897 openPrepareWriteRequests << WriteRequest(handle, bt_get_le16(packet.constData() + 3),
2900 QByteArray response = packet;
2902 sendPacket(response);
2905void QLowEnergyControllerPrivateBluez::handleExecuteWriteRequest(
const QByteArray &packet)
2909 if (!checkPacketSize(packet, 2))
2911 const bool cancel = packet.at(1) == 0;
2912 qCDebug(QT_BT_BLUEZ) <<
"client sends execute write request; flag is"
2913 << (cancel ?
"cancel" :
"flush");
2915 QList<WriteRequest> requests = openPrepareWriteRequests;
2916 openPrepareWriteRequests.clear();
2917 QList<QLowEnergyCharacteristic> characteristics;
2918 QList<QLowEnergyDescriptor> descriptors;
2920 for (
const WriteRequest &request : std::as_const(requests)) {
2921 Attribute &attribute = localAttributes[request.handle];
2922 if (request.valueOffset > attribute.value.size()) {
2923 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2924 request.handle, QBluezConst::AttError::ATT_ERROR_INVALID_OFFSET);
2927 const QByteArray newValue = attribute.value.left(request.valueOffset) + request.value;
2928 if (newValue.size() > attribute.maxLength) {
2929 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2931 QBluezConst::AttError::ATT_ERROR_INVAL_ATTR_VALUE_LEN);
2934 QLowEnergyCharacteristic characteristic;
2935 QLowEnergyDescriptor descriptor;
2938 updateLocalAttributeValue(request.handle, newValue, characteristic, descriptor);
2939 if (characteristic.isValid()) {
2940 characteristics << characteristic;
2941 }
else if (descriptor.isValid()) {
2942 Q_ASSERT(descriptor.isValid());
2943 descriptors << descriptor;
2948 sendPacket(QByteArray(
2949 1,
static_cast<quint8>(QBluezConst::AttCommand::ATT_OP_EXECUTE_WRITE_RESPONSE)));
2951 for (
const QLowEnergyCharacteristic &characteristic : std::as_const(characteristics))
2952 emit characteristic.d_ptr->characteristicChanged(characteristic, characteristic.value());
2953 for (
const QLowEnergyDescriptor &descriptor : std::as_const(descriptors))
2954 emit descriptor.d_ptr->descriptorWritten(descriptor, descriptor.value());
2967 packet[1] =
static_cast<quint8>(request);
2968 putBtData(handle, packet.data() + 2);
2969 packet[4] =
static_cast<quint8>(code);
2970 qCWarning(QT_BT_BLUEZ) <<
"sending error response; request:"
2971 << request <<
"handle:" << handle
2976void QLowEnergyControllerPrivateBluez::sendListResponse(
const QByteArray &packetStart,
2978 const QList<Attribute> &attributes,
2979 const ElemWriter &elemWriter)
2981 const qsizetype offset = packetStart.size();
2982 const qsizetype elemCount = (
std::min)(attributes.size(), (mtuSize - offset) / elemSize);
2983 const qsizetype totalPacketSize = offset + elemCount * elemSize;
2984 QByteArray response(totalPacketSize, Qt::Uninitialized);
2985 using namespace std;
2986 memcpy(response.data(), packetStart.constData(), offset);
2987 char *data = response.data() + offset;
2988 for_each(attributes.constBegin(), attributes.constBegin() + elemCount,
2989 [&data, elemWriter](
const Attribute &attr) { elemWriter(attr, data); });
2990 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2991 sendPacket(response);
2994void QLowEnergyControllerPrivateBluez::sendNotification(QLowEnergyHandle handle)
2996 sendNotificationOrIndication(QBluezConst::AttCommand::ATT_OP_HANDLE_VAL_NOTIFICATION, handle);
2999void QLowEnergyControllerPrivateBluez::sendIndication(QLowEnergyHandle handle)
3001 Q_ASSERT(!indicationInFlight);
3002 indicationInFlight =
true;
3003 sendNotificationOrIndication(QBluezConst::AttCommand::ATT_OP_HANDLE_VAL_INDICATION, handle);
3007 QLowEnergyHandle handle)
3009 Q_ASSERT(handle <= lastLocalHandle);
3010 const Attribute &attribute = localAttributes.at(handle);
3011 const qsizetype maxValueLength = (
std::min)(attribute.value.size(), qsizetype(mtuSize) - 3);
3012 QByteArray packet(3 + maxValueLength, Qt::Uninitialized);
3013 packet[0] =
static_cast<quint8>(opCode);
3014 putBtData(handle, packet.data() + 1);
3015 using namespace std;
3016 memcpy(packet.data() + 3, attribute.value.constData(), maxValueLength);
3017 qCDebug(QT_BT_BLUEZ) <<
"sending notification/indication:" << packet.toHex();
3021void QLowEnergyControllerPrivateBluez::sendNextIndication()
3023 if (!scheduledIndications.isEmpty())
3024 sendIndication(scheduledIndications.takeFirst());
3029 const QString peerAddressString = peerAddress.toString();
3032 QStringLiteral(
"/"),
3033 QDBusConnection::systemBus());
3034 QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects();
3035 reply.waitForFinished();
3036 if (reply.isError())
3040 for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
3041 const InterfaceList &ifaceList = it.value();
3043 for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) {
3044 const QString &iface = jt.key();
3045 const QVariantMap &ifaceValues = jt.value();
3047 if (iface == QStringLiteral(
"org.bluez.Device1")) {
3048 if (ifaceValues.value(QStringLiteral(
"Address")).toString() == peerAddressString)
3049 return ifaceValues.value(QStringLiteral(
"Alias")).toString();
3056void QLowEnergyControllerPrivateBluez::handleConnectionRequest()
3058 if (state != QLowEnergyController::AdvertisingState) {
3059 qCWarning(QT_BT_BLUEZ) <<
"Incoming connection request in unexpected state" << state;
3062 Q_ASSERT(serverSocketNotifier);
3063 serverSocketNotifier->setEnabled(
false);
3065 socklen_t clientAddrSize =
sizeof clientAddr;
3066 const int clientSocket = accept(serverSocketNotifier->socket(),
3067 reinterpret_cast<sockaddr *>(&clientAddr), &clientAddrSize);
3068 if (clientSocket == -1) {
3070 qCWarning(QT_BT_BLUEZ) <<
"accept() failed:" << qt_error_string(errno);
3071 serverSocketNotifier->setEnabled(
true);
3075 remoteDevice = QBluetoothAddress(convertAddress(clientAddr.l2_bdaddr.b));
3076 remoteName = nameOfRemoteCentral(remoteDevice);
3077 qCDebug(QT_BT_BLUEZ) <<
"GATT connection from device" << remoteDevice << remoteName;
3079 if (connectionHandle == 0)
3080 qCWarning(QT_BT_BLUEZ) <<
"Received client connection, but no connection complete event";
3083 disconnect(l2cpSocket);
3084 if (l2cpSocket->isOpen())
3085 l2cpSocket->close();
3087 l2cpSocket->deleteLater();
3088 l2cpSocket =
nullptr;
3090 closeServerSocket();
3093 l2cpSocket =
new QBluetoothSocket(
3094 rawSocketPrivate, QBluetoothServiceInfo::L2capProtocol,
this);
3095 connect(l2cpSocket, &QBluetoothSocket::disconnected,
3096 this, &QLowEnergyControllerPrivateBluez::l2cpDisconnected);
3097 connect(l2cpSocket, &QBluetoothSocket::errorOccurred,
this,
3098 &QLowEnergyControllerPrivateBluez::l2cpErrorChanged);
3099 connect(l2cpSocket, &QIODevice::readyRead,
this, &QLowEnergyControllerPrivateBluez::l2cpReadyRead);
3100 l2cpSocket->d_ptr->lowEnergySocketType = addressType == QLowEnergyController::PublicAddress
3102 l2cpSocket->setSocketDescriptor(clientSocket, QBluetoothServiceInfo::L2capProtocol,
3103 QBluetoothSocket::SocketState::ConnectedState, QIODevice::ReadWrite | QIODevice::Unbuffered);
3104 restoreClientConfigurations();
3105 loadSigningDataIfNecessary(RemoteSigningKey);
3107 Q_Q(QLowEnergyController);
3108 setState(QLowEnergyController::ConnectedState);
3109 emit q->connected();
3112void QLowEnergyControllerPrivateBluez::closeServerSocket()
3114 if (!serverSocketNotifier)
3116 serverSocketNotifier->disconnect();
3117 close(serverSocketNotifier->socket());
3118 serverSocketNotifier->deleteLater();
3119 serverSocketNotifier =
nullptr;
3122bool QLowEnergyControllerPrivateBluez::isBonded()
const
3126 return QBluetoothLocalDevice(localAdapter).pairingStatus(remoteDevice)
3127 != QBluetoothLocalDevice::Unpaired;
3130QList<QLowEnergyControllerPrivateBluez::TempClientConfigurationData>
3131QLowEnergyControllerPrivateBluez::gatherClientConfigData()
3133 QList<TempClientConfigurationData> data;
3134 for (
const auto &service : std::as_const(localServices)) {
3135 for (
auto charIt = service->characteristicList.begin();
3136 charIt != service->characteristicList.end(); ++charIt) {
3137 QLowEnergyServicePrivate::CharData &charData = charIt.value();
3138 for (
auto descIt = charData.descriptorList.begin();
3139 descIt != charData.descriptorList.end(); ++descIt) {
3140 QLowEnergyServicePrivate::DescData &descData = descIt.value();
3141 if (descData.uuid == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration) {
3142 data << TempClientConfigurationData(&descData, charData.valueHandle,
3152void QLowEnergyControllerPrivateBluez::storeClientConfigurations()
3155 clientConfigData.remove(remoteDevice.toUInt64());
3158 QList<ClientConfigurationData> clientConfigs;
3159 const QList<TempClientConfigurationData> &tempConfigList = gatherClientConfigData();
3160 for (
const auto &tempConfigData : tempConfigList) {
3161 Q_ASSERT(tempConfigData.descData->value.size() == 2);
3162 const quint16 value = bt_get_le16(tempConfigData.descData->value.constData());
3164 clientConfigs << ClientConfigurationData(tempConfigData.charValueHandle,
3165 tempConfigData.configHandle, value);
3168 clientConfigData.insert(remoteDevice.toUInt64(), clientConfigs);
3171void QLowEnergyControllerPrivateBluez::restoreClientConfigurations()
3173 const QList<TempClientConfigurationData> &tempConfigList = gatherClientConfigData();
3174 const QList<ClientConfigurationData> &restoredClientConfigs = isBonded()
3175 ? clientConfigData.value(remoteDevice.toUInt64())
3176 : QList<ClientConfigurationData>();
3177 QList<QLowEnergyHandle> notifications;
3178 for (
const auto &tempConfigData : tempConfigList) {
3179 bool wasRestored =
false;
3180 for (
const auto &restoredData : restoredClientConfigs) {
3181 if (restoredData.charValueHandle == tempConfigData.charValueHandle) {
3182 Q_ASSERT(tempConfigData.descData->value.size() == 2);
3183 putBtData(restoredData.configValue, tempConfigData.descData->value.data());
3185 if (restoredData.charValueWasUpdated) {
3186 if (isNotificationEnabled(restoredData.configValue))
3187 notifications << restoredData.charValueHandle;
3188 else if (isIndicationEnabled(restoredData.configValue))
3189 scheduledIndications << restoredData.charValueHandle;
3195 tempConfigData.descData->value = QByteArray(2, 0);
3196 Q_ASSERT(lastLocalHandle >= tempConfigData.configHandle);
3197 Q_ASSERT(tempConfigData.configHandle > tempConfigData.charValueHandle);
3198 localAttributes[tempConfigData.configHandle].value = tempConfigData.descData->value;
3201 for (
const QLowEnergyHandle handle : std::as_const(notifications))
3202 sendNotification(handle);
3203 sendNextIndication();
3206void QLowEnergyControllerPrivateBluez::loadSigningDataIfNecessary(SigningKeyType keyType)
3208 const auto signingDataIt = signingData.constFind(remoteDevice.toUInt64());
3209 if (signingDataIt != signingData.constEnd())
3211 const QString settingsFilePath = keySettingsFilePath();
3212 if (!QFileInfo(settingsFilePath).exists()) {
3213 qCDebug(QT_BT_BLUEZ) <<
"No settings found for peer device.";
3216 QSettings settings(settingsFilePath, QSettings::IniFormat);
3217 const QString group = signingKeySettingsGroup(keyType);
3218 settings.beginGroup(group);
3219 const QByteArray keyString = settings.value(QLatin1String(
"Key")).toByteArray();
3220 if (keyString.isEmpty()) {
3221 qCDebug(QT_BT_BLUEZ) <<
"Group" << group <<
"not found in settings file";
3224 const QByteArray keyData = QByteArray::fromHex(keyString);
3225 if (keyData.size() != qsizetype(
sizeof(
BluezUint128))) {
3226 qCWarning(QT_BT_BLUEZ) <<
"Signing key in settings file has invalid size"
3227 << keyString.size();
3230 const quint32 counter = settings.value(QLatin1String(
"Counter"), 0).toUInt();
3231 using namespace std;
3233 memcpy(csrk.data, keyData.constData(), keyData.size());
3234 signingData.insert(remoteDevice.toUInt64(), SigningData(csrk, counter - 1));
3237void QLowEnergyControllerPrivateBluez::storeSignCounter(SigningKeyType keyType)
const
3239 const auto signingDataIt = signingData.constFind(remoteDevice.toUInt64());
3240 if (signingDataIt == signingData.constEnd())
3242 const QString settingsFilePath = keySettingsFilePath();
3243 if (!QFileInfo(settingsFilePath).exists())
3245 QSettings settings(settingsFilePath, QSettings::IniFormat);
3246 if (!settings.isWritable())
3248 settings.beginGroup(signingKeySettingsGroup(keyType));
3249 const QString counterKey = QLatin1String(
"Counter");
3250 if (!settings.allKeys().contains(counterKey))
3252 const quint32 counterValue = signingDataIt.value().counter + 1;
3253 if (counterValue == settings.value(counterKey).toUInt())
3255 settings.setValue(counterKey, counterValue);
3258QString QLowEnergyControllerPrivateBluez::signingKeySettingsGroup(SigningKeyType keyType)
const
3260 return QLatin1String(keyType == LocalSigningKey ?
"LocalSignatureKey" :
"RemoteSignatureKey");
3263QString QLowEnergyControllerPrivateBluez::keySettingsFilePath()
const
3265 return QString::fromLatin1(
"/var/lib/bluetooth/%1/%2/info")
3266 .arg(localAdapter.toString(), remoteDevice.toString());
3271 QByteArray ba(
sizeof(uuid), Qt::Uninitialized);
3272 char *ptr = ba.data();
3273 putDataAndIncrement(uuid, ptr);
3274 ba.resize(ptr - ba.constData());
3279 QLowEnergyHandle startHandle)
3285 localAttributes.resize(lastLocalHandle + 1);
3287 serviceAttribute.handle = startHandle;
3288 serviceAttribute.type = QBluetoothUuid(
static_cast<quint16>(service.type()));
3289 serviceAttribute.properties = QLowEnergyCharacteristic::Read;
3290 serviceAttribute.value = uuidToByteArray(service.uuid());
3291 QLowEnergyHandle currentHandle = startHandle;
3292 const QList<QLowEnergyService *> includedServices = service.includedServices();
3293 for (
const QLowEnergyService *
const service : includedServices) {
3294 Attribute attribute;
3295 attribute.handle = ++currentHandle;
3297 attribute.properties = QLowEnergyCharacteristic::Read;
3298 const bool includeUuidInValue = service->serviceUuid().minimumSize() == 2;
3299 attribute.value.resize((2 + includeUuidInValue) *
sizeof(QLowEnergyHandle));
3300 char *valueData = attribute.value.data();
3301 putDataAndIncrement(service->d_ptr->startHandle, valueData);
3302 putDataAndIncrement(service->d_ptr->endHandle, valueData);
3303 if (includeUuidInValue)
3304 putDataAndIncrement(service->serviceUuid(), valueData);
3305 localAttributes[attribute.handle] = attribute;
3307 const QList<QLowEnergyCharacteristicData> characteristics = service.characteristics();
3308 for (
const QLowEnergyCharacteristicData &cd : characteristics) {
3309 Attribute attribute;
3312 attribute.handle = ++currentHandle;
3313 attribute.groupEndHandle = attribute.handle + 1 + cd.descriptors().size();
3315 attribute.properties = QLowEnergyCharacteristic::Read;
3316 attribute.value.resize(1 +
sizeof(QLowEnergyHandle) + cd.uuid().minimumSize());
3317 char *valueData = attribute.value.data();
3318 putDataAndIncrement(
static_cast<quint8>(cd.properties()), valueData);
3319 putDataAndIncrement(QLowEnergyHandle(currentHandle + 1), valueData);
3320 putDataAndIncrement(cd.uuid(), valueData);
3321 localAttributes[attribute.handle] = attribute;
3324 attribute.handle = ++currentHandle;
3325 attribute.groupEndHandle = attribute.handle;
3326 attribute.type = cd.uuid();
3327 attribute.properties = cd.properties();
3328 attribute.readConstraints = cd.readConstraints();
3329 attribute.writeConstraints = cd.writeConstraints();
3330 attribute.value = cd.value();
3331 attribute.minLength = cd.minimumValueLength();
3332 attribute.maxLength = cd.maximumValueLength();
3333 localAttributes[attribute.handle] = attribute;
3335 const QList<QLowEnergyDescriptorData> descriptors = cd.descriptors();
3336 for (
const QLowEnergyDescriptorData &dd : descriptors) {
3337 attribute.handle = ++currentHandle;
3338 attribute.groupEndHandle = attribute.handle;
3339 attribute.type = dd.uuid();
3340 attribute.properties = QLowEnergyCharacteristic::PropertyTypes();
3341 attribute.readConstraints = AttAccessConstraints();
3342 attribute.writeConstraints = AttAccessConstraints();
3343 attribute.minLength = 0;
3344 attribute.maxLength = INT_MAX;
3347 if (attribute.type == QBluetoothUuid::DescriptorType::CharacteristicExtendedProperties) {
3348 attribute.properties = QLowEnergyCharacteristic::Read;
3349 attribute.minLength = attribute.maxLength = 2;
3350 }
else if (attribute.type == QBluetoothUuid::DescriptorType::CharacteristicPresentationFormat) {
3351 attribute.properties = QLowEnergyCharacteristic::Read;
3352 attribute.minLength = attribute.maxLength = 7;
3353 }
else if (attribute.type == QBluetoothUuid::DescriptorType::CharacteristicAggregateFormat) {
3354 attribute.properties = QLowEnergyCharacteristic::Read;
3355 attribute.minLength = 4;
3356 }
else if (attribute.type == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration
3357 || attribute.type == QBluetoothUuid::DescriptorType::ServerCharacteristicConfiguration) {
3358 attribute.properties = QLowEnergyCharacteristic::Read
3359 | QLowEnergyCharacteristic::Write
3360 | QLowEnergyCharacteristic::WriteNoResponse
3361 | QLowEnergyCharacteristic::WriteSigned;
3362 attribute.writeConstraints = dd.writeConstraints();
3363 attribute.minLength = attribute.maxLength = 2;
3365 if (dd.isReadable())
3366 attribute.properties |= QLowEnergyCharacteristic::Read;
3367 if (dd.isWritable()) {
3368 attribute.properties |= QLowEnergyCharacteristic::Write;
3369 attribute.properties |= QLowEnergyCharacteristic::WriteNoResponse;
3370 attribute.properties |= QLowEnergyCharacteristic::WriteSigned;
3372 attribute.readConstraints = dd.readConstraints();
3373 attribute.writeConstraints = dd.writeConstraints();
3376 attribute.value = dd.value();
3377 if (attribute.value.size() < attribute.minLength
3378 || attribute.value.size() > attribute.maxLength) {
3379 qCWarning(QT_BT_BLUEZ) <<
"attribute of type" << attribute.type
3380 <<
"has invalid length of" << attribute.value.size()
3382 attribute.value = QByteArray(attribute.minLength, 0);
3384 localAttributes[attribute.handle] = attribute;
3387 serviceAttribute.groupEndHandle = currentHandle;
3388 localAttributes[serviceAttribute.handle] = serviceAttribute;
3391int QLowEnergyControllerPrivateBluez::
mtu()
const
3396void QLowEnergyControllerPrivateBluez::ensureUniformAttributes(
3397 QList<Attribute> &attributes,
const std::function<
int(
const Attribute &)> &getSize)
3399 if (attributes.isEmpty())
3401 const int firstSize = getSize(attributes.first());
3402 const auto it =
std::find_if(attributes.begin() + 1, attributes.end(),
3403 [firstSize, getSize](
const Attribute &attr) {
return getSize(attr) != firstSize; });
3404 if (it != attributes.end())
3405 attributes.erase(it, attributes.end());
3409void QLowEnergyControllerPrivateBluez::ensureUniformUuidSizes(QList<Attribute> &attributes)
3411 ensureUniformAttributes(attributes,
3412 [](
const Attribute &attr) {
return getUuidSize(attr.type); });
3415void QLowEnergyControllerPrivateBluez::ensureUniformValueSizes(QList<Attribute> &attributes)
3417 ensureUniformAttributes(attributes,
3418 [](
const Attribute &attr) {
return attr.value.size(); });
3421QList<QLowEnergyControllerPrivateBluez::Attribute>
3422QLowEnergyControllerPrivateBluez::getAttributes(QLowEnergyHandle startHandle,
3423 QLowEnergyHandle endHandle,
3424 const AttributePredicate &attributePredicate)
3426 QList<Attribute> results;
3427 if (startHandle > lastLocalHandle)
3429 if (lastLocalHandle == 0)
3431 Q_ASSERT(startHandle <= endHandle);
3432 const QLowEnergyHandle firstHandle = qMin(startHandle, lastLocalHandle);
3433 const QLowEnergyHandle lastHandle = qMin(endHandle, lastLocalHandle);
3434 for (QLowEnergyHandle i = firstHandle; i <= lastHandle; ++i) {
3435 const Attribute &attr = localAttributes.at(i);
3436 if (attributePredicate(attr))
3443QLowEnergyControllerPrivateBluez::checkPermissions(
const Attribute &attr,
3444 QLowEnergyCharacteristic::PropertyType type)
3446 const bool isReadAccess = type == QLowEnergyCharacteristic::Read;
3447 const bool isWriteCommand = type == QLowEnergyCharacteristic::WriteNoResponse;
3448 const bool isWriteAccess = type == QLowEnergyCharacteristic::Write
3449 || type == QLowEnergyCharacteristic::WriteSigned
3451 Q_ASSERT(isReadAccess || isWriteAccess);
3452 if (!(attr.properties & type)) {
3458 const bool unsignedWriteOk = isWriteCommand
3459 && (attr.properties & QLowEnergyCharacteristic::WriteSigned)
3461 if (!unsignedWriteOk)
3464 const AttAccessConstraints constraints = isReadAccess
3465 ? attr.readConstraints : attr.writeConstraints;
3466 if (constraints.testFlag(AttAccessConstraint::AttAuthorizationRequired))
3469 if (constraints.testFlag(AttAccessConstraint::AttEncryptionRequired)
3472 if (constraints.testFlag(AttAccessConstraint::AttAuthenticationRequired)
3482 return checkPermissions(attr, QLowEnergyCharacteristic::Read);
3486QLowEnergyControllerPrivateBluez::checkReadPermissions(QList<Attribute> &attributes)
3488 if (attributes.isEmpty())
3500 std::find_if(attributes.begin() + 1, attributes.end(), [
this](
const Attribute &attr) {
3503 if (it != attributes.end())
3504 attributes.erase(it, attributes.end());
3508bool QLowEnergyControllerPrivateBluez::verifyMac(
const QByteArray &message,
BluezUint128 csrk,
3509 quint32 signCounter, quint64 expectedMac)
3511 if (!cmacCalculator)
3512 cmacCalculator =
new LeCmacCalculator;
3513 return cmacCalculator->verify(LeCmacCalculator::createFullMessage(message, signCounter), csrk,
3519#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