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