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 65 QT_BEGIN_NAMESPACE 66 67 class QLowEnergyServiceData; 68 class QTimer; 69 70 class HciManager; 71 class LeCmacCalculator; 72 class QSocketNotifier; 73 class RemoteDeviceManager; 74 75 extern void registerQLowEnergyControllerMetaType(); 76 77 class QLeAdvertiser; 78 79 class QLowEnergyControllerPrivateBluez final: public QLowEnergyControllerPrivate 80 { 81 Q_OBJECT 82 public: 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 ¶ms, 95 const QLowEnergyAdvertisingData &advertisingData, 96 const QLowEnergyAdvertisingData &scanResponseData) override; 97 void stopAdvertising() override; 98 99 void requestConnectionUpdate(const QLowEnergyConnectionParameters ¶ms) 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 { AttributeAttribute121 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 135 private: 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 { WriteRequestWriteRequest149 WriteRequest() {} WriteRequestWriteRequest150 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) descDataTempClientConfigurationData165 : 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) charValueHandleClientConfigurationData175 : 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)) keySigningData187 : 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 343 private 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 353 Q_DECLARE_TYPEINFO(QLowEnergyControllerPrivateBluez::Attribute, Q_MOVABLE_TYPE); 354 355 QT_END_NAMESPACE 356 357 #endif //QLOWENERGYCONTROLLERBLUEZ_P_H 358