xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/gatt/remote_service_manager.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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