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_manager.h"
16
17 #include <pw_bytes/endian.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/att/error.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
21 #include "pw_bluetooth_sapphire/internal/host/gatt/remote_service.h"
22
23 namespace bt::gatt::internal {
24
ServiceListRequest(ServiceListCallback callback,const std::vector<UUID> & uuids)25 RemoteServiceManager::ServiceListRequest::ServiceListRequest(
26 ServiceListCallback callback, const std::vector<UUID>& uuids)
27 : callback_(std::move(callback)), uuids_(uuids) {
28 PW_DCHECK(callback_);
29 }
30
Complete(att::Result<> status,const ServiceMap & services)31 void RemoteServiceManager::ServiceListRequest::Complete(
32 att::Result<> status, const ServiceMap& services) {
33 TRACE_DURATION("bluetooth",
34 "gatt::RemoteServiceManager::ServiceListRequest::Complete");
35
36 ServiceList result;
37
38 if (status.is_error() || services.empty()) {
39 callback_(status, std::move(result));
40 return;
41 }
42
43 for (const auto& iter : services) {
44 auto& svc = iter.second;
45 auto pred = [&svc](const UUID& uuid) { return svc->uuid() == uuid; };
46 if (uuids_.empty() ||
47 std::find_if(uuids_.begin(), uuids_.end(), pred) != uuids_.end()) {
48 result.push_back(iter.second->GetWeakPtr());
49 }
50 }
51
52 callback_(status, std::move(result));
53 }
54
RemoteServiceManager(std::unique_ptr<Client> client)55 RemoteServiceManager::RemoteServiceManager(std::unique_ptr<Client> client)
56 : client_(std::move(client)), initialized_(false), weak_self_(this) {
57 PW_DCHECK(client_);
58
59 client_->SetNotificationHandler(
60 fit::bind_member<&RemoteServiceManager::OnNotification>(this));
61 }
62
~RemoteServiceManager()63 RemoteServiceManager::~RemoteServiceManager() {
64 client_->SetNotificationHandler({});
65 services_.clear();
66
67 // Resolve all pending requests with an error.
68 att::Result<> status = ToResult(HostError::kFailed);
69
70 auto pending = std::move(pending_list_services_requests_);
71 while (!pending.empty()) {
72 // This copies |services|.
73 pending.front().Complete(status, services_);
74 pending.pop();
75 }
76 }
77
Initialize(att::ResultFunction<> result_callback,fit::callback<void (uint16_t)> mtu_callback,std::vector<UUID> services_to_discover)78 void RemoteServiceManager::Initialize(
79 att::ResultFunction<> result_callback,
80 fit::callback<void(uint16_t)> mtu_callback,
81 std::vector<UUID> services_to_discover) {
82 auto self = weak_self_.GetWeakPtr();
83
84 auto wrapped_result_cb = [self, user_result_cb = std::move(result_callback)](
85 att::Result<> status) mutable {
86 TRACE_DURATION("bluetooth",
87 "gatt::RemoteServiceManager::Initialize::init_cb");
88 bt_log(DEBUG, "gatt", "RemoteServiceManager initialization complete");
89
90 // The Client's Bearer may outlive this object.
91 if (!self.is_alive()) {
92 return;
93 }
94
95 self->initialized_ = true;
96
97 if (status.is_ok() && self->svc_watcher_) {
98 // Notify all discovered services here.
99 TRACE_DURATION("bluetooth", "gatt::RemoteServiceManager::svc_watcher_");
100 ServiceList added;
101 std::transform(
102 self->services_.begin(),
103 self->services_.end(),
104 std::back_inserter(added),
105 [](ServiceMap::value_type& svc) { return svc.second->GetWeakPtr(); });
106 self->svc_watcher_(/*removed=*/{}, std::move(added), /*modified=*/{});
107 }
108
109 // Notify pending ListService() requests.
110 while (!self->pending_list_services_requests_.empty()) {
111 self->pending_list_services_requests_.front().Complete(status,
112 self->services_);
113 self->pending_list_services_requests_.pop();
114 }
115
116 user_result_cb(status);
117 };
118
119 client_->ExchangeMTU([self,
120 result_cb = std::move(wrapped_result_cb),
121 mtu_cb = std::move(mtu_callback),
122 discovery_services = std::move(services_to_discover)](
123 att::Result<uint16_t> mtu_result) mutable {
124 // The Client's Bearer may outlive this object.
125 if (!self.is_alive()) {
126 return;
127 }
128
129 // Support for the MTU exchange is optional, so if the peer indicated they
130 // don't support it, we continue with initialization.
131 if (mtu_result.is_ok() ||
132 mtu_result.error_value().is(att::ErrorCode::kRequestNotSupported)) {
133 bt_is_error(mtu_result, DEBUG, "gatt", "MTU exchange not supported");
134 mtu_cb(mtu_result.value_or(att::kLEMinMTU));
135 } else {
136 bt_log(INFO,
137 "gatt",
138 "MTU exchange failed: %s",
139 bt_str(mtu_result.error_value()));
140 result_cb(fit::error(mtu_result.error_value()));
141 return;
142 }
143
144 self->InitializeGattProfileService([self,
145 init_cb = std::move(result_cb),
146 services =
147 std::move(discovery_services)](
148 att::Result<> init_status) mutable {
149 if (init_status == ToResult(HostError::kNotFound)) {
150 // The GATT Profile service's Service Changed characteristic is
151 // optional. Its absence implies that the set of GATT services on
152 // the server is fixed, so the kNotFound error can be safely
153 // ignored.
154 bt_log(DEBUG,
155 "gatt",
156 "GATT Profile service not found. Assuming services are fixed.");
157 } else if (init_status.is_error()) {
158 init_cb(init_status);
159 return;
160 }
161
162 self->DiscoverServices(
163 std::move(services),
164 [self, discover_cb = std::move(init_cb)](
165 att::Result<> discovery_status) mutable {
166 if (discovery_status.is_error()) {
167 discover_cb(discovery_status);
168 return;
169 }
170
171 // Handle Service Changed notifications received during service
172 // discovery. Skip notifying the service watcher callback as it
173 // will be notified in the init_cb callback. We handle Service
174 // Changed notifications before notifying the service watcher
175 // and init_cb in order to reduce the likelihood that returned
176 // services are instantly invalidated by Service Changed
177 // notifications. It is likely that bonded peers will send a
178 // Service Changed notification upon connection to indicate that
179 // services changed since the last connection, and such
180 // notifications will probably be received before service
181 // discovery completes. (Core Spec v5.3, Vol 3, Part G,
182 // Sec 2.5.2)
183 self->MaybeHandleNextServiceChangedNotification(
184 [self, cb = std::move(discover_cb)]() mutable {
185 cb(fit::ok());
186 });
187 });
188 });
189 });
190 }
191
GattProfileService()192 RemoteService* RemoteServiceManager::GattProfileService() {
193 auto service_iter =
194 std::find_if(services_.begin(), services_.end(), [](auto& s) {
195 return s.second->uuid() == types::kGenericAttributeService;
196 });
197 return service_iter == services_.end() ? nullptr : service_iter->second.get();
198 }
199
ConfigureServiceChangedNotifications(RemoteService * gatt_profile_service,att::ResultFunction<> result_callback)200 void RemoteServiceManager::ConfigureServiceChangedNotifications(
201 RemoteService* gatt_profile_service,
202 att::ResultFunction<> result_callback) {
203 auto self = weak_self_.GetWeakPtr();
204 gatt_profile_service->DiscoverCharacteristics(
205 [self, callback = std::move(result_callback)](
206 att::Result<> status,
207 const CharacteristicMap& characteristics) mutable {
208 // The Client's Bearer may outlive this object.
209 if (!self.is_alive()) {
210 return;
211 }
212
213 if (bt_is_error(
214 status,
215 WARN,
216 "gatt",
217 "Error discovering GATT Profile service characteristics")) {
218 callback(status);
219 return;
220 }
221
222 RemoteService* profile_service = self->GattProfileService();
223 PW_CHECK(profile_service);
224
225 auto svc_changed_char_iter = std::find_if(
226 characteristics.begin(),
227 characteristics.end(),
228 [](CharacteristicMap::const_reference c) {
229 const CharacteristicData& data = c.second.first;
230 return data.type == types::kServiceChangedCharacteristic;
231 });
232
233 // The Service Changed characteristic is optional, and its absence
234 // implies that the set of GATT services on the server is fixed.
235 if (svc_changed_char_iter == characteristics.end()) {
236 callback(ToResult(HostError::kNotFound));
237 return;
238 }
239
240 const bt::gatt::CharacteristicHandle svc_changed_char_handle =
241 svc_changed_char_iter->first;
242
243 auto notification_cb = [self](const ByteBuffer& value,
244 bool /*maybe_truncated*/) {
245 // The Client's Bearer may outlive this object.
246 if (self.is_alive()) {
247 self->OnServiceChangedNotification(value);
248 }
249 };
250
251 // Don't save handler_id as notifications never need to be disabled.
252 auto status_cb = [self, cb = std::move(callback)](
253 att::Result<> enable_status,
254 IdType /*handler_id*/) {
255 // The Client's Bearer may outlive this object.
256 if (!self.is_alive()) {
257 return;
258 }
259
260 // If the Service Changed characteristic exists, notification support
261 // is mandatory (Core Spec v5.2, Vol 3, Part G, Sec 7.1).
262 if (bt_is_error(enable_status,
263 WARN,
264 "gatt",
265 "Enabling notifications of Service Changed "
266 "characteristic failed")) {
267 cb(enable_status);
268 return;
269 }
270
271 cb(fit::ok());
272 };
273
274 profile_service->EnableNotifications(svc_changed_char_handle,
275 std::move(notification_cb),
276 std::move(status_cb));
277 });
278 }
279
InitializeGattProfileService(att::ResultFunction<> init_callback)280 void RemoteServiceManager::InitializeGattProfileService(
281 att::ResultFunction<> init_callback) {
282 auto self = weak_self_.GetWeakPtr();
283 DiscoverGattProfileService([self, callback = std::move(init_callback)](
284 att::Result<> discovery_status) mutable {
285 // The Client's Bearer may outlive this object.
286 if (!self.is_alive()) {
287 return;
288 }
289
290 if (discovery_status.is_error()) {
291 callback(discovery_status);
292 return;
293 }
294
295 RemoteService* gatt_svc = self->GattProfileService();
296 PW_CHECK(gatt_svc);
297 self->ConfigureServiceChangedNotifications(
298 gatt_svc,
299 [self, cb = std::move(callback)](att::Result<> config_status) {
300 // The Client's Bearer may outlive this object.
301 if (!self.is_alive()) {
302 return;
303 }
304
305 cb(config_status);
306 });
307 });
308 }
309
DiscoverGattProfileService(att::ResultFunction<> callback)310 void RemoteServiceManager::DiscoverGattProfileService(
311 att::ResultFunction<> callback) {
312 auto self = weak_self_.GetWeakPtr();
313 auto status_cb = [self, cb = std::move(callback)](att::Result<> status) {
314 if (!self.is_alive()) {
315 return;
316 }
317
318 if (bt_is_error(
319 status, WARN, "gatt", "Error discovering GATT Profile service")) {
320 cb(status);
321 return;
322 }
323
324 // The GATT Profile service is optional, and its absence implies that the
325 // set of GATT services on the server is fixed.
326 if (self->services_.empty()) {
327 cb(ToResult(HostError::kNotFound));
328 return;
329 }
330
331 // At most one instance of the GATT Profile service may exist (Core Spec
332 // v5.2, Vol 3, Part G, Sec 7).
333 if (self->services_.size() > 1) {
334 bt_log(WARN,
335 "gatt",
336 "Discovered (%zu) GATT Profile services, expected 1",
337 self->services_.size());
338 cb(ToResult(HostError::kFailed));
339 return;
340 }
341
342 UUID uuid = self->services_.begin()->second->uuid();
343 // The service UUID is filled in by Client based on the service discovery
344 // request, so it should be the same as the requested UUID.
345 PW_CHECK(uuid == types::kGenericAttributeService);
346
347 cb(fit::ok());
348 };
349 DiscoverServicesOfKind(ServiceKind::PRIMARY,
350 {types::kGenericAttributeService},
351 std::move(status_cb));
352 }
353
AddService(const ServiceData & service_data)354 void RemoteServiceManager::AddService(const ServiceData& service_data) {
355 att::Handle handle = service_data.range_start;
356 auto iter = services_.find(handle);
357 if (iter != services_.end()) {
358 // The GATT Profile service is discovered before general service discovery,
359 // so it may be discovered twice.
360 if (iter->second->uuid() != types::kGenericAttributeService) {
361 bt_log(WARN,
362 "gatt",
363 "found duplicate service attribute handle! (%#.4x)",
364 handle);
365 }
366 return;
367 }
368
369 auto svc =
370 std::make_unique<RemoteService>(service_data, client_->GetWeakPtr());
371 if (!svc) {
372 bt_log(DEBUG, "gatt", "failed to allocate RemoteService");
373 return;
374 }
375
376 services_[handle] = std::move(svc);
377 }
378
DiscoverServicesOfKind(ServiceKind kind,std::vector<UUID> service_uuids,att::ResultFunction<> status_cb)379 void RemoteServiceManager::DiscoverServicesOfKind(
380 ServiceKind kind,
381 std::vector<UUID> service_uuids,
382 att::ResultFunction<> status_cb) {
383 auto self = weak_self_.GetWeakPtr();
384 ServiceCallback svc_cb = [self](const ServiceData& service_data) {
385 // The Client's Bearer may outlive this object.
386 if (self.is_alive()) {
387 self->AddService(service_data);
388 }
389 };
390
391 if (!service_uuids.empty()) {
392 client_->DiscoverServicesWithUuids(kind,
393 std::move(svc_cb),
394 std::move(status_cb),
395 std::move(service_uuids));
396 } else {
397 client_->DiscoverServices(kind, std::move(svc_cb), std::move(status_cb));
398 }
399 }
400
DiscoverServices(std::vector<UUID> service_uuids,att::ResultFunction<> status_callback)401 void RemoteServiceManager::DiscoverServices(
402 std::vector<UUID> service_uuids, att::ResultFunction<> status_callback) {
403 auto self = weak_self_.GetWeakPtr();
404 auto status_cb_wrapper =
405 [self, status_cb = std::move(status_callback)](att::Result<> status) {
406 TRACE_DURATION(
407 "bluetooth",
408 "gatt::RemoteServiceManager::DiscoverServices::status_cb_wrapper");
409
410 // The Client's Bearer may outlive this object.
411 if (!self.is_alive()) {
412 status_cb(ToResult(HostError::kFailed));
413 return;
414 }
415
416 // Service discovery support is mandatory for servers (v5.0, Vol 3, Part
417 // G, 4.2).
418 if (bt_is_error(status, TRACE, "gatt", "failed to discover services")) {
419 self->services_.clear();
420 }
421
422 status_cb(status);
423 };
424
425 ServiceCallback svc_cb = [self](const ServiceData& service_data) {
426 // The Client's Bearer may outlive this object.
427 if (self.is_alive()) {
428 self->AddService(service_data);
429 }
430 };
431
432 DiscoverPrimaryAndSecondaryServicesInRange(std::move(service_uuids),
433 att::kHandleMin,
434 att::kHandleMax,
435 std::move(svc_cb),
436 std::move(status_cb_wrapper));
437 }
438
DiscoverPrimaryAndSecondaryServicesInRange(std::vector<UUID> service_uuids,att::Handle start,att::Handle end,ServiceCallback service_cb,att::ResultFunction<> status_callback)439 void RemoteServiceManager::DiscoverPrimaryAndSecondaryServicesInRange(
440 std::vector<UUID> service_uuids,
441 att::Handle start,
442 att::Handle end,
443 ServiceCallback service_cb,
444 att::ResultFunction<> status_callback) {
445 auto self = weak_self_.GetWeakPtr();
446 auto primary_discov_cb = [self,
447 service_uuids,
448 start,
449 end,
450 svc_cb = service_cb.share(),
451 status_cb = std::move(status_callback)](
452 att::Result<> primary_status) mutable {
453 if (!self.is_alive() || primary_status.is_error()) {
454 status_cb(primary_status);
455 return;
456 }
457
458 auto secondary_discov_cb = [cb = std::move(status_cb)](
459 att::Result<> secondary_status) mutable {
460 // Not all GATT servers support the "secondary service" group type. We
461 // suppress the "Unsupported Group Type" error code and simply report no
462 // services instead of treating it as a fatal condition (errors propagated
463 // up the stack from here will cause the connection to be terminated).
464 if (secondary_status == ToResult(att::ErrorCode::kUnsupportedGroupType)) {
465 bt_log(DEBUG,
466 "gatt",
467 "peer does not support secondary services; ignoring ATT error");
468 secondary_status = fit::ok();
469 }
470
471 cb(secondary_status);
472 };
473
474 if (!service_uuids.empty()) {
475 self->client_->DiscoverServicesWithUuidsInRange(
476 ServiceKind::SECONDARY,
477 start,
478 end,
479 std::move(svc_cb),
480 std::move(secondary_discov_cb),
481 std::move(service_uuids));
482 } else {
483 self->client_->DiscoverServicesInRange(ServiceKind::SECONDARY,
484 start,
485 end,
486 std::move(svc_cb),
487 std::move(secondary_discov_cb));
488 }
489 };
490
491 if (!service_uuids.empty()) {
492 client_->DiscoverServicesWithUuidsInRange(ServiceKind::PRIMARY,
493 start,
494 end,
495 std::move(service_cb),
496 std::move(primary_discov_cb),
497 std::move(service_uuids));
498 } else {
499 client_->DiscoverServicesInRange(ServiceKind::PRIMARY,
500 start,
501 end,
502 std::move(service_cb),
503 std::move(primary_discov_cb));
504 }
505 }
506
ListServices(const std::vector<UUID> & uuids,ServiceListCallback callback)507 void RemoteServiceManager::ListServices(const std::vector<UUID>& uuids,
508 ServiceListCallback callback) {
509 ServiceListRequest request(std::move(callback), uuids);
510 if (initialized_) {
511 request.Complete(fit::ok(), services_);
512 } else {
513 pending_list_services_requests_.push(std::move(request));
514 }
515 }
516
FindService(att::Handle handle)517 RemoteService::WeakPtr RemoteServiceManager::FindService(att::Handle handle) {
518 auto iter = services_.find(handle);
519 return iter == services_.end() ? RemoteService::WeakPtr()
520 : iter->second->GetWeakPtr();
521 }
522
OnNotification(bool,att::Handle value_handle,const ByteBuffer & value,bool maybe_truncated)523 void RemoteServiceManager::OnNotification(bool /*indication*/,
524 att::Handle value_handle,
525 const ByteBuffer& value,
526 bool maybe_truncated) {
527 if (services_.empty()) {
528 bt_log(DEBUG, "gatt", "ignoring notification from unknown service");
529 return;
530 }
531
532 // Find the service that |value_handle| belongs to.
533 auto iter = services_.upper_bound(value_handle);
534 if (iter != services_.begin())
535 --iter;
536
537 // If |value_handle| is within the previous service then we found it.
538 auto& svc = iter->second;
539 PW_DCHECK(value_handle >= svc->handle());
540
541 if (svc->info().range_end >= value_handle) {
542 svc->HandleNotification(value_handle, value, maybe_truncated);
543 }
544 }
545
OnServiceChangedNotification(const ByteBuffer & buffer)546 void RemoteServiceManager::OnServiceChangedNotification(
547 const ByteBuffer& buffer) {
548 bt_log(DEBUG, "gatt", "received service changed notification");
549
550 if (buffer.size() != sizeof(ServiceChangedCharacteristicValue)) {
551 bt_log(WARN,
552 "gatt",
553 "service changed notification value malformed; ignoring (size: %zu)",
554 buffer.size());
555 return;
556 }
557
558 ServiceChangedCharacteristicValue value;
559 value.range_start_handle = pw::bytes::ConvertOrderFrom(
560 cpp20::endian::little,
561 buffer.ReadMember<
562 &ServiceChangedCharacteristicValue::range_start_handle>());
563 value.range_end_handle = pw::bytes::ConvertOrderFrom(
564 cpp20::endian::little,
565 buffer
566 .ReadMember<&ServiceChangedCharacteristicValue::range_end_handle>());
567 if (value.range_start_handle > value.range_end_handle) {
568 bt_log(
569 WARN,
570 "gatt",
571 "service changed notification value malformed; ignoring (start > end)");
572 return;
573 }
574
575 queued_service_changes_.push(value);
576
577 // Bonded devices may send service changed notifications upon connection if
578 // services changed while the device was disconnected (Core Spec v5.3, Vol 3,
579 // Part G, Sec 7.1). These notifications may be received during the initial
580 // service discovery procedure. Queue the service changes and process them as
581 // the last step of initialization.
582 if (!initialized_) {
583 bt_log(DEBUG,
584 "gatt",
585 "Received service changed notification before RemoteServiceManager "
586 "initialization "
587 "complete; queueing.");
588 return;
589 }
590
591 MaybeHandleNextServiceChangedNotification();
592 }
593
MaybeHandleNextServiceChangedNotification(fit::callback<void ()> on_complete)594 void RemoteServiceManager::MaybeHandleNextServiceChangedNotification(
595 fit::callback<void()> on_complete) {
596 if (on_complete) {
597 service_changes_complete_callbacks_.push_back(std::move(on_complete));
598 }
599
600 if (current_service_change_.has_value()) {
601 return;
602 }
603
604 if (queued_service_changes_.empty()) {
605 for (auto& cb : service_changes_complete_callbacks_) {
606 cb();
607 }
608 service_changes_complete_callbacks_.clear();
609 return;
610 }
611
612 bt_log(DEBUG, "gatt", "handling next Service Changed notification");
613
614 current_service_change_ = ServiceChangedState{
615 .value = queued_service_changes_.front(), .services = {}};
616 queued_service_changes_.pop();
617
618 auto self = weak_self_.GetWeakPtr();
619 ServiceCallback svc_cb = [self](const ServiceData& service_data) {
620 if (self.is_alive()) {
621 PW_CHECK(self->current_service_change_.has_value());
622 // gatt::Client verifies that service discovery results are in the
623 // requested range.
624 PW_CHECK(service_data.range_start >=
625 self->current_service_change_->value.range_start_handle);
626 PW_CHECK(service_data.range_start <=
627 self->current_service_change_->value.range_end_handle);
628 self->current_service_change_->services.emplace(service_data.range_start,
629 service_data);
630 }
631 };
632
633 att::ResultFunction<> status_cb = [self](att::Result<> status) mutable {
634 if (!self.is_alive()) {
635 return;
636 }
637
638 if (!bt_is_error(
639 status,
640 WARN,
641 "gatt",
642 "service discovery for service changed notification failed")) {
643 PW_CHECK(self->current_service_change_.has_value());
644 self->ProcessServiceChangedDiscoveryResults(
645 self->current_service_change_.value());
646 }
647
648 self->current_service_change_.reset();
649 self->MaybeHandleNextServiceChangedNotification();
650 };
651
652 DiscoverPrimaryAndSecondaryServicesInRange(
653 /*service_uuids=*/{},
654 self->current_service_change_->value.range_start_handle,
655 self->current_service_change_->value.range_end_handle,
656 std::move(svc_cb),
657 std::move(status_cb));
658 }
659
ProcessServiceChangedDiscoveryResults(const ServiceChangedState & service_changed)660 void RemoteServiceManager::ProcessServiceChangedDiscoveryResults(
661 const ServiceChangedState& service_changed) {
662 std::vector<ServiceMap::iterator> removed_iters;
663 std::vector<ServiceData> added_data;
664 std::vector<std::pair<ServiceMap::iterator, ServiceData>>
665 modified_iters_and_data;
666 CalculateServiceChanges(
667 service_changed, removed_iters, added_data, modified_iters_and_data);
668
669 bt_log(INFO,
670 "gatt",
671 "service changed notification added %zu, removed %zu, and modified "
672 "%zu services",
673 added_data.size(),
674 removed_iters.size(),
675 modified_iters_and_data.size());
676
677 std::vector<att::Handle> removed_service_handles;
678 for (ServiceMap::iterator& service_iter : removed_iters) {
679 removed_service_handles.push_back(service_iter->first);
680 service_iter->second->set_service_changed(true);
681 services_.erase(service_iter);
682 }
683
684 ServiceList modified_services;
685 modified_services.reserve(modified_iters_and_data.size());
686 for (auto& [service_iter, new_service_data] : modified_iters_and_data) {
687 if (service_iter->second->uuid() == types::kGenericAttributeService) {
688 // The specification is ambiguous about what to do if the GATT Profile
689 // Service changes, but it implies that it means the Database Hash or
690 // Server Supported Features values have changed. At the very least, the
691 // Service Changed Characteristic is not supposed to change if the server
692 // is bonded with any client. We don't want to reset the service and
693 // potentially miss notifications until characteristics have been
694 // rediscovered. See Core Spec v5.3, Vol 3, Part G, Sec 7.1.
695 bt_log(INFO,
696 "gatt",
697 "GATT Profile Service changed; assuming same characteristics "
698 "(server values probably "
699 "changed)");
700 modified_services.push_back(service_iter->second->GetWeakPtr());
701 continue;
702 }
703
704 // Destroy the old service and replace with a new service in order to easily
705 // cancel ongoing procedures and ensure clients handle service change.
706 service_iter->second->set_service_changed(true);
707 service_iter->second.reset();
708
709 auto new_service = std::make_unique<RemoteService>(new_service_data,
710 client_->GetWeakPtr());
711 PW_CHECK(new_service->handle() == service_iter->first);
712 modified_services.push_back(new_service->GetWeakPtr());
713 service_iter->second = std::move(new_service);
714 }
715
716 ServiceList added_services;
717 added_services.reserve(added_data.size());
718 for (ServiceData service_data : added_data) {
719 auto service =
720 std::make_unique<RemoteService>(service_data, client_->GetWeakPtr());
721 added_services.push_back(service->GetWeakPtr());
722 auto [_, inserted] =
723 services_.try_emplace(service->handle(), std::move(service));
724 PW_CHECK(inserted,
725 "service with handle (%#.4x) already exists",
726 service->handle());
727 }
728
729 // Skip notifying the service watcher callback during initialization as it
730 // will be notified in the init_cb callback.
731 if (initialized_) {
732 svc_watcher_(std::move(removed_service_handles),
733 std::move(added_services),
734 std::move(modified_services));
735 }
736 }
737
CalculateServiceChanges(const ServiceChangedState & service_changed,std::vector<ServiceMap::iterator> & removed_services,std::vector<ServiceData> & added_services,std::vector<std::pair<ServiceMap::iterator,ServiceData>> & modified_services)738 void RemoteServiceManager::CalculateServiceChanges(
739 const ServiceChangedState& service_changed,
740 std::vector<ServiceMap::iterator>& removed_services,
741 std::vector<ServiceData>& added_services,
742 std::vector<std::pair<ServiceMap::iterator, ServiceData>>&
743 modified_services) {
744 // iterator to first service greater than or equal to the start of the
745 // affected range.
746 auto services_iter =
747 services_.lower_bound(service_changed.value.range_start_handle);
748 // iterator to first service greater than the end of the affected range.
749 auto services_end =
750 services_.upper_bound(service_changed.value.range_end_handle);
751 auto new_services_iter = service_changed.services.begin();
752 auto new_services_end = service_changed.services.end();
753
754 // Iterate through the lists of services and calculate the difference. Both
755 // the old and new services are stored in ordered maps, so we can iterate
756 // through both linearly in one pass.
757 while (services_iter != services_end &&
758 new_services_iter != new_services_end) {
759 if (services_iter->first < new_services_iter->first) {
760 removed_services.push_back(services_iter);
761 services_iter++;
762 } else if (services_iter->first == new_services_iter->first) {
763 if (services_iter->second->uuid() == new_services_iter->second.type) {
764 // Assume service with same handle & UUID has been modified, since all
765 // services in the Service Change range must be affected, by definition:
766 // "The Service Changed Characteristic Value [...] indicates the
767 // beginning and ending Attribute Handles affected by an addition,
768 // removal, or modification to a GATT-based service on the server" (Core
769 // Spec v5.3, Vol 3, Part G, Sec 7.1).
770 modified_services.emplace_back(services_iter,
771 new_services_iter->second);
772 } else {
773 // A new service has been added with the same handle but different type.
774 removed_services.push_back(services_iter);
775 added_services.push_back(new_services_iter->second);
776 }
777 services_iter++;
778 new_services_iter++;
779 } else {
780 added_services.push_back(new_services_iter->second);
781 new_services_iter++;
782 }
783 }
784
785 // Remaining old services must have been removed.
786 while (services_iter != services_end) {
787 removed_services.push_back(services_iter);
788 services_iter++;
789 }
790
791 // Remaining new services must have been added.
792 if (new_services_iter != new_services_end) {
793 std::transform(
794 new_services_iter,
795 new_services_end,
796 std::back_inserter(added_services),
797 [](const std::map<att::Handle, ServiceData>::value_type& value) {
798 return value.second;
799 });
800 }
801 }
802
803 } // namespace bt::gatt::internal
804