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 << QByteArray(
reinterpret_cast<
const char *>(csrk.data),
217 sizeof csrk).toHex();
218 signingData.insert(remoteDevice.toUInt64(), SigningData(csrk));
222 if (role == QLowEnergyController::CentralRole) {
223 if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(
"BLUETOOTH_GATT_TIMEOUT"))) {
225 int value = qEnvironmentVariableIntValue(
"BLUETOOTH_GATT_TIMEOUT", &ok);
227 gattRequestTimeout = value;
231 if (gattRequestTimeout > 0) {
232 qCWarning(QT_BT_BLUEZ) <<
"Enabling GATT request timeout behavior" << gattRequestTimeout;
233 requestTimer =
new QTimer(
this);
234 requestTimer->setSingleShot(
true);
235 requestTimer->setInterval(gattRequestTimeout);
236 connect(requestTimer, &QTimer::timeout,
237 this, &QLowEnergyControllerPrivateBluez::handleGattRequestTimeout);
242void QLowEnergyControllerPrivateBluez::handleGattRequestTimeout()
245 if (encryptionChangePending) {
247 qCWarning(QT_BT_BLUEZ) <<
"****** Encryption change event blocking further GATT requests";
251 if (!openRequests.isEmpty() && requestPending) {
252 const Request currentRequest = openRequests.dequeue();
253 requestPending =
false;
255 qCWarning(QT_BT_BLUEZ).nospace() <<
"****** Request type 0x" << currentRequest.command
256 <<
" to server/peripheral timed out";
257 qCWarning(QT_BT_BLUEZ) <<
"****** Looks like the characteristic or descriptor does NOT act in"
258 <<
"accordance to Bluetooth 4.x spec.";
259 qCWarning(QT_BT_BLUEZ) <<
"****** Please check server implementation."
260 <<
"Continuing under reservation.";
264 QLowEnergyHandle handle) {
267 errorPackage[1] =
static_cast<quint8>(
269 putBtData(handle, errorPackage.data() + 2);
280 case QBluezConst::AttCommand::ATT_OP_READ_BY_GROUP_REQUEST:
282 case QBluezConst::AttCommand::ATT_OP_READ_BY_TYPE_REQUEST:
286 processReply(currentRequest, createRequestErrorMessage(command, 0));
294 uint handleData = currentRequest.reference.toUInt();
295 const QLowEnergyHandle charHandle = (handleData & 0xffff);
296 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
297 processReply(currentRequest, createRequestErrorMessage(command,
298 descriptorHandle ? descriptorHandle : charHandle));
300 case QBluezConst::AttCommand::ATT_OP_FIND_INFORMATION_REQUEST:
301 processReply(currentRequest, createRequestErrorMessage(
302 command, currentRequest.reference2.toUInt()));
309 uint handleData = currentRequest.reference.toUInt();
310 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
311 processReply(currentRequest,
312 createRequestErrorMessage(command, attrHandle));
316 qCWarning(QT_BT_BLUEZ) <<
"Missing response for ATT peripheral command: "
317 << Qt::hex << command;
322 sendNextPendingRequest();
329 delete cmacCalculator;
330 cmacCalculator =
nullptr;
336 bool listen(
const QBluetoothAddress &localAdapter)
338 m_socket = ::socket(AF_BLUETOOTH, SOCK_SEQPACKET,
BTPROTO_L2CAP);
339 if (m_socket == -1) {
340 qCWarning(QT_BT_BLUEZ) <<
"socket creation failed:" << qt_error_string(errno);
348 memset(&addr, 0,
sizeof addr);
350 addr.l2_family = AF_BLUETOOTH;
353 convertAddress(localAdapter.toUInt64(), addr.l2_bdaddr.b);
354 if (::bind(m_socket,
reinterpret_cast<sockaddr *>(&addr),
sizeof addr) == -1) {
355 qCWarning(QT_BT_BLUEZ) <<
"bind() failed:" << qt_error_string(errno);
358 if (::listen(m_socket, 1)) {
359 qCWarning(QT_BT_BLUEZ) <<
"listen() failed:" << qt_error_string(errno);
373 const int socket = m_socket;
383void QLowEnergyControllerPrivateBluez::
startAdvertising(
const QLowEnergyAdvertisingParameters ¶ms,
384 const QLowEnergyAdvertisingData &advertisingData,
385 const QLowEnergyAdvertisingData &scanResponseData)
387 qCDebug(QT_BT_BLUEZ) <<
"Starting to advertise";
389 advertiser =
new QLeAdvertiserBluez(params, advertisingData, scanResponseData, hciManager,
391 connect(advertiser, &QLeAdvertiser::errorOccurred,
this,
392 &QLowEnergyControllerPrivateBluez::handleAdvertisingError);
394 setState(QLowEnergyController::AdvertisingState);
395 advertiser->startAdvertising();
396 if (params.mode() == QLowEnergyAdvertisingParameters::AdvNonConnInd
397 || params.mode() == QLowEnergyAdvertisingParameters::AdvScanInd) {
398 qCDebug(QT_BT_BLUEZ) <<
"Non-connectable advertising requested, "
399 "not listening for connections.";
404 if (!serverSocket.listen(localAdapter)) {
405 setError(QLowEnergyController::AdvertisingError);
406 setState(QLowEnergyController::UnconnectedState);
411 serverSocketNotifier =
new QSocketNotifier(socketFd, QSocketNotifier::Read,
this);
412 connect(serverSocketNotifier, &QSocketNotifier::activated,
this,
413 &QLowEnergyControllerPrivateBluez::handleConnectionRequest);
418 setState(QLowEnergyController::UnconnectedState);
419 advertiser->stopAdvertising();
422void QLowEnergyControllerPrivateBluez::requestConnectionUpdate(
const QLowEnergyConnectionParameters ¶ms)
428 if (role == QLowEnergyController::CentralRole)
429 hciManager->sendConnectionUpdateCommand(connectionHandle, params);
431 hciManager->sendConnectionParameterUpdateRequest(connectionHandle, params);
436 if (remoteDevice.isNull()) {
437 qCWarning(QT_BT_BLUEZ) <<
"Invalid/null remote device address";
438 setError(QLowEnergyController::UnknownRemoteDeviceError);
442 setState(QLowEnergyController::ConnectingState);
445 l2cpSocket =
nullptr;
448 createServicesForCentralIfRequired();
454 QList<quint16> activeHandles = hciManager->activeLowEnergyConnections();
455 if (!activeHandles.isEmpty()) {
456 qCWarning(QT_BT_BLUEZ) <<
"Cannot connect due to pending active LE connections";
458 if (!device1Manager) {
459 device1Manager =
new RemoteDeviceManager(localAdapter,
this);
460 connect(device1Manager, &RemoteDeviceManager::finished,
461 this, &QLowEnergyControllerPrivateBluez::activeConnectionTerminationDone);
464 QList<QBluetoothAddress> connectedAddresses;
465 for (
const auto handle: activeHandles) {
466 const QBluetoothAddress addr = hciManager->addressForConnectionHandle(handle);
468 connectedAddresses.push_back(addr);
470 device1Manager->scheduleJob(RemoteDeviceManager::JobType::JobDisconnectDevice, connectedAddresses);
472 establishL2cpClientSocket();
477
478
479void QLowEnergyControllerPrivateBluez::activeConnectionTerminationDone()
484 qCDebug(QT_BT_BLUEZ) <<
"RemoteDeviceManager finished attempting"
485 <<
"to close external connections";
487 QList<quint16> activeHandles = hciManager->activeLowEnergyConnections();
488 if (!activeHandles.isEmpty()) {
489 qCWarning(QT_BT_BLUEZ) <<
"Cannot close pending external BTLE connections. Aborting connect attempt";
490 setError(QLowEnergyController::ConnectionError);
491 setState(QLowEnergyController::UnconnectedState);
495 establishL2cpClientSocket();
500
501
502void QLowEnergyControllerPrivateBluez::establishL2cpClientSocket()
506 l2cpSocket =
new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol,
this);
507 connect(l2cpSocket, SIGNAL(connected()),
this, SLOT(l2cpConnected()));
508 connect(l2cpSocket, SIGNAL(disconnected()),
this, SLOT(l2cpDisconnected()));
509 connect(l2cpSocket, SIGNAL(errorOccurred(QBluetoothSocket::SocketError)),
this,
510 SLOT(l2cpErrorChanged(QBluetoothSocket::SocketError)));
511 connect(l2cpSocket, SIGNAL(readyRead()),
this, SLOT(l2cpReadyRead()));
513 quint32 addressTypeToUse = (addressType == QLowEnergyController::PublicAddress)
522 qCDebug(QT_BT_BLUEZ) <<
"addresstypeToUse:"
524 ? QStringLiteral(
"Random") : QStringLiteral(
"Public"));
526 l2cpSocket->d_ptr->lowEnergySocketType = addressTypeToUse;
528 int sockfd = l2cpSocket->socketDescriptor();
530 qCWarning(QT_BT_BLUEZ) <<
"l2cp socket not initialised";
531 setError(QLowEnergyController::ConnectionError);
532 setState(QLowEnergyController::UnconnectedState);
537 memset(&addr, 0,
sizeof(addr));
538 addr.l2_family = AF_BLUETOOTH;
541 convertAddress(localAdapter.toUInt64(), addr.l2_bdaddr.b);
544 if (::bind(sockfd, (
struct sockaddr *)&addr,
sizeof(addr)) < 0) {
545 qCWarning(QT_BT_BLUEZ) << qt_error_string(errno);
546 setError(QLowEnergyController::ConnectionError);
547 setState(QLowEnergyController::UnconnectedState);
554 QIODevice::ReadWrite | QIODevice::Unbuffered);
555 loadSigningDataIfNecessary(LocalSigningKey);
558void QLowEnergyControllerPrivateBluez::createServicesForCentralIfRequired()
561 int value = qEnvironmentVariableIntValue(
"QT_DEFAULT_CENTRAL_SERVICES", &ok);
562 if (Q_UNLIKELY(ok && value == 0))
566 if (localServices.contains(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::GenericAccess)))
569 qCDebug(QT_BT_BLUEZ) <<
"Creating default GAP/GATT services";
573 QLowEnergyServiceData gapServiceData;
574 gapServiceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
575 gapServiceData.setUuid(QBluetoothUuid::ServiceClassUuid::GenericAccess);
577 QLowEnergyCharacteristicData gapDeviceName;
578 gapDeviceName.setUuid(QBluetoothUuid::CharacteristicType::DeviceName);
579 gapDeviceName.setProperties(QLowEnergyCharacteristic::Read);
581 QBluetoothLocalDevice mainAdapter;
582 gapDeviceName.setValue(mainAdapter.name().toLatin1());
584 QLowEnergyCharacteristicData gapAppearance;
585 gapAppearance.setUuid(QBluetoothUuid::CharacteristicType::Appearance);
586 gapAppearance.setProperties(QLowEnergyCharacteristic::Read);
587 gapAppearance.setValue(QByteArray::fromHex(
"80"));
589 QLowEnergyCharacteristicData gapPrivacyFlag;
590 gapPrivacyFlag.setUuid(QBluetoothUuid::CharacteristicType::PeripheralPrivacyFlag);
591 gapPrivacyFlag.setProperties(QLowEnergyCharacteristic::Read);
592 gapPrivacyFlag.setValue(QByteArray::fromHex(
"00"));
594 gapServiceData.addCharacteristic(gapDeviceName);
595 gapServiceData.addCharacteristic(gapAppearance);
596 gapServiceData.addCharacteristic(gapPrivacyFlag);
598 Q_Q(QLowEnergyController);
599 QLowEnergyService *service = addServiceHelper(gapServiceData);
601 service->setParent(q);
603 QLowEnergyServiceData gattServiceData;
604 gattServiceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
605 gattServiceData.setUuid(QBluetoothUuid::ServiceClassUuid::GenericAttribute);
607 QLowEnergyCharacteristicData serviceChangedChar;
608 serviceChangedChar.setUuid(QBluetoothUuid::CharacteristicType::ServiceChanged);
609 serviceChangedChar.setProperties(QLowEnergyCharacteristic::Indicate);
611 serviceChangedChar.setValue(QByteArray::fromHex(
"0104"));
613 const QLowEnergyDescriptorData clientConfig(
614 QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
616 serviceChangedChar.addDescriptor(clientConfig);
617 gattServiceData.addCharacteristic(serviceChangedChar);
619 service = addServiceHelper(gattServiceData);
621 service->setParent(q);
624void QLowEnergyControllerPrivateBluez::l2cpConnected()
626 Q_Q(QLowEnergyController);
628 securityLevelValue = securityLevel();
631 setState(QLowEnergyController::ConnectedState);
637 setState(QLowEnergyController::ClosingState);
645 qWarning(QT_BT_BLUEZ) <<
"Unexpected closure of device. Cleaning up internal states.";
650void QLowEnergyControllerPrivateBluez::l2cpDisconnected()
652 Q_Q(QLowEnergyController);
654 if (role == QLowEnergyController::PeripheralRole) {
655 storeClientConfigurations();
656 remoteDevice.clear();
661 setState(QLowEnergyController::UnconnectedState);
662 emit q->disconnected();
665void QLowEnergyControllerPrivateBluez::l2cpErrorChanged(QBluetoothSocket::SocketError e)
668 case QBluetoothSocket::SocketError::HostNotFoundError:
669 setError(QLowEnergyController::UnknownRemoteDeviceError);
670 qCDebug(QT_BT_BLUEZ) <<
"The passed remote device address cannot be found";
672 case QBluetoothSocket::SocketError::NetworkError:
673 setError(QLowEnergyController::NetworkError);
674 qCDebug(QT_BT_BLUEZ) <<
"Network IO error while talking to LE device";
676 case QBluetoothSocket::SocketError::RemoteHostClosedError:
677 setError(QLowEnergyController::RemoteHostClosedError);
678 qCDebug(QT_BT_BLUEZ) <<
"Remote host closed the connection";
680 case QBluetoothSocket::SocketError::UnknownSocketError:
681 case QBluetoothSocket::SocketError::UnsupportedProtocolError:
682 case QBluetoothSocket::SocketError::OperationError:
683 case QBluetoothSocket::SocketError::ServiceNotFoundError:
687 qCDebug(QT_BT_BLUEZ) <<
"Unknown l2cp socket error: " << e << l2cpSocket->errorString();
688 setError(QLowEnergyController::UnknownError);
694 setState(QLowEnergyController::UnconnectedState);
698void QLowEnergyControllerPrivateBluez::resetController()
700 openRequests.clear();
701 openPrepareWriteRequests.clear();
702 scheduledIndications.clear();
703 indicationInFlight =
false;
704 requestPending =
false;
705 encryptionChangePending =
false;
706 receivedMtuExchangeRequest =
false;
707 mtuSize = ATT_DEFAULT_LE_MTU;
708 securityLevelValue = -1;
709 connectionHandle = 0;
711 if (role == QLowEnergyController::PeripheralRole) {
714 advertiser->stopAdvertising();
716 advertiser =
nullptr;
718 localAttributes.clear();
722void QLowEnergyControllerPrivateBluez::restartRequestTimer()
727 if (gattRequestTimeout > 0)
728 requestTimer->start(gattRequestTimeout);
731void QLowEnergyControllerPrivateBluez::l2cpReadyRead()
733 const QByteArray incomingPacket = l2cpSocket->readAll();
734 qCDebug(QT_BT_BLUEZ) <<
"Received size:" << incomingPacket.size() <<
"data:"
735 << incomingPacket.toHex();
736 if (incomingPacket.isEmpty())
743 processUnsolicitedReply(incomingPacket);
752 processUnsolicitedReply(incomingPacket);
757 case QBluezConst::AttCommand::ATT_OP_EXCHANGE_MTU_REQUEST:
758 handleExchangeMtuRequest(incomingPacket);
760 case QBluezConst::AttCommand::ATT_OP_FIND_INFORMATION_REQUEST:
761 handleFindInformationRequest(incomingPacket);
763 case QBluezConst::AttCommand::ATT_OP_FIND_BY_TYPE_VALUE_REQUEST:
764 handleFindByTypeValueRequest(incomingPacket);
766 case QBluezConst::AttCommand::ATT_OP_READ_BY_TYPE_REQUEST:
767 handleReadByTypeRequest(incomingPacket);
769 case QBluezConst::AttCommand::ATT_OP_READ_REQUEST:
770 handleReadRequest(incomingPacket);
772 case QBluezConst::AttCommand::ATT_OP_READ_BLOB_REQUEST:
773 handleReadBlobRequest(incomingPacket);
775 case QBluezConst::AttCommand::ATT_OP_READ_MULTIPLE_REQUEST:
776 handleReadMultipleRequest(incomingPacket);
778 case QBluezConst::AttCommand::ATT_OP_READ_BY_GROUP_REQUEST:
779 handleReadByGroupTypeRequest(incomingPacket);
781 case QBluezConst::AttCommand::ATT_OP_WRITE_REQUEST:
782 case QBluezConst::AttCommand::ATT_OP_WRITE_COMMAND:
783 case QBluezConst::AttCommand::ATT_OP_SIGNED_WRITE_COMMAND:
784 handleWriteRequestOrCommand(incomingPacket);
786 case QBluezConst::AttCommand::ATT_OP_PREPARE_WRITE_REQUEST:
787 handlePrepareWriteRequest(incomingPacket);
789 case QBluezConst::AttCommand::ATT_OP_EXECUTE_WRITE_REQUEST:
790 handleExecuteWriteRequest(incomingPacket);
793 if (indicationInFlight) {
794 indicationInFlight =
false;
795 sendNextIndication();
797 qCWarning(QT_BT_BLUEZ) <<
"received unexpected handle value confirmation";
803 requestPending =
false;
807 if (openRequests.isEmpty()) {
808 qCWarning(QT_BT_BLUEZ) <<
"Received unexpected packet from peer, disconnecting.";
813 const Request request = openRequests.dequeue();
814 processReply(request, incomingPacket);
816 sendNextPendingRequest();
820
821
822
823
824
825
826
827
828void QLowEnergyControllerPrivateBluez::encryptionChangedEvent(
829 const QBluetoothAddress &address,
bool wasSuccess)
831 if (!encryptionChangePending)
834 if (remoteDevice != address)
837 securityLevelValue = securityLevel();
844 Q_ASSERT(!openRequests.isEmpty());
845 Request failedRequest = openRequests.takeFirst();
849 uint ref = failedRequest.reference.toUInt();
850 const QLowEnergyHandle charHandle = (ref & 0xffff);
851 const QLowEnergyHandle descriptorHandle = ((ref >> 16) & 0xffff);
853 QSharedPointer<QLowEnergyServicePrivate> service
854 = serviceForHandle(charHandle);
855 if (!service.isNull() && service->characteristicList.contains(charHandle)) {
856 if (!descriptorHandle)
857 service->setError(QLowEnergyService::CharacteristicWriteError);
859 service->setError(QLowEnergyService::DescriptorWriteError);
862 uint handleData = failedRequest.reference.toUInt();
863 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
864 const QByteArray newValue = failedRequest.reference2.toByteArray();
869 sendExecuteWriteRequest(attrHandle, newValue,
true);
873 encryptionChangePending =
false;
874 sendNextPendingRequest();
877void QLowEnergyControllerPrivateBluez::sendPacket(
const QByteArray &packet)
879 qint64 result = l2cpSocket->write(packet.constData(),
885 qCDebug(QT_BT_BLUEZ) <<
"Cannot write L2CP packet:" << Qt::hex
887 << l2cpSocket->errorString();
888 setError(QLowEnergyController::NetworkError);
889 }
else if (result < packet.size()) {
890 qCWarning(QT_BT_BLUEZ) <<
"L2CP write request incomplete:"
891 << result <<
"of" << packet.size();
896void QLowEnergyControllerPrivateBluez::sendNextPendingRequest()
898 if (openRequests.isEmpty() || requestPending || encryptionChangePending)
901 const Request &request = openRequests.head();
905 requestPending =
true;
906 restartRequestTimer();
907 sendPacket(request.payload);
912 const char *data, quint16 elementLength)
916 Q_ASSERT(elementLength >= 5);
918 QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
919 charData->properties =
920 (QLowEnergyCharacteristic::PropertyTypes)(data[2] & 0xff);
921 charData->valueHandle = bt_get_le16(&data[3]);
924 if (elementLength == 7)
925 charData->uuid = QBluetoothUuid(bt_get_le16(&data[5]));
926 else if (elementLength == 21)
927 charData->uuid = QUuid::fromBytes(&data[5], QSysInfo::LittleEndian);
929 qCDebug(QT_BT_BLUEZ) <<
"Found handle:" << Qt::hex << attributeHandle
930 <<
"properties:" << charData->properties
931 <<
"value handle:" << charData->valueHandle
932 <<
"uuid:" << charData->uuid.toString();
934 return attributeHandle;
938 QList<QBluetoothUuid> *foundServices,
939 const char *data, quint16 elementLength)
941 Q_ASSERT(foundServices);
943 Q_ASSERT(elementLength >= 6);
945 QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
958 if (elementLength == 8)
959 foundServices->append(QBluetoothUuid(bt_get_le16(&data[6])));
960 else if (elementLength == 22)
961 foundServices->append(QUuid::fromBytes(&data[6], QSysInfo::LittleEndian));
963 qCDebug(QT_BT_BLUEZ) <<
"Found included service: " << Qt::hex
964 << attributeHandle <<
"uuid:" << *foundServices;
966 return attributeHandle;
972 qCDebug(QT_BT_BLUEZ,
"%s malformed data: %s", qt_getEnumName(cmd),
973 response.toHex().constData());
976void QLowEnergyControllerPrivateBluez::processReply(
977 const Request &request,
const QByteArray &response)
979 Q_Q(QLowEnergyController);
983 Q_ASSERT(!response.isEmpty());
987 bool isErrorResponse =
false;
990 if (!dumpErrorInformation(response))
993 isErrorResponse =
true;
1000 quint16 oldMtuSize = mtuSize;
1001 if (isErrorResponse) {
1002 mtuSize = ATT_DEFAULT_LE_MTU;
1004 if (response.size() < 3) {
1005 reportMalformedData(command, response);
1008 const char *data = response.constData();
1009 quint16 mtu = bt_get_le16(&data[1]);
1011 if (mtuSize < ATT_DEFAULT_LE_MTU)
1012 mtuSize = ATT_DEFAULT_LE_MTU;
1014 qCDebug(QT_BT_BLUEZ) <<
"Server MTU:" << mtu <<
"resulting mtu:" << mtuSize;
1016 if (oldMtuSize != mtuSize)
1017 emit q->mtuChanged(mtuSize);
1024 const quint16 type = request.reference.toUInt();
1026 if (isErrorResponse) {
1028 setState(QLowEnergyController::DiscoveredState);
1029 q->discoveryFinished();
1038 if (response.size() < 2 || response[1] < 4) {
1039 reportMalformedData(command, response);
1043 QLowEnergyHandle start = 0, end = 0;
1044 const quint16 elementLength = response.constData()[1];
1045 const quint16 numElements = (response.size() - 2) / elementLength;
1047 const char *data = response.constData();
1048 for (
int i = 0; i < numElements; i++) {
1049 start = bt_get_le16(&data[offset]);
1050 end = bt_get_le16(&data[offset+2]);
1052 QBluetoothUuid uuid;
1053 if (elementLength == 6)
1054 uuid = QBluetoothUuid(bt_get_le16(&data[offset+4]));
1055 else if (elementLength == 20)
1056 uuid = QUuid::fromBytes(&data[offset+4], QSysInfo::LittleEndian);
1059 offset += elementLength;
1062 qCDebug(QT_BT_BLUEZ) <<
"Found uuid:" << uuid <<
"start handle:" << Qt::hex
1063 << start <<
"end handle:" << end;
1067 priv->startHandle = start;
1068 priv->endHandle = end;
1070 priv->type &= ~QLowEnergyService::PrimaryService;
1073 QSharedPointer<QLowEnergyServicePrivate> pointer(priv);
1075 serviceList.insert(uuid, pointer);
1076 emit q->serviceDiscovered(uuid);
1079 if (end != 0xFFFF) {
1080 sendReadByGroupRequest(end+1, 0xFFFF, type);
1083 setState(QLowEnergyController::DiscoveredState);
1084 emit q->discoveryFinished();
1095 QSharedPointer<QLowEnergyServicePrivate> p =
1096 request.reference.value<QSharedPointer<QLowEnergyServicePrivate> >();
1097 const quint16 attributeType = request.reference2.toUInt();
1099 if (isErrorResponse) {
1104 if (!p->characteristicList.isEmpty()) {
1105 readServiceValues(p->uuid,
true);
1109 p->setState(QLowEnergyService::RemoteServiceDiscovered);
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1134 if (response.size() < 2 || response[1] < minimumElementLength) {
1135 reportMalformedData(command, response);
1139 QLowEnergyHandle lastHandle;
1140 const quint16 elementLength = response.constData()[1];
1141 const quint16 numElements = (response.size() - 2) / elementLength;
1143 const char *data = response.constData();
1144 for (
int i = 0; i < numElements; i++) {
1147 lastHandle = parseReadByTypeCharDiscovery(
1148 &characteristic, &data[offset], elementLength);
1149 p->characteristicList[lastHandle] = characteristic;
1150 offset += elementLength;
1152 QList<QBluetoothUuid> includedServices;
1153 lastHandle = parseReadByTypeIncludeDiscovery(
1154 &includedServices, &data[offset], elementLength);
1155 p->includedServices = includedServices;
1156 for (
const QBluetoothUuid &uuid : std::as_const(includedServices)) {
1157 if (serviceList.contains(uuid))
1158 serviceList[uuid]->type |= QLowEnergyService::IncludedService;
1163 if (lastHandle + 1 < p->endHandle) {
1164 sendReadByTypeRequest(p, lastHandle + 1, attributeType);
1169 readServiceValues(p->uuid,
true);
1177 uint handleData = request.reference.toUInt();
1178 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1179 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1181 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1182 Q_ASSERT(!service.isNull());
1183 bool isServiceDiscoveryRun
1184 = !(service->state == QLowEnergyService::RemoteServiceDiscovered);
1186 if (isErrorResponse) {
1187 Q_ASSERT(!encryptionChangePending);
1189 encryptionChangePending = increaseEncryptLevelfRequired(err);
1190 if (encryptionChangePending) {
1193 openRequests.prepend(request);
1195 }
else if (!isServiceDiscoveryRun) {
1197 if (!descriptorHandle)
1198 service->setError(QLowEnergyService::CharacteristicReadError);
1200 service->setError(QLowEnergyService::DescriptorReadError);
1203 if (!descriptorHandle)
1204 updateValueOfCharacteristic(charHandle, response.mid(1),
NEW_VALUE);
1206 updateValueOfDescriptor(charHandle, descriptorHandle,
1209 if (response.size() == mtuSize) {
1210 qCDebug(QT_BT_BLUEZ) <<
"Switching to blob reads for"
1211 << charHandle << descriptorHandle
1212 << service->characteristicList[charHandle].uuid.toString();
1214 readServiceValuesByOffset(handleData, mtuSize-1,
1215 request.reference2.toBool());
1217 }
else if (!isServiceDiscoveryRun) {
1219 if (!descriptorHandle) {
1220 QLowEnergyCharacteristic ch(service, charHandle);
1221 emit service->characteristicRead(ch, response.mid(1));
1223 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1224 emit service->descriptorRead(descriptor, response.mid(1));
1230 if (request.reference2.toBool() && isServiceDiscoveryRun) {
1236 if (!descriptorHandle)
1237 discoverServiceDescriptors(service->uuid);
1239 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1247 uint handleData = request.reference.toUInt();
1248 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1249 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1251 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1252 Q_ASSERT(!service.isNull());
1255
1256
1257
1258
1259
1260 if (!isErrorResponse) {
1262 if (!descriptorHandle)
1263 length = updateValueOfCharacteristic(charHandle, response.mid(1),
APPEND_VALUE);
1265 length = updateValueOfDescriptor(charHandle, descriptorHandle,
1268 if (response.size() == mtuSize) {
1269 readServiceValuesByOffset(handleData, length,
1270 request.reference2.toBool());
1272 }
else if (service->state == QLowEnergyService::RemoteServiceDiscovered) {
1274 if (!descriptorHandle) {
1275 QLowEnergyCharacteristic ch(service, charHandle);
1276 emit service->characteristicRead(ch, ch.value());
1278 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1279 emit service->descriptorRead(descriptor, descriptor.value());
1284 qWarning() <<
"READ BLOB for char:" << charHandle
1285 <<
"descriptor:" << descriptorHandle <<
"on service"
1286 << service->uuid.toString() <<
"failed (service discovery run:"
1287 << (service->state == QLowEnergyService::RemoteServiceDiscovered) <<
")";
1290 if (request.reference2.toBool()) {
1294 if (!descriptorHandle)
1295 discoverServiceDescriptors(service->uuid);
1297 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1307
1308
1309
1310
1312 QList<QLowEnergyHandle> keys = request.reference.value<QList<QLowEnergyHandle> >();
1313 if (keys.isEmpty()) {
1314 qCWarning(QT_BT_BLUEZ) <<
"Descriptor discovery for unknown characteristic received";
1317 QLowEnergyHandle charHandle = keys.first();
1319 QSharedPointer<QLowEnergyServicePrivate> p =
1320 serviceForHandle(charHandle);
1321 Q_ASSERT(!p.isNull());
1323 if (isErrorResponse) {
1324 if (keys.size() == 1) {
1326 readServiceValues(p->uuid,
false);
1330 discoverNextDescriptor(p, keys, keys.first());
1336 if (response.size() < 6) {
1337 reportMalformedData(command, response);
1341 const quint8 format = response[1];
1342 quint16 elementLength;
1345 elementLength = 2 + 2;
1348 elementLength = 2 + 16;
1351 qCWarning(QT_BT_BLUEZ) <<
"Unknown format in FIND_INFORMATION_RESPONSE";
1355 const quint16 numElements = (response.size() - 2) / elementLength;
1358 QLowEnergyHandle descriptorHandle {};
1359 QBluetoothUuid uuid;
1360 const char *data = response.constData();
1361 for (
int i = 0; i < numElements; i++) {
1362 descriptorHandle = bt_get_le16(&data[offset]);
1365 uuid = QBluetoothUuid(bt_get_le16(&data[offset+2]));
1366 else if (format == 0x02)
1367 uuid = QUuid::fromBytes(&data[offset+2], QSysInfo::LittleEndian);
1369 offset += elementLength;
1374 quint16 shortUuid = uuid.toUInt16(&ok);
1377 qCDebug(QT_BT_BLUEZ) <<
"Suppressing primary/characteristic" << Qt::hex << shortUuid;
1382 if (descriptorHandle == p->characteristicList[charHandle].valueHandle) {
1383 qCDebug(QT_BT_BLUEZ) <<
"Suppressing char handle" << Qt::hex << descriptorHandle;
1389 p->characteristicList[charHandle].descriptorList.insert(
1390 descriptorHandle, data);
1392 qCDebug(QT_BT_BLUEZ) <<
"Descriptor found, uuid:"
1394 <<
"descriptor handle:" << Qt::hex << descriptorHandle;
1397 const QLowEnergyHandle nextPotentialHandle = descriptorHandle + 1;
1398 if (keys.size() == 1) {
1407 if ((p->endHandle != 0xffff && nextPotentialHandle >= p->endHandle + 1)
1408 || (descriptorHandle == 0xffff)) {
1412 readServiceValues(p->uuid,
false);
1414 discoverNextDescriptor(p, keys, nextPotentialHandle);
1417 if (nextPotentialHandle >= keys[1])
1419 discoverNextDescriptor(p, keys, nextPotentialHandle);
1427 uint ref = request.reference.toUInt();
1428 const QLowEnergyHandle charHandle = (ref & 0xffff);
1429 const QLowEnergyHandle descriptorHandle = ((ref >> 16) & 0xffff);
1431 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
1432 if (service.isNull() || !service->characteristicList.contains(charHandle))
1435 if (isErrorResponse) {
1436 Q_ASSERT(!encryptionChangePending);
1438 encryptionChangePending = increaseEncryptLevelfRequired(err);
1439 if (encryptionChangePending) {
1440 openRequests.prepend(request);
1444 if (!descriptorHandle)
1445 service->setError(QLowEnergyService::CharacteristicWriteError);
1447 service->setError(QLowEnergyService::DescriptorWriteError);
1451 const QByteArray newValue = request.reference2.toByteArray();
1452 if (!descriptorHandle) {
1453 QLowEnergyCharacteristic ch(service, charHandle);
1454 if (ch.properties() & QLowEnergyCharacteristic::Read)
1455 updateValueOfCharacteristic(charHandle, newValue,
NEW_VALUE);
1456 emit service->characteristicWritten(ch, newValue);
1458 updateValueOfDescriptor(charHandle, descriptorHandle, newValue,
NEW_VALUE);
1459 QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
1460 emit service->descriptorWritten(descriptor, newValue);
1468 uint handleData = request.reference.toUInt();
1469 const QLowEnergyHandle attrHandle = (handleData & 0xffff);
1470 const QByteArray newValue = request.reference2.toByteArray();
1471 const int writtenPayload = ((handleData >> 16) & 0xffff);
1473 if (isErrorResponse) {
1474 Q_ASSERT(!encryptionChangePending);
1476 encryptionChangePending = increaseEncryptLevelfRequired(err);
1477 if (encryptionChangePending) {
1478 openRequests.prepend(request);
1482 sendExecuteWriteRequest(attrHandle, newValue,
true);
1484 if (writtenPayload < newValue.size()) {
1485 sendNextPrepareWriteRequest(attrHandle, newValue, writtenPayload);
1487 sendExecuteWriteRequest(attrHandle, newValue,
false);
1497 uint handleData = request.reference.toUInt();
1498 const QLowEnergyHandle attrHandle = handleData & 0xffff;
1499 bool wasCancellation = !((handleData >> 16) & 0xffff);
1500 const QByteArray newValue = request.reference2.toByteArray();
1503 const QLowEnergyDescriptor descriptor = descriptorForHandle(attrHandle);
1504 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(attrHandle);
1505 Q_ASSERT(!service.isNull());
1507 if (isErrorResponse || wasCancellation) {
1509 if (descriptor.isValid())
1510 service->setError(QLowEnergyService::DescriptorWriteError);
1512 service->setError(QLowEnergyService::CharacteristicWriteError);
1514 if (descriptor.isValid()) {
1515 updateValueOfDescriptor(descriptor.characteristicHandle(),
1517 emit service->descriptorWritten(descriptor, newValue);
1519 QLowEnergyCharacteristic ch(service, attrHandle);
1520 if (ch.properties() & QLowEnergyCharacteristic::Read)
1521 updateValueOfCharacteristic(attrHandle, newValue,
NEW_VALUE);
1522 emit service->characteristicWritten(ch, newValue);
1527 qCDebug(QT_BT_BLUEZ) <<
"Unknown packet: " << response.toHex();
1537void QLowEnergyControllerPrivateBluez::sendReadByGroupRequest(
1538 QLowEnergyHandle start, QLowEnergyHandle end, quint16 type)
1544 putBtData(start, &packet[1]);
1545 putBtData(end, &packet[3]);
1546 putBtData(type, &packet[5]);
1550 qCDebug(QT_BT_BLUEZ) <<
"Sending read_by_group_type request, startHandle:" << Qt::hex
1551 << start <<
"endHandle:" << end << type;
1554 request.payload = data;
1556 request.reference = type;
1557 openRequests.enqueue(request);
1559 sendNextPendingRequest();
1563 QLowEnergyService::DiscoveryMode mode)
1565 if (!serviceList.contains(service)) {
1566 qCWarning(QT_BT_BLUEZ) <<
"Discovery of unknown service" << service.toString()
1571 QSharedPointer<QLowEnergyServicePrivate> serviceData = serviceList.value(service);
1572 serviceData->mode = mode;
1573 serviceData->characteristicList.clear();
1577void QLowEnergyControllerPrivateBluez::sendReadByTypeRequest(
1578 QSharedPointer<QLowEnergyServicePrivate> serviceData,
1579 QLowEnergyHandle nextHandle, quint16 attributeType)
1584 putBtData(nextHandle, &packet[1]);
1585 putBtData(serviceData->endHandle, &packet[3]);
1586 putBtData(attributeType, &packet[5]);
1590 qCDebug(QT_BT_BLUEZ) <<
"Sending read_by_type request, startHandle:" << Qt::hex
1591 << nextHandle <<
"endHandle:" << serviceData->endHandle
1592 <<
"type:" << attributeType <<
"packet:" << data.toHex();
1595 request.payload = data;
1597 request.reference = QVariant::fromValue(serviceData);
1598 request.reference2 = attributeType;
1599 openRequests.enqueue(request);
1601 sendNextPendingRequest();
1605
1606
1607
1608
1609
1610
1611
1612
1613void QLowEnergyControllerPrivateBluez::readServiceValues(
1614 const QBluetoothUuid &serviceUuid,
bool readCharacteristics)
1617 if (QT_BT_BLUEZ().isDebugEnabled()) {
1618 if (readCharacteristics)
1619 qCDebug(QT_BT_BLUEZ) <<
"Reading all characteristic values for"
1620 << serviceUuid.toString();
1622 qCDebug(QT_BT_BLUEZ) <<
"Reading all descriptor values for"
1623 << serviceUuid.toString();
1626 QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid);
1628 if (service->mode == QLowEnergyService::SkipValueDiscovery) {
1629 if (readCharacteristics) {
1631 discoverServiceDescriptors(service->uuid);
1633 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1640 QPair<QLowEnergyHandle, quint32> pair;
1643 QList<QPair<QLowEnergyHandle, quint32> > targetHandles;
1645 CharacteristicDataMap::const_iterator charIt = service->characteristicList.constBegin();
1646 for ( ; charIt != service->characteristicList.constEnd(); ++charIt) {
1647 const QLowEnergyHandle charHandle = charIt.key();
1650 if (readCharacteristics) {
1654 if (!(charDetails.properties & QLowEnergyCharacteristic::Read))
1657 pair.first = charDetails.valueHandle;
1658 pair.second = charHandle;
1659 targetHandles.append(pair);
1663 DescriptorDataMap::const_iterator descIt = charDetails.descriptorList.constBegin();
1664 for ( ; descIt != charDetails.descriptorList.constEnd(); ++descIt) {
1665 const QLowEnergyHandle descriptorHandle = descIt.key();
1667 pair.first = descriptorHandle;
1668 pair.second = (charHandle | (descriptorHandle << 16));
1669 targetHandles.append(pair);
1675 if (targetHandles.isEmpty()) {
1676 if (readCharacteristics) {
1679 discoverServiceDescriptors(service->uuid);
1682 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1687 for (qsizetype i = 0; i < targetHandles.size(); i++) {
1688 pair = targetHandles.at(i);
1690 putBtData(pair.first, &packet[1]);
1696 request.payload = data;
1698 request.reference = pair.second;
1700 request.reference2 = QVariant((
bool)(i + 1 == targetHandles.size()));
1701 openRequests.enqueue(request);
1704 sendNextPendingRequest();
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717void QLowEnergyControllerPrivateBluez::readServiceValuesByOffset(
1718 uint handleData, quint16 offset,
bool isLastValue)
1720 const QLowEnergyHandle charHandle = (handleData & 0xffff);
1721 const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
1726 QLowEnergyHandle handleToRead = charHandle;
1727 if (descriptorHandle) {
1728 handleToRead = descriptorHandle;
1729 qCDebug(QT_BT_BLUEZ) <<
"Reading descriptor via blob request"
1730 << Qt::hex << descriptorHandle;
1733 QSharedPointer<QLowEnergyServicePrivate> service =
1734 serviceForHandle(charHandle);
1735 if (!service.isNull()
1736 && service->characteristicList.contains(charHandle)) {
1737 handleToRead = service->characteristicList[charHandle].valueHandle;
1738 qCDebug(QT_BT_BLUEZ) <<
"Reading characteristic via blob request"
1739 << Qt::hex << handleToRead;
1745 putBtData(handleToRead, data.data() + 1);
1746 putBtData(offset, data.data() + 3);
1749 request.payload = data;
1751 request.reference = handleData;
1752 request.reference2 = isLastValue;
1753 openRequests.prepend(request);
1756void QLowEnergyControllerPrivateBluez::discoverServiceDescriptors(
1757 const QBluetoothUuid &serviceUuid)
1759 qCDebug(QT_BT_BLUEZ) <<
"Discovering descriptor values for"
1760 << serviceUuid.toString();
1761 QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid);
1763 if (service->characteristicList.isEmpty()) {
1765 service->setState(QLowEnergyService::RemoteServiceDiscovered);
1770 QList<QLowEnergyHandle> keys = service->characteristicList.keys();
1771 std::sort(keys.begin(), keys.end());
1773 discoverNextDescriptor(service, keys, keys[0]);
1776void QLowEnergyControllerPrivateBluez::processUnsolicitedReply(
const QByteArray &payload)
1778 Q_ASSERT(!payload.isEmpty());
1780 const char *data = payload.constData();
1782 bool isNotification = (command
1785 if (payload.size() < 3) {
1786 reportMalformedData(command, payload);
1790 const QLowEnergyHandle changedHandle = bt_get_le16(&data[1]);
1792 if (QT_BT_BLUEZ().isDebugEnabled()) {
1794 qCDebug(QT_BT_BLUEZ) <<
"Change notification for handle" << Qt::hex << changedHandle;
1796 qCDebug(QT_BT_BLUEZ) <<
"Change indication for handle" << Qt::hex << changedHandle;
1799 const QLowEnergyCharacteristic ch = characteristicForHandle(changedHandle);
1800 if (ch.isValid() && ch.handle() == changedHandle) {
1801 if (ch.properties() & QLowEnergyCharacteristic::Read)
1802 updateValueOfCharacteristic(ch.attributeHandle(), payload.mid(3),
NEW_VALUE);
1803 emit ch.d_ptr->characteristicChanged(ch, payload.mid(3));
1805 qCWarning(QT_BT_BLUEZ) <<
"Cannot find matching characteristic for "
1806 "notification/indication";
1810void QLowEnergyControllerPrivateBluez::exchangeMTU()
1812 qCDebug(QT_BT_BLUEZ) <<
"Exchanging MTU";
1816 putBtData(ATT_MAX_LE_MTU, &packet[1]);
1822 request.payload = data;
1824 openRequests.enqueue(request);
1826 sendNextPendingRequest();
1829int QLowEnergyControllerPrivateBluez::securityLevel()
const
1831 int socket = l2cpSocket->socketDescriptor();
1833 qCWarning(QT_BT_BLUEZ) <<
"Invalid l2cp socket, aborting getting of sec level";
1837 struct bt_security secData;
1838 socklen_t length =
sizeof(secData);
1839 memset(&secData, 0, length);
1842 qCDebug(QT_BT_BLUEZ) <<
"Current l2cp sec level:" << secData.level;
1843 return secData.level;
1846 if (errno != ENOPROTOOPT)
1851 length =
sizeof(optval);
1861 qCDebug(QT_BT_BLUEZ) <<
"Current l2cp sec level (old):" << level;
1868bool QLowEnergyControllerPrivateBluez::setSecurityLevel(
int level)
1873 int socket = l2cpSocket->socketDescriptor();
1875 qCWarning(QT_BT_BLUEZ) <<
"Invalid l2cp socket, aborting setting of sec level";
1879 struct bt_security secData;
1880 socklen_t length =
sizeof(secData);
1881 memset(&secData, 0, length);
1882 secData.level = level;
1885 qCDebug(QT_BT_BLUEZ) <<
"Setting new l2cp sec level:" << secData.level;
1889 if (errno != ENOPROTOOPT)
1908 qCDebug(QT_BT_BLUEZ) <<
"Old l2cp sec level:" << optval;
1915void QLowEnergyControllerPrivateBluez::discoverNextDescriptor(
1916 QSharedPointer<QLowEnergyServicePrivate> serviceData,
1917 const QList<QLowEnergyHandle> pendingCharHandles,
1918 const QLowEnergyHandle startingHandle)
1920 Q_ASSERT(!pendingCharHandles.isEmpty());
1921 Q_ASSERT(!serviceData.isNull());
1923 qCDebug(QT_BT_BLUEZ) <<
"Sending find_info request" << Qt::hex
1924 << pendingCharHandles << startingHandle;
1929 const QLowEnergyHandle charStartHandle = startingHandle;
1930 QLowEnergyHandle charEndHandle = 0;
1931 if (pendingCharHandles.size() == 1)
1932 charEndHandle = serviceData->endHandle;
1934 charEndHandle = pendingCharHandles[1] - 1;
1936 putBtData(charStartHandle, &packet[1]);
1937 putBtData(charEndHandle, &packet[3]);
1943 request.payload = data;
1945 request.reference = QVariant::fromValue<QList<QLowEnergyHandle> >(pendingCharHandles);
1946 request.reference2 = startingHandle;
1947 openRequests.enqueue(request);
1949 sendNextPendingRequest();
1952void QLowEnergyControllerPrivateBluez::sendNextPrepareWriteRequest(
1953 const QLowEnergyHandle handle,
const QByteArray &newValue,
1957 QLowEnergyHandle targetHandle = 0;
1958 const QLowEnergyDescriptor descriptor = descriptorForHandle(handle);
1959 if (descriptor.isValid())
1960 targetHandle = descriptor.handle();
1962 targetHandle = characteristicForHandle(handle).handle();
1964 if (!targetHandle) {
1965 qCWarning(QT_BT_BLUEZ) <<
"sendNextPrepareWriteRequest cancelled due to invalid handle"
1972 putBtData(targetHandle, &packet[1]);
1973 putBtData(offset, &packet[3]);
1975 qCDebug(QT_BT_BLUEZ) <<
"Writing long characteristic (prepare):"
1976 << Qt::hex << handle;
1980 const qsizetype requiredPayload = (
std::min)(newValue.size() - offset, maxAvailablePayload);
1983 Q_ASSERT((offset + requiredPayload) <= newValue.size());
1984 Q_ASSERT(dataSize <= mtuSize);
1986 QByteArray data(dataSize, Qt::Uninitialized);
1992 request.payload = data;
1994 request.reference = (handle | ((offset + requiredPayload) << 16));
1995 request.reference2 = newValue;
1996 openRequests.enqueue(request);
2000
2001
2002
2003
2004
2005
2006
2007void QLowEnergyControllerPrivateBluez::sendExecuteWriteRequest(
2008 const QLowEnergyHandle attrHandle,
const QByteArray &newValue,
2021 qCDebug(QT_BT_BLUEZ) <<
"Sending Execute Write Request for long characteristic value"
2022 << Qt::hex << attrHandle;
2025 request.payload = data;
2027 request.reference = (attrHandle | ((isCancelation ? 0x00 : 0x01) << 16));
2028 request.reference2 = newValue;
2029 openRequests.prepend(request);
2034
2035
2036
2037
2038
2040 const QSharedPointer<QLowEnergyServicePrivate> service,
2041 const QLowEnergyHandle charHandle,
2042 const QByteArray &newValue,
2043 QLowEnergyService::WriteMode mode)
2045 Q_ASSERT(!service.isNull());
2047 if (!service->characteristicList.contains(charHandle))
2051 if (role == QLowEnergyController::PeripheralRole)
2052 writeCharacteristicForPeripheral(charData, newValue);
2054 writeCharacteristicForCentral(service, charHandle, charData.valueHandle, newValue, mode);
2058 const QSharedPointer<QLowEnergyServicePrivate> service,
2059 const QLowEnergyHandle charHandle,
2060 const QLowEnergyHandle descriptorHandle,
2061 const QByteArray &newValue)
2063 Q_ASSERT(!service.isNull());
2065 if (role == QLowEnergyController::PeripheralRole)
2066 writeDescriptorForPeripheral(service, charHandle, descriptorHandle, newValue);
2068 writeDescriptorForCentral(charHandle, descriptorHandle, newValue);
2072
2073
2074
2075
2077 const QSharedPointer<QLowEnergyServicePrivate> service,
2078 const QLowEnergyHandle charHandle)
2080 Q_ASSERT(!service.isNull());
2081 if (!service->characteristicList.contains(charHandle))
2085 = service->characteristicList[charHandle];
2086 if (!(charDetails.properties & QLowEnergyCharacteristic::Read)) {
2090 qCWarning(QT_BT_BLUEZ) <<
"Reading non-readable char" << charHandle;
2095 putBtData(charDetails.valueHandle, &packet[1]);
2100 qCDebug(QT_BT_BLUEZ) <<
"Targeted reading characteristic" << Qt::hex << charHandle;
2103 request.payload = data;
2105 request.reference = charHandle;
2108 request.reference2 =
false;
2109 openRequests.enqueue(request);
2111 sendNextPendingRequest();
2115 const QSharedPointer<QLowEnergyServicePrivate> service,
2116 const QLowEnergyHandle charHandle,
2117 const QLowEnergyHandle descriptorHandle)
2119 Q_ASSERT(!service.isNull());
2120 if (!service->characteristicList.contains(charHandle))
2124 = service->characteristicList[charHandle];
2125 if (!charDetails.descriptorList.contains(descriptorHandle))
2130 putBtData(descriptorHandle, &packet[1]);
2135 qCDebug(QT_BT_BLUEZ) <<
"Targeted reading descriptor" << Qt::hex << descriptorHandle;
2138 request.payload = data;
2140 request.reference = (charHandle | (descriptorHandle << 16));
2143 request.reference2 =
false;
2144 openRequests.enqueue(request);
2146 sendNextPendingRequest();
2150
2151
2152
2153bool QLowEnergyControllerPrivateBluez::increaseEncryptLevelfRequired(
2159 switch (errorCode) {
2163 if (!hciManager->isValid())
2165 if (!hciManager->monitorEvent(HciManager::HciEvent::EVT_ENCRYPT_CHANGE))
2168 qCDebug(QT_BT_BLUEZ) <<
"Requesting encrypted link";
2170 restartRequestTimer();
2182void QLowEnergyControllerPrivateBluez::handleAdvertisingError()
2184 qCWarning(QT_BT_BLUEZ) <<
"received advertising error";
2185 setError(QLowEnergyController::AdvertisingError);
2186 setState(QLowEnergyController::UnconnectedState);
2189bool QLowEnergyControllerPrivateBluez::checkPacketSize(
const QByteArray &packet,
int minSize,
2194 if (Q_LIKELY(packet.size() >= minSize && packet.size() <= maxSize))
2196 qCWarning(QT_BT_BLUEZ) <<
"client request of type" << packet.at(0)
2197 <<
"has unexpected packet size" << packet.size();
2198 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2199 QBluezConst::AttError::ATT_ERROR_INVALID_PDU);
2203bool QLowEnergyControllerPrivateBluez::checkHandle(
const QByteArray &packet, QLowEnergyHandle handle)
2205 if (handle != 0 && handle <= lastLocalHandle)
2207 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2208 QBluezConst::AttError::ATT_ERROR_INVALID_HANDLE);
2213 QLowEnergyHandle startingHandle,
2214 QLowEnergyHandle endingHandle)
2216 if (startingHandle == 0 || startingHandle > endingHandle) {
2217 qCDebug(QT_BT_BLUEZ) <<
"handle range invalid";
2218 sendErrorResponse(request, startingHandle, QBluezConst::AttError::ATT_ERROR_INVALID_HANDLE);
2224void QLowEnergyControllerPrivateBluez::handleExchangeMtuRequest(
const QByteArray &packet)
2228 if (!checkPacketSize(packet, 3))
2230 if (receivedMtuExchangeRequest) {
2231 qCDebug(QT_BT_BLUEZ) <<
"Client sent extraneous MTU exchange packet";
2232 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2233 QBluezConst::AttError::ATT_ERROR_REQUEST_NOT_SUPPORTED);
2236 receivedMtuExchangeRequest =
true;
2241 putBtData(ATT_MAX_LE_MTU, reply.data() + 1);
2245 const quint16 clientRxMtu = bt_get_le16(packet.constData() + 1);
2246 mtuSize = std::clamp(clientRxMtu, ATT_DEFAULT_LE_MTU, ATT_MAX_LE_MTU);
2247 qCDebug(QT_BT_BLUEZ) <<
"MTU request from client:" << clientRxMtu
2248 <<
"effective client RX MTU:" << mtuSize;
2249 qCDebug(QT_BT_BLUEZ) <<
"Sending server RX MTU" << ATT_MAX_LE_MTU;
2252void QLowEnergyControllerPrivateBluez::handleFindInformationRequest(
const QByteArray &packet)
2256 if (!checkPacketSize(packet, 5))
2258 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2259 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2260 qCDebug(QT_BT_BLUEZ) <<
"client sends find information request; start:" << startingHandle
2261 <<
"end:" << endingHandle;
2262 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2266 QList<Attribute> results = getAttributes(startingHandle, endingHandle);
2267 if (results.isEmpty()) {
2268 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2269 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2272 ensureUniformUuidSizes(results);
2274 QByteArray responsePrefix(2, Qt::Uninitialized);
2275 const int uuidSize = getUuidSize(results.first().type);
2278 responsePrefix[1] = uuidSize == 2 ? 0x1 : 0x2;
2279 const int elementSize =
sizeof(QLowEnergyHandle) + uuidSize;
2280 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2281 putDataAndIncrement(attr.handle, data);
2282 putDataAndIncrement(attr.type, data);
2284 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2288void QLowEnergyControllerPrivateBluez::handleFindByTypeValueRequest(
const QByteArray &packet)
2292 if (!checkPacketSize(packet, 7, mtuSize))
2294 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2295 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2296 const quint16 type = bt_get_le16(packet.constData() + 5);
2297 const QByteArray value = QByteArray::fromRawData(packet.constData() + 7, packet.size() - 7);
2298 qCDebug(QT_BT_BLUEZ) <<
"client sends find by type value request; start:" << startingHandle
2299 <<
"end:" << endingHandle <<
"type:" << type
2300 <<
"value:" << value.toHex();
2301 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2305 const auto predicate = [value,
this, type](
const Attribute &attr) {
2306 return attr.type == QBluetoothUuid(type) && attr.value == value
2307 && checkReadPermissions(attr) == QBluezConst::AttError::ATT_ERROR_NO_ERROR;
2309 const QList<Attribute> results = getAttributes(startingHandle, endingHandle, predicate);
2310 if (results.isEmpty()) {
2311 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2312 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2316 QByteArray responsePrefix(
2318 const int elemSize = 2 *
sizeof(QLowEnergyHandle);
2319 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2320 putDataAndIncrement(attr.handle, data);
2321 putDataAndIncrement(attr.groupEndHandle, data);
2323 sendListResponse(responsePrefix, elemSize, results, elemWriter);
2326void QLowEnergyControllerPrivateBluez::handleReadByTypeRequest(
const QByteArray &packet)
2330 if (!checkPacketSize(packet, 7, 21))
2332 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2333 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2334 const void *
const typeStart = packet.constData() + 5;
2335 const bool is16BitUuid = packet.size() == 7;
2336 const bool is128BitUuid = packet.size() == 21;
2337 QBluetoothUuid type;
2339 type = QBluetoothUuid(bt_get_le16(typeStart));
2340 }
else if (is128BitUuid) {
2341 type = QUuid::fromBytes(typeStart, QSysInfo::LittleEndian);
2343 qCWarning(QT_BT_BLUEZ) <<
"read by type request has invalid packet size" << packet.size();
2344 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2345 QBluezConst::AttError::ATT_ERROR_INVALID_PDU);
2348 qCDebug(QT_BT_BLUEZ) <<
"client sends read by type request, start:" << startingHandle
2349 <<
"end:" << endingHandle <<
"type:" << type;
2350 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2355 QList<Attribute> results =
2356 getAttributes(startingHandle, endingHandle,
2357 [type](
const Attribute &attr) {
return attr.type == type; });
2358 ensureUniformValueSizes(results);
2360 if (results.isEmpty()) {
2361 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2362 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2368 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2369 results.first().handle, error);
2373 const qsizetype elementSize =
sizeof(QLowEnergyHandle) + results.first().value.size();
2374 QByteArray responsePrefix(2, Qt::Uninitialized);
2376 responsePrefix[1] = elementSize;
2377 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2378 putDataAndIncrement(attr.handle, data);
2379 putDataAndIncrement(attr.value, data);
2381 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2384void QLowEnergyControllerPrivateBluez::handleReadRequest(
const QByteArray &packet)
2388 if (!checkPacketSize(packet, 3))
2390 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2391 qCDebug(QT_BT_BLUEZ) <<
"client sends read request; handle:" << handle;
2393 if (!checkHandle(packet, handle))
2395 const Attribute &attribute = localAttributes.at(handle);
2398 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2403 const qsizetype sentValueLength = (
std::min)(attribute.value.size(), qsizetype(mtuSize) - 1);
2404 QByteArray response(1 + sentValueLength, Qt::Uninitialized);
2406 using namespace std;
2407 memcpy(response.data() + 1, attribute.value.constData(), sentValueLength);
2408 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2409 sendPacket(response);
2412void QLowEnergyControllerPrivateBluez::handleReadBlobRequest(
const QByteArray &packet)
2416 if (!checkPacketSize(packet, 5))
2418 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2419 const quint16 valueOffset = bt_get_le16(packet.constData() + 3);
2420 qCDebug(QT_BT_BLUEZ) <<
"client sends read blob request; handle:" << handle
2421 <<
"offset:" << valueOffset;
2423 if (!checkHandle(packet, handle))
2425 const Attribute &attribute = localAttributes.at(handle);
2428 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2432 if (valueOffset > attribute.value.size()) {
2433 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2434 QBluezConst::AttError::ATT_ERROR_INVALID_OFFSET);
2437 if (attribute.value.size() <= mtuSize - 3) {
2438 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2439 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_LONG);
2444 const qsizetype sentValueLength = (
std::min)(attribute.value.size() - valueOffset,
2445 qsizetype(mtuSize) - 1);
2447 QByteArray response(1 + sentValueLength, Qt::Uninitialized);
2449 using namespace std;
2450 memcpy(response.data() + 1, attribute.value.constData() + valueOffset, sentValueLength);
2451 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2452 sendPacket(response);
2455void QLowEnergyControllerPrivateBluez::handleReadMultipleRequest(
const QByteArray &packet)
2459 if (!checkPacketSize(packet, 5, mtuSize))
2461 QList<QLowEnergyHandle> handles((packet.size() - 1) /
sizeof(QLowEnergyHandle));
2462 auto *packetPtr =
reinterpret_cast<
const QLowEnergyHandle *>(packet.constData() + 1);
2463 for (qsizetype i = 0; i < handles.size(); ++i, ++packetPtr)
2464 handles[i] = bt_get_le16(packetPtr);
2465 qCDebug(QT_BT_BLUEZ) <<
"client sends read multiple request for handles" << handles;
2467 const auto it =
std::find_if(handles.constBegin(), handles.constEnd(),
2468 [
this](QLowEnergyHandle handle) {
return handle >= lastLocalHandle; });
2469 if (it != handles.constEnd()) {
2470 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), *it,
2471 QBluezConst::AttError::ATT_ERROR_INVALID_HANDLE);
2474 const QList<Attribute> results = getAttributes(handles.first(), handles.last());
2475 QByteArray response(
2477 for (
const Attribute &attr : results) {
2478 const QBluezConst::AttError error = checkReadPermissions(attr);
2479 if (error != QBluezConst::AttError::ATT_ERROR_NO_ERROR) {
2480 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), attr.handle,
2487 response += attr.value.left(mtuSize - response.size());
2490 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2491 sendPacket(response);
2494void QLowEnergyControllerPrivateBluez::handleReadByGroupTypeRequest(
const QByteArray &packet)
2498 if (!checkPacketSize(packet, 7, 21))
2500 const QLowEnergyHandle startingHandle = bt_get_le16(packet.constData() + 1);
2501 const QLowEnergyHandle endingHandle = bt_get_le16(packet.constData() + 3);
2502 const bool is16BitUuid = packet.size() == 7;
2503 const bool is128BitUuid = packet.size() == 21;
2504 const void *
const typeStart = packet.constData() + 5;
2505 QBluetoothUuid type;
2507 type = QBluetoothUuid(bt_get_le16(typeStart));
2508 }
else if (is128BitUuid) {
2509 type = QUuid::fromBytes(typeStart, QSysInfo::LittleEndian);
2511 qCWarning(QT_BT_BLUEZ) <<
"read by group type request has invalid packet size"
2513 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), 0,
2514 QBluezConst::AttError::ATT_ERROR_INVALID_PDU);
2517 qCDebug(QT_BT_BLUEZ) <<
"client sends read by group type request, start:" << startingHandle
2518 <<
"end:" << endingHandle <<
"type:" << type;
2520 if (!checkHandlePair(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2525 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2526 QBluezConst::AttError::ATT_ERROR_UNSUPPRTED_GROUP_TYPE);
2530 QList<Attribute> results =
2531 getAttributes(startingHandle, endingHandle,
2532 [type](
const Attribute &attr) {
return attr.type == type; });
2533 if (results.isEmpty()) {
2534 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), startingHandle,
2535 QBluezConst::AttError::ATT_ERROR_ATTRIBUTE_NOT_FOUND);
2540 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2541 results.first().handle, error);
2545 ensureUniformValueSizes(results);
2547 const qsizetype elementSize = 2 *
sizeof(QLowEnergyHandle) + results.first().value.size();
2548 QByteArray responsePrefix(2, Qt::Uninitialized);
2550 responsePrefix[1] = elementSize;
2551 const auto elemWriter = [](
const Attribute &attr,
char *&data) {
2552 putDataAndIncrement(attr.handle, data);
2553 putDataAndIncrement(attr.groupEndHandle, data);
2554 putDataAndIncrement(attr.value, data);
2556 sendListResponse(responsePrefix, elementSize, results, elemWriter);
2559void QLowEnergyControllerPrivateBluez::updateLocalAttributeValue(
2560 QLowEnergyHandle handle,
2561 const QByteArray &value,
2562 QLowEnergyCharacteristic &characteristic,
2563 QLowEnergyDescriptor &descriptor)
2565 localAttributes[handle].value = value;
2566 for (
const auto &service : std::as_const(localServices)) {
2567 if (handle < service->startHandle || handle > service->endHandle)
2569 for (
auto charIt = service->characteristicList.begin();
2570 charIt != service->characteristicList.end(); ++charIt) {
2571 QLowEnergyServicePrivate::CharData &charData = charIt.value();
2572 if (handle == charIt.key() + 1) {
2573 charData.value = value;
2574 characteristic = QLowEnergyCharacteristic(service, charIt.key());
2577 for (
auto descIt = charData.descriptorList.begin();
2578 descIt != charData.descriptorList.end(); ++descIt) {
2579 if (handle == descIt.key()) {
2580 descIt.value().value = value;
2581 descriptor = QLowEnergyDescriptor(service, charIt.key(), handle);
2587 qFatal(
"local services map inconsistent with local attribute map");
2593void QLowEnergyControllerPrivateBluez::writeCharacteristicForPeripheral(
2595 const QByteArray &newValue)
2597 const QLowEnergyHandle valueHandle = charData.valueHandle;
2598 Q_ASSERT(valueHandle <= lastLocalHandle);
2599 Attribute &attribute = localAttributes[valueHandle];
2601 qCWarning(QT_BT_BLUEZ) <<
"ignoring value of invalid length" << newValue.size()
2602 <<
"for attribute" << valueHandle;
2605 attribute.value = newValue;
2606 charData.value = newValue;
2607 const bool hasNotifyProperty = attribute.properties & QLowEnergyCharacteristic::Notify;
2608 const bool hasIndicateProperty
2609 = attribute.properties & QLowEnergyCharacteristic::Indicate;
2610 if (!hasNotifyProperty && !hasIndicateProperty)
2612 for (
const QLowEnergyServicePrivate::DescData &desc : std::as_const(charData.descriptorList)) {
2613 if (desc.uuid != QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)
2617 const bool isConnected = state == QLowEnergyController::ConnectedState;
2619 Q_ASSERT(desc.value.size() == 2);
2620 quint16 configValue = bt_get_le16(desc.value.constData());
2621 if (isNotificationEnabled(configValue) && hasNotifyProperty) {
2622 sendNotification(valueHandle);
2623 }
else if (isIndicationEnabled(configValue) && hasIndicateProperty) {
2624 if (indicationInFlight)
2625 scheduledIndications << valueHandle;
2627 sendIndication(valueHandle);
2632 for (
auto it = clientConfigData.begin(); it != clientConfigData.end(); ++it) {
2633 if (isConnected && it.key() == remoteDevice.toUInt64())
2635 QList<ClientConfigurationData> &configDataList = it.value();
2636 for (ClientConfigurationData &configData : configDataList) {
2637 if (configData.charValueHandle != valueHandle)
2639 if ((isNotificationEnabled(configData.configValue) && hasNotifyProperty)
2640 || (isIndicationEnabled(configData.configValue) && hasIndicateProperty)) {
2641 configData.charValueWasUpdated =
true;
2650void QLowEnergyControllerPrivateBluez::writeCharacteristicForCentral(
const QSharedPointer<QLowEnergyServicePrivate> &service,
2651 QLowEnergyHandle charHandle,
2652 QLowEnergyHandle valueHandle,
2653 const QByteArray &newValue,
2654 QLowEnergyService::WriteMode mode)
2657 putBtData(valueHandle, packet.data() + 1);
2658 memcpy(packet.data() + 3, newValue.constData(), newValue.size());
2659 bool writeWithResponse =
false;
2661 case QLowEnergyService::WriteWithResponse:
2663 sendNextPrepareWriteRequest(charHandle, newValue, 0);
2664 sendNextPendingRequest();
2669 writeWithResponse =
true;
2671 case QLowEnergyService::WriteWithoutResponse:
2674 case QLowEnergyService::WriteSigned:
2677 qCWarning(QT_BT_BLUEZ) <<
"signed write not possible: requires bond between devices";
2678 service->setError(QLowEnergyService::CharacteristicWriteError);
2682 qCWarning(QT_BT_BLUEZ) <<
"signed write not possible: not allowed on encrypted link";
2683 service->setError(QLowEnergyService::CharacteristicWriteError);
2686 const auto signingDataIt = signingData.find(remoteDevice.toUInt64());
2687 if (signingDataIt == signingData.end()) {
2688 qCWarning(QT_BT_BLUEZ) <<
"signed write not possible: no signature key found";
2689 service->setError(QLowEnergyService::CharacteristicWriteError);
2692 ++signingDataIt.value().counter;
2693 packet = LeCmacCalculator::createFullMessage(packet, signingDataIt.value().counter);
2694 const quint64 mac = LeCmacCalculator().calculateMac(packet, signingDataIt.value().key);
2695 packet.resize(packet.size() +
sizeof mac);
2696 putBtData(mac, packet.data() + packet.size() -
sizeof mac);
2697 storeSignCounter(LocalSigningKey);
2701 qCDebug(QT_BT_BLUEZ) <<
"Writing characteristic" << Qt::hex << charHandle
2702 <<
"(size:" << packet.size() <<
"with response:"
2703 << (mode == QLowEnergyService::WriteWithResponse)
2704 <<
"signed:" << (mode == QLowEnergyService::WriteSigned) <<
")";
2709 if (!writeWithResponse) {
2715 request.payload = packet;
2717 request.reference = charHandle;
2718 request.reference2 = newValue;
2719 openRequests.enqueue(request);
2721 sendNextPendingRequest();
2724void QLowEnergyControllerPrivateBluez::writeDescriptorForPeripheral(
2725 const QSharedPointer<QLowEnergyServicePrivate> &service,
2726 const QLowEnergyHandle charHandle,
2727 const QLowEnergyHandle descriptorHandle,
2728 const QByteArray &newValue)
2730 Q_ASSERT(descriptorHandle <= lastLocalHandle);
2731 Attribute &attribute = localAttributes[descriptorHandle];
2733 qCWarning(QT_BT_BLUEZ) <<
"invalid value of size" << newValue.size()
2734 <<
"for attribute" << descriptorHandle;
2737 attribute.value = newValue;
2738 service->characteristicList[charHandle].descriptorList[descriptorHandle].value = newValue;
2741void QLowEnergyControllerPrivateBluez::writeDescriptorForCentral(
2742 const QLowEnergyHandle charHandle,
2743 const QLowEnergyHandle descriptorHandle,
2744 const QByteArray &newValue)
2747 sendNextPrepareWriteRequest(descriptorHandle, newValue, 0);
2748 sendNextPendingRequest();
2754 putBtData(descriptorHandle, &packet[1]);
2757 QByteArray data(size, Qt::Uninitialized);
2761 qCDebug(QT_BT_BLUEZ) <<
"Writing descriptor" << Qt::hex << descriptorHandle
2762 <<
"(size:" << size <<
")";
2765 request.payload = data;
2767 request.reference = (charHandle | (descriptorHandle << 16));
2768 request.reference2 = newValue;
2769 openRequests.enqueue(request);
2771 sendNextPendingRequest();
2774void QLowEnergyControllerPrivateBluez::handleWriteRequestOrCommand(
const QByteArray &packet)
2782 if (!checkPacketSize(packet, isSigned ? 15 : 3, mtuSize))
2784 const QLowEnergyHandle handle = bt_get_le16(packet.constData() + 1);
2785 qCDebug(QT_BT_BLUEZ) <<
"client sends" << (isSigned ?
"signed" :
"") <<
"write"
2786 << (isRequest ?
"request" :
"command") <<
"for handle" << handle;
2788 if (!checkHandle(packet, handle))
2791 Attribute &attribute = localAttributes[handle];
2792 const QLowEnergyCharacteristic::PropertyType type = isRequest
2793 ? QLowEnergyCharacteristic::Write : isSigned
2794 ? QLowEnergyCharacteristic::WriteSigned : QLowEnergyCharacteristic::WriteNoResponse;
2797 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2805 qCWarning(QT_BT_BLUEZ) <<
"Ignoring signed write from non-bonded device.";
2809 qCWarning(QT_BT_BLUEZ) <<
"Ignoring signed write on encrypted link.";
2812 const auto signingDataIt = signingData.find(remoteDevice.toUInt64());
2813 if (signingDataIt == signingData.constEnd()) {
2814 qCWarning(QT_BT_BLUEZ) <<
"No CSRK found for peer device, ignoring signed write";
2818 const quint32 signCounter = getBtData<quint32>(packet.data() + packet.size() - 12);
2819 if (signCounter < signingDataIt.value().counter + 1) {
2820 qCWarning(QT_BT_BLUEZ) <<
"Client's' sign counter" << signCounter
2821 <<
"not greater than local sign counter"
2822 << signingDataIt.value().counter
2823 <<
"; ignoring signed write command.";
2827 const quint64 macFromClient = getBtData<quint64>(packet.data() + packet.size() - 8);
2828 const bool signatureCorrect = verifyMac(packet.left(packet.size() - 12),
2829 signingDataIt.value().key, signCounter, macFromClient);
2830 if (!signatureCorrect) {
2831 qCWarning(QT_BT_BLUEZ) <<
"Signed Write packet has wrong signature, disconnecting";
2836 signingDataIt.value().counter = signCounter;
2837 storeSignCounter(RemoteSigningKey);
2838 valueLength = packet.size() - 15;
2840 valueLength = packet.size() - 3;
2844 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2845 QBluezConst::AttError::ATT_ERROR_INVAL_ATTR_VALUE_LEN);
2851 QByteArray value = packet.mid(3, valueLength);
2853 value += attribute.value.mid(valueLength, attribute
.maxLength - valueLength);
2855 QLowEnergyCharacteristic characteristic;
2856 QLowEnergyDescriptor descriptor;
2857 updateLocalAttributeValue(handle, value, characteristic, descriptor);
2860 const QByteArray response =
2862 sendPacket(response);
2865 if (characteristic.isValid()) {
2866 emit characteristic.d_ptr->characteristicChanged(characteristic, value);
2868 Q_ASSERT(descriptor.isValid());
2869 emit descriptor.d_ptr->descriptorWritten(descriptor, value);
2873void QLowEnergyControllerPrivateBluez::handlePrepareWriteRequest(
const QByteArray &packet)
2877 if (!checkPacketSize(packet, 5, mtuSize))
2879 const quint16 handle = bt_get_le16(packet.constData() + 1);
2880 qCDebug(QT_BT_BLUEZ) <<
"client sends prepare write request for handle" << handle;
2882 if (!checkHandle(packet, handle))
2884 const Attribute &attribute = localAttributes.at(handle);
2886 checkPermissions(attribute, QLowEnergyCharacteristic::Write);
2888 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2892 if (openPrepareWriteRequests.size() >= maxPrepareQueueSize) {
2893 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)), handle,
2894 QBluezConst::AttError::ATT_ERROR_PREPARE_QUEUE_FULL);
2899 openPrepareWriteRequests << WriteRequest(handle, bt_get_le16(packet.constData() + 3),
2902 QByteArray response = packet;
2904 sendPacket(response);
2907void QLowEnergyControllerPrivateBluez::handleExecuteWriteRequest(
const QByteArray &packet)
2911 if (!checkPacketSize(packet, 2))
2913 const bool cancel = packet.at(1) == 0;
2914 qCDebug(QT_BT_BLUEZ) <<
"client sends execute write request; flag is"
2915 << (cancel ?
"cancel" :
"flush");
2917 QList<WriteRequest> requests = openPrepareWriteRequests;
2918 openPrepareWriteRequests.clear();
2919 QList<QLowEnergyCharacteristic> characteristics;
2920 QList<QLowEnergyDescriptor> descriptors;
2922 for (
const WriteRequest &request : std::as_const(requests)) {
2923 Attribute &attribute = localAttributes[request.handle];
2924 if (request.valueOffset > attribute.value.size()) {
2925 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2926 request.handle, QBluezConst::AttError::ATT_ERROR_INVALID_OFFSET);
2929 const QByteArray newValue = attribute.value.left(request.valueOffset) + request.value;
2930 if (newValue.size() > attribute.maxLength) {
2931 sendErrorResponse(
static_cast<QBluezConst::AttCommand>(packet.at(0)),
2933 QBluezConst::AttError::ATT_ERROR_INVAL_ATTR_VALUE_LEN);
2936 QLowEnergyCharacteristic characteristic;
2937 QLowEnergyDescriptor descriptor;
2940 updateLocalAttributeValue(request.handle, newValue, characteristic, descriptor);
2941 if (characteristic.isValid()) {
2942 characteristics << characteristic;
2943 }
else if (descriptor.isValid()) {
2944 Q_ASSERT(descriptor.isValid());
2945 descriptors << descriptor;
2950 sendPacket(QByteArray(
2951 1,
static_cast<quint8>(QBluezConst::AttCommand::ATT_OP_EXECUTE_WRITE_RESPONSE)));
2953 for (
const QLowEnergyCharacteristic &characteristic : std::as_const(characteristics))
2954 emit characteristic.d_ptr->characteristicChanged(characteristic, characteristic.value());
2955 for (
const QLowEnergyDescriptor &descriptor : std::as_const(descriptors))
2956 emit descriptor.d_ptr->descriptorWritten(descriptor, descriptor.value());
2969 packet[1] =
static_cast<quint8>(request);
2970 putBtData(handle, packet.data() + 2);
2971 packet[4] =
static_cast<quint8>(code);
2972 qCWarning(QT_BT_BLUEZ) <<
"sending error response; request:"
2973 << request <<
"handle:" << handle
2978void QLowEnergyControllerPrivateBluez::sendListResponse(
const QByteArray &packetStart,
2980 const QList<Attribute> &attributes,
2981 const ElemWriter &elemWriter)
2983 const qsizetype offset = packetStart.size();
2984 const qsizetype elemCount = (
std::min)(attributes.size(), (mtuSize - offset) / elemSize);
2985 const qsizetype totalPacketSize = offset + elemCount * elemSize;
2986 QByteArray response(totalPacketSize, Qt::Uninitialized);
2987 using namespace std;
2988 memcpy(response.data(), packetStart.constData(), offset);
2989 char *data = response.data() + offset;
2990 for_each(attributes.constBegin(), attributes.constBegin() + elemCount,
2991 [&data, elemWriter](
const Attribute &attr) { elemWriter(attr, data); });
2992 qCDebug(QT_BT_BLUEZ) <<
"sending response:" << response.toHex();
2993 sendPacket(response);
2996void QLowEnergyControllerPrivateBluez::sendNotification(QLowEnergyHandle handle)
2998 sendNotificationOrIndication(QBluezConst::AttCommand::ATT_OP_HANDLE_VAL_NOTIFICATION, handle);
3001void QLowEnergyControllerPrivateBluez::sendIndication(QLowEnergyHandle handle)
3003 Q_ASSERT(!indicationInFlight);
3004 indicationInFlight =
true;
3005 sendNotificationOrIndication(QBluezConst::AttCommand::ATT_OP_HANDLE_VAL_INDICATION, handle);
3009 QLowEnergyHandle handle)
3011 Q_ASSERT(handle <= lastLocalHandle);
3012 const Attribute &attribute = localAttributes.at(handle);
3013 const qsizetype maxValueLength = (
std::min)(attribute.value.size(), qsizetype(mtuSize) - 3);
3014 QByteArray packet(3 + maxValueLength, Qt::Uninitialized);
3015 packet[0] =
static_cast<quint8>(opCode);
3016 putBtData(handle, packet.data() + 1);
3017 using namespace std;
3018 memcpy(packet.data() + 3, attribute.value.constData(), maxValueLength);
3019 qCDebug(QT_BT_BLUEZ) <<
"sending notification/indication:" << packet.toHex();
3023void QLowEnergyControllerPrivateBluez::sendNextIndication()
3025 if (!scheduledIndications.isEmpty())
3026 sendIndication(scheduledIndications.takeFirst());
3031 const QString peerAddressString = peerAddress.toString();
3034 QStringLiteral(
"/"),
3035 QDBusConnection::systemBus());
3036 QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects();
3037 reply.waitForFinished();
3038 if (reply.isError())
3042 for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
3043 const InterfaceList &ifaceList = it.value();
3045 for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) {
3046 const QString &iface = jt.key();
3047 const QVariantMap &ifaceValues = jt.value();
3049 if (iface == QStringLiteral(
"org.bluez.Device1")) {
3050 if (ifaceValues.value(QStringLiteral(
"Address")).toString() == peerAddressString)
3051 return ifaceValues.value(QStringLiteral(
"Alias")).toString();
3058void QLowEnergyControllerPrivateBluez::handleConnectionRequest()
3060 if (state != QLowEnergyController::AdvertisingState) {
3061 qCWarning(QT_BT_BLUEZ) <<
"Incoming connection request in unexpected state" << state;
3064 Q_ASSERT(serverSocketNotifier);
3065 serverSocketNotifier->setEnabled(
false);
3067 socklen_t clientAddrSize =
sizeof clientAddr;
3068 const int clientSocket = accept(serverSocketNotifier->socket(),
3069 reinterpret_cast<sockaddr *>(&clientAddr), &clientAddrSize);
3070 if (clientSocket == -1) {
3072 qCWarning(QT_BT_BLUEZ) <<
"accept() failed:" << qt_error_string(errno);
3073 serverSocketNotifier->setEnabled(
true);
3077 remoteDevice = QBluetoothAddress(convertAddress(clientAddr.l2_bdaddr.b));
3078 remoteName = nameOfRemoteCentral(remoteDevice);
3079 qCDebug(QT_BT_BLUEZ) <<
"GATT connection from device" << remoteDevice << remoteName;
3081 if (connectionHandle == 0)
3082 qCWarning(QT_BT_BLUEZ) <<
"Received client connection, but no connection complete event";
3085 disconnect(l2cpSocket);
3086 if (l2cpSocket->isOpen())
3087 l2cpSocket->close();
3089 l2cpSocket->deleteLater();
3090 l2cpSocket =
nullptr;
3092 closeServerSocket();
3095 l2cpSocket =
new QBluetoothSocket(
3096 rawSocketPrivate, QBluetoothServiceInfo::L2capProtocol,
this);
3097 connect(l2cpSocket, &QBluetoothSocket::disconnected,
3098 this, &QLowEnergyControllerPrivateBluez::l2cpDisconnected);
3099 connect(l2cpSocket, &QBluetoothSocket::errorOccurred,
this,
3100 &QLowEnergyControllerPrivateBluez::l2cpErrorChanged);
3101 connect(l2cpSocket, &QIODevice::readyRead,
this, &QLowEnergyControllerPrivateBluez::l2cpReadyRead);
3102 l2cpSocket->d_ptr->lowEnergySocketType = addressType == QLowEnergyController::PublicAddress
3104 l2cpSocket->setSocketDescriptor(clientSocket, QBluetoothServiceInfo::L2capProtocol,
3105 QBluetoothSocket::SocketState::ConnectedState, QIODevice::ReadWrite | QIODevice::Unbuffered);
3106 restoreClientConfigurations();
3107 loadSigningDataIfNecessary(RemoteSigningKey);
3109 Q_Q(QLowEnergyController);
3110 setState(QLowEnergyController::ConnectedState);
3111 emit q->connected();
3114void QLowEnergyControllerPrivateBluez::closeServerSocket()
3116 if (!serverSocketNotifier)
3118 serverSocketNotifier->disconnect();
3119 close(serverSocketNotifier->socket());
3120 serverSocketNotifier->deleteLater();
3121 serverSocketNotifier =
nullptr;
3124bool QLowEnergyControllerPrivateBluez::isBonded()
const
3128 return QBluetoothLocalDevice(localAdapter).pairingStatus(remoteDevice)
3129 != QBluetoothLocalDevice::Unpaired;
3132QList<QLowEnergyControllerPrivateBluez::TempClientConfigurationData>
3133QLowEnergyControllerPrivateBluez::gatherClientConfigData()
3135 QList<TempClientConfigurationData> data;
3136 for (
const auto &service : std::as_const(localServices)) {
3137 for (
auto charIt = service->characteristicList.begin();
3138 charIt != service->characteristicList.end(); ++charIt) {
3139 QLowEnergyServicePrivate::CharData &charData = charIt.value();
3140 for (
auto descIt = charData.descriptorList.begin();
3141 descIt != charData.descriptorList.end(); ++descIt) {
3142 QLowEnergyServicePrivate::DescData &descData = descIt.value();
3143 if (descData.uuid == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration) {
3144 data << TempClientConfigurationData(&descData, charData.valueHandle,
3154void QLowEnergyControllerPrivateBluez::storeClientConfigurations()
3157 clientConfigData.remove(remoteDevice.toUInt64());
3160 QList<ClientConfigurationData> clientConfigs;
3161 const QList<TempClientConfigurationData> &tempConfigList = gatherClientConfigData();
3162 for (
const auto &tempConfigData : tempConfigList) {
3163 Q_ASSERT(tempConfigData.descData->value.size() == 2);
3164 const quint16 value = bt_get_le16(tempConfigData.descData->value.constData());
3166 clientConfigs << ClientConfigurationData(tempConfigData.charValueHandle,
3167 tempConfigData.configHandle, value);
3170 clientConfigData.insert(remoteDevice.toUInt64(), clientConfigs);
3173void QLowEnergyControllerPrivateBluez::restoreClientConfigurations()
3175 const QList<TempClientConfigurationData> &tempConfigList = gatherClientConfigData();
3176 const QList<ClientConfigurationData> &restoredClientConfigs = isBonded()
3177 ? clientConfigData.value(remoteDevice.toUInt64())
3178 : QList<ClientConfigurationData>();
3179 QList<QLowEnergyHandle> notifications;
3180 for (
const auto &tempConfigData : tempConfigList) {
3181 bool wasRestored =
false;
3182 for (
const auto &restoredData : restoredClientConfigs) {
3183 if (restoredData.charValueHandle == tempConfigData.charValueHandle) {
3184 Q_ASSERT(tempConfigData.descData->value.size() == 2);
3185 putBtData(restoredData.configValue, tempConfigData.descData->value.data());
3187 if (restoredData.charValueWasUpdated) {
3188 if (isNotificationEnabled(restoredData.configValue))
3189 notifications << restoredData.charValueHandle;
3190 else if (isIndicationEnabled(restoredData.configValue))
3191 scheduledIndications << restoredData.charValueHandle;
3197 tempConfigData.descData->value = QByteArray(2, 0);
3198 Q_ASSERT(lastLocalHandle >= tempConfigData.configHandle);
3199 Q_ASSERT(tempConfigData.configHandle > tempConfigData.charValueHandle);
3200 localAttributes[tempConfigData.configHandle].value = tempConfigData.descData->value;
3203 for (
const QLowEnergyHandle handle : std::as_const(notifications))
3204 sendNotification(handle);
3205 sendNextIndication();
3208void QLowEnergyControllerPrivateBluez::loadSigningDataIfNecessary(SigningKeyType keyType)
3210 const auto signingDataIt = signingData.constFind(remoteDevice.toUInt64());
3211 if (signingDataIt != signingData.constEnd())
3213 const QString settingsFilePath = keySettingsFilePath();
3214 if (!QFileInfo(settingsFilePath).exists()) {
3215 qCDebug(QT_BT_BLUEZ) <<
"No settings found for peer device.";
3218 QSettings settings(settingsFilePath, QSettings::IniFormat);
3219 const QString group = signingKeySettingsGroup(keyType);
3220 settings.beginGroup(group);
3221 const QByteArray keyString = settings.value(QLatin1String(
"Key")).toByteArray();
3222 if (keyString.isEmpty()) {
3223 qCDebug(QT_BT_BLUEZ) <<
"Group" << group <<
"not found in settings file";
3226 const QByteArray keyData = QByteArray::fromHex(keyString);
3227 if (keyData.size() != qsizetype(
sizeof(
BluezUint128))) {
3228 qCWarning(QT_BT_BLUEZ) <<
"Signing key in settings file has invalid size"
3229 << keyString.size();
3232 qCDebug(QT_BT_BLUEZ) <<
"CSRK of peer device is" << keyString;
3233 const quint32 counter = settings.value(QLatin1String(
"Counter"), 0).toUInt();
3234 using namespace std;
3236 memcpy(csrk.data, keyData.constData(), keyData.size());
3237 signingData.insert(remoteDevice.toUInt64(), SigningData(csrk, counter - 1));
3240void QLowEnergyControllerPrivateBluez::storeSignCounter(SigningKeyType keyType)
const
3242 const auto signingDataIt = signingData.constFind(remoteDevice.toUInt64());
3243 if (signingDataIt == signingData.constEnd())
3245 const QString settingsFilePath = keySettingsFilePath();
3246 if (!QFileInfo(settingsFilePath).exists())
3248 QSettings settings(settingsFilePath, QSettings::IniFormat);
3249 if (!settings.isWritable())
3251 settings.beginGroup(signingKeySettingsGroup(keyType));
3252 const QString counterKey = QLatin1String(
"Counter");
3253 if (!settings.allKeys().contains(counterKey))
3255 const quint32 counterValue = signingDataIt.value().counter + 1;
3256 if (counterValue == settings.value(counterKey).toUInt())
3258 settings.setValue(counterKey, counterValue);
3261QString QLowEnergyControllerPrivateBluez::signingKeySettingsGroup(SigningKeyType keyType)
const
3263 return QLatin1String(keyType == LocalSigningKey ?
"LocalSignatureKey" :
"RemoteSignatureKey");
3266QString QLowEnergyControllerPrivateBluez::keySettingsFilePath()
const
3268 return QString::fromLatin1(
"/var/lib/bluetooth/%1/%2/info")
3269 .arg(localAdapter.toString(), remoteDevice.toString());
3274 QByteArray ba(
sizeof(uuid), Qt::Uninitialized);
3275 char *ptr = ba.data();
3276 putDataAndIncrement(uuid, ptr);
3277 ba.resize(ptr - ba.constData());
3282 QLowEnergyHandle startHandle)
3288 localAttributes.resize(lastLocalHandle + 1);
3290 serviceAttribute.handle = startHandle;
3291 serviceAttribute.type = QBluetoothUuid(
static_cast<quint16>(service.type()));
3292 serviceAttribute.properties = QLowEnergyCharacteristic::Read;
3293 serviceAttribute.value = uuidToByteArray(service.uuid());
3294 QLowEnergyHandle currentHandle = startHandle;
3295 const QList<QLowEnergyService *> includedServices = service.includedServices();
3296 for (
const QLowEnergyService *
const service : includedServices) {
3297 Attribute attribute;
3298 attribute.handle = ++currentHandle;
3300 attribute.properties = QLowEnergyCharacteristic::Read;
3301 const bool includeUuidInValue = service->serviceUuid().minimumSize() == 2;
3302 attribute.value.resize((2 + includeUuidInValue) *
sizeof(QLowEnergyHandle));
3303 char *valueData = attribute.value.data();
3304 putDataAndIncrement(service->d_ptr->startHandle, valueData);
3305 putDataAndIncrement(service->d_ptr->endHandle, valueData);
3306 if (includeUuidInValue)
3307 putDataAndIncrement(service->serviceUuid(), valueData);
3308 localAttributes[attribute.handle] = attribute;
3310 const QList<QLowEnergyCharacteristicData> characteristics = service.characteristics();
3311 for (
const QLowEnergyCharacteristicData &cd : characteristics) {
3312 Attribute attribute;
3315 attribute.handle = ++currentHandle;
3316 attribute.groupEndHandle = attribute.handle + 1 + cd.descriptors().size();
3318 attribute.properties = QLowEnergyCharacteristic::Read;
3319 attribute.value.resize(1 +
sizeof(QLowEnergyHandle) + cd.uuid().minimumSize());
3320 char *valueData = attribute.value.data();
3321 putDataAndIncrement(
static_cast<quint8>(cd.properties()), valueData);
3322 putDataAndIncrement(QLowEnergyHandle(currentHandle + 1), valueData);
3323 putDataAndIncrement(cd.uuid(), valueData);
3324 localAttributes[attribute.handle] = attribute;
3327 attribute.handle = ++currentHandle;
3328 attribute.groupEndHandle = attribute.handle;
3329 attribute.type = cd.uuid();
3330 attribute.properties = cd.properties();
3331 attribute.readConstraints = cd.readConstraints();
3332 attribute.writeConstraints = cd.writeConstraints();
3333 attribute.value = cd.value();
3334 attribute.minLength = cd.minimumValueLength();
3335 attribute.maxLength = cd.maximumValueLength();
3336 localAttributes[attribute.handle] = attribute;
3338 const QList<QLowEnergyDescriptorData> descriptors = cd.descriptors();
3339 for (
const QLowEnergyDescriptorData &dd : descriptors) {
3340 attribute.handle = ++currentHandle;
3341 attribute.groupEndHandle = attribute.handle;
3342 attribute.type = dd.uuid();
3343 attribute.properties = QLowEnergyCharacteristic::PropertyTypes();
3344 attribute.readConstraints = AttAccessConstraints();
3345 attribute.writeConstraints = AttAccessConstraints();
3346 attribute.minLength = 0;
3347 attribute.maxLength = INT_MAX;
3350 if (attribute.type == QBluetoothUuid::DescriptorType::CharacteristicExtendedProperties) {
3351 attribute.properties = QLowEnergyCharacteristic::Read;
3352 attribute.minLength = attribute.maxLength = 2;
3353 }
else if (attribute.type == QBluetoothUuid::DescriptorType::CharacteristicPresentationFormat) {
3354 attribute.properties = QLowEnergyCharacteristic::Read;
3355 attribute.minLength = attribute.maxLength = 7;
3356 }
else if (attribute.type == QBluetoothUuid::DescriptorType::CharacteristicAggregateFormat) {
3357 attribute.properties = QLowEnergyCharacteristic::Read;
3358 attribute.minLength = 4;
3359 }
else if (attribute.type == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration
3360 || attribute.type == QBluetoothUuid::DescriptorType::ServerCharacteristicConfiguration) {
3361 attribute.properties = QLowEnergyCharacteristic::Read
3362 | QLowEnergyCharacteristic::Write
3363 | QLowEnergyCharacteristic::WriteNoResponse
3364 | QLowEnergyCharacteristic::WriteSigned;
3365 attribute.writeConstraints = dd.writeConstraints();
3366 attribute.minLength = attribute.maxLength = 2;
3368 if (dd.isReadable())
3369 attribute.properties |= QLowEnergyCharacteristic::Read;
3370 if (dd.isWritable()) {
3371 attribute.properties |= QLowEnergyCharacteristic::Write;
3372 attribute.properties |= QLowEnergyCharacteristic::WriteNoResponse;
3373 attribute.properties |= QLowEnergyCharacteristic::WriteSigned;
3375 attribute.readConstraints = dd.readConstraints();
3376 attribute.writeConstraints = dd.writeConstraints();
3379 attribute.value = dd.value();
3380 if (attribute.value.size() < attribute.minLength
3381 || attribute.value.size() > attribute.maxLength) {
3382 qCWarning(QT_BT_BLUEZ) <<
"attribute of type" << attribute.type
3383 <<
"has invalid length of" << attribute.value.size()
3385 attribute.value = QByteArray(attribute.minLength, 0);
3387 localAttributes[attribute.handle] = attribute;
3390 serviceAttribute.groupEndHandle = currentHandle;
3391 localAttributes[serviceAttribute.handle] = serviceAttribute;
3394int QLowEnergyControllerPrivateBluez::
mtu()
const
3399void QLowEnergyControllerPrivateBluez::ensureUniformAttributes(
3400 QList<Attribute> &attributes,
const std::function<
int(
const Attribute &)> &getSize)
3402 if (attributes.isEmpty())
3404 const int firstSize = getSize(attributes.first());
3405 const auto it =
std::find_if(attributes.begin() + 1, attributes.end(),
3406 [firstSize, getSize](
const Attribute &attr) {
return getSize(attr) != firstSize; });
3407 if (it != attributes.end())
3408 attributes.erase(it, attributes.end());
3412void QLowEnergyControllerPrivateBluez::ensureUniformUuidSizes(QList<Attribute> &attributes)
3414 ensureUniformAttributes(attributes,
3415 [](
const Attribute &attr) {
return getUuidSize(attr.type); });
3418void QLowEnergyControllerPrivateBluez::ensureUniformValueSizes(QList<Attribute> &attributes)
3420 ensureUniformAttributes(attributes,
3421 [](
const Attribute &attr) {
return attr.value.size(); });
3424QList<QLowEnergyControllerPrivateBluez::Attribute>
3425QLowEnergyControllerPrivateBluez::getAttributes(QLowEnergyHandle startHandle,
3426 QLowEnergyHandle endHandle,
3427 const AttributePredicate &attributePredicate)
3429 QList<Attribute> results;
3430 if (startHandle > lastLocalHandle)
3432 if (lastLocalHandle == 0)
3434 Q_ASSERT(startHandle <= endHandle);
3435 const QLowEnergyHandle firstHandle = qMin(startHandle, lastLocalHandle);
3436 const QLowEnergyHandle lastHandle = qMin(endHandle, lastLocalHandle);
3437 for (QLowEnergyHandle i = firstHandle; i <= lastHandle; ++i) {
3438 const Attribute &attr = localAttributes.at(i);
3439 if (attributePredicate(attr))
3446QLowEnergyControllerPrivateBluez::checkPermissions(
const Attribute &attr,
3447 QLowEnergyCharacteristic::PropertyType type)
3449 const bool isReadAccess = type == QLowEnergyCharacteristic::Read;
3450 const bool isWriteCommand = type == QLowEnergyCharacteristic::WriteNoResponse;
3451 const bool isWriteAccess = type == QLowEnergyCharacteristic::Write
3452 || type == QLowEnergyCharacteristic::WriteSigned
3454 Q_ASSERT(isReadAccess || isWriteAccess);
3455 if (!(attr.properties & type)) {
3461 const bool unsignedWriteOk = isWriteCommand
3462 && (attr.properties & QLowEnergyCharacteristic::WriteSigned)
3464 if (!unsignedWriteOk)
3467 const AttAccessConstraints constraints = isReadAccess
3468 ? attr.readConstraints : attr.writeConstraints;
3469 if (constraints.testFlag(AttAccessConstraint::AttAuthorizationRequired))
3472 if (constraints.testFlag(AttAccessConstraint::AttEncryptionRequired)
3475 if (constraints.testFlag(AttAccessConstraint::AttAuthenticationRequired)
3485 return checkPermissions(attr, QLowEnergyCharacteristic::Read);
3489QLowEnergyControllerPrivateBluez::checkReadPermissions(QList<Attribute> &attributes)
3491 if (attributes.isEmpty())
3503 std::find_if(attributes.begin() + 1, attributes.end(), [
this](
const Attribute &attr) {
3506 if (it != attributes.end())
3507 attributes.erase(it, attributes.end());
3511bool QLowEnergyControllerPrivateBluez::verifyMac(
const QByteArray &message,
BluezUint128 csrk,
3512 quint32 signCounter, quint64 expectedMac)
3514 if (!cmacCalculator)
3515 cmacCalculator =
new LeCmacCalculator;
3516 return cmacCalculator->verify(LeCmacCalculator::createFullMessage(message, signCounter), csrk,
3522#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