1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/gatt/client.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/att/att.h"
18 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
19 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/slab_allocator.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/trace.h"
22 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt_defs.h"
23
24 #pragma clang diagnostic ignored "-Wshadow"
25
26 using bt::HostError;
27
28 namespace bt::gatt {
29 namespace {
30
NewPDU(size_t param_size)31 MutableByteBufferPtr NewPDU(size_t param_size) {
32 auto pdu = NewBuffer(sizeof(att::Header) + param_size);
33 if (!pdu) {
34 bt_log(DEBUG, "att", "out of memory");
35 }
36 return pdu;
37 }
38
39 template <att::UUIDType Format,
40 typename EntryType = att::InformationData<Format>>
ProcessDescriptorDiscoveryResponse(att::Handle range_start,att::Handle range_end,BufferView entries,Client::DescriptorCallback desc_callback,att::Handle * out_last_handle)41 bool ProcessDescriptorDiscoveryResponse(
42 att::Handle range_start,
43 att::Handle range_end,
44 BufferView entries,
45 Client::DescriptorCallback desc_callback,
46 att::Handle* out_last_handle) {
47 BT_DEBUG_ASSERT(out_last_handle);
48
49 if (entries.size() % sizeof(EntryType)) {
50 bt_log(DEBUG, "gatt", "malformed information data list");
51 return false;
52 }
53
54 att::Handle last_handle = range_end;
55 while (entries.size()) {
56 const EntryType& entry = entries.To<EntryType>();
57
58 att::Handle desc_handle = le16toh(entry.handle);
59
60 // Stop and report an error if the server erroneously responds with
61 // an attribute outside the requested range.
62 if (desc_handle > range_end || desc_handle < range_start) {
63 bt_log(DEBUG,
64 "gatt",
65 "descriptor handle out of range (handle: %#.4x, "
66 "range: %#.4x - %#.4x)",
67 desc_handle,
68 range_start,
69 range_end);
70 return false;
71 }
72
73 // The handles must be strictly increasing.
74 if (last_handle != range_end && desc_handle <= last_handle) {
75 bt_log(DEBUG, "gatt", "descriptor handles not strictly increasing");
76 return false;
77 }
78
79 last_handle = desc_handle;
80
81 // Notify the handler.
82 desc_callback(DescriptorData(desc_handle, UUID(entry.uuid)));
83
84 entries = entries.view(sizeof(EntryType));
85 }
86
87 *out_last_handle = last_handle;
88 return true;
89 }
90
91 } // namespace
92
93 class Impl final : public Client {
94 public:
Impl(att::Bearer::WeakPtr bearer)95 explicit Impl(att::Bearer::WeakPtr bearer)
96 : att_(std::move(bearer)), weak_self_(this) {
97 BT_DEBUG_ASSERT(att_.is_alive());
98
99 auto handler = [this](auto txn_id, const att::PacketReader& pdu) {
100 BT_DEBUG_ASSERT(pdu.opcode() == att::kNotification ||
101 pdu.opcode() == att::kIndication);
102
103 if (pdu.payload_size() < sizeof(att::NotificationParams)) {
104 // Received a malformed notification. Disconnect the link.
105 bt_log(DEBUG, "gatt", "malformed notification/indication PDU");
106 att_->ShutDown();
107 return;
108 }
109
110 bool is_ind = pdu.opcode() == att::kIndication;
111 const auto& params = pdu.payload<att::NotificationParams>();
112 att::Handle handle = le16toh(params.handle);
113 size_t value_size = pdu.payload_size() - sizeof(att::Handle);
114
115 // Auto-confirm indications.
116 if (is_ind) {
117 auto pdu = NewPDU(0u);
118 if (pdu) {
119 att::PacketWriter(att::kConfirmation, pdu.get());
120 att_->Reply(txn_id, std::move(pdu));
121 } else {
122 att_->ReplyWithError(
123 txn_id, handle, att::ErrorCode::kInsufficientResources);
124 }
125 }
126
127 bool maybe_truncated = false;
128 // If the value is the max size that fits in the MTU, it may be truncated.
129 if (value_size ==
130 att_->mtu() - sizeof(att::OpCode) - sizeof(att::Handle)) {
131 maybe_truncated = true;
132 }
133
134 // Run the handler
135 if (notification_handler_) {
136 notification_handler_(is_ind,
137 handle,
138 BufferView(params.value, value_size),
139 maybe_truncated);
140 } else {
141 bt_log(
142 TRACE, "gatt", "dropped notification/indication without handler");
143 }
144 };
145
146 not_handler_id_ = att_->RegisterHandler(att::kNotification, handler);
147 ind_handler_id_ = att_->RegisterHandler(att::kIndication, handler);
148 }
149
~Impl()150 ~Impl() override {
151 att_->UnregisterHandler(not_handler_id_);
152 att_->UnregisterHandler(ind_handler_id_);
153 }
154
155 using WeakPtr = WeakSelf<Client>::WeakPtr;
GetWeakPtr()156 WeakPtr GetWeakPtr() override { return weak_self_.GetWeakPtr(); }
157
158 private:
mtu() const159 uint16_t mtu() const override { return att_->mtu(); }
160
ExchangeMTU(MTUCallback mtu_cb)161 void ExchangeMTU(MTUCallback mtu_cb) override {
162 auto pdu = NewPDU(sizeof(att::ExchangeMTURequestParams));
163 if (!pdu) {
164 mtu_cb(fit::error(att::Error(HostError::kOutOfMemory)));
165 return;
166 }
167
168 att::PacketWriter writer(att::kExchangeMTURequest, pdu.get());
169 auto params = writer.mutable_payload<att::ExchangeMTURequestParams>();
170 params->client_rx_mtu = htole16(att_->preferred_mtu());
171
172 auto rsp_cb = [this, mtu_cb = std::move(mtu_cb)](
173 att::Bearer::TransactionResult result) mutable {
174 if (result.is_ok()) {
175 const att::PacketReader& rsp = result.value();
176 BT_DEBUG_ASSERT(rsp.opcode() == att::kExchangeMTUResponse);
177
178 if (rsp.payload_size() != sizeof(att::ExchangeMTUResponseParams)) {
179 // Received a malformed response. Disconnect the link.
180 att_->ShutDown();
181
182 mtu_cb(fit::error(att::Error(HostError::kPacketMalformed)));
183 return;
184 }
185
186 const auto& rsp_params = rsp.payload<att::ExchangeMTUResponseParams>();
187 uint16_t server_mtu = le16toh(rsp_params.server_rx_mtu);
188
189 // If the minimum value is less than the default MTU, then go with the
190 // default MTU (Vol 3, Part F, 3.4.2.2).
191 uint16_t final_mtu = std::max(
192 att::kLEMinMTU, std::min(server_mtu, att_->preferred_mtu()));
193 att_->set_mtu(final_mtu);
194
195 mtu_cb(fit::ok(final_mtu));
196 return;
197 }
198 const auto& [error, handle] = result.error_value();
199
200 // "If the Error Response is sent by the server with the Error Code
201 // set to Request Not Supported, [...] the default MTU shall be used
202 // (Vol 3, Part G, 4.3.1)"
203 if (error.is(att::ErrorCode::kRequestNotSupported)) {
204 bt_log(
205 DEBUG, "gatt", "peer does not support MTU exchange: using default");
206 att_->set_mtu(att::kLEMinMTU);
207 mtu_cb(fit::error(error));
208 return;
209 }
210
211 bt_log(DEBUG, "gatt", "MTU exchange failed: %s", bt_str(error));
212 mtu_cb(fit::error(error));
213 };
214
215 att_->StartTransaction(std::move(pdu), BindCallback(std::move(rsp_cb)));
216 }
217
DiscoverServices(ServiceKind kind,ServiceCallback svc_callback,att::ResultFunction<> status_callback)218 void DiscoverServices(ServiceKind kind,
219 ServiceCallback svc_callback,
220 att::ResultFunction<> status_callback) override {
221 DiscoverServicesInRange(kind,
222 att::kHandleMin,
223 att::kHandleMax,
224 std::move(svc_callback),
225 std::move(status_callback));
226 }
227
DiscoverServicesInRange(ServiceKind kind,att::Handle range_start,att::Handle range_end,ServiceCallback svc_callback,att::ResultFunction<> status_callback)228 void DiscoverServicesInRange(ServiceKind kind,
229 att::Handle range_start,
230 att::Handle range_end,
231 ServiceCallback svc_callback,
232 att::ResultFunction<> status_callback) override {
233 BT_ASSERT(range_start <= range_end);
234
235 auto pdu = NewPDU(sizeof(att::ReadByGroupTypeRequestParams16));
236 if (!pdu) {
237 status_callback(ToResult(HostError::kOutOfMemory));
238 return;
239 }
240
241 att::PacketWriter writer(att::kReadByGroupTypeRequest, pdu.get());
242 auto* params =
243 writer.mutable_payload<att::ReadByGroupTypeRequestParams16>();
244 params->start_handle = htole16(range_start);
245 params->end_handle = htole16(range_end);
246 params->type =
247 htole16(kind == ServiceKind::PRIMARY ? types::kPrimaryService16
248 : types::kSecondaryService16);
249
250 auto rsp_cb = [this,
251 kind,
252 range_start,
253 range_end,
254 svc_cb = std::move(svc_callback),
255 res_cb = std::move(status_callback)](
256 att::Bearer::TransactionResult result) mutable {
257 if (result.is_error()) {
258 const att::Error& error = result.error_value().first;
259
260 // An Error Response code of "Attribute Not Found" indicates the end of
261 // the procedure (v5.0, Vol 3, Part G, 4.4.1).
262 if (error.is(att::ErrorCode::kAttributeNotFound)) {
263 res_cb(fit::ok());
264 return;
265 }
266
267 res_cb(fit::error(error));
268 return;
269 }
270
271 const att::PacketReader& rsp = result.value();
272 BT_DEBUG_ASSERT(rsp.opcode() == att::kReadByGroupTypeResponse);
273 TRACE_DURATION("bluetooth",
274 "gatt::Client::DiscoverServicesInRange rsp_cb",
275 "size",
276 rsp.size());
277
278 if (rsp.payload_size() < sizeof(att::ReadByGroupTypeResponseParams)) {
279 // Received malformed response. Disconnect the link.
280 bt_log(DEBUG, "gatt", "received malformed Read By Group Type response");
281 att_->ShutDown();
282 res_cb(ToResult(HostError::kPacketMalformed));
283 return;
284 }
285
286 const auto& rsp_params =
287 rsp.payload<att::ReadByGroupTypeResponseParams>();
288 uint8_t entry_length = rsp_params.length;
289
290 // We expect the returned attribute value to be a 16-bit or 128-bit
291 // service UUID.
292 constexpr size_t kAttrDataSize16 =
293 sizeof(att::AttributeGroupDataEntry) + sizeof(att::AttributeType16);
294 constexpr size_t kAttrDataSize128 =
295 sizeof(att::AttributeGroupDataEntry) + sizeof(att::AttributeType128);
296
297 if (entry_length != kAttrDataSize16 && entry_length != kAttrDataSize128) {
298 bt_log(DEBUG, "gatt", "invalid attribute data length");
299 att_->ShutDown();
300 res_cb(ToResult(HostError::kPacketMalformed));
301 return;
302 }
303
304 BufferView attr_data_list(rsp_params.attribute_data_list,
305 rsp.payload_size() - 1);
306 if (attr_data_list.size() % entry_length) {
307 bt_log(DEBUG, "gatt", "malformed attribute data list");
308 att_->ShutDown();
309 res_cb(ToResult(HostError::kPacketMalformed));
310 return;
311 }
312
313 std::optional<att::Handle> last_handle;
314 while (attr_data_list.size()) {
315 att::Handle start = le16toh(
316 attr_data_list
317 .ReadMember<&att::AttributeGroupDataEntry::start_handle>());
318 att::Handle end = le16toh(
319 attr_data_list
320 .ReadMember<&att::AttributeGroupDataEntry::group_end_handle>());
321
322 if (end < start) {
323 bt_log(DEBUG, "gatt", "received malformed service range values");
324 res_cb(ToResult(HostError::kPacketMalformed));
325 return;
326 }
327
328 if (start < range_start || start > range_end) {
329 bt_log(DEBUG,
330 "gatt",
331 "received service range values outside of requested range");
332 res_cb(ToResult(HostError::kPacketMalformed));
333 return;
334 }
335
336 // "The Attribute Data List is ordered sequentially based on the
337 // attribute handles." (Core Spec v5.3, Vol 3, Part F, Sec 3.4.4.10)
338 if (last_handle.has_value() && start <= last_handle.value()) {
339 bt_log(DEBUG, "gatt", "received services out of order");
340 res_cb(ToResult(HostError::kPacketMalformed));
341 return;
342 }
343
344 // This must succeed as we have performed the appropriate checks above.
345 auto uuid_bytes =
346 attr_data_list.view(offsetof(att::AttributeGroupDataEntry, value),
347 entry_length - (2 * sizeof(att::Handle)));
348 UUID uuid(uuid_bytes);
349
350 ServiceData service(kind, start, end, uuid);
351 last_handle = service.range_end;
352
353 // Notify the handler.
354 svc_cb(service);
355
356 attr_data_list = attr_data_list.view(entry_length);
357 }
358
359 // The procedure is over if we have reached the end of the handle range.
360 if (!last_handle.has_value() || last_handle.value() == range_end) {
361 res_cb(fit::ok());
362 return;
363 }
364
365 // Request the next batch.
366 DiscoverServicesInRange(kind,
367 last_handle.value() + 1,
368 range_end,
369 std::move(svc_cb),
370 std::move(res_cb));
371 };
372
373 att_->StartTransaction(std::move(pdu), BindCallback(std::move(rsp_cb)));
374 }
375
DiscoverServicesWithUuids(ServiceKind kind,ServiceCallback svc_cb,att::ResultFunction<> status_cb,std::vector<UUID> uuids)376 void DiscoverServicesWithUuids(ServiceKind kind,
377 ServiceCallback svc_cb,
378 att::ResultFunction<> status_cb,
379 std::vector<UUID> uuids) override {
380 DiscoverServicesWithUuidsInRange(kind,
381 att::kHandleMin,
382 att::kHandleMax,
383 std::move(svc_cb),
384 std::move(status_cb),
385 std::move(uuids));
386 }
387
DiscoverServicesWithUuidsInRange(ServiceKind kind,att::Handle range_start,att::Handle range_end,ServiceCallback svc_callback,att::ResultFunction<> status_callback,std::vector<UUID> uuids)388 void DiscoverServicesWithUuidsInRange(ServiceKind kind,
389 att::Handle range_start,
390 att::Handle range_end,
391 ServiceCallback svc_callback,
392 att::ResultFunction<> status_callback,
393 std::vector<UUID> uuids) override {
394 BT_ASSERT(range_start <= range_end);
395 BT_ASSERT(!uuids.empty());
396 UUID uuid = uuids.back();
397 uuids.pop_back();
398
399 auto recursive_status_cb = [this,
400 range_start,
401 range_end,
402 kind,
403 svc_cb = svc_callback.share(),
404 status_cb = std::move(status_callback),
405 remaining_uuids =
406 std::move(uuids)](auto status) mutable {
407 // Base case
408 if (status.is_error() || remaining_uuids.empty()) {
409 status_cb(status);
410 return;
411 }
412
413 // Recursively discover with the remaining UUIDs.
414 DiscoverServicesWithUuidsInRange(kind,
415 range_start,
416 range_end,
417 std::move(svc_cb),
418 std::move(status_cb),
419 std::move(remaining_uuids));
420 };
421
422 // Discover the last uuid in uuids.
423 DiscoverServicesByUuidInRange(kind,
424 range_start,
425 range_end,
426 std::move(svc_callback),
427 std::move(recursive_status_cb),
428 uuid);
429 }
430
DiscoverServicesByUuidInRange(ServiceKind kind,att::Handle start,att::Handle end,ServiceCallback svc_callback,att::ResultFunction<> status_callback,UUID uuid)431 void DiscoverServicesByUuidInRange(ServiceKind kind,
432 att::Handle start,
433 att::Handle end,
434 ServiceCallback svc_callback,
435 att::ResultFunction<> status_callback,
436 UUID uuid) {
437 size_t uuid_size_bytes = uuid.CompactSize(/* allow 32 bit UUIDs */ false);
438 auto pdu =
439 NewPDU(sizeof(att::FindByTypeValueRequestParams) + uuid_size_bytes);
440 if (!pdu) {
441 status_callback(ToResult(HostError::kOutOfMemory));
442 return;
443 }
444
445 att::PacketWriter writer(att::kFindByTypeValueRequest, pdu.get());
446 auto* params = writer.mutable_payload<att::FindByTypeValueRequestParams>();
447 params->start_handle = htole16(start);
448 params->end_handle = htole16(end);
449 params->type =
450 htole16(kind == ServiceKind::PRIMARY ? types::kPrimaryService16
451 : types::kSecondaryService16);
452 MutableBufferView value_view(params->value, uuid_size_bytes);
453 uuid.ToBytes(&value_view, /* allow 32 bit UUIDs */ false);
454
455 auto rsp_cb = [this,
456 kind,
457 discovery_range_start = start,
458 discovery_range_end = end,
459 svc_cb = std::move(svc_callback),
460 res_cb = std::move(status_callback),
461 uuid](att::Bearer::TransactionResult result) mutable {
462 if (result.is_error()) {
463 const att::Error& error = result.error_value().first;
464
465 // An Error Response code of "Attribute Not Found" indicates the end of
466 // the procedure (v5.0, Vol 3, Part G, 4.4.2).
467 if (error.is(att::ErrorCode::kAttributeNotFound)) {
468 res_cb(fit::ok());
469 return;
470 }
471
472 res_cb(fit::error(error));
473 return;
474 }
475
476 const att::PacketReader& rsp = result.value();
477 BT_DEBUG_ASSERT(rsp.opcode() == att::kFindByTypeValueResponse);
478
479 size_t payload_size = rsp.payload_size();
480 if (payload_size < 1 ||
481 payload_size % sizeof(att::FindByTypeValueResponseParams) != 0) {
482 // Received malformed response. Disconnect the link.
483 bt_log(DEBUG,
484 "gatt",
485 "received malformed Find By Type Value response with size %zu",
486 payload_size);
487 att_->ShutDown();
488 res_cb(ToResult(HostError::kPacketMalformed));
489 return;
490 }
491
492 BufferView handle_list = rsp.payload_data();
493
494 std::optional<att::Handle> last_handle;
495 while (handle_list.size()) {
496 const auto& entry = handle_list.To<att::HandlesInformationList>();
497
498 att::Handle start = le16toh(entry.handle);
499 att::Handle end = le16toh(entry.group_end_handle);
500
501 if (end < start) {
502 bt_log(DEBUG, "gatt", "received malformed service range values");
503 res_cb(ToResult(HostError::kPacketMalformed));
504 return;
505 }
506
507 if (start < discovery_range_start || start > discovery_range_end) {
508 bt_log(DEBUG,
509 "gatt",
510 "received service range values outside of requested range");
511 res_cb(ToResult(HostError::kPacketMalformed));
512 return;
513 }
514
515 // "The Handles Information List is ordered sequentially based on the
516 // found attribute handles." (Core Spec v5.3, Vol 3, Part F,
517 // Sec 3.4.3.4)
518 if (last_handle.has_value() && start <= last_handle.value()) {
519 bt_log(DEBUG, "gatt", "received services out of order");
520 res_cb(ToResult(HostError::kPacketMalformed));
521 return;
522 }
523
524 ServiceData service(kind, start, end, uuid);
525
526 // Notify the handler.
527 svc_cb(service);
528
529 // HandlesInformationList is a single element of the list.
530 size_t entry_length = sizeof(att::HandlesInformationList);
531 handle_list = handle_list.view(entry_length);
532
533 last_handle = service.range_end;
534 }
535
536 // The procedure is over if we have reached the end of the handle range.
537 if (!last_handle.has_value() ||
538 last_handle.value() == discovery_range_end) {
539 res_cb(fit::ok());
540 return;
541 }
542
543 // Request the next batch.
544 DiscoverServicesByUuidInRange(kind,
545 last_handle.value() + 1,
546 discovery_range_end,
547 std::move(svc_cb),
548 std::move(res_cb),
549 uuid);
550 };
551
552 att_->StartTransaction(std::move(pdu), BindCallback(std::move(rsp_cb)));
553 }
554
DiscoverCharacteristics(att::Handle range_start,att::Handle range_end,CharacteristicCallback chrc_callback,att::ResultFunction<> status_callback)555 void DiscoverCharacteristics(att::Handle range_start,
556 att::Handle range_end,
557 CharacteristicCallback chrc_callback,
558 att::ResultFunction<> status_callback) override {
559 BT_ASSERT(range_start <= range_end);
560 BT_ASSERT(chrc_callback);
561 BT_ASSERT(status_callback);
562
563 if (range_start == range_end) {
564 status_callback(fit::ok());
565 return;
566 }
567
568 auto read_by_type_cb = [this,
569 range_end,
570 chrc_cb = std::move(chrc_callback),
571 res_cb = status_callback.share()](
572 ReadByTypeResult result) mutable {
573 TRACE_DURATION("bluetooth",
574 "gatt::Client::DiscoverCharacteristics read_by_type_cb");
575
576 if (result.is_error()) {
577 const auto error = result.error_value().error;
578
579 // An Error Response code of "Attribute Not Found" indicates the end
580 // of the procedure (v5.0, Vol 3, Part G, 4.6.1).
581 if (error.is(att::ErrorCode::kAttributeNotFound)) {
582 res_cb(fit::ok());
583 return;
584 }
585
586 res_cb(fit::error(error));
587 return;
588 }
589
590 auto& attributes = result.value();
591
592 // ReadByTypeRequest() should return an error result if there are no
593 // attributes in a success response.
594 BT_ASSERT(!attributes.empty());
595
596 for (auto& char_attr : attributes) {
597 Properties properties = 0u;
598 att::Handle value_handle = 0u;
599 UUID value_uuid;
600
601 // The characteristic declaration value contains:
602 // 1 octet: properties
603 // 2 octets: value handle
604 // 2 or 16 octets: UUID
605 if (char_attr.value.size() ==
606 sizeof(CharacteristicDeclarationAttributeValue<
607 att::UUIDType::k16Bit>)) {
608 auto attr_value = char_attr.value.To<
609 CharacteristicDeclarationAttributeValue<att::UUIDType::k16Bit>>();
610 properties = attr_value.properties;
611 value_handle = le16toh(attr_value.value_handle);
612 value_uuid = UUID(attr_value.value_uuid);
613 } else if (char_attr.value.size() ==
614 sizeof(CharacteristicDeclarationAttributeValue<
615 att::UUIDType::k128Bit>)) {
616 auto attr_value =
617 char_attr.value.To<CharacteristicDeclarationAttributeValue<
618 att::UUIDType::k128Bit>>();
619 properties = attr_value.properties;
620 value_handle = le16toh(attr_value.value_handle);
621 value_uuid = UUID(attr_value.value_uuid);
622 } else {
623 bt_log(DEBUG,
624 "gatt",
625 "invalid characteristic declaration attribute value size");
626 att_->ShutDown();
627 res_cb(ToResult(HostError::kPacketMalformed));
628 return;
629 }
630
631 // Vol 3, Part G, 3.3: "The Characteristic Value declaration shall
632 // exist immediately following the characteristic declaration."
633 if (value_handle != char_attr.handle + 1) {
634 bt_log(
635 DEBUG, "gatt", "characteristic value doesn't follow declaration");
636 res_cb(ToResult(HostError::kPacketMalformed));
637 return;
638 }
639
640 // Notify the handler. By default, there are no extended properties to
641 // report.
642 chrc_cb(CharacteristicData(properties,
643 /*ext_props=*/std::nullopt,
644 char_attr.handle,
645 value_handle,
646 value_uuid));
647 }
648
649 // The procedure is over if we have reached the end of the handle
650 // range.
651 const auto last_handle = attributes.back().handle;
652 if (last_handle == range_end) {
653 res_cb(fit::ok());
654 return;
655 }
656
657 // Request the next batch.
658 DiscoverCharacteristics(
659 last_handle + 1, range_end, std::move(chrc_cb), std::move(res_cb));
660 };
661
662 ReadByTypeRequest(types::kCharacteristicDeclaration,
663 range_start,
664 range_end,
665 std::move(read_by_type_cb));
666 }
667
DiscoverDescriptors(att::Handle range_start,att::Handle range_end,DescriptorCallback desc_callback,att::ResultFunction<> status_callback)668 void DiscoverDescriptors(att::Handle range_start,
669 att::Handle range_end,
670 DescriptorCallback desc_callback,
671 att::ResultFunction<> status_callback) override {
672 BT_DEBUG_ASSERT(range_start <= range_end);
673 BT_DEBUG_ASSERT(desc_callback);
674 BT_DEBUG_ASSERT(status_callback);
675
676 auto pdu = NewPDU(sizeof(att::FindInformationRequestParams));
677 if (!pdu) {
678 status_callback(ToResult(HostError::kOutOfMemory));
679 return;
680 }
681
682 att::PacketWriter writer(att::kFindInformationRequest, pdu.get());
683 auto* params = writer.mutable_payload<att::FindInformationRequestParams>();
684 params->start_handle = htole16(range_start);
685 params->end_handle = htole16(range_end);
686
687 auto rsp_cb = [this,
688 range_start,
689 range_end,
690 desc_cb = std::move(desc_callback),
691 res_cb = std::move(status_callback)](
692 att::Bearer::TransactionResult result) mutable {
693 if (result.is_error()) {
694 const att::Error& error = result.error_value().first;
695
696 // An Error Response code of "Attribute Not Found" indicates the end of
697 // the procedure (v5.0, Vol 3, Part G, 4.7.1).
698 if (error.is(att::ErrorCode::kAttributeNotFound)) {
699 res_cb(fit::ok());
700 return;
701 }
702
703 res_cb(fit::error(error));
704 return;
705 }
706 const att::PacketReader& rsp = result.value();
707 BT_DEBUG_ASSERT(rsp.opcode() == att::kFindInformationResponse);
708 TRACE_DURATION("bluetooth",
709 "gatt::Client::DiscoverDescriptors rsp_cb",
710 "size",
711 rsp.size());
712
713 if (rsp.payload_size() < sizeof(att::FindInformationResponseParams)) {
714 bt_log(DEBUG, "gatt", "received malformed Find Information response");
715 att_->ShutDown();
716 res_cb(ToResult(HostError::kPacketMalformed));
717 return;
718 }
719
720 const auto& rsp_params =
721 rsp.payload<att::FindInformationResponseParams>();
722 BufferView entries = rsp.payload_data().view(sizeof(rsp_params.format));
723
724 att::Handle last_handle;
725 bool well_formed;
726 switch (rsp_params.format) {
727 case att::UUIDType::k16Bit:
728 well_formed =
729 ProcessDescriptorDiscoveryResponse<att::UUIDType::k16Bit>(
730 range_start,
731 range_end,
732 entries,
733 desc_cb.share(),
734 &last_handle);
735 break;
736 case att::UUIDType::k128Bit:
737 well_formed =
738 ProcessDescriptorDiscoveryResponse<att::UUIDType::k128Bit>(
739 range_start,
740 range_end,
741 entries,
742 desc_cb.share(),
743 &last_handle);
744 break;
745 default:
746 bt_log(DEBUG, "gatt", "invalid information data format");
747 well_formed = false;
748 break;
749 }
750
751 if (!well_formed) {
752 att_->ShutDown();
753 res_cb(ToResult(HostError::kPacketMalformed));
754 return;
755 }
756
757 // The procedure is over if we have reached the end of the handle range.
758 if (last_handle == range_end) {
759 res_cb(fit::ok());
760 return;
761 }
762
763 // Request the next batch.
764 DiscoverDescriptors(
765 last_handle + 1, range_end, std::move(desc_cb), std::move(res_cb));
766 };
767
768 att_->StartTransaction(std::move(pdu), BindCallback(std::move(rsp_cb)));
769 }
770
ReadRequest(att::Handle handle,ReadCallback callback)771 void ReadRequest(att::Handle handle, ReadCallback callback) override {
772 auto pdu = NewPDU(sizeof(att::ReadRequestParams));
773 if (!pdu) {
774 callback(ToResult(HostError::kOutOfMemory),
775 BufferView(),
776 /*maybe_truncated=*/false);
777 return;
778 }
779
780 att::PacketWriter writer(att::kReadRequest, pdu.get());
781 auto params = writer.mutable_payload<att::ReadRequestParams>();
782 params->handle = htole16(handle);
783
784 auto rsp_cb = [this, callback = std::move(callback)](
785 att::Bearer::TransactionResult result) {
786 if (result.is_ok()) {
787 const att::PacketReader& rsp = result.value();
788 BT_DEBUG_ASSERT(rsp.opcode() == att::kReadResponse);
789 bool maybe_truncated =
790 (rsp.payload_size() != att::kMaxAttributeValueLength) &&
791 (rsp.payload_size() == (mtu() - sizeof(rsp.opcode())));
792 callback(fit::ok(), rsp.payload_data(), maybe_truncated);
793 return;
794 }
795 const auto& [error, handle] = result.error_value();
796 bt_log(DEBUG,
797 "gatt",
798 "read request failed: %s, handle %#.4x",
799 bt_str(error),
800 handle);
801 callback(fit::error(error), BufferView(), /*maybe_truncated=*/false);
802 };
803
804 att_->StartTransaction(std::move(pdu), BindCallback(std::move(rsp_cb)));
805 }
806
ReadByTypeRequest(const UUID & type,att::Handle start_handle,att::Handle end_handle,ReadByTypeCallback callback)807 void ReadByTypeRequest(const UUID& type,
808 att::Handle start_handle,
809 att::Handle end_handle,
810 ReadByTypeCallback callback) override {
811 size_t type_size = type.CompactSize(/*allow_32bit=*/false);
812 BT_ASSERT(type_size == sizeof(uint16_t) || type_size == sizeof(UInt128));
813 auto pdu = NewPDU(type_size == sizeof(uint16_t)
814 ? sizeof(att::ReadByTypeRequestParams16)
815 : sizeof(att::ReadByTypeRequestParams128));
816 if (!pdu) {
817 callback(fit::error(
818 ReadByTypeError{Error(HostError::kOutOfMemory), std::nullopt}));
819 return;
820 }
821
822 att::PacketWriter writer(att::kReadByTypeRequest, pdu.get());
823 if (type_size == sizeof(uint16_t)) {
824 auto params = writer.mutable_payload<att::ReadByTypeRequestParams16>();
825 params->start_handle = htole16(start_handle);
826 params->end_handle = htole16(end_handle);
827 auto type_view = MutableBufferView(¶ms->type, sizeof(params->type));
828 type.ToBytes(&type_view, /*allow_32bit=*/false);
829 } else {
830 auto params = writer.mutable_payload<att::ReadByTypeRequestParams128>();
831 params->start_handle = htole16(start_handle);
832 params->end_handle = htole16(end_handle);
833 auto type_view = MutableBufferView(¶ms->type, sizeof(params->type));
834 type.ToBytes(&type_view, /*allow_32bit=*/false);
835 }
836
837 auto rsp_cb = [this,
838 callback = std::move(callback),
839 start_handle,
840 end_handle](att::Bearer::TransactionResult result) {
841 if (result.is_error()) {
842 const auto& [error, handle] = result.error_value();
843 bt_log(DEBUG,
844 "gatt",
845 "read by type request failed: %s, handle %#.4x",
846 bt_str(error),
847 handle);
848 // Only some errors have handles.
849 std::optional<att::Handle> cb_handle =
850 handle ? std::optional(handle) : std::nullopt;
851 callback(fit::error(ReadByTypeError{error, cb_handle}));
852 return;
853 }
854 const att::PacketReader& rsp = result.value();
855 BT_ASSERT(rsp.opcode() == att::kReadByTypeResponse);
856 if (rsp.payload_size() < sizeof(att::ReadByTypeResponseParams)) {
857 callback(fit::error(
858 ReadByTypeError{Error(HostError::kPacketMalformed), std::nullopt}));
859 return;
860 }
861
862 const auto& params = rsp.payload<att::ReadByTypeResponseParams>();
863 // The response contains a list of attribute handle-value pairs of uniform
864 // length.
865 const size_t list_size = rsp.payload_size() - sizeof(params.length);
866 const size_t pair_size = params.length;
867
868 // Success response must:
869 // a) Specify valid pair length (at least the size of a handle).
870 // b) Have at least 1 pair (otherwise the Attribute Not Found error should
871 // have been
872 // sent).
873 // c) Have a list size that is evenly divisible by pair size.
874 if (pair_size < sizeof(att::Handle) || list_size < sizeof(att::Handle) ||
875 list_size % pair_size != 0) {
876 callback(fit::error(
877 ReadByTypeError{Error(HostError::kPacketMalformed), std::nullopt}));
878 return;
879 }
880
881 std::vector<ReadByTypeValue> attributes;
882 BufferView attr_list_view(params.attribute_data_list,
883 rsp.payload_size() - sizeof(params.length));
884 while (attr_list_view.size() >= params.length) {
885 const BufferView pair_view = attr_list_view.view(0, pair_size);
886 const att::Handle handle = le16toh(pair_view.To<att::Handle>());
887
888 if (handle < start_handle || handle > end_handle) {
889 bt_log(TRACE,
890 "gatt",
891 "client received read by type response with handle outside of "
892 "requested range");
893 callback(fit::error(ReadByTypeError{
894 Error(HostError::kPacketMalformed), std::nullopt}));
895 return;
896 }
897
898 if (!attributes.empty() && attributes.back().handle >= handle) {
899 bt_log(TRACE,
900 "gatt",
901 "client received read by type response with handles in "
902 "non-increasing order");
903 callback(fit::error(ReadByTypeError{
904 Error(HostError::kPacketMalformed), std::nullopt}));
905 return;
906 }
907
908 auto value_view = pair_view.view(sizeof(att::Handle));
909
910 // The value may be truncated if it maxes out the length parameter or
911 // the MTU, whichever is smaller (Core Spec v5.2, Vol 3, Part F,
912 // Sec 3.4.4).
913 const size_t mtu_max_value_size =
914 mtu() - sizeof(att::kReadByTypeResponse) -
915 sizeof(att::ReadByTypeResponseParams) - sizeof(att::Handle);
916 bool maybe_truncated =
917 (value_view.size() ==
918 std::min(static_cast<size_t>(att::kMaxReadByTypeValueLength),
919 mtu_max_value_size));
920
921 attributes.push_back(
922 ReadByTypeValue{handle, value_view, maybe_truncated});
923
924 // Advance list view to next pair (or end of list).
925 attr_list_view = attr_list_view.view(pair_size);
926 }
927 BT_ASSERT(attr_list_view.size() == 0);
928
929 callback(fit::ok(std::move(attributes)));
930 };
931
932 att_->StartTransaction(std::move(pdu), BindCallback(std::move(rsp_cb)));
933 }
934
ReadBlobRequest(att::Handle handle,uint16_t offset,ReadCallback callback)935 void ReadBlobRequest(att::Handle handle,
936 uint16_t offset,
937 ReadCallback callback) override {
938 auto pdu = NewPDU(sizeof(att::ReadBlobRequestParams));
939 if (!pdu) {
940 callback(ToResult(HostError::kOutOfMemory),
941 BufferView(),
942 /*maybe_truncated=*/false);
943 return;
944 }
945
946 att::PacketWriter writer(att::kReadBlobRequest, pdu.get());
947 auto params = writer.mutable_payload<att::ReadBlobRequestParams>();
948 params->handle = htole16(handle);
949 params->offset = htole16(offset);
950
951 auto rsp_cb = [this, offset, callback = std::move(callback)](
952 att::Bearer::TransactionResult result) {
953 if (result.is_ok()) {
954 const att::PacketReader& rsp = result.value();
955 BT_DEBUG_ASSERT(rsp.opcode() == att::kReadBlobResponse);
956 bool maybe_truncated =
957 (static_cast<size_t>(offset) + rsp.payload_size() !=
958 att::kMaxAttributeValueLength) &&
959 (rsp.payload_data().size() == (mtu() - sizeof(att::OpCode)));
960 callback(fit::ok(), rsp.payload_data(), maybe_truncated);
961 return;
962 }
963 const auto& [error, handle] = result.error_value();
964 bt_log(DEBUG,
965 "gatt",
966 "read blob request failed: %s, handle: %#.4x",
967 bt_str(error),
968 handle);
969 callback(fit::error(error), BufferView(), /*maybe_truncated=*/false);
970 };
971
972 att_->StartTransaction(std::move(pdu), BindCallback(std::move(rsp_cb)));
973 }
974
WriteRequest(att::Handle handle,const ByteBuffer & value,att::ResultFunction<> callback)975 void WriteRequest(att::Handle handle,
976 const ByteBuffer& value,
977 att::ResultFunction<> callback) override {
978 const size_t payload_size = sizeof(att::WriteRequestParams) + value.size();
979 if (sizeof(att::OpCode) + payload_size > att_->mtu()) {
980 bt_log(TRACE, "gatt", "write request payload exceeds MTU");
981 callback(ToResult(HostError::kPacketMalformed));
982 return;
983 }
984
985 auto pdu = NewPDU(payload_size);
986 if (!pdu) {
987 callback(ToResult(HostError::kOutOfMemory));
988 return;
989 }
990
991 att::PacketWriter writer(att::kWriteRequest, pdu.get());
992 auto params = writer.mutable_payload<att::WriteRequestParams>();
993 params->handle = htole16(handle);
994
995 auto value_view =
996 writer.mutable_payload_data().mutable_view(sizeof(att::Handle));
997 value.Copy(&value_view);
998
999 auto rsp_cb = [this, callback = std::move(callback)](
1000 att::Bearer::TransactionResult result) {
1001 if (result.is_error()) {
1002 const auto& [error, handle] = result.error_value();
1003 bt_log(DEBUG,
1004 "gatt",
1005 "write request failed: %s, handle: %#.2x",
1006 bt_str(error),
1007 handle);
1008 callback(fit::error(error));
1009 return;
1010 }
1011 const att::PacketReader& rsp = result.value();
1012 BT_DEBUG_ASSERT(rsp.opcode() == att::kWriteResponse);
1013
1014 if (rsp.payload_size()) {
1015 att_->ShutDown();
1016 callback(ToResult(HostError::kPacketMalformed));
1017 return;
1018 }
1019
1020 callback(fit::ok());
1021 };
1022
1023 att_->StartTransaction(std::move(pdu), BindCallback(std::move(rsp_cb)));
1024 }
1025
1026 // An internal object for storing the write queue, callback, and reliability
1027 // mode of a long write operation.
1028 struct PreparedWrite {
1029 bt::att::PrepareWriteQueue prep_write_queue;
1030 bt::att::ResultFunction<> callback;
1031 ReliableMode reliable_mode;
1032 };
1033
ExecutePrepareWrites(att::PrepareWriteQueue prep_write_queue,ReliableMode reliable_mode,att::ResultFunction<> callback)1034 void ExecutePrepareWrites(att::PrepareWriteQueue prep_write_queue,
1035 ReliableMode reliable_mode,
1036 att::ResultFunction<> callback) override {
1037 PreparedWrite new_request;
1038 new_request.prep_write_queue = std::move(prep_write_queue);
1039 new_request.callback = std::move(callback);
1040 new_request.reliable_mode = std::move(reliable_mode);
1041 long_write_queue_.push(std::move(new_request));
1042
1043 // If the |long_write_queue| has a pending request, then appending this
1044 // request will be sufficient, otherwise kick off the request.
1045 if (long_write_queue_.size() == 1) {
1046 ProcessWriteQueue(std::move(long_write_queue_.front()));
1047 }
1048 }
1049
ProcessWriteQueue(PreparedWrite prep_write)1050 void ProcessWriteQueue(PreparedWrite prep_write) {
1051 if (!prep_write.prep_write_queue.empty()) {
1052 att::QueuedWrite prep_write_request =
1053 std::move(prep_write.prep_write_queue.front());
1054 // A copy of the |prep_write_request| is made to pass into the capture
1055 // list for |prep_write_cb|. It will be used to validate the echoed blob.
1056 auto prep_write_copy = att::QueuedWrite(prep_write_request.handle(),
1057 prep_write_request.offset(),
1058 prep_write_request.value());
1059 prep_write.prep_write_queue.pop();
1060
1061 auto prep_write_cb = [this,
1062 prep_write = std::move(prep_write),
1063 requested_blob = std::move(prep_write_copy)](
1064 att::Result<> status,
1065 const ByteBuffer& blob) mutable {
1066 // If the write fails, cancel the prep writes and then move on to the
1067 // next long write in the queue. The device will echo the value written
1068 // in the blob, according to the spec (Vol 3, Part G, 4.9.4). The offset
1069 // and value will be verified if the requested |prep_write.mode| is
1070 // enabled (Vol 3, Part G, 4.9.5).
1071
1072 if (prep_write.reliable_mode == ReliableMode::kEnabled) {
1073 if (blob.size() < sizeof(att::PrepareWriteResponseParams)) {
1074 // The response blob is malformed.
1075 status = ToResult(HostError::kNotReliable);
1076 } else {
1077 auto blob_offset = le16toh(
1078 blob.ReadMember<&att::PrepareWriteResponseParams::offset>());
1079 auto blob_value =
1080 blob.view(sizeof(att::PrepareWriteResponseParams));
1081 if ((blob_offset != requested_blob.offset()) ||
1082 !(blob_value == requested_blob.value())) {
1083 status = ToResult(HostError::kNotReliable);
1084 }
1085 }
1086 }
1087
1088 if (status.is_error()) {
1089 auto exec_write_cb = [this,
1090 callback = std::move(prep_write.callback),
1091 prep_write_status =
1092 status](att::Result<> status) mutable {
1093 // In this case return the original failure status. This effectively
1094 // overrides the ExecuteWrite status.
1095 callback(prep_write_status);
1096 // Now that this request is complete, remove it from the overall
1097 // queue.
1098 BT_DEBUG_ASSERT(!long_write_queue_.empty());
1099 long_write_queue_.pop();
1100
1101 if (long_write_queue_.size() > 0) {
1102 ProcessWriteQueue(std::move(long_write_queue_.front()));
1103 }
1104 };
1105
1106 ExecuteWriteRequest(att::ExecuteWriteFlag::kCancelAll,
1107 std::move(exec_write_cb));
1108
1109 return;
1110 }
1111
1112 ProcessWriteQueue(std::move(prep_write));
1113 };
1114
1115 PrepareWriteRequest(prep_write_request.handle(),
1116 prep_write_request.offset(),
1117 std::move(prep_write_request.value()),
1118 std::move(prep_write_cb));
1119 }
1120 // End of this write, send and prepare for next item in overall write queue
1121 else {
1122 auto exec_write_cb = [this, callback = std::move(prep_write.callback)](
1123 att::Result<> status) mutable {
1124 callback(status);
1125 // Now that this request is complete, remove it from the overall
1126 // queue.
1127 BT_DEBUG_ASSERT(!long_write_queue_.empty());
1128 long_write_queue_.pop();
1129
1130 // If the super queue still has any long writes left to execute,
1131 // initiate them
1132 if (long_write_queue_.size() > 0) {
1133 ProcessWriteQueue(std::move(long_write_queue_.front()));
1134 }
1135 };
1136
1137 ExecuteWriteRequest(att::ExecuteWriteFlag::kWritePending,
1138 std::move(exec_write_cb));
1139 }
1140 }
1141
PrepareWriteRequest(att::Handle handle,uint16_t offset,const ByteBuffer & part_value,PrepareCallback callback)1142 void PrepareWriteRequest(att::Handle handle,
1143 uint16_t offset,
1144 const ByteBuffer& part_value,
1145 PrepareCallback callback) override {
1146 const size_t payload_size =
1147 sizeof(att::PrepareWriteRequestParams) + part_value.size();
1148 if (sizeof(att::OpCode) + payload_size > att_->mtu()) {
1149 bt_log(TRACE, "gatt", "prepare write request payload exceeds MTU");
1150 callback(ToResult(HostError::kPacketMalformed), BufferView());
1151 return;
1152 }
1153
1154 auto pdu = NewPDU(payload_size);
1155 if (!pdu) {
1156 callback(ToResult(HostError::kOutOfMemory), BufferView());
1157 return;
1158 }
1159
1160 att::PacketWriter writer(att::kPrepareWriteRequest, pdu.get());
1161 auto params = writer.mutable_payload<att::PrepareWriteRequestParams>();
1162 params->handle = htole16(handle);
1163 params->offset = htole16(offset);
1164
1165 auto header_size = sizeof(att::Handle) + sizeof(uint16_t);
1166 auto value_view = writer.mutable_payload_data().mutable_view(header_size);
1167 part_value.Copy(&value_view);
1168
1169 auto rsp_cb = [callback = std::move(callback)](
1170 att::Bearer::TransactionResult result) {
1171 if (result.is_ok()) {
1172 const att::PacketReader& rsp = result.value();
1173 BT_DEBUG_ASSERT(rsp.opcode() == att::kPrepareWriteResponse);
1174 callback(fit::ok(), rsp.payload_data());
1175 return;
1176 }
1177 const auto& [error, handle] = result.error_value();
1178 bt_log(DEBUG,
1179 "gatt",
1180 "prepare write request failed: %s, handle:"
1181 "%#.4x",
1182 bt_str(error),
1183 handle);
1184 callback(fit::error(error), BufferView());
1185 };
1186
1187 att_->StartTransaction(std::move(pdu), BindCallback(std::move(rsp_cb)));
1188 }
1189
ExecuteWriteRequest(att::ExecuteWriteFlag flag,att::ResultFunction<> callback)1190 void ExecuteWriteRequest(att::ExecuteWriteFlag flag,
1191 att::ResultFunction<> callback) override {
1192 const size_t payload_size = sizeof(att::ExecuteWriteRequestParams);
1193 if (sizeof(att::OpCode) + payload_size > att_->mtu()) {
1194 // This really shouldn't happen because we aren't consuming any actual
1195 // payload here, but just in case...
1196 bt_log(TRACE, "gatt", "execute write request size exceeds MTU");
1197 callback(ToResult(HostError::kPacketMalformed));
1198 return;
1199 }
1200
1201 auto pdu = NewPDU(payload_size);
1202 if (!pdu) {
1203 callback(ToResult(HostError::kOutOfMemory));
1204 return;
1205 }
1206
1207 att::PacketWriter writer(att::kExecuteWriteRequest, pdu.get());
1208 auto params = writer.mutable_payload<att::ExecuteWriteRequestParams>();
1209 params->flags = flag;
1210
1211 auto rsp_cb = [this, callback = std::move(callback)](
1212 att::Bearer::TransactionResult result) {
1213 if (result.is_ok()) {
1214 const att::PacketReader& rsp = result.value();
1215 BT_DEBUG_ASSERT(rsp.opcode() == att::kExecuteWriteResponse);
1216
1217 if (rsp.payload_size()) {
1218 att_->ShutDown();
1219 callback(ToResult(HostError::kPacketMalformed));
1220 return;
1221 }
1222
1223 callback(fit::ok());
1224 return;
1225 }
1226 const att::Error& error = result.error_value().first;
1227 bt_log(DEBUG, "gatt", "execute write request failed: %s", bt_str(error));
1228 callback(fit::error(error));
1229 };
1230
1231 att_->StartTransaction(std::move(pdu), BindCallback(std::move(rsp_cb)));
1232 }
1233
WriteWithoutResponse(att::Handle handle,const ByteBuffer & value,att::ResultFunction<> callback)1234 void WriteWithoutResponse(att::Handle handle,
1235 const ByteBuffer& value,
1236 att::ResultFunction<> callback) override {
1237 const size_t payload_size = sizeof(att::WriteRequestParams) + value.size();
1238 if (sizeof(att::OpCode) + payload_size > att_->mtu()) {
1239 bt_log(DEBUG, "gatt", "write request payload exceeds MTU");
1240 callback(ToResult(HostError::kFailed));
1241 return;
1242 }
1243
1244 auto pdu = NewPDU(payload_size);
1245 if (!pdu) {
1246 callback(ToResult(HostError::kOutOfMemory));
1247 return;
1248 }
1249
1250 att::PacketWriter writer(att::kWriteCommand, pdu.get());
1251 auto params = writer.mutable_payload<att::WriteRequestParams>();
1252 params->handle = htole16(handle);
1253
1254 auto value_view =
1255 writer.mutable_payload_data().mutable_view(sizeof(att::Handle));
1256 value.Copy(&value_view);
1257
1258 [[maybe_unused]] bool _ = att_->SendWithoutResponse(std::move(pdu));
1259 callback(fit::ok());
1260 }
1261
SetNotificationHandler(NotificationCallback handler)1262 void SetNotificationHandler(NotificationCallback handler) override {
1263 notification_handler_ = std::move(handler);
1264 }
1265
1266 // Wraps |callback| in a TransactionCallback that only runs if this Client is
1267 // still alive.
BindCallback(att::Bearer::TransactionCallback callback)1268 att::Bearer::TransactionCallback BindCallback(
1269 att::Bearer::TransactionCallback callback) {
1270 return [self = weak_self_.GetWeakPtr(),
1271 callback = std::move(callback)](auto rsp) mutable {
1272 if (self.is_alive()) {
1273 callback(rsp);
1274 }
1275 };
1276 }
1277
1278 att::Bearer::WeakPtr att_;
1279 att::Bearer::HandlerId not_handler_id_;
1280 att::Bearer::HandlerId ind_handler_id_;
1281
1282 NotificationCallback notification_handler_;
1283 // |long_write_queue_| contains long write requests, their
1284 // associated callbacks and reliable write modes.
1285 // Series of PrepareWrites are executed or cancelled at the same time so
1286 // this is used to block while a single series is processed.
1287 //
1288 // While the top element is processed, the |PrepareWriteQueue| and callback
1289 // will be empty and will be popped once the queue is cancelled or executed.
1290 // Following the processing of each queue, the client will automatically
1291 // process the next queue in the |long_write_queue_|.
1292 std::queue<PreparedWrite> long_write_queue_;
1293 WeakSelf<Client> weak_self_;
1294
1295 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Impl);
1296 };
1297
1298 // static
Create(att::Bearer::WeakPtr bearer)1299 std::unique_ptr<Client> Client::Create(att::Bearer::WeakPtr bearer) {
1300 return std::make_unique<Impl>(std::move(bearer));
1301 }
1302
1303 } // namespace bt::gatt
1304