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