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