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