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