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