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