Merge the ServiceNotValidError and OperationError of QLEService
[qt:qtconnectivity.git] / src / bluetooth / gatoperipheral.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/QDebug>
35
36 #include <assert.h>
37 #include <bluetooth/bluetooth.h>
38
39 #include "gatoperipheral_p.h"
40 #include "gatoaddress.h"
41 #include "gatouuid.h"
42 #include "helpers.h"
43
44 QT_BEGIN_NAMESPACE
45
46 enum EIRDataFields {
47     EIRFlags = 0x01,
48     EIRIncompleteUUID16List = 0x02,
49     EIRCompleteUUID16List = 0x03,
50     EIRIncompleteUUID32List = 0x04,
51     EIRCompleteUUID32List = 0x05,
52     EIRIncompleteUUID128List = 0x06,
53     EIRCompleteUUID128List = 0x07,
54     EIRIncompleteLocalName = 0x08,
55     EIRCompleteLocalName = 0x09,
56     EIRTxPowerLevel = 0x0A,
57     EIRDeviceClass = 0x0D,
58     EIRSecurityManagerTKValue = 0x10,
59     EIRSecurityManagerOutOfBandFlags = 0x11,
60     EIRSolicitedUUID128List = 0x15
61 };
62
63 GatoPeripheral::GatoPeripheral(const GatoAddress &addr, QObject *parent) :
64     QObject(parent), d_ptr(new GatoPeripheralPrivate(this))
65 {
66     Q_D(GatoPeripheral);
67     d->addr = addr;
68     d->att = new GatoAttClient(this);
69
70     connect(d->att, SIGNAL(connected()), d, SLOT(handleAttConnected()));
71     connect(d->att, SIGNAL(disconnected()), d, SLOT(handleAttDisconnected()));
72     connect(d->att, SIGNAL(attributeUpdated(GatoHandle,QByteArray,bool)), d, SLOT(handleAttAttributeUpdated(GatoHandle,QByteArray,bool)));
73 }
74
75 GatoPeripheral::~GatoPeripheral()
76 {
77     if (state() != StateDisconnected) {
78         disconnect();
79     }
80     delete d_ptr;
81 }
82
83 GatoPeripheral::State GatoPeripheral::state() const
84 {
85     Q_D(const GatoPeripheral);
86     return static_cast<State>(d->att->state());
87 }
88
89 GatoAddress GatoPeripheral::address() const
90 {
91     Q_D(const GatoPeripheral);
92     return d->addr;
93 }
94
95 QString GatoPeripheral::name() const
96 {
97     Q_D(const GatoPeripheral);
98     return d->name;
99 }
100
101 QList<GatoService> GatoPeripheral::services() const
102 {
103     Q_D(const GatoPeripheral);
104     return d->services.values();
105 }
106
107 void GatoPeripheral::parseEIR(quint8 data[], int len)
108 {
109     Q_D(GatoPeripheral);
110
111     int pos = 0;
112     while (pos < len) {
113         int item_len = data[pos];
114         pos++;
115         if (item_len == 0) break;
116         int type = data[pos];
117         assert(pos + item_len <= len);
118         switch (type) {
119         case EIRFlags:
120             d->parseEIRFlags(&data[pos + 1], item_len - 1);
121             break;
122         case EIRIncompleteUUID16List:
123             d->parseEIRUUIDs(16/8, false, &data[pos + 1], item_len - 1);
124             break;
125         case EIRCompleteUUID16List:
126             d->parseEIRUUIDs(16/8, true, &data[pos + 1], item_len - 1);
127             break;
128         case EIRIncompleteUUID32List:
129             d->parseEIRUUIDs(32/8, false, &data[pos + 1], item_len - 1);
130             break;
131         case EIRCompleteUUID32List:
132             d->parseEIRUUIDs(32/8, true, &data[pos + 1], item_len - 1);
133             break;
134         case EIRIncompleteUUID128List:
135             d->parseEIRUUIDs(128/8, false, &data[pos + 1], item_len - 1);
136             break;
137         case EIRCompleteUUID128List:
138             d->parseEIRUUIDs(128/8, true, &data[pos + 1], item_len - 1);
139             break;
140         case EIRIncompleteLocalName:
141             d->parseName(false, &data[pos + 1], item_len - 1);
142             break;
143         case EIRCompleteLocalName:
144             d->parseName(true, &data[pos + 1], item_len - 1);
145             break;
146         case EIRTxPowerLevel:
147         case EIRSolicitedUUID128List:
148             qDebug() << "Unhandled EIR data type" << type;
149             break;
150         default:
151             qWarning() << "Unknown EIR data type" << type;
152             break;
153         }
154
155         pos += item_len;
156     }
157
158     assert(pos == len);
159 }
160
161 bool GatoPeripheral::advertisesService(const GatoUUID &uuid) const
162 {
163     Q_D(const GatoPeripheral);
164     return d->service_uuids.contains(uuid);
165 }
166
167 void GatoPeripheral::connectPeripheral()
168 {
169     Q_D(GatoPeripheral);
170     if (d->att->state() != GatoSocket::StateDisconnected) {
171         qDebug() << "Already connecting";
172         return;
173     }
174
175     d->att->connectTo(d->addr);
176 }
177
178 void GatoPeripheral::disconnectPeripheral()
179 {
180     Q_D(GatoPeripheral);
181
182     d->att->close();
183 }
184
185 void GatoPeripheral::discoverServices()
186 {
187     Q_D(GatoPeripheral);
188     if (!d->complete_services && state() == StateConnected) {
189         d->clearServices();
190         d->att->requestReadByGroupType(0x0001, 0xFFFF, GatoUUID::GattPrimaryService,
191                                        d, SLOT(handlePrimary(QList<GatoAttClient::AttributeGroupData>)));
192     } else {
193         qWarning() << "Not connected";
194     }
195 }
196
197 void GatoPeripheral::discoverServices(const QList<GatoUUID> &serviceUUIDs)
198 {
199     Q_D(GatoPeripheral);
200     if (serviceUUIDs.isEmpty()) return;
201     if (state() == StateConnected) {
202         foreach (const GatoUUID& uuid, serviceUUIDs) {
203             QByteArray value = gatouuid_to_bytearray(uuid, true, false);
204             uint req = d->att->requestFindByTypeValue(0x0001, 0xFFFF, GatoUUID::GattPrimaryService, value,
205                                                       d, SLOT(handlePrimaryForService(uint,QList<GatoAttClient::HandleInformation>)));
206             d->pending_primary_reqs.insert(req, uuid);
207         }
208     } else {
209         qWarning() << "Not connected";
210     }
211 }
212
213 void GatoPeripheral::discoverCharacteristics(const GatoService &service)
214 {
215     Q_D(GatoPeripheral);
216
217     if (!d->services.contains(service.startHandle())) {
218         qWarning() << "Unknown service for this peripheral";
219         return;
220     }
221
222     GatoService &our_service = d->services[service.startHandle()];
223
224     if (our_service.startHandle() != service.startHandle() ||
225             our_service.endHandle() != service.endHandle() ||
226             our_service.uuid() != service.uuid()) {
227         qWarning() << "Unknown service for this peripheral";
228         return;
229     }
230
231     if (state() == StateConnected) {
232         GatoHandle start = our_service.startHandle();
233         GatoHandle end = our_service.endHandle();
234
235         d->clearServiceCharacteristics(&our_service);
236
237         uint req = d->att->requestReadByType(start, end, GatoUUID::GattCharacteristic,
238                                              d, SLOT(handleCharacteristic(QList<GatoAttClient::AttributeData>)));
239         d->pending_characteristic_reqs.insert(req, start);
240     } else {
241         qWarning() << "Not connected";
242     }
243 }
244
245 void GatoPeripheral::discoverCharacteristics(const GatoService &service, const QList<GatoUUID> &characteristicUUIDs)
246 {
247     // TODO There seems to be no way to ask for the peripheral to filter by uuid
248     Q_UNUSED(characteristicUUIDs);
249     discoverCharacteristics(service);
250 }
251
252 void GatoPeripheral::discoverDescriptors(const GatoCharacteristic &characteristic)
253 {
254     Q_D(GatoPeripheral);
255
256     GatoHandle char_handle = characteristic.startHandle();
257     GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
258
259     if (!service_handle) {
260         qWarning() << "Unknown characteristic for this peripheral";
261         return;
262     }
263
264     GatoService &our_service = d->services[service_handle];
265     Q_ASSERT(our_service.containsCharacteristic(char_handle));
266     GatoCharacteristic our_char = our_service.getCharacteristic(char_handle);
267     Q_ASSERT(our_char.startHandle() == char_handle);
268
269     if (state() == StateConnected) {
270         d->clearCharacteristicDescriptors(&our_char);
271         our_service.addCharacteristic(our_char); // Update service with empty descriptors list
272         uint req = d->att->requestFindInformation(our_char.startHandle() + 1, our_char.endHandle(),
273                                                   d, SLOT(handleDescriptors(uint,QList<GatoAttClient::InformationData>)));
274         d->pending_descriptor_reqs.insert(req, char_handle);
275     } else {
276         qWarning() << "Not connected";
277     }
278 }
279
280 void GatoPeripheral::readValue(const GatoCharacteristic &characteristic)
281 {
282     Q_D(GatoPeripheral);
283
284     GatoHandle char_handle = characteristic.startHandle();
285     GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
286
287     if (!service_handle) {
288         qWarning() << "Unknown characteristic for this peripheral";
289         return;
290     }
291
292     GatoService &our_service = d->services[service_handle];
293     Q_ASSERT(our_service.containsCharacteristic(char_handle));
294
295     if (state() == StateConnected) {
296         uint req = d->att->requestRead(characteristic.valueHandle(),
297                                        d, SLOT(handleCharacteristicRead(uint,QByteArray)));
298         d->pending_characteristic_read_reqs.insert(req, char_handle);
299     } else {
300         qWarning() << "Not connected";
301     }
302 }
303
304 void GatoPeripheral::readValue(const GatoDescriptor &descriptor)
305 {
306     Q_D(GatoPeripheral);
307
308     GatoHandle desc_handle = descriptor.handle();
309     GatoHandle char_handle = d->descriptor_to_characteristic.value(desc_handle);
310
311     if (!char_handle) {
312         qWarning() << "Unknown descriptor for this peripheral";
313         return;
314     }
315
316     GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
317     Q_ASSERT(service_handle);
318
319     GatoService &our_service = d->services[service_handle];
320     Q_ASSERT(our_service.containsCharacteristic(char_handle));
321
322     if (state() == StateConnected) {
323         uint req = d->att->requestRead(descriptor.handle(),
324                                        d, SLOT(handleDescriptorRead(uint,QByteArray)));
325         d->pending_descriptor_read_reqs.insert(req, char_handle);
326     } else {
327         qWarning() << "Not connected";
328     }
329 }
330
331 void GatoPeripheral::writeValue(const GatoCharacteristic &characteristic, const QByteArray &data, WriteType type)
332 {
333     Q_D(GatoPeripheral);
334
335     GatoHandle char_handle = characteristic.startHandle();
336     GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
337
338     if (!service_handle) {
339         qWarning() << "Unknown characteristic for this peripheral";
340         return;
341     }
342
343     GatoService &our_service = d->services[service_handle];
344     Q_ASSERT(our_service.containsCharacteristic(char_handle));
345
346     if (state() == StateConnected) {
347         switch (type) {
348         case WriteWithResponse:
349             d->att->requestWrite(characteristic.valueHandle(), data,
350                                  d, SLOT(handleCharacteristicWrite(uint,bool)));
351             break;
352         case WriteWithoutResponse:
353             d->att->commandWrite(characteristic.valueHandle(), data);
354             break;
355         }
356
357
358     } else {
359         qWarning() << "Not connected";
360     }
361 }
362
363 void GatoPeripheral::writeValue(const GatoDescriptor &descriptor, const QByteArray &data)
364 {
365     Q_D(GatoPeripheral);
366
367     GatoHandle desc_handle = descriptor.handle();
368     GatoHandle char_handle = d->descriptor_to_characteristic.value(desc_handle);
369
370     if (!char_handle) {
371         qWarning() << "Unknown descriptor for this peripheral";
372         return;
373     }
374
375     GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
376     Q_ASSERT(service_handle);
377
378     GatoService &our_service = d->services[service_handle];
379     Q_ASSERT(our_service.containsCharacteristic(char_handle));
380
381     if (state() == StateConnected) {
382         d->att->requestWrite(descriptor.handle(), data,
383                              d, SLOT(handleDescriptorWrite(uint,bool)));
384     } else {
385         qWarning() << "Not connected";
386     }
387 }
388
389 void GatoPeripheral::setNotification(const GatoCharacteristic &characteristic, bool enabled)
390 {
391     Q_D(GatoPeripheral);
392
393     GatoHandle char_handle = characteristic.startHandle();
394     GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
395
396     if (!service_handle) {
397         qWarning() << "Unknown characteristic for this peripheral";
398         return;
399     }
400
401     GatoService &our_service = d->services[service_handle];
402     Q_ASSERT(our_service.containsCharacteristic(char_handle));
403     GatoCharacteristic our_char = our_service.getCharacteristic(char_handle);
404
405     if (!(our_char.properties() & GatoCharacteristic::PropertyNotify)) {
406         qWarning() << "Characteristic does not support notifications";
407         return;
408     }
409
410     if (state() != StateConnected) {
411         qWarning() << "Not connected";
412         return;
413     }
414
415     const GatoUUID uuid(GatoUUID::GattClientCharacteristicConfiguration);
416     if (our_char.containsDescriptor(uuid)) {
417         GatoDescriptor desc = our_char.getDescriptor(uuid);
418         d->pending_set_notify.remove(char_handle);
419         writeValue(characteristic, d->genClientCharConfiguration(true, false));
420     } else {
421         d->pending_set_notify[char_handle] = enabled;
422         discoverDescriptors(our_char); // May need to find appropiate descriptor
423     }
424 }
425
426 GatoPeripheralPrivate::GatoPeripheralPrivate(GatoPeripheral *parent)
427     : QObject(parent), q_ptr(parent),
428       complete_name(false), complete_services(false)
429 {
430 }
431
432 GatoPeripheralPrivate::~GatoPeripheralPrivate()
433 {
434     delete att;
435 }
436
437 void GatoPeripheralPrivate::parseEIRFlags(quint8 data[], int len)
438 {
439     Q_UNUSED(data);
440     Q_UNUSED(len);
441     // Nothing to do for now.
442 }
443
444 void GatoPeripheralPrivate::parseEIRUUIDs(int size, bool complete, quint8 data[], int len)
445 {
446     Q_UNUSED(complete);
447
448     if (size != 16/8 && size != 32/8 && size != 128/8) {
449         qWarning() << "Unhandled UUID size: " << size;
450         return;
451     }
452
453     for (int pos = 0; pos < len; pos += size) {
454         char *ptr = reinterpret_cast<char*>(&data[pos]);
455         QByteArray ba = QByteArray::fromRawData(ptr, size/8);
456
457         service_uuids.insert(bytearray_to_gatouuid(ba));
458     }
459 }
460
461 void GatoPeripheralPrivate::parseName(bool complete, quint8 data[], int len)
462 {
463     Q_Q(GatoPeripheral);
464     if (complete || !complete_name) {
465         name = QString::fromUtf8(reinterpret_cast<char*>(data), len);
466         complete_name = complete;
467         emit q->nameChanged();
468     }
469 }
470
471 GatoCharacteristic GatoPeripheralPrivate::parseCharacteristicValue(const QByteArray &ba)
472 {
473     GatoCharacteristic characteristic;
474     const char *data = ba.constData();
475
476     quint8 properties = data[0];
477     characteristic.setProperties(GatoCharacteristic::Properties(properties));
478
479     GatoHandle handle = read_le<quint16>(&data[1]);
480     characteristic.setValueHandle(handle);
481
482     GatoUUID uuid = bytearray_to_gatouuid(ba.mid(3));
483     characteristic.setUuid(uuid);
484
485     return characteristic;
486 }
487
488 QByteArray GatoPeripheralPrivate::genClientCharConfiguration(bool notification, bool indication)
489 {
490     QByteArray ba;
491     ba.resize(sizeof(quint16));
492
493     quint16 val = 0;
494     if (notification)
495         val |= 0x1;
496     if (indication)
497         val |= 0x2;
498
499     write_le<quint16>(val, ba.data());
500
501     return ba;
502 }
503
504 void GatoPeripheralPrivate::clearServices()
505 {
506     characteristic_to_service.clear();
507     value_to_characteristic.clear();
508     descriptor_to_characteristic.clear();
509     services.clear();
510 }
511
512 void GatoPeripheralPrivate::clearServiceCharacteristics(GatoService *service)
513 {
514     QList<GatoCharacteristic> chars = service->characteristics();
515     QList<GatoCharacteristic>::iterator it;
516     for (it = chars.begin(); it != chars.end(); ++it) {
517         clearCharacteristicDescriptors(&*it);
518         characteristic_to_service.remove(it->startHandle());
519         value_to_characteristic.remove(it->valueHandle());
520     }
521     service->clearCharacteristics();
522 }
523
524 void GatoPeripheralPrivate::clearCharacteristicDescriptors(GatoCharacteristic *characteristic)
525 {
526     QList<GatoDescriptor> descs = characteristic->descriptors();
527     foreach (const GatoDescriptor& d, descs) {
528         descriptor_to_characteristic.remove(d.handle());
529     }
530     characteristic->clearDescriptors();
531 }
532
533 void GatoPeripheralPrivate::finishSetNotifyOperations(const GatoCharacteristic &characteristic)
534 {
535     Q_Q(GatoPeripheral);
536
537     GatoHandle handle = characteristic.startHandle();
538
539     if (pending_set_notify.contains(handle)) {
540         const GatoUUID uuid(GatoUUID::GattClientCharacteristicConfiguration);
541         bool notify = pending_set_notify.value(handle);
542
543         foreach (const GatoDescriptor &descriptor, characteristic.descriptors()) {
544             if (descriptor.uuid() == uuid) {
545                 q->writeValue(descriptor, genClientCharConfiguration(notify, false));
546             }
547         }
548
549         pending_set_notify.remove(handle);
550     }
551 }
552
553 void GatoPeripheralPrivate::handleAttConnected()
554 {
555     Q_Q(GatoPeripheral);
556
557     emit q->connected();
558 }
559
560 void GatoPeripheralPrivate::handleAttDisconnected()
561 {
562     Q_Q(GatoPeripheral);
563
564     // Forget about all pending requests
565     pending_primary_reqs.clear();
566     pending_characteristic_reqs.clear();
567     pending_characteristic_read_reqs.clear();
568     pending_descriptor_reqs.clear();
569     pending_descriptor_read_reqs.clear();
570
571     emit q->disconnected();
572 }
573
574 void GatoPeripheralPrivate::handleAttAttributeUpdated(GatoHandle handle, const QByteArray &value, bool confirmed)
575 {
576     Q_Q(GatoPeripheral);
577     Q_UNUSED(confirmed);
578
579     // Let's see if this is a handle we know about.
580     if (value_to_characteristic.contains(handle)) {
581         // Ok, it's a characteristic value.
582         GatoHandle char_handle = value_to_characteristic.value(handle);
583         GatoHandle service_handle = characteristic_to_service.value(char_handle);
584         if (!service_handle) {
585             qWarning() << "Got a notification for a characteristic I don't know about";
586             return;
587         }
588
589         GatoService &service = services[service_handle];
590         GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
591
592         emit q->valueUpdated(characteristic, value);
593     }
594 }
595
596 void GatoPeripheralPrivate::handlePrimary(uint req, const QList<GatoAttClient::AttributeGroupData> &list)
597 {
598     Q_Q(GatoPeripheral);
599     Q_UNUSED(req);
600
601     if (list.isEmpty()) {
602         complete_services = true;
603         emit q->servicesDiscovered();
604     } else {
605         GatoHandle last_handle = 0;
606
607         foreach (const GatoAttClient::AttributeGroupData &data, list) {
608             GatoUUID uuid = bytearray_to_gatouuid(data.value);
609             GatoService service;
610
611             service.setUuid(uuid);
612             service.setStartHandle(data.start);
613             service.setEndHandle(data.end);
614
615             services.insert(data.start, service);
616             service_uuids.insert(uuid);
617
618             last_handle = data.end;
619         }
620
621         // Fetch following attributes
622         att->requestReadByGroupType(last_handle + 1, 0xFFFF, GatoUUID::GattPrimaryService,
623                                     this, SLOT(handlePrimary(uint,QList<GatoAttClient::AttributeGroupData>)));
624     }
625 }
626
627 void GatoPeripheralPrivate::handlePrimaryForService(uint req, const QList<GatoAttClient::HandleInformation> &list)
628 {
629     Q_Q(GatoPeripheral);
630
631     GatoUUID uuid = pending_primary_reqs.value(req, GatoUUID());
632     if (uuid.isNull()) {
633         qDebug() << "Got primary for service response for a request I did not make";
634         return;
635     }
636     pending_primary_reqs.remove(req);
637
638     if (list.isEmpty()) {
639         if (pending_primary_reqs.isEmpty()) {
640             emit q->servicesDiscovered();
641         }
642     } else {
643         GatoHandle last_handle = 0;
644
645         foreach (const GatoAttClient::HandleInformation &data, list) {
646             GatoService service;
647
648             service.setUuid(uuid);
649             service.setStartHandle(data.start);
650             service.setEndHandle(data.end);
651
652             services.insert(data.start, service);
653             service_uuids.insert(uuid);
654
655             last_handle = data.end;
656         }
657
658         // Fetch following attributes
659         QByteArray value = gatouuid_to_bytearray(uuid, true, false);
660         uint req = att->requestFindByTypeValue(last_handle + 1, 0xFFFF, GatoUUID::GattPrimaryService, value,
661                                                this, SLOT(handlePrimaryForService(uint,QList<GatoAttClient::HandleInformation>)));
662         pending_primary_reqs.insert(req, uuid);
663     }
664 }
665
666 void GatoPeripheralPrivate::handleCharacteristic(uint req, const QList<GatoAttClient::AttributeData> &list)
667 {
668     Q_Q(GatoPeripheral);
669
670     GatoHandle service_start = pending_characteristic_reqs.value(req, 0);
671     if (!service_start) {
672         qDebug() << "Got characteristics for a request I did not make";
673         return;
674     }
675     pending_characteristic_reqs.remove(req);
676
677     Q_ASSERT(services.contains(service_start));
678     GatoService &service = services[service_start];
679     Q_ASSERT(service.startHandle() == service_start);
680
681     if (list.isEmpty()) {
682         emit q->characteristicsDiscovered(service);
683     } else {
684         GatoHandle last_handle = 0;
685
686         // If we are continuing a characteristic list, this means the
687         // last service we discovered in the previous iteration was not
688         // the last one, so we have to reduce its endHandle!
689         QList<GatoCharacteristic> cur_chars = service.characteristics();
690         if (!cur_chars.isEmpty()) {
691             GatoCharacteristic &last = cur_chars.back();
692             last.setEndHandle(list.front().handle - 1);
693             service.addCharacteristic(last);
694         }
695
696         for (int i = 0; i < list.size(); i++) {
697             const GatoAttClient::AttributeData &data = list.at(i);
698             GatoCharacteristic characteristic = parseCharacteristicValue(data.value);
699
700             characteristic.setStartHandle(data.handle);
701             if (i + 1 < list.size()) {
702                 characteristic.setEndHandle(list.at(i + 1).handle - 1);
703             } else {
704                 characteristic.setEndHandle(service.endHandle());
705             }
706
707             service.addCharacteristic(characteristic);
708             characteristic_to_service.insert(data.handle, service_start);
709             value_to_characteristic.insert(characteristic.valueHandle(), data.handle);
710
711             last_handle = data.handle;
712         }
713
714         if (last_handle >= service.endHandle()) {
715             // Already finished, no need to send another request
716             emit q->characteristicsDiscovered(service);
717             return;
718         }
719
720         // Fetch following attributes
721         uint req = att->requestReadByType(last_handle + 1, service.endHandle(), GatoUUID::GattCharacteristic,
722                                           this, SLOT(handleCharacteristic(uint,QList<GatoAttClient::AttributeData>)));
723         pending_characteristic_reqs.insert(req, service.startHandle());
724     }
725 }
726
727 void GatoPeripheralPrivate::handleDescriptors(uint req, const QList<GatoAttClient::InformationData> &list)
728 {
729     Q_Q(GatoPeripheral);
730
731     GatoHandle char_handle = pending_descriptor_reqs.value(req);
732     if (!char_handle) {
733         qDebug() << "Got descriptor for a request I did not make";
734         return;
735     }
736     pending_descriptor_reqs.remove(req);
737     GatoHandle service_handle = characteristic_to_service.value(char_handle);
738     if (!service_handle) {
739         qWarning() << "Unknown characteristic during descriptor discovery: " << char_handle;
740         return;
741     }
742
743     Q_ASSERT(services.contains(service_handle));
744     GatoService &service = services[service_handle];
745     Q_ASSERT(service.startHandle() == service_handle);
746
747     Q_ASSERT(service.containsCharacteristic(char_handle));
748     GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
749
750     if (list.isEmpty()) {
751         finishSetNotifyOperations(characteristic);
752         emit q->descriptorsDiscovered(characteristic);
753     } else {
754         GatoHandle last_handle = 0;
755
756         foreach (const GatoAttClient::InformationData &data, list) {
757             // Skip the value attribute itself.
758             if (data.handle == characteristic.valueHandle()) continue;
759
760             GatoDescriptor descriptor;
761
762             descriptor.setHandle(data.handle);
763             descriptor.setUuid(data.uuid);
764
765             characteristic.addDescriptor(descriptor);
766
767             service.addCharacteristic(characteristic);
768             descriptor_to_characteristic.insert(data.handle, char_handle);
769
770             last_handle = data.handle;
771         }
772
773         service.addCharacteristic(characteristic);
774
775         if (last_handle >= characteristic.endHandle()) {
776             // Already finished, no need to send another request
777             finishSetNotifyOperations(characteristic);
778             emit q->descriptorsDiscovered(characteristic);
779             return;
780         }
781
782         // Fetch following attributes
783         uint req = att->requestFindInformation(last_handle + 1, characteristic.endHandle(),
784                                                this, SLOT(handleDescriptors(uint,QList<GatoAttClient::InformationData>)));
785         pending_descriptor_reqs.insert(req, char_handle);
786
787     }
788 }
789
790 void GatoPeripheralPrivate::handleCharacteristicRead(uint req, const QByteArray &value)
791 {
792     Q_Q(GatoPeripheral);
793
794     GatoHandle char_handle = pending_characteristic_read_reqs.value(req);
795     if (!char_handle) {
796         qDebug() << "Got characteristics for a request I did not make";
797         return;
798     }
799     pending_characteristic_read_reqs.remove(req);
800     GatoHandle service_handle = characteristic_to_service.value(char_handle);
801     if (!service_handle) {
802         qWarning() << "Unknown characteristic during read: " << char_handle;
803         return;
804     }
805
806     Q_ASSERT(services.contains(service_handle));
807     GatoService &service = services[service_handle];
808     Q_ASSERT(service.startHandle() == service_handle);
809
810     Q_ASSERT(service.containsCharacteristic(char_handle));
811     GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
812
813     emit q->valueUpdated(characteristic, value);
814 }
815
816 void GatoPeripheralPrivate::handleDescriptorRead(uint req, const QByteArray &value)
817 {
818     Q_Q(GatoPeripheral);
819
820     GatoHandle desc_handle = pending_descriptor_read_reqs.value(req);
821     if (!desc_handle) {
822         qDebug() << "Got characteristics for a request I did not make";
823         return;
824     }
825     pending_descriptor_read_reqs.remove(req);
826     GatoHandle char_handle = descriptor_to_characteristic.value(desc_handle);
827     if (!char_handle) {
828         qWarning() << "Unknown characteristic during read: " << char_handle;
829         return;
830     }
831     GatoHandle service_handle = characteristic_to_service.value(char_handle);
832     if (!service_handle) {
833         qWarning() << "Unknown characteristic during read: " << char_handle;
834         return;
835     }
836
837     Q_ASSERT(services.contains(service_handle));
838     GatoService &service = services[service_handle];
839     Q_ASSERT(service.startHandle() == service_handle);
840
841     Q_ASSERT(service.containsCharacteristic(char_handle));
842     GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
843
844     Q_ASSERT(characteristic.containsDescriptor(desc_handle));
845     GatoDescriptor descriptor = characteristic.getDescriptor(desc_handle);
846
847     emit q->descriptorValueUpdated(descriptor, value);
848 }
849
850 void GatoPeripheralPrivate::handleCharacteristicWrite(uint req, bool ok)
851 {
852     Q_UNUSED(req);
853     if (!ok) {
854         qWarning() << "Failed to write some characteristic";
855     }
856 }
857
858 void GatoPeripheralPrivate::handleDescriptorWrite(uint req, bool ok)
859 {
860     Q_UNUSED(req);
861     if (!ok) {
862         qWarning() << "Failed to write some characteristic";
863     }
864 }
865
866 QT_END_NAMESPACE