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