• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&params->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(&params->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