Merge the ServiceNotValidError and OperationError of QLEService
[qt:qtconnectivity.git] / src / bluetooth / gatoattclient.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Javier de San Pedro <dev.git@javispedro.com>
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtBluetooth module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL21$
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** In addition, as a special exception, Digia gives you certain additional
27 ** rights. These rights are described in the Digia Qt LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** $QT_END_LICENSE$
31 **
32 ****************************************************************************/
33
34 #include <QtCore/QDataStream>
35 #include <QtCore/QDebug>
36
37 #include "gatoattclient.h"
38 #include "helpers.h"
39
40 #define PROTOCOL_DEBUG 0
41
42 #define ATT_CID 4
43 #define ATT_PSM 31
44
45 #define ATT_DEFAULT_LE_MTU 23
46 #define ATT_MAX_LE_MTU 0x200
47
48 QT_BEGIN_NAMESPACE
49
50 enum AttOpcode {
51     AttOpNone = 0,
52     AttOpErrorResponse = 0x1,
53     AttOpExchangeMTURequest = 0x2,
54     AttOpExchangeMTUResponse = 0x3,
55     AttOpFindInformationRequest = 0x4,
56     AttOpFindInformationResponse = 0x5,
57     AttOpFindByTypeValueRequest = 0x6,
58     AttOpFindByTypeValueResponse = 0x7,
59     AttOpReadByTypeRequest = 0x8,
60     AttOpReadByTypeResponse = 0x9,
61     AttOpReadRequest = 0xA,
62     AttOpReadResponse = 0xB,
63     AttOpReadBlobRequest = 0xC,
64     AttOpReadBlobResponse = 0xD,
65     AttOpReadMultipleRequest = 0xE,
66     AttOpReadMultipleResponse = 0xF,
67     AttOpReadByGroupTypeRequest = 0x10,
68     AttOpReadByGroupTypeResponse = 0x11,
69     AttOpWriteRequest = 0x12,
70     AttOpWriteResponse = 0x13,
71     AttOpWriteCommand = 0x52,
72     AttOpPrepareWriteRequest = 0x16,
73     AttOpPrepareWriteResponse = 0x17,
74     AttOpExecuteWriteRequest = 0x18,
75     AttOpExecuteWriteResponse = 0x19,
76     AttOpHandleValueNotification = 0x1B,
77     AttOpHandleValueIndication = 0x1D,
78     AttOpHandleValueConfirmation = 0x1E,
79     AttOpSignedWriteCommand = 0xD2
80 };
81
82 static QByteArray remove_method_signature(const char *sig)
83 {
84     const char* bracketPosition = strchr(sig, '(');
85     if (!bracketPosition || !(sig[0] >= '0' && sig[0] <= '3')) {
86         qWarning("Invalid slot specification");
87         return QByteArray();
88     }
89     return QByteArray(sig + 1, bracketPosition - 1 - sig);
90 }
91
92 GatoAttClient::GatoAttClient(QObject *parent) :
93     QObject(parent), socket(new GatoSocket(this)), cur_mtu(ATT_DEFAULT_LE_MTU), next_id(1)
94 {
95     connect(socket, SIGNAL(connected()), SLOT(handleSocketConnected()));
96     connect(socket, SIGNAL(disconnected()), SLOT(handleSocketDisconnected()));
97     connect(socket, SIGNAL(readyRead()), SLOT(handleSocketReadyRead()));
98 }
99
100 GatoAttClient::~GatoAttClient()
101 {
102 }
103
104 GatoSocket::State GatoAttClient::state() const
105 {
106     return socket->state();
107 }
108
109 bool GatoAttClient::connectTo(const GatoAddress &addr)
110 {
111     return socket->connectTo(addr, ATT_CID);
112 }
113
114 void GatoAttClient::close()
115 {
116     socket->close();
117 }
118
119 int GatoAttClient::mtu() const
120 {
121     return cur_mtu;
122 }
123
124 uint GatoAttClient::request(int opcode, const QByteArray &data, QObject *receiver, const char *member)
125 {
126     Request req;
127     req.id = next_id++;
128     req.opcode = opcode;
129     req.pkt = data;
130     req.pkt.prepend(static_cast<char>(opcode));
131     req.receiver = receiver;
132     req.member = remove_method_signature(member);
133
134     pending_requests.enqueue(req);
135
136     if (pending_requests.size() == 1) {
137         // So we can just send this request instead of waiting for others to complete
138         sendARequest();
139     }
140
141     return req.id;
142 }
143
144 void GatoAttClient::cancelRequest(uint id)
145 {
146     QQueue<Request>::iterator it = pending_requests.begin();
147     while (it != pending_requests.end()) {
148         if (it->id == id) {
149             it = pending_requests.erase(it);
150         } else {
151             ++it;
152         }
153     }
154 }
155
156 uint GatoAttClient::requestExchangeMTU(quint16 client_mtu, QObject *receiver, const char *member)
157 {
158     QByteArray data;
159     QDataStream s(&data, QIODevice::WriteOnly);
160     s.setByteOrder(QDataStream::LittleEndian);
161     s << client_mtu;
162
163     return request(AttOpExchangeMTURequest, data, receiver, member);
164 }
165
166 uint GatoAttClient::requestFindInformation(GatoHandle start, GatoHandle end, QObject *receiver, const char *member)
167 {
168     QByteArray data;
169     QDataStream s(&data, QIODevice::WriteOnly);
170     s.setByteOrder(QDataStream::LittleEndian);
171     s << start << end;
172
173     return request(AttOpFindInformationRequest, data, receiver, member);
174 }
175
176 uint GatoAttClient::requestFindByTypeValue(GatoHandle start, GatoHandle end, const GatoUUID &uuid, const QByteArray &value, QObject *receiver, const char *member)
177 {
178     QByteArray data;
179     QDataStream s(&data, QIODevice::WriteOnly);
180     s.setByteOrder(QDataStream::LittleEndian);
181     s << start << end;
182
183     bool uuid16_ok;
184     quint16 uuid16 = uuid.toUInt16(&uuid16_ok);
185     if (uuid16_ok) {
186         s << uuid16;
187     } else {
188         qWarning() << "FindByTypeValue does not support UUIDs other than UUID16";
189         return -1;
190     }
191
192     s << value;
193
194     return request(AttOpFindByTypeValueRequest, data, receiver, member);
195 }
196
197 uint GatoAttClient::requestReadByType(GatoHandle start, GatoHandle end, const GatoUUID &uuid, QObject *receiver, const char *member)
198 {
199     QByteArray data;
200     QDataStream s(&data, QIODevice::WriteOnly);
201     s.setByteOrder(QDataStream::LittleEndian);
202     s << start << end;
203     write_gatouuid(s, uuid, true, false);
204
205     return request(AttOpReadByTypeRequest, data, receiver, member);
206 }
207
208 uint GatoAttClient::requestRead(GatoHandle handle, QObject *receiver, const char *member)
209 {
210     QByteArray data;
211     QDataStream s(&data, QIODevice::WriteOnly);
212     s.setByteOrder(QDataStream::LittleEndian);
213     s << handle;
214
215     return request(AttOpReadRequest, data, receiver, member);
216 }
217
218 uint GatoAttClient::requestReadByGroupType(GatoHandle start, GatoHandle end, const GatoUUID &uuid, QObject *receiver, const char *member)
219 {
220     QByteArray data;
221     QDataStream s(&data, QIODevice::WriteOnly);
222     s.setByteOrder(QDataStream::LittleEndian);
223     s << start << end;
224     write_gatouuid(s, uuid, true, false);
225
226     return request(AttOpReadByGroupTypeRequest, data, receiver, member);
227 }
228
229 uint GatoAttClient::requestWrite(GatoHandle handle, const QByteArray &value, QObject *receiver, const char *member)
230 {
231     QByteArray data;
232     QDataStream s(&data, QIODevice::WriteOnly);
233     s.setByteOrder(QDataStream::LittleEndian);
234     s << handle;
235     s.writeRawData(value.constData(), value.length());
236
237     return request(AttOpWriteRequest, data, receiver, member);
238 }
239
240 void GatoAttClient::command(int opcode, const QByteArray &data)
241 {
242     QByteArray packet = data;
243     packet.prepend(static_cast<char>(opcode));
244
245     socket->send(packet);
246
247 #if PROTOCOL_DEBUG
248     qDebug() << "Wrote" << packet.size() << "bytes (command)" << packet.toHex();
249 #endif
250 }
251
252 void GatoAttClient::commandWrite(GatoHandle handle, const QByteArray &value)
253 {
254     QByteArray data;
255     QDataStream s(&data, QIODevice::WriteOnly);
256     s.setByteOrder(QDataStream::LittleEndian);
257     s << handle;
258     s.writeRawData(value.constData(), value.length());
259
260     command(AttOpWriteCommand, data);
261 }
262
263 void GatoAttClient::sendARequest()
264 {
265     if (pending_requests.isEmpty()) {
266         return;
267     }
268
269     Request &req = pending_requests.head();
270     socket->send(req.pkt);
271
272 #if PROTOCOL_DEBUG
273     qDebug() << "Wrote" << req.pkt.size() << "bytes (request)" << req.pkt.toHex();
274 #endif
275 }
276
277 bool GatoAttClient::handleEvent(const QByteArray &event)
278 {
279     const char *data = event.constData();
280     quint8 opcode = event[0];
281     GatoHandle handle;
282
283     switch (opcode) {
284     case AttOpHandleValueNotification:
285         handle = read_le<GatoHandle>(&data[1]);
286         emit attributeUpdated(handle, event.mid(3), false);
287         return true;
288     case AttOpHandleValueIndication:
289         handle = read_le<GatoHandle>(&data[1]);
290
291         // Send the confirmation back
292         command(AttOpHandleValueConfirmation, QByteArray());
293
294         emit attributeUpdated(handle, event.mid(3), true);
295         return true;
296     default:
297         return false;
298     }
299 }
300
301 bool GatoAttClient::handleResponse(const Request &req, const QByteArray &response)
302 {
303     // If we know the request, we can provide a decoded answer
304     switch (req.opcode) {
305     case AttOpExchangeMTURequest:
306         if (response[0] == AttOpExchangeMTUResponse) {
307             if (req.receiver) {
308                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
309                                           Q_ARG(uint, req.id),
310                                           Q_ARG(quint16, read_le<quint16>(response.constData() + 1)));
311             }
312             return true;
313         } else if (response[0] == AttOpErrorResponse && response[1] == AttOpExchangeMTURequest) {
314             if (req.receiver) {
315                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
316                                           Q_ARG(uint, req.id),
317                                           Q_ARG(quint16, 0));
318             }
319             return true;
320         } else {
321             return false;
322         }
323         break;
324     case AttOpFindInformationRequest:
325         if (response[0] == AttOpFindInformationResponse) {
326             if (req.receiver) {
327                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
328                                           Q_ARG(uint, req.id),
329                                           Q_ARG(QList<GatoAttClient::InformationData>, parseInformationData(response.mid(1))));
330             }
331             return true;
332         } else if (response[0] == AttOpErrorResponse && response[1] == AttOpFindInformationRequest) {
333             if (req.receiver) {
334                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
335                                           Q_ARG(uint, req.id),
336                                           Q_ARG(QList<GatoAttClient::InformationData>, QList<InformationData>()));
337             }
338             return true;
339         } else {
340             return false;
341         }
342         break;
343     case AttOpFindByTypeValueRequest:
344         if (response[0] == AttOpFindByTypeValueResponse) {
345             if (req.receiver) {
346                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
347                                           Q_ARG(uint, req.id),
348                                           Q_ARG(QList<GatoAttClient::HandleInformation>, parseHandleInformation(response.mid(1))));
349             }
350             return true;
351         } else if (response[0] == AttOpErrorResponse && response[1] == AttOpFindByTypeValueRequest) {
352             if (req.receiver) {
353                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
354                                           Q_ARG(uint, req.id),
355                                           Q_ARG(QList<GatoAttClient::HandleInformation>, QList<HandleInformation>()));
356             }
357             return true;
358         } else {
359             return false;
360         }
361         break;
362     case AttOpReadByTypeRequest:
363         if (response[0] == AttOpReadByTypeResponse) {
364             if (req.receiver) {
365                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
366                                           Q_ARG(uint, req.id),
367                                           Q_ARG(QList<GatoAttClient::AttributeData>, parseAttributeData(response.mid(1))));
368             }
369             return true;
370         } else if (response[0] == AttOpErrorResponse && response[1] == AttOpReadByTypeRequest) {
371             if (req.receiver) {
372                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
373                                           Q_ARG(uint, req.id),
374                                           Q_ARG(QList<GatoAttClient::AttributeData>, QList<AttributeData>()));
375             }
376             return true;
377         } else {
378             return false;
379         }
380         break;
381     case AttOpReadRequest:
382         if (response[0] == AttOpReadResponse) {
383             if (req.receiver) {
384                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
385                                           Q_ARG(uint, req.id),
386                                           Q_ARG(QByteArray, response.mid(1)));
387             }
388             return true;
389         } else if (response[0] == AttOpErrorResponse && response[1] == AttOpReadRequest) {
390             if (req.receiver) {
391                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
392                                           Q_ARG(uint, req.id),
393                                           Q_ARG(QByteArray, QByteArray()));
394             }
395             return true;
396         } else {
397             return false;
398         }
399         break;
400     case AttOpReadByGroupTypeRequest:
401         if (response[0] == AttOpReadByGroupTypeResponse) {
402             if (req.receiver) {
403                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
404                                           Q_ARG(uint, req.id),
405                                           Q_ARG(QList<GatoAttClient::AttributeGroupData>, parseAttributeGroupData(response.mid(1))));
406             }
407             return true;
408         } else if (response[0] == AttOpErrorResponse && response[1] == AttOpReadByGroupTypeRequest) {
409             if (req.receiver) {
410                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
411                                           Q_ARG(uint, req.id),
412                                           Q_ARG(QList<GatoAttClient::AttributeGroupData>, QList<AttributeGroupData>()));
413             }
414             return true;
415         } else {
416             return false;
417         }
418         break;
419     case AttOpWriteRequest:
420         if (response[0] == AttOpWriteResponse) {
421             if (req.receiver) {
422                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
423                                           Q_ARG(uint, req.id),
424                                           Q_ARG(bool, true));
425             }
426             return true;
427         } else if (response[0] == AttOpErrorResponse && response[1] == AttOpWriteRequest) {
428             if (req.receiver) {
429                 QMetaObject::invokeMethod(req.receiver, req.member.constData(),
430                                           Q_ARG(uint, req.id),
431                                           Q_ARG(bool, false));
432             }
433             return true;
434         } else {
435             return false;
436         }
437         break;
438     default: // Otherwise just send a QByteArray.
439         if (req.receiver) {
440             QMetaObject::invokeMethod(req.receiver, req.member.constData(),
441                                       Q_ARG(const QByteArray&, response));
442         }
443         return true;
444     }
445 }
446
447 QList<GatoAttClient::InformationData> GatoAttClient::parseInformationData(const QByteArray &data)
448 {
449     const int format = data[0];
450     QList<InformationData> list;
451     int item_len;
452
453     switch (format) {
454     case 1:
455         item_len = 2 + 2;
456         break;
457     case 2:
458         item_len = 2 + 16;
459         break;
460     default:
461         qWarning() << "Unknown InformationData format!";
462         return list;
463     }
464
465     int items = (data.size() - 1) / item_len;
466     list.reserve(items);
467
468     int pos = 1;
469     const char *s = data.constData();
470     for (int i = 0; i < items; i++) {
471         InformationData d;
472         QByteArray uuid;
473         d.handle = read_le<GatoHandle>(&s[pos]);
474         switch (format) {
475         case 1:
476             uuid = data.mid(pos + 2, 2);
477             break;
478         case 2:
479             uuid = data.mid(pos + 2, 16);
480             break;
481         }
482         d.uuid = bytearray_to_gatouuid(uuid);
483
484         list.append(d);
485
486         pos += item_len;
487     }
488
489     return list;
490 }
491
492 QList<GatoAttClient::HandleInformation> GatoAttClient::parseHandleInformation(const QByteArray &data)
493 {
494     const int item_len = 2;
495     const int items = data.size() / item_len;
496     QList<HandleInformation> list;
497     list.reserve(items);
498
499     int pos = 0;
500     const char *s = data.constData();
501     for (int i = 0; i < items; i++) {
502         HandleInformation d;
503         d.start = read_le<GatoHandle>(&s[pos]);
504         d.end = read_le<GatoHandle>(&s[pos + 2]);
505         list.append(d);
506
507         pos += item_len;
508     }
509
510     return list;
511 }
512
513 QList<GatoAttClient::AttributeData> GatoAttClient::parseAttributeData(const QByteArray &data)
514 {
515     const int item_len = data[0];
516     const int items = (data.size() - 1) / item_len;
517     QList<AttributeData> list;
518     list.reserve(items);
519
520     int pos = 1;
521     const char *s = data.constData();
522     for (int i = 0; i < items; i++) {
523         AttributeData d;
524         d.handle = read_le<GatoHandle>(&s[pos]);
525         d.value = data.mid(pos + 2, item_len - 2);
526         list.append(d);
527
528         pos += item_len;
529     }
530
531     return list;
532 }
533
534 QList<GatoAttClient::AttributeGroupData> GatoAttClient::parseAttributeGroupData(const QByteArray &data)
535 {
536     const int item_len = data[0];
537     const int items = (data.size() - 1) / item_len;
538     QList<AttributeGroupData> list;
539     list.reserve(items);
540
541     int pos = 1;
542     const char *s = data.constData();
543     for (int i = 0; i < items; i++) {
544         AttributeGroupData d;
545         d.start = read_le<GatoHandle>(&s[pos]);
546         d.end = read_le<GatoHandle>(&s[pos + 2]);
547         d.value = data.mid(pos + 4, item_len - 4);
548         list.append(d);
549
550         pos += item_len;
551     }
552
553     return list;
554 }
555
556 void GatoAttClient::handleSocketConnected()
557 {
558     requestExchangeMTU(ATT_MAX_LE_MTU, this, SLOT(handleServerMTU(quint16)));
559     emit connected();
560 }
561
562 void GatoAttClient::handleSocketDisconnected()
563 {
564     emit disconnected();
565 }
566
567 void GatoAttClient::handleSocketReadyRead()
568 {
569     QByteArray pkt = socket->receive();
570     if (!pkt.isEmpty()) {
571 #if PROTOCOL_DEBUG
572         qDebug() << "Received" << pkt.size() << "bytes" << pkt.toHex();
573 #endif
574
575         // Check if it is an event
576         if (handleEvent(pkt)) {
577             return;
578         }
579
580         // Otherwise, if we have a request waiting, check if this answers it
581         if (!pending_requests.isEmpty()) {
582             if (handleResponse(pending_requests.head(), pkt)) {
583                 pending_requests.dequeue();
584                 // Proceed to next request
585                 if (!pending_requests.isEmpty()) {
586                     sendARequest();
587                 }
588                 return;
589             }
590         }
591
592         qDebug() << "No idea what this packet ("
593                  << QString("0x%1").arg(uint(pkt.at(0)), 2, 16, QLatin1Char('0'))
594                  << ") is";
595     }
596 }
597
598 void GatoAttClient::handleServerMTU(uint req, quint16 server_mtu)
599 {
600     Q_UNUSED(req);
601     if (server_mtu) {
602         cur_mtu = server_mtu;
603         if (cur_mtu < ATT_DEFAULT_LE_MTU) {
604             cur_mtu = ATT_DEFAULT_LE_MTU;
605         }
606     }
607 }
608
609 QT_END_NAMESPACE