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