xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/gatt/remote_service.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/remote_service.h"
16 
17 #include "lib/fit/defer.h"
18 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
19 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/slab_allocator.h"
21 
22 namespace bt::gatt {
23 namespace {
24 
IsInternalUuid(const UUID & uuid)25 bool IsInternalUuid(const UUID& uuid) {
26   // clang-format off
27   return
28     uuid == types::kPrimaryService ||
29     uuid == types::kSecondaryService ||
30     uuid == types::kIncludeDeclaration ||
31     uuid == types::kCharacteristicDeclaration ||
32     uuid == types::kCharacteristicExtProperties ||
33     uuid == types::kCharacteristicUserDescription ||
34     uuid == types::kClientCharacteristicConfig ||
35     uuid == types::kServerCharacteristicConfig ||
36     uuid == types::kCharacteristicFormat ||
37     uuid == types::kCharacteristicAggregateFormat;
38   // clang-format on
39 }
40 
ReportReadValueError(att::Result<> status,RemoteService::ReadValueCallback callback)41 void ReportReadValueError(att::Result<> status,
42                           RemoteService::ReadValueCallback callback) {
43   callback(status, BufferView(), /*maybe_truncated=*/false);
44 }
45 
CharacteristicsToCharacteristicMap(const std::map<CharacteristicHandle,RemoteCharacteristic> & characteristics)46 CharacteristicMap CharacteristicsToCharacteristicMap(
47     const std::map<CharacteristicHandle, RemoteCharacteristic>&
48         characteristics) {
49   CharacteristicMap characteristic_map;
50   for (const auto& [_handle, chrc] : characteristics) {
51     characteristic_map.try_emplace(_handle, chrc.info(), chrc.descriptors());
52   }
53   return characteristic_map;
54 }
55 
56 }  // namespace
57 
RemoteService(const ServiceData & service_data,Client::WeakPtr client)58 RemoteService::RemoteService(const ServiceData& service_data,
59                              Client::WeakPtr client)
60     : service_data_(service_data),
61       client_(std::move(client)),
62       remaining_descriptor_requests_(kSentinel) {
63   PW_DCHECK(client_.is_alive());
64 }
65 
~RemoteService()66 RemoteService::~RemoteService() {
67   for (auto& chr : characteristics_) {
68     chr.second.set_service_changed(service_changed_);
69   }
70   characteristics_.clear();
71 
72   std::vector<fit::callback<void()>> rm_handlers = std::move(rm_handlers_);
73   for (auto& handler : rm_handlers) {
74     handler();
75   }
76 }
77 
AddRemovedHandler(fit::closure handler)78 bool RemoteService::AddRemovedHandler(fit::closure handler) {
79   rm_handlers_.emplace_back(std::move(handler));
80   return true;
81 }
82 
DiscoverCharacteristics(CharacteristicCallback callback)83 void RemoteService::DiscoverCharacteristics(CharacteristicCallback callback) {
84   // Characteristics already discovered. Return success.
85   if (HasCharacteristics()) {
86     // We return a new copy of only the immutable data of our characteristics
87     // and their descriptors. This requires a copy, which *could* be expensive
88     // in the (unlikely) case that a service has a very large number of
89     // characteristics.
90     callback(fit::ok(), CharacteristicsToCharacteristicMap(characteristics_));
91     return;
92   }
93 
94   // Queue this request.
95   pending_discov_reqs_.emplace_back(std::move(callback));
96 
97   // Nothing to do if a write request is already pending.
98   if (pending_discov_reqs_.size() > 1u)
99     return;
100 
101   auto self = GetWeakPtr();
102   auto chrc_cb = [self](const CharacteristicData& chr) {
103     if (!self.is_alive()) {
104       return;
105     }
106     // try_emplace should not fail here; our GATT::Client explicitly ensures
107     // that handles are strictly ascending (as described in the spec) so we
108     // should never see a handle collision
109     self->characteristics_.try_emplace(
110         CharacteristicHandle(chr.value_handle), self->client_, chr);
111   };
112 
113   auto res_cb = [self](att::Result<> status) mutable {
114     if (!self.is_alive()) {
115       return;
116     }
117 
118     if (bt_is_error(status, TRACE, "gatt", "characteristic discovery failed")) {
119       self->characteristics_.clear();
120     }
121 
122     if (self->characteristics_.empty()) {
123       if (status.is_ok()) {
124         // This marks that characteristic discovery has completed
125         // successfully.
126         self->remaining_descriptor_requests_ = 0u;
127       }
128 
129       // Skip descriptor discovery and end the procedure as no characteristics
130       // were found (or the operation failed).
131       self->CompleteCharacteristicDiscovery(status);
132       return;
133     }
134 
135     self->StartDescriptorDiscovery();
136   };
137 
138   client_->DiscoverCharacteristics(service_data_.range_start,
139                                    service_data_.range_end,
140                                    std::move(chrc_cb),
141                                    std::move(res_cb));
142 }
143 
IsDiscovered() const144 bool RemoteService::IsDiscovered() const {
145   // TODO(armansito): Return true only if included services have also been
146   // discovered.
147   return HasCharacteristics();
148 }
149 
ReadCharacteristic(CharacteristicHandle id,ReadValueCallback callback)150 void RemoteService::ReadCharacteristic(CharacteristicHandle id,
151                                        ReadValueCallback callback) {
152   RemoteCharacteristic* chrc;
153   fit::result status = GetCharacteristic(id, &chrc);
154   PW_DCHECK(chrc || status.is_error());
155   if (status.is_error()) {
156     ReportReadValueError(status, std::move(callback));
157     return;
158   }
159 
160   if (!(chrc->info().properties & Property::kRead)) {
161     bt_log(DEBUG, "gatt", "characteristic does not support \"read\"");
162     ReportReadValueError(ToResult(HostError::kNotSupported),
163                          std::move(callback));
164     return;
165   }
166 
167   client_->ReadRequest(chrc->info().value_handle, std::move(callback));
168 }
169 
ReadLongCharacteristic(CharacteristicHandle id,uint16_t offset,size_t max_bytes,ReadValueCallback callback)170 void RemoteService::ReadLongCharacteristic(CharacteristicHandle id,
171                                            uint16_t offset,
172                                            size_t max_bytes,
173                                            ReadValueCallback callback) {
174   RemoteCharacteristic* chrc;
175   fit::result status = GetCharacteristic(id, &chrc);
176   PW_DCHECK(chrc || status.is_error());
177   if (status.is_error()) {
178     ReportReadValueError(status, std::move(callback));
179     return;
180   }
181 
182   if (!(chrc->info().properties & Property::kRead)) {
183     bt_log(DEBUG, "gatt", "characteristic does not support \"read\"");
184     ReportReadValueError(ToResult(HostError::kNotSupported),
185                          std::move(callback));
186     return;
187   }
188 
189   if (max_bytes == 0) {
190     bt_log(TRACE, "gatt", "invalid value for |max_bytes|: 0");
191     ReportReadValueError(ToResult(HostError::kInvalidParameters),
192                          std::move(callback));
193     return;
194   }
195 
196   // Set up the buffer in which we'll accumulate the blobs.
197   auto buffer = NewBuffer(std::min(max_bytes, att::kMaxAttributeValueLength));
198   if (!buffer) {
199     ReportReadValueError(ToResult(HostError::kOutOfMemory),
200                          std::move(callback));
201     return;
202   }
203 
204   ReadLongHelper(chrc->info().value_handle,
205                  offset,
206                  std::move(buffer),
207                  0u /* bytes_read */,
208                  std::move(callback));
209 }
210 
ReadByType(const UUID & type,ReadByTypeCallback callback)211 void RemoteService::ReadByType(const UUID& type, ReadByTypeCallback callback) {
212   // Caller should not request a UUID of an internal attribute (e.g. service
213   // declaration).
214   if (IsInternalUuid(type)) {
215     bt_log(TRACE,
216            "gatt",
217            "ReadByType called with internal GATT type (type: %s)",
218            bt_str(type));
219     callback(ToResult(HostError::kInvalidParameters), {});
220     return;
221   }
222 
223   // Read range is entire service range.
224   ReadByTypeHelper(type,
225                    service_data_.range_start,
226                    service_data_.range_end,
227                    {},
228                    std::move(callback));
229 }
230 
WriteCharacteristic(CharacteristicHandle id,std::vector<uint8_t> value,att::ResultFunction<> cb)231 void RemoteService::WriteCharacteristic(CharacteristicHandle id,
232                                         std::vector<uint8_t> value,
233                                         att::ResultFunction<> cb) {
234   RemoteCharacteristic* chrc;
235   fit::result status = GetCharacteristic(id, &chrc);
236   PW_DCHECK(chrc || status.is_error());
237   if (status.is_error()) {
238     cb(status);
239     return;
240   }
241 
242   if (!(chrc->info().properties & Property::kWrite)) {
243     bt_log(DEBUG, "gatt", "characteristic does not support \"write\"");
244     cb(ToResult(HostError::kNotSupported));
245     return;
246   }
247 
248   client_->WriteRequest(chrc->info().value_handle,
249                         BufferView(value.data(), value.size()),
250                         std::move(cb));
251 }
252 
WriteLongCharacteristic(CharacteristicHandle id,uint16_t offset,std::vector<uint8_t> value,ReliableMode reliable_mode,att::ResultFunction<> callback)253 void RemoteService::WriteLongCharacteristic(CharacteristicHandle id,
254                                             uint16_t offset,
255                                             std::vector<uint8_t> value,
256                                             ReliableMode reliable_mode,
257                                             att::ResultFunction<> callback) {
258   RemoteCharacteristic* chrc;
259   fit::result status = GetCharacteristic(id, &chrc);
260   PW_DCHECK(chrc || status.is_error());
261   if (status.is_error()) {
262     callback(status);
263     return;
264   }
265 
266   if (!(chrc->info().properties & Property::kWrite)) {
267     bt_log(DEBUG, "gatt", "characteristic does not support \"write\"");
268     callback(ToResult(HostError::kNotSupported));
269     return;
270   }
271 
272   if ((reliable_mode == ReliableMode::kEnabled) &&
273       ((!chrc->extended_properties().has_value()) ||
274        (!(chrc->extended_properties().value() &
275           ExtendedProperty::kReliableWrite)))) {
276     bt_log(DEBUG,
277            "gatt",
278            "characteristic does not support \"reliable write\"; attempting "
279            "request anyway");
280   }
281 
282   SendLongWriteRequest(chrc->info().value_handle,
283                        offset,
284                        BufferView(value.data(), value.size()),
285                        reliable_mode,
286                        std::move(callback));
287 }
288 
WriteCharacteristicWithoutResponse(CharacteristicHandle id,std::vector<uint8_t> value,att::ResultFunction<> cb)289 void RemoteService::WriteCharacteristicWithoutResponse(
290     CharacteristicHandle id,
291     std::vector<uint8_t> value,
292     att::ResultFunction<> cb) {
293   RemoteCharacteristic* chrc;
294   fit::result status = GetCharacteristic(id, &chrc);
295   PW_DCHECK(chrc || status.is_error());
296   if (status.is_error()) {
297     cb(status);
298     return;
299   }
300 
301   if (!(chrc->info().properties &
302         (Property::kWrite | Property::kWriteWithoutResponse))) {
303     bt_log(DEBUG,
304            "gatt",
305            "characteristic does not support \"write without response\"");
306     cb(ToResult(HostError::kNotSupported));
307     return;
308   }
309 
310   client_->WriteWithoutResponse(chrc->info().value_handle,
311                                 BufferView(value.data(), value.size()),
312                                 std::move(cb));
313 }
314 
ReadDescriptor(DescriptorHandle id,ReadValueCallback callback)315 void RemoteService::ReadDescriptor(DescriptorHandle id,
316                                    ReadValueCallback callback) {
317   const DescriptorData* desc;
318   fit::result status = GetDescriptor(id, &desc);
319   PW_DCHECK(desc || status.is_error());
320   if (status.is_error()) {
321     ReportReadValueError(status, std::move(callback));
322     return;
323   }
324 
325   client_->ReadRequest(desc->handle, std::move(callback));
326 }
327 
ReadLongDescriptor(DescriptorHandle id,uint16_t offset,size_t max_bytes,ReadValueCallback callback)328 void RemoteService::ReadLongDescriptor(DescriptorHandle id,
329                                        uint16_t offset,
330                                        size_t max_bytes,
331                                        ReadValueCallback callback) {
332   const DescriptorData* desc;
333   att::Result<> status = GetDescriptor(id, &desc);
334   PW_DCHECK(desc || status.is_error());
335   if (status.is_error()) {
336     ReportReadValueError(status, std::move(callback));
337     return;
338   }
339 
340   if (max_bytes == 0) {
341     bt_log(TRACE, "gatt", "invalid value for |max_bytes|: 0");
342     ReportReadValueError(ToResult(HostError::kInvalidParameters),
343                          std::move(callback));
344     return;
345   }
346 
347   // Set up the buffer in which we'll accumulate the blobs.
348   auto buffer = NewBuffer(std::min(max_bytes, att::kMaxAttributeValueLength));
349   if (!buffer) {
350     ReportReadValueError(ToResult(HostError::kOutOfMemory),
351                          std::move(callback));
352     return;
353   }
354 
355   ReadLongHelper(desc->handle,
356                  offset,
357                  std::move(buffer),
358                  0u /* bytes_read */,
359                  std::move(callback));
360 }
361 
WriteDescriptor(DescriptorHandle id,std::vector<uint8_t> value,att::ResultFunction<> callback)362 void RemoteService::WriteDescriptor(DescriptorHandle id,
363                                     std::vector<uint8_t> value,
364                                     att::ResultFunction<> callback) {
365   const DescriptorData* desc;
366   fit::result status = GetDescriptor(id, &desc);
367   PW_DCHECK(desc || status.is_error());
368   if (status.is_error()) {
369     callback(status);
370     return;
371   }
372 
373   // Do not allow writing to internally reserved descriptors.
374   if (desc->type == types::kClientCharacteristicConfig) {
375     bt_log(DEBUG, "gatt", "writing to CCC descriptor not allowed");
376     callback(ToResult(HostError::kNotSupported));
377     return;
378   }
379 
380   client_->WriteRequest(desc->handle,
381                         BufferView(value.data(), value.size()),
382                         std::move(callback));
383 }
384 
WriteLongDescriptor(DescriptorHandle id,uint16_t offset,std::vector<uint8_t> value,att::ResultFunction<> callback)385 void RemoteService::WriteLongDescriptor(DescriptorHandle id,
386                                         uint16_t offset,
387                                         std::vector<uint8_t> value,
388                                         att::ResultFunction<> callback) {
389   const DescriptorData* desc;
390   fit::result status = GetDescriptor(id, &desc);
391   PW_DCHECK(desc || status.is_error());
392   if (status.is_error()) {
393     callback(status);
394     return;
395   }
396 
397   // Do not allow writing to internally reserved descriptors.
398   if (desc->type == types::kClientCharacteristicConfig) {
399     bt_log(DEBUG, "gatt", "writing to CCC descriptor not allowed");
400     callback(ToResult(HostError::kNotSupported));
401     return;
402   }
403 
404   // For writing long descriptors, reliable mode is not supported.
405   auto mode = ReliableMode::kDisabled;
406   SendLongWriteRequest(desc->handle,
407                        offset,
408                        BufferView(value.data(), value.size()),
409                        mode,
410                        std::move(callback));
411 }
412 
EnableNotifications(CharacteristicHandle id,ValueCallback callback,NotifyStatusCallback status_callback)413 void RemoteService::EnableNotifications(CharacteristicHandle id,
414                                         ValueCallback callback,
415                                         NotifyStatusCallback status_callback) {
416   RemoteCharacteristic* chrc;
417   fit::result status = GetCharacteristic(id, &chrc);
418   PW_DCHECK(chrc || status.is_error());
419   if (status.is_error()) {
420     status_callback(status, kInvalidId);
421     return;
422   }
423 
424   chrc->EnableNotifications(std::move(callback), std::move(status_callback));
425 }
426 
DisableNotifications(CharacteristicHandle id,IdType handler_id,att::ResultFunction<> status_callback)427 void RemoteService::DisableNotifications(
428     CharacteristicHandle id,
429     IdType handler_id,
430     att::ResultFunction<> status_callback) {
431   RemoteCharacteristic* chrc;
432   fit::result status = GetCharacteristic(id, &chrc);
433   PW_DCHECK(chrc || status.is_error());
434   if (status.is_ok() && !chrc->DisableNotifications(handler_id)) {
435     status = ToResult(HostError::kNotFound);
436   }
437   status_callback(status);
438 }
439 
StartDescriptorDiscovery()440 void RemoteService::StartDescriptorDiscovery() {
441   PW_DCHECK(!pending_discov_reqs_.empty());
442 
443   PW_CHECK(!characteristics_.empty());
444   remaining_descriptor_requests_ = characteristics_.size();
445 
446   auto self = GetWeakPtr();
447 
448   // Callback called for each characteristic. This may be called in any
449   // order since we request the descriptors of all characteristics all at
450   // once.
451   auto desc_done_callback = [self](att::Result<> status) {
452     if (!self.is_alive()) {
453       return;
454     }
455 
456     // Do nothing if discovery was concluded earlier (which would have cleared
457     // the pending discovery requests).
458     if (self->pending_discov_reqs_.empty()) {
459       return;
460     }
461 
462     if (status.is_ok()) {
463       self->remaining_descriptor_requests_ -= 1;
464 
465       // Defer handling
466       if (self->remaining_descriptor_requests_ > 0) {
467         return;
468       }
469 
470       // HasCharacteristics() should return true now.
471       PW_DCHECK(self->HasCharacteristics());
472 
473       // Fall through and notify clients below.
474     } else {
475       PW_DCHECK(!self->HasCharacteristics());
476       bt_log(DEBUG, "gatt", "descriptor discovery failed %s", bt_str(status));
477       self->characteristics_.clear();
478 
479       // Fall through and notify the clients below.
480     }
481 
482     self->CompleteCharacteristicDiscovery(status);
483   };
484 
485   // Characteristics are stored in an (ordered) std::map by value_handle, so we
486   // iterate in order; according to the spec (BT 5.0 Vol 3, part G, 3.3), the
487   // value handle must appear immediately after the characteristic handle so the
488   // handles are also guaranteed to be in order. Therefore we can use the next
489   // in the iteration to calculate the handle range.
490   for (auto iter = characteristics_.begin(); iter != characteristics_.end();
491        ++iter) {
492     auto next = iter;
493     ++next;
494     att::Handle end_handle;
495     if (next == characteristics_.end()) {
496       end_handle = service_data_.range_end;
497     } else {
498       end_handle = next->second.info().handle - 1;
499     }
500 
501     PW_DCHECK(client_.is_alive());
502     iter->second.DiscoverDescriptors(end_handle, desc_done_callback);
503   }
504 }
505 
GetCharacteristic(CharacteristicHandle id,RemoteCharacteristic ** out_char)506 fit::result<Error<>> RemoteService::GetCharacteristic(
507     CharacteristicHandle id, RemoteCharacteristic** out_char) {
508   PW_DCHECK(out_char);
509 
510   if (!HasCharacteristics()) {
511     *out_char = nullptr;
512     return fit::error(HostError::kNotReady);
513   }
514 
515   auto chr = characteristics_.find(id);
516   if (chr == characteristics_.end()) {
517     *out_char = nullptr;
518     return fit::error(HostError::kNotFound);
519   }
520 
521   *out_char = &chr->second;
522   return fit::ok();
523 }
524 
GetDescriptor(DescriptorHandle id,const DescriptorData ** out_desc)525 fit::result<Error<>> RemoteService::GetDescriptor(
526     DescriptorHandle id, const DescriptorData** out_desc) {
527   PW_DCHECK(out_desc);
528 
529   if (!HasCharacteristics()) {
530     *out_desc = nullptr;
531     return fit::error(HostError::kNotReady);
532   }
533 
534   for (auto iter = characteristics_.begin(); iter != characteristics_.end();
535        ++iter) {
536     auto next = iter;
537     ++next;
538     if (next == characteristics_.end() ||
539         next->second.info().handle > id.value) {
540       const auto& descriptors = iter->second.descriptors();
541       auto desc = descriptors.find(id);
542       if (desc != descriptors.end()) {
543         *out_desc = &desc->second;
544         return fit::ok();
545       }
546     }
547   }
548 
549   *out_desc = nullptr;
550   return fit::error(HostError::kNotFound);
551 }
552 
CompleteCharacteristicDiscovery(att::Result<> status)553 void RemoteService::CompleteCharacteristicDiscovery(att::Result<> status) {
554   PW_DCHECK(!pending_discov_reqs_.empty());
555   PW_DCHECK(status.is_error() || remaining_descriptor_requests_ == 0u);
556 
557   // We return a new copy of only the immutable data of our characteristics and
558   // their descriptors. This requires a copy, which *could* be expensive in the
559   // (unlikely) case that a service has a very large number of characteristics.
560   CharacteristicMap characteristic_map =
561       CharacteristicsToCharacteristicMap(characteristics_);
562 
563   auto pending = std::move(pending_discov_reqs_);
564   for (auto& discovery_req_cb : pending) {
565     discovery_req_cb(status, characteristic_map);
566   }
567 }
568 
SendLongWriteRequest(att::Handle handle,uint16_t offset,BufferView value,ReliableMode reliable_mode,att::ResultFunction<> final_cb)569 void RemoteService::SendLongWriteRequest(att::Handle handle,
570                                          uint16_t offset,
571                                          BufferView value,
572                                          ReliableMode reliable_mode,
573                                          att::ResultFunction<> final_cb) {
574   att::PrepareWriteQueue long_write_queue;
575   auto header_ln = sizeof(att::PrepareWriteRequestParams) + sizeof(att::OpCode);
576   uint16_t bytes_written = 0;
577 
578   // Divide up the long write into it's constituent PreparedWrites and add them
579   // to the queue.
580   while (bytes_written < value.size()) {
581     uint16_t part_value_size = static_cast<uint16_t>(
582         std::min(client_->mtu() - header_ln, value.size() - bytes_written));
583     auto part_buffer = value.view(bytes_written, part_value_size);
584 
585     long_write_queue.push(att::QueuedWrite(handle, offset, part_buffer));
586 
587     bytes_written += part_value_size;
588     offset += part_value_size;
589   }
590 
591   client_->ExecutePrepareWrites(std::move(long_write_queue),
592                                 std::move(reliable_mode),
593                                 std::move(final_cb));
594 }
595 
ReadLongHelper(att::Handle value_handle,uint16_t offset,MutableByteBufferPtr out_buffer,size_t bytes_read,ReadValueCallback callback)596 void RemoteService::ReadLongHelper(att::Handle value_handle,
597                                    uint16_t offset,
598                                    MutableByteBufferPtr out_buffer,
599                                    size_t bytes_read,
600                                    ReadValueCallback callback) {
601   PW_DCHECK(callback);
602   PW_DCHECK(out_buffer);
603 
604   auto self = GetWeakPtr();
605   auto read_cb = [self,
606                   value_handle,
607                   offset,
608                   buffer = std::move(out_buffer),
609                   bytes_read,
610                   cb = std::move(callback)](
611                      att::Result<> status,
612                      const ByteBuffer& blob,
613                      bool maybe_truncated_by_mtu) mutable {
614     if (!self.is_alive()) {
615       return;
616     }
617 
618     if (status.is_error()) {
619       // "If the Characteristic Value is not longer than (ATT_MTU – 1) an
620       // ATT_ERROR_RSP PDU with the error code set to kAttributeNotLong shall be
621       // received on the first ATT_READ_BLOB_REQ PDU." (Core Spec v5.2, Vol 3,
622       // Part G, Sec 4.8.3). Report the short value read in the previous
623       // ATT_READ_REQ in this case.
624       if (status.error_value().is(att::ErrorCode::kAttributeNotLong) &&
625           offset == self->client_->mtu() - sizeof(att::OpCode)) {
626         cb(fit::ok(),
627            buffer->view(0, bytes_read),
628            /*maybe_truncated=*/false);
629         return;
630       }
631 
632       ReportReadValueError(status, std::move(cb));
633       return;
634     }
635 
636     // Copy the blob into our |buffer|. |blob| may be truncated depending on the
637     // size of |buffer|.
638     PW_CHECK(bytes_read < buffer->size());
639     size_t copy_size = std::min(blob.size(), buffer->size() - bytes_read);
640     bool truncated_by_max_bytes = (blob.size() != copy_size);
641     buffer->Write(blob.view(0, copy_size), bytes_read);
642     bytes_read += copy_size;
643 
644     // We are done if the read was not truncated by the MTU or we have read the
645     // maximum number of bytes requested.
646     PW_CHECK(bytes_read <= buffer->size());
647     if (!maybe_truncated_by_mtu || bytes_read == buffer->size()) {
648       cb(fit::ok(),
649          buffer->view(0, bytes_read),
650          maybe_truncated_by_mtu || truncated_by_max_bytes);
651       return;
652     }
653 
654     // We have more bytes to read. Read the next blob.
655     self->ReadLongHelper(value_handle,
656                          static_cast<uint16_t>(offset + blob.size()),
657                          std::move(buffer),
658                          bytes_read,
659                          std::move(cb));
660   };
661 
662   // "To read the complete Characteristic Value an ATT_READ_REQ PDU should be
663   // used for the first part of the value and ATT_READ_BLOB_REQ PDUs shall used
664   // for the rest." (Core Spec v5.2, Vol 3, part G, Sec 4.8.3).
665   if (offset == 0) {
666     client_->ReadRequest(value_handle, std::move(read_cb));
667     return;
668   }
669 
670   client_->ReadBlobRequest(value_handle, offset, std::move(read_cb));
671 }
672 
ReadByTypeHelper(const UUID & type,att::Handle start,att::Handle end,std::vector<RemoteService::ReadByTypeResult> results,ReadByTypeCallback callback)673 void RemoteService::ReadByTypeHelper(
674     const UUID& type,
675     att::Handle start,
676     att::Handle end,
677     std::vector<RemoteService::ReadByTypeResult> results,
678     ReadByTypeCallback callback) {
679   if (start > end) {
680     callback(fit::ok(), std::move(results));
681     return;
682   }
683 
684   auto read_cb = [self = GetWeakPtr(),
685                   type,
686                   start,
687                   end,
688                   values_accum = std::move(results),
689                   cb = std::move(callback)](
690                      Client::ReadByTypeResult result) mutable {
691     if (!self.is_alive()) {
692       return;
693     }
694 
695     // Pass results to client when this goes out of scope.
696     auto deferred_cb =
697         fit::defer_callback([&]() { cb(fit::ok(), std::move(values_accum)); });
698     if (result.is_error()) {
699       const att::Error& error = result.error_value().error;
700       deferred_cb = [&cb, error] { cb(fit::error(error), /*values=*/{}); };
701       if (error.is(att::ErrorCode::kAttributeNotFound)) {
702         // Treat kAttributeNotFound error as success, since it's used to
703         // indicate when a sequence of reads has successfully read all matching
704         // attributes.
705         deferred_cb = [&cb, &values_accum]() {
706           cb(fit::ok(), std::move(values_accum));
707         };
708         return;
709       }
710       if (error.is_any_of(att::ErrorCode::kRequestNotSupported,
711                           att::ErrorCode::kInsufficientResources,
712                           att::ErrorCode::kInvalidPDU)) {
713         // Pass up these protocol errors as they aren't handle specific or
714         // recoverable.
715         return;
716       }
717       if (error.is_protocol_error()) {
718         // Other errors may correspond to reads of specific handles, so treat
719         // them as a result and continue reading after the error.
720 
721         // A handle must be provided and in the requested read handle range.
722         if (!result.error_value().handle.has_value()) {
723           return;
724         }
725         att::Handle error_handle = result.error_value().handle.value();
726         if (error_handle < start || error_handle > end) {
727           deferred_cb = [&cb] {
728             cb(fit::error(Error(HostError::kPacketMalformed)), /*values=*/{});
729           };
730           return;
731         }
732 
733         values_accum.push_back(
734             RemoteService::ReadByTypeResult{CharacteristicHandle(error_handle),
735                                             fit::error(error.protocol_error()),
736                                             /*maybe_truncated=*/false});
737 
738         // Do not attempt to read from the next handle if the error handle is
739         // the max handle, as this would cause an overflow.
740         if (error_handle == std::numeric_limits<att::Handle>::max()) {
741           deferred_cb = [&cb, &values_accum]() {
742             cb(fit::ok(), std::move(values_accum));
743           };
744           return;
745         }
746 
747         // Start next read right after attribute causing error.
748         att::Handle start_next = error_handle + 1;
749 
750         self->ReadByTypeHelper(
751             type, start_next, end, std::move(values_accum), std::move(cb));
752         deferred_cb.cancel();
753         return;
754       }
755       return;
756     }
757 
758     const std::vector<Client::ReadByTypeValue>& values = result.value();
759     // Client already checks for invalid response where status is success but no
760     // values are returned.
761     PW_CHECK(!values.empty());
762 
763     // Convert and accumulate values.
764     for (const auto& val : values) {
765       auto buffer = NewBuffer(val.value.size());
766       val.value.Copy(buffer.get());
767       values_accum.push_back(ReadByTypeResult{CharacteristicHandle(val.handle),
768                                               fit::ok(std::move(buffer)),
769                                               val.maybe_truncated});
770     }
771 
772     // Do not attempt to read from the next handle if the last value handle is
773     // the max handle, as this would cause an overflow.
774     if (values.back().handle == std::numeric_limits<att::Handle>::max()) {
775       return;
776     }
777 
778     // Start next read right after last returned attribute. Client already
779     // checks that value handles are ascending and in range, so we are
780     // guaranteed to make progress.
781     att::Handle start_next = values.back().handle + 1;
782 
783     self->ReadByTypeHelper(
784         type, start_next, end, std::move(values_accum), std::move(cb));
785     deferred_cb.cancel();
786   };
787   client_->ReadByTypeRequest(type, start, end, std::move(read_cb));
788 }
789 
HandleNotification(att::Handle value_handle,const ByteBuffer & value,bool maybe_truncated)790 void RemoteService::HandleNotification(att::Handle value_handle,
791                                        const ByteBuffer& value,
792                                        bool maybe_truncated) {
793   auto iter = characteristics_.find(CharacteristicHandle(value_handle));
794   if (iter != characteristics_.end()) {
795     iter->second.HandleNotification(value, maybe_truncated);
796   }
797 }
798 
799 }  // namespace bt::gatt
800