1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QLOWENERGYCONTROLLERBLUEZ_P_H
5#define QLOWENERGYCONTROLLERBLUEZ_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <qglobal.h>
19#include <QtCore/QList>
20#include <QtCore/QQueue>
21#include <QtBluetooth/qbluetooth.h>
22#include <QtBluetooth/qlowenergycharacteristic.h>
23#include "qlowenergycontroller.h"
24#include "qlowenergycontrollerbase_p.h"
25#include "bluez/bluez_data_p.h"
26
27#include <QtBluetooth/QBluetoothSocket>
28#include <functional>
29
30QT_BEGIN_NAMESPACE
31
32class QLowEnergyServiceData;
33class QTimer;
34
35class HciManager;
36class LeCmacCalculator;
37class QSocketNotifier;
38class RemoteDeviceManager;
39
40extern void registerQLowEnergyControllerMetaType();
41
42class QLeAdvertiser;
43
44class QLowEnergyControllerPrivateBluez final: public QLowEnergyControllerPrivate
45{
46 Q_OBJECT
47public:
48 QLowEnergyControllerPrivateBluez();
49 ~QLowEnergyControllerPrivateBluez() override;
50
51 void init() override;
52
53 void connectToDevice() override;
54 void disconnectFromDevice() override;
55
56 void discoverServices() override;
57 void discoverServiceDetails(const QBluetoothUuid &service,
58 QLowEnergyService::DiscoveryMode mode) override;
59
60 void startAdvertising(const QLowEnergyAdvertisingParameters &params,
61 const QLowEnergyAdvertisingData &advertisingData,
62 const QLowEnergyAdvertisingData &scanResponseData) override;
63 void stopAdvertising() override;
64
65 void requestConnectionUpdate(const QLowEnergyConnectionParameters &params) override;
66
67 // read data
68 void readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
69 const QLowEnergyHandle charHandle) override;
70 void readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
71 const QLowEnergyHandle charHandle,
72 const QLowEnergyHandle descriptorHandle) override;
73
74 // write data
75 void writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
76 const QLowEnergyHandle charHandle,
77 const QByteArray &newValue, QLowEnergyService::WriteMode mode) override;
78 void writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
79 const QLowEnergyHandle charHandle,
80 const QLowEnergyHandle descriptorHandle,
81 const QByteArray &newValue) override;
82
83 void addToGenericAttributeList(const QLowEnergyServiceData &service,
84 QLowEnergyHandle startHandle) override;
85
86 int mtu() const override;
87
88 struct Attribute {
89 Attribute() : handle(0) {}
90
91 QLowEnergyHandle handle;
92 QLowEnergyHandle groupEndHandle;
93 QLowEnergyCharacteristic::PropertyTypes properties;
94 QBluetooth::AttAccessConstraints readConstraints;
95 QBluetooth::AttAccessConstraints writeConstraints;
96 QBluetoothUuid type;
97 QByteArray value;
98 int minLength;
99 int maxLength;
100 };
101 QList<Attribute> localAttributes;
102
103private:
104 quint16 connectionHandle = 0;
105 QBluetoothSocket *l2cpSocket = nullptr;
106 struct Request {
107 QBluezConst::AttCommand command;
108 QByteArray payload;
109 // TODO reference below is ugly but until we know all commands and their
110 // requirements this is WIP
111 QVariant reference;
112 QVariant reference2;
113 };
114 QQueue<Request> openRequests;
115
116 struct WriteRequest {
117 WriteRequest() {}
118 WriteRequest(quint16 h, quint16 o, const QByteArray &v)
119 : handle(h), valueOffset(o), value(v) {}
120 quint16 handle;
121 quint16 valueOffset;
122 QByteArray value;
123 };
124 QList<WriteRequest> openPrepareWriteRequests;
125
126 // Invariant: !scheduledIndications.isEmpty => indicationInFlight == true
127 QList<QLowEnergyHandle> scheduledIndications;
128 bool indicationInFlight = false;
129
130 struct TempClientConfigurationData {
131 TempClientConfigurationData(QLowEnergyServicePrivate::DescData *dd = nullptr,
132 QLowEnergyHandle chHndl = 0, QLowEnergyHandle coHndl = 0)
133 : descData(dd), charValueHandle(chHndl), configHandle(coHndl) {}
134
135 QLowEnergyServicePrivate::DescData *descData;
136 QLowEnergyHandle charValueHandle;
137 QLowEnergyHandle configHandle;
138 };
139
140 struct ClientConfigurationData {
141 ClientConfigurationData(QLowEnergyHandle chHndl = 0, QLowEnergyHandle coHndl = 0,
142 quint16 val = 0)
143 : charValueHandle(chHndl), configHandle(coHndl), configValue(val) {}
144
145 QLowEnergyHandle charValueHandle;
146 QLowEnergyHandle configHandle;
147 quint16 configValue;
148 bool charValueWasUpdated = false;
149 };
150 QHash<quint64, QList<ClientConfigurationData>> clientConfigData;
151
152 struct SigningData {
153 SigningData() = default;
154 SigningData(BluezUint128 csrk, quint32 signCounter = quint32(-1))
155 : key(csrk), counter(signCounter) {}
156
157 BluezUint128 key;
158 quint32 counter = quint32(-1);
159 };
160 QHash<quint64, SigningData> signingData;
161 LeCmacCalculator *cmacCalculator = nullptr;
162
163 bool requestPending;
164 quint16 mtuSize;
165 int securityLevelValue;
166 bool encryptionChangePending;
167 bool receivedMtuExchangeRequest = false;
168
169 std::shared_ptr<HciManager> hciManager;
170 QLeAdvertiser *advertiser = nullptr;
171 QSocketNotifier *serverSocketNotifier = nullptr;
172 QTimer *requestTimer = nullptr;
173 RemoteDeviceManager* device1Manager = nullptr;
174
175 /*
176 Defines the maximum number of milliseconds the implementation will
177 wait for requests that require a response.
178
179 This addresses the problem that some non-conformant BTLE devices
180 do not implement the request/response system properly. In such cases
181 the queue system would hang forever.
182
183 Once timeout has been triggered we gracefully continue with the next request.
184 Depending on the type of the timed out ATT command we either ignore it
185 or artifically trigger an error response to ensure the API gives the
186 appropriate response. Potentially this can cause problems when the
187 response for the dropped requests arrives very late. That's why a big warning
188 is printed about the compromised state when a timeout is triggered.
189 */
190 int gattRequestTimeout = 20000;
191
192 void handleConnectionRequest();
193 void closeServerSocket();
194
195 bool isBonded() const;
196 QList<TempClientConfigurationData> gatherClientConfigData();
197 void storeClientConfigurations();
198 void restoreClientConfigurations();
199
200 enum SigningKeyType { LocalSigningKey, RemoteSigningKey };
201 void loadSigningDataIfNecessary(SigningKeyType keyType);
202 void storeSignCounter(SigningKeyType keyType) const;
203 QString signingKeySettingsGroup(SigningKeyType keyType) const;
204 QString keySettingsFilePath() const;
205
206 void sendPacket(const QByteArray &packet);
207 void sendNextPendingRequest();
208 void processReply(const Request &request, const QByteArray &reply);
209
210 void sendReadByGroupRequest(QLowEnergyHandle start, QLowEnergyHandle end,
211 quint16 type);
212 void sendReadByTypeRequest(QSharedPointer<QLowEnergyServicePrivate> serviceData,
213 QLowEnergyHandle nextHandle, quint16 attributeType);
214 void sendReadValueRequest(QLowEnergyHandle attributeHandle, bool isDescriptor);
215 void readServiceValues(const QBluetoothUuid &service,
216 bool readCharacteristics);
217 void readServiceValuesByOffset(uint handleData, quint16 offset,
218 bool isLastValue);
219
220 void discoverServiceDescriptors(const QBluetoothUuid &serviceUuid);
221 void discoverNextDescriptor(QSharedPointer<QLowEnergyServicePrivate> serviceData,
222 const QList<QLowEnergyHandle> pendingCharHandles,
223 QLowEnergyHandle startingHandle);
224 void processUnsolicitedReply(const QByteArray &msg);
225 void exchangeMTU();
226 bool setSecurityLevel(int level);
227 int securityLevel() const;
228 void sendExecuteWriteRequest(const QLowEnergyHandle attrHandle,
229 const QByteArray &newValue,
230 bool isCancelation);
231 void sendNextPrepareWriteRequest(const QLowEnergyHandle handle,
232 const QByteArray &newValue, quint16 offset);
233 bool increaseEncryptLevelfRequired(QBluezConst::AttError errorCode);
234
235 void resetController();
236
237 void handleAdvertisingError();
238
239 bool checkPacketSize(const QByteArray &packet, int minSize, int maxSize = -1);
240 bool checkHandle(const QByteArray &packet, QLowEnergyHandle handle);
241 bool checkHandlePair(QBluezConst::AttCommand request, QLowEnergyHandle startingHandle,
242 QLowEnergyHandle endingHandle);
243
244 void handleExchangeMtuRequest(const QByteArray &packet);
245 void handleFindInformationRequest(const QByteArray &packet);
246 void handleFindByTypeValueRequest(const QByteArray &packet);
247 void handleReadByTypeRequest(const QByteArray &packet);
248 void handleReadRequest(const QByteArray &packet);
249 void handleReadBlobRequest(const QByteArray &packet);
250 void handleReadMultipleRequest(const QByteArray &packet);
251 void handleReadByGroupTypeRequest(const QByteArray &packet);
252 void handleWriteRequestOrCommand(const QByteArray &packet);
253 void handlePrepareWriteRequest(const QByteArray &packet);
254 void handleExecuteWriteRequest(const QByteArray &packet);
255
256 void sendErrorResponse(QBluezConst::AttCommand request, quint16 handle,
257 QBluezConst::AttError code);
258
259 using ElemWriter = std::function<void(const Attribute &, char *&)>;
260 void sendListResponse(const QByteArray &packetStart, qsizetype elemSize,
261 const QList<Attribute> &attributes, const ElemWriter &elemWriter);
262
263 void sendNotification(QLowEnergyHandle handle);
264 void sendIndication(QLowEnergyHandle handle);
265 void sendNotificationOrIndication(QBluezConst::AttCommand opCode, QLowEnergyHandle handle);
266 void sendNextIndication();
267
268 void ensureUniformAttributes(QList<Attribute> &attributes,
269 const std::function<int(const Attribute &)> &getSize);
270 void ensureUniformUuidSizes(QList<Attribute> &attributes);
271 void ensureUniformValueSizes(QList<Attribute> &attributes);
272
273 using AttributePredicate = std::function<bool(const Attribute &)>;
274 QList<Attribute> getAttributes(
275 QLowEnergyHandle startHandle, QLowEnergyHandle endHandle,
276 const AttributePredicate &attributePredicate = [](const Attribute &) { return true; });
277
278 QBluezConst::AttError checkPermissions(const Attribute &attr,
279 QLowEnergyCharacteristic::PropertyType type);
280 QBluezConst::AttError checkReadPermissions(const Attribute &attr);
281 QBluezConst::AttError checkReadPermissions(QList<Attribute> &attributes);
282
283 bool verifyMac(const QByteArray &message, BluezUint128 csrk, quint32 signCounter,
284 quint64 expectedMac);
285
286 void updateLocalAttributeValue(
287 QLowEnergyHandle handle,
288 const QByteArray &value,
289 QLowEnergyCharacteristic &characteristic,
290 QLowEnergyDescriptor &descriptor);
291
292 void writeCharacteristicForPeripheral(
293 QLowEnergyServicePrivate::CharData &charData,
294 const QByteArray &newValue);
295 void writeCharacteristicForCentral(const QSharedPointer<QLowEnergyServicePrivate> &service,
296 QLowEnergyHandle charHandle,
297 QLowEnergyHandle valueHandle,
298 const QByteArray &newValue,
299 QLowEnergyService::WriteMode mode);
300
301 void writeDescriptorForPeripheral(
302 const QSharedPointer<QLowEnergyServicePrivate> &service,
303 const QLowEnergyHandle charHandle,
304 const QLowEnergyHandle descriptorHandle,
305 const QByteArray &newValue);
306 void writeDescriptorForCentral(
307 const QLowEnergyHandle charHandle,
308 const QLowEnergyHandle descriptorHandle,
309 const QByteArray &newValue);
310
311 void restartRequestTimer();
312 void establishL2cpClientSocket();
313 void createServicesForCentralIfRequired();
314
315private slots:
316 void l2cpConnected();
317 void l2cpDisconnected();
318 void l2cpErrorChanged(QBluetoothSocket::SocketError);
319 void l2cpReadyRead();
320 void encryptionChangedEvent(const QBluetoothAddress&, bool);
321 void handleGattRequestTimeout();
322 void activeConnectionTerminationDone();
323};
324
325Q_DECLARE_TYPEINFO(QLowEnergyControllerPrivateBluez::Attribute, Q_RELOCATABLE_TYPE);
326
327QT_END_NAMESPACE
328
329#endif //QLOWENERGYCONTROLLERBLUEZ_P_H
330

source code of qtconnectivity/src/bluetooth/qlowenergycontroller_bluez_p.h