xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/fuchsia/host/fidl/gatt2_remote_service_server.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 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/fuchsia/host/fidl/gatt2_remote_service_server.h"
16 
17 #include <utility>
18 
19 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/helpers.h"
20 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/measure_tape/hlcpp_measure_tape_for_read_by_type_result.h"
21 #include "pw_bluetooth_sapphire/internal/host/att/att.h"
22 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h"
23 
24 namespace fbg = fuchsia::bluetooth::gatt2;
25 namespace measure_fbg = measure_tape::fuchsia::bluetooth::gatt2;
26 
27 namespace bthost {
28 namespace {
29 
MakeStatusCallback(bt::PeerId peer_id,const char * request_name,fbg::Handle fidl_handle,fit::function<void (fpromise::result<void,fbg::Error>)> callback)30 bt::att::ResultFunction<> MakeStatusCallback(
31     bt::PeerId peer_id,
32     const char* request_name,
33     fbg::Handle fidl_handle,
34     fit::function<void(fpromise::result<void, fbg::Error>)> callback) {
35   return [peer_id, fidl_handle, callback = std::move(callback), request_name](
36              bt::att::Result<> status) {
37     if (bt_is_error(status,
38                     INFO,
39                     "fidl",
40                     "%s: error (peer: %s, handle: 0x%lX)",
41                     request_name,
42                     bt_str(peer_id),
43                     fidl_handle.value)) {
44       callback(fpromise::error(
45           fidl_helpers::AttErrorToGattFidlError(status.error_value())));
46       return;
47     }
48 
49     callback(fpromise::ok());
50   };
51 }
52 
CharacteristicToFidl(const bt::gatt::CharacteristicData & characteristic,const std::map<bt::gatt::DescriptorHandle,bt::gatt::DescriptorData> & descriptors)53 fbg::Characteristic CharacteristicToFidl(
54     const bt::gatt::CharacteristicData& characteristic,
55     const std::map<bt::gatt::DescriptorHandle, bt::gatt::DescriptorData>&
56         descriptors) {
57   fbg::Characteristic fidl_char;
58   fidl_char.set_handle(fbg::Handle{characteristic.value_handle});
59   fidl_char.set_type(fuchsia::bluetooth::Uuid{characteristic.type.value()});
60 
61   // The FIDL property bitfield combines the properties and extended properties
62   // bits. We mask away the kExtendedProperties property.
63   constexpr uint8_t kRemoveExtendedPropertiesMask = 0x7F;
64   fbg::CharacteristicPropertyBits fidl_properties =
65       static_cast<fbg::CharacteristicPropertyBits>(
66           characteristic.properties & kRemoveExtendedPropertiesMask);
67   if (characteristic.extended_properties) {
68     if (*characteristic.extended_properties &
69         bt::gatt::ExtendedProperty::kReliableWrite) {
70       fidl_properties |= fbg::CharacteristicPropertyBits::RELIABLE_WRITE;
71     }
72     if (*characteristic.extended_properties &
73         bt::gatt::ExtendedProperty::kWritableAuxiliaries) {
74       fidl_properties |= fbg::CharacteristicPropertyBits::WRITABLE_AUXILIARIES;
75     }
76   }
77   fidl_char.set_properties(fidl_properties);
78 
79   if (!descriptors.empty()) {
80     std::vector<fbg::Descriptor> fidl_descriptors;
81     for (const auto& [handle, data] : descriptors) {
82       fbg::Descriptor fidl_descriptor;
83       fidl_descriptor.set_handle(fbg::Handle{handle.value});
84       fidl_descriptor.set_type(fuchsia::bluetooth::Uuid{data.type.value()});
85       fidl_descriptors.push_back(std::move(fidl_descriptor));
86     }
87     fidl_char.set_descriptors(std::move(fidl_descriptors));
88   }
89 
90   return fidl_char;
91 }
92 
93 // Returned result is supposed to match Read{Characteristic, Descriptor}Callback
94 // (result type is converted by FIDL move constructor).
95 [[nodiscard]] fpromise::result<::fuchsia::bluetooth::gatt2::ReadValue,
96                                ::fuchsia::bluetooth::gatt2::Error>
ReadResultToFidl(bt::PeerId peer_id,fbg::Handle handle,bt::att::Result<> status,const bt::ByteBuffer & value,bool maybe_truncated,const char * request)97 ReadResultToFidl(bt::PeerId peer_id,
98                  fbg::Handle handle,
99                  bt::att::Result<> status,
100                  const bt::ByteBuffer& value,
101                  bool maybe_truncated,
102                  const char* request) {
103   if (bt_is_error(status,
104                   INFO,
105                   "fidl",
106                   "%s: error (peer: %s, handle: 0x%lX)",
107                   request,
108                   bt_str(peer_id),
109                   handle.value)) {
110     return fpromise::error(
111         fidl_helpers::AttErrorToGattFidlError(status.error_value()));
112   }
113 
114   fbg::ReadValue fidl_value;
115   fidl_value.set_handle(handle);
116   fidl_value.set_value(value.ToVector());
117   fidl_value.set_maybe_truncated(maybe_truncated);
118   return fpromise::ok(std::move(fidl_value));
119 }
120 
FillInReadOptionsDefaults(fbg::ReadOptions & options)121 void FillInReadOptionsDefaults(fbg::ReadOptions& options) {
122   if (options.is_short_read()) {
123     return;
124   }
125   if (!options.long_read().has_offset()) {
126     options.long_read().set_offset(0);
127   }
128   if (!options.long_read().has_max_bytes()) {
129     options.long_read().set_max_bytes(fbg::MAX_VALUE_LENGTH);
130   }
131 }
132 
FillInDefaultWriteOptions(fbg::WriteOptions & options)133 void FillInDefaultWriteOptions(fbg::WriteOptions& options) {
134   if (!options.has_write_mode()) {
135     *options.mutable_write_mode() = fbg::WriteMode::DEFAULT;
136   }
137   if (!options.has_offset()) {
138     *options.mutable_offset() = 0;
139   }
140 }
141 
ReliableModeFromFidl(const fbg::WriteMode & mode)142 bt::gatt::ReliableMode ReliableModeFromFidl(const fbg::WriteMode& mode) {
143   return mode == fbg::WriteMode::RELIABLE ? bt::gatt::ReliableMode::kEnabled
144                                           : bt::gatt::ReliableMode::kDisabled;
145 }
146 
147 }  // namespace
148 
Gatt2RemoteServiceServer(bt::gatt::RemoteService::WeakPtr service,bt::gatt::GATT::WeakPtr gatt,bt::PeerId peer_id,fidl::InterfaceRequest<fuchsia::bluetooth::gatt2::RemoteService> request)149 Gatt2RemoteServiceServer::Gatt2RemoteServiceServer(
150     bt::gatt::RemoteService::WeakPtr service,
151     bt::gatt::GATT::WeakPtr gatt,
152     bt::PeerId peer_id,
153     fidl::InterfaceRequest<fuchsia::bluetooth::gatt2::RemoteService> request)
154     : GattServerBase(std::move(gatt), this, std::move(request)),
155       service_(std::move(service)),
156       peer_id_(peer_id),
157       weak_self_(this) {}
158 
~Gatt2RemoteServiceServer()159 Gatt2RemoteServiceServer::~Gatt2RemoteServiceServer() {
160   // Disable all notifications to prevent leaks.
161   for (auto& [_, notifier] : characteristic_notifiers_) {
162     service_->DisableNotifications(notifier.characteristic_handle,
163                                    notifier.handler_id,
164                                    /*status_callback=*/[](auto /*status*/) {});
165   }
166   characteristic_notifiers_.clear();
167 }
168 
Close(zx_status_t status)169 void Gatt2RemoteServiceServer::Close(zx_status_t status) {
170   binding()->Close(status);
171 }
172 
DiscoverCharacteristics(DiscoverCharacteristicsCallback callback)173 void Gatt2RemoteServiceServer::DiscoverCharacteristics(
174     DiscoverCharacteristicsCallback callback) {
175   auto res_cb = [callback = std::move(callback)](
176                     bt::att::Result<> status,
177                     const bt::gatt::CharacteristicMap& characteristics) {
178     if (status.is_error()) {
179       callback({});
180       return;
181     }
182 
183     std::vector<fbg::Characteristic> fidl_characteristics;
184     for (const auto& [_, characteristic] : characteristics) {
185       const auto& [data, descriptors] = characteristic;
186       fidl_characteristics.push_back(CharacteristicToFidl(data, descriptors));
187     }
188     callback(std::move(fidl_characteristics));
189   };
190 
191   service_->DiscoverCharacteristics(std::move(res_cb));
192 }
193 
ReadByType(::fuchsia::bluetooth::Uuid uuid,ReadByTypeCallback callback)194 void Gatt2RemoteServiceServer::ReadByType(::fuchsia::bluetooth::Uuid uuid,
195                                           ReadByTypeCallback callback) {
196   service_->ReadByType(
197       fidl_helpers::UuidFromFidl(uuid),
198       [self = weak_self_.GetWeakPtr(),
199        cb = std::move(callback),
200        func = __FUNCTION__](
201           bt::att::Result<> status,
202           std::vector<bt::gatt::RemoteService::ReadByTypeResult> results) {
203         if (!self.is_alive()) {
204           return;
205         }
206 
207         if (status == ToResult(bt::HostError::kInvalidParameters)) {
208           bt_log(WARN,
209                  "fidl",
210                  "%s: called with invalid parameters (peer: %s)",
211                  func,
212                  bt_str(self->peer_id_));
213           cb(fpromise::error(fbg::Error::INVALID_PARAMETERS));
214           return;
215         } else if (status.is_error()) {
216           cb(fpromise::error(fbg::Error::UNLIKELY_ERROR));
217           return;
218         }
219 
220         const size_t kVectorOverhead =
221             sizeof(fidl_message_header_t) + sizeof(fidl_vector_t);
222         const size_t kMaxBytes = ZX_CHANNEL_MAX_MSG_BYTES - kVectorOverhead;
223         size_t bytes_used = 0;
224 
225         std::vector<fuchsia::bluetooth::gatt2::ReadByTypeResult> fidl_results;
226         fidl_results.reserve(results.size());
227 
228         for (const bt::gatt::RemoteService::ReadByTypeResult& result :
229              results) {
230           fuchsia::bluetooth::gatt2::ReadByTypeResult fidl_result;
231           fidl_result.set_handle(fbg::Handle{result.handle.value});
232           if (result.result.is_ok()) {
233             fbg::ReadValue read_value;
234             read_value.set_handle(fbg::Handle{result.handle.value});
235             read_value.set_value(result.result.value()->ToVector());
236             read_value.set_maybe_truncated(result.maybe_truncated);
237             fidl_result.set_value(std::move(read_value));
238           } else {
239             fidl_result.set_error(fidl_helpers::AttErrorToGattFidlError(
240                 bt::att::Error(result.result.error_value())));
241           }
242 
243           measure_fbg::Size result_size = measure_fbg::Measure(fidl_result);
244           PW_CHECK(result_size.num_handles == 0);
245           bytes_used += result_size.num_bytes;
246 
247           if (bytes_used > kMaxBytes) {
248             cb(fpromise::error(
249                 fuchsia::bluetooth::gatt2::Error::TOO_MANY_RESULTS));
250             return;
251           }
252 
253           fidl_results.push_back(std::move(fidl_result));
254         }
255 
256         cb(fpromise::ok(std::move(fidl_results)));
257       });
258 }
259 
ReadCharacteristic(fbg::Handle fidl_handle,fbg::ReadOptions options,ReadCharacteristicCallback callback)260 void Gatt2RemoteServiceServer::ReadCharacteristic(
261     fbg::Handle fidl_handle,
262     fbg::ReadOptions options,
263     ReadCharacteristicCallback callback) {
264   if (!fidl_helpers::IsFidlGattHandleValid(fidl_handle)) {
265     callback(fpromise::error(fbg::Error::INVALID_HANDLE));
266     return;
267   }
268   bt::gatt::CharacteristicHandle handle(
269       static_cast<bt::att::Handle>(fidl_handle.value));
270 
271   FillInReadOptionsDefaults(options);
272 
273   const char* kRequestName = __FUNCTION__;
274   bt::gatt::RemoteService::ReadValueCallback read_cb =
275       [peer_id = peer_id_,
276        fidl_handle,
277        kRequestName,
278        callback = std::move(callback)](bt::att::Result<> status,
279                                        const bt::ByteBuffer& value,
280                                        bool maybe_truncated) {
281         callback(ReadResultToFidl(peer_id,
282                                   fidl_handle,
283                                   status,
284                                   value,
285                                   maybe_truncated,
286                                   kRequestName));
287       };
288 
289   if (options.is_short_read()) {
290     service_->ReadCharacteristic(handle, std::move(read_cb));
291     return;
292   }
293 
294   service_->ReadLongCharacteristic(handle,
295                                    options.long_read().offset(),
296                                    options.long_read().max_bytes(),
297                                    std::move(read_cb));
298 }
299 
WriteCharacteristic(fbg::Handle fidl_handle,std::vector<uint8_t> value,fbg::WriteOptions options,WriteCharacteristicCallback callback)300 void Gatt2RemoteServiceServer::WriteCharacteristic(
301     fbg::Handle fidl_handle,
302     std::vector<uint8_t> value,
303     fbg::WriteOptions options,
304     WriteCharacteristicCallback callback) {
305   if (!fidl_helpers::IsFidlGattHandleValid(fidl_handle)) {
306     callback(fpromise::error(fbg::Error::INVALID_HANDLE));
307     return;
308   }
309   bt::gatt::CharacteristicHandle handle(
310       static_cast<bt::att::Handle>(fidl_handle.value));
311 
312   FillInDefaultWriteOptions(options);
313 
314   bt::att::ResultFunction<> write_cb = MakeStatusCallback(
315       peer_id_, __FUNCTION__, fidl_handle, std::move(callback));
316 
317   if (options.write_mode() == fbg::WriteMode::WITHOUT_RESPONSE) {
318     if (options.offset() != 0) {
319       write_cb(bt::ToResult(bt::HostError::kInvalidParameters));
320       return;
321     }
322     service_->WriteCharacteristicWithoutResponse(
323         handle, std::move(value), std::move(write_cb));
324     return;
325   }
326 
327   const uint16_t kMaxShortWriteValueLength =
328       service_->att_mtu() - sizeof(bt::att::OpCode) -
329       sizeof(bt::att::WriteRequestParams);
330   if (options.offset() == 0 &&
331       options.write_mode() == fbg::WriteMode::DEFAULT &&
332       value.size() <= kMaxShortWriteValueLength) {
333     service_->WriteCharacteristic(
334         handle, std::move(value), std::move(write_cb));
335     return;
336   }
337 
338   service_->WriteLongCharacteristic(handle,
339                                     options.offset(),
340                                     std::move(value),
341                                     ReliableModeFromFidl(options.write_mode()),
342                                     std::move(write_cb));
343 }
344 
ReadDescriptor(::fuchsia::bluetooth::gatt2::Handle fidl_handle,::fuchsia::bluetooth::gatt2::ReadOptions options,ReadDescriptorCallback callback)345 void Gatt2RemoteServiceServer::ReadDescriptor(
346     ::fuchsia::bluetooth::gatt2::Handle fidl_handle,
347     ::fuchsia::bluetooth::gatt2::ReadOptions options,
348     ReadDescriptorCallback callback) {
349   if (!fidl_helpers::IsFidlGattHandleValid(fidl_handle)) {
350     callback(fpromise::error(fbg::Error::INVALID_HANDLE));
351     return;
352   }
353   bt::gatt::DescriptorHandle handle(
354       static_cast<bt::att::Handle>(fidl_handle.value));
355 
356   FillInReadOptionsDefaults(options);
357 
358   const char* kRequestName = __FUNCTION__;
359   bt::gatt::RemoteService::ReadValueCallback read_cb =
360       [peer_id = peer_id_,
361        fidl_handle,
362        kRequestName,
363        callback = std::move(callback)](bt::att::Result<> status,
364                                        const bt::ByteBuffer& value,
365                                        bool maybe_truncated) {
366         callback(ReadResultToFidl(peer_id,
367                                   fidl_handle,
368                                   status,
369                                   value,
370                                   maybe_truncated,
371                                   kRequestName));
372       };
373 
374   if (options.is_short_read()) {
375     service_->ReadDescriptor(handle, std::move(read_cb));
376     return;
377   }
378 
379   service_->ReadLongDescriptor(handle,
380                                options.long_read().offset(),
381                                options.long_read().max_bytes(),
382                                std::move(read_cb));
383 }
384 
WriteDescriptor(fbg::Handle fidl_handle,std::vector<uint8_t> value,fbg::WriteOptions options,WriteDescriptorCallback callback)385 void Gatt2RemoteServiceServer::WriteDescriptor(
386     fbg::Handle fidl_handle,
387     std::vector<uint8_t> value,
388     fbg::WriteOptions options,
389     WriteDescriptorCallback callback) {
390   if (!fidl_helpers::IsFidlGattHandleValid(fidl_handle)) {
391     callback(fpromise::error(fbg::Error::INVALID_HANDLE));
392     return;
393   }
394   bt::gatt::DescriptorHandle handle(
395       static_cast<bt::att::Handle>(fidl_handle.value));
396 
397   FillInDefaultWriteOptions(options);
398 
399   bt::att::ResultFunction<> write_cb = MakeStatusCallback(
400       peer_id_, __FUNCTION__, fidl_handle, std::move(callback));
401 
402   // WITHOUT_RESPONSE and RELIABLE write modes are not supported for
403   // descriptors.
404   if (options.write_mode() == fbg::WriteMode::WITHOUT_RESPONSE ||
405       options.write_mode() == fbg::WriteMode::RELIABLE) {
406     write_cb(bt::ToResult(bt::HostError::kInvalidParameters));
407     return;
408   }
409 
410   const uint16_t kMaxShortWriteValueLength =
411       service_->att_mtu() - sizeof(bt::att::OpCode) -
412       sizeof(bt::att::WriteRequestParams);
413   if (options.offset() == 0 && value.size() <= kMaxShortWriteValueLength) {
414     service_->WriteDescriptor(handle, std::move(value), std::move(write_cb));
415     return;
416   }
417 
418   service_->WriteLongDescriptor(
419       handle, options.offset(), std::move(value), std::move(write_cb));
420 }
421 
RegisterCharacteristicNotifier(fbg::Handle fidl_handle,fidl::InterfaceHandle<fbg::CharacteristicNotifier> notifier_handle,RegisterCharacteristicNotifierCallback callback)422 void Gatt2RemoteServiceServer::RegisterCharacteristicNotifier(
423     fbg::Handle fidl_handle,
424     fidl::InterfaceHandle<fbg::CharacteristicNotifier> notifier_handle,
425     RegisterCharacteristicNotifierCallback callback) {
426   bt::gatt::CharacteristicHandle char_handle(
427       static_cast<bt::att::Handle>(fidl_handle.value));
428   NotifierId notifier_id = next_notifier_id_++;
429   auto self = weak_self_.GetWeakPtr();
430 
431   auto value_cb = [self, notifier_id, fidl_handle](const bt::ByteBuffer& value,
432                                                    bool maybe_truncated) {
433     if (!self.is_alive()) {
434       return;
435     }
436 
437     auto notifier_iter = self->characteristic_notifiers_.find(notifier_id);
438     // The lower layers guarantee that the status callback is always invoked
439     // before sending notifications. Notifiers are only removed during
440     // destruction (addressed by previous `self` check) and in the
441     // `DisableNotifications` completion callback in
442     // `OnCharacteristicNotifierError`, so no notifications should be received
443     // after removing a notifier.
444     PW_CHECK(
445         notifier_iter != self->characteristic_notifiers_.end(),
446         "characteristic notification value received after notifier unregistered"
447         "(peer: %s, characteristic: 0x%lX) ",
448         bt_str(self->peer_id_),
449         fidl_handle.value);
450     CharacteristicNotifier& notifier = notifier_iter->second;
451 
452     // The `- 1` is needed because there is one unacked notification that we've
453     // already sent to the client aside from the values in the queue.
454     if (notifier.queued_values.size() == kMaxPendingNotifierValues - 1) {
455       bt_log(WARN,
456              "fidl",
457              "GATT CharacteristicNotifier pending values limit reached, "
458              "closing protocol (peer: "
459              "%s, characteristic: %#.2x)",
460              bt_str(self->peer_id_),
461              notifier.characteristic_handle.value);
462       self->OnCharacteristicNotifierError(
463           notifier_id, notifier.characteristic_handle, notifier.handler_id);
464       return;
465     }
466 
467     fbg::ReadValue fidl_value;
468     fidl_value.set_handle(fidl_handle);
469     fidl_value.set_value(value.ToVector());
470     fidl_value.set_maybe_truncated(maybe_truncated);
471 
472     bt_log(TRACE,
473            "fidl",
474            "Queueing GATT notification value (characteristic: %#.2x)",
475            notifier.characteristic_handle.value);
476     notifier.queued_values.push(std::move(fidl_value));
477 
478     self->MaybeNotifyNextValue(notifier_id);
479   };
480 
481   auto status_cb = [self,
482                     service = service_,
483                     char_handle,
484                     notifier_id,
485                     notifier_handle = std::move(notifier_handle),
486                     callback = std::move(callback)](
487                        bt::att::Result<> status,
488                        bt::gatt::IdType handler_id) mutable {
489     if (!self.is_alive()) {
490       if (status.is_ok()) {
491         // Disable this handler so it doesn't leak.
492         service->DisableNotifications(
493             char_handle, handler_id, [](auto /*status*/) {
494               // There is no notifier to clean up because the server has been
495               // destroyed.
496             });
497       }
498       return;
499     }
500 
501     if (status.is_error()) {
502       callback(fpromise::error(
503           fidl_helpers::AttErrorToGattFidlError(status.error_value())));
504       return;
505     }
506 
507     CharacteristicNotifier notifier{.handler_id = handler_id,
508                                     .characteristic_handle = char_handle,
509                                     .notifier = notifier_handle.Bind()};
510     auto [notifier_iter, emplaced] = self->characteristic_notifiers_.emplace(
511         notifier_id, std::move(notifier));
512     PW_CHECK(emplaced);
513 
514     // When the client closes the protocol, unregister the notifier.
515     notifier_iter->second.notifier.set_error_handler(
516         [self, char_handle, handler_id, notifier_id](auto /*status*/) {
517           self->OnCharacteristicNotifierError(
518               notifier_id, char_handle, handler_id);
519         });
520 
521     callback(fpromise::ok());
522   };
523 
524   service_->EnableNotifications(
525       char_handle, std::move(value_cb), std::move(status_cb));
526 }
527 
MaybeNotifyNextValue(NotifierId notifier_id)528 void Gatt2RemoteServiceServer::MaybeNotifyNextValue(NotifierId notifier_id) {
529   auto notifier_iter = characteristic_notifiers_.find(notifier_id);
530   if (notifier_iter == characteristic_notifiers_.end()) {
531     return;
532   }
533   CharacteristicNotifier& notifier = notifier_iter->second;
534 
535   if (notifier.queued_values.empty()) {
536     return;
537   }
538 
539   if (!notifier.last_value_ack) {
540     return;
541   }
542   notifier.last_value_ack = false;
543 
544   fbg::ReadValue value = std::move(notifier.queued_values.front());
545   notifier.queued_values.pop();
546 
547   bt_log(DEBUG,
548          "fidl",
549          "Sending GATT notification value (handle: 0x%lX)",
550          value.handle().value);
551   auto self = weak_self_.GetWeakPtr();
552   notifier.notifier->OnNotification(std::move(value), [self, notifier_id]() {
553     if (!self.is_alive()) {
554       return;
555     }
556 
557     auto notifier_iter = self->characteristic_notifiers_.find(notifier_id);
558     if (notifier_iter == self->characteristic_notifiers_.end()) {
559       return;
560     }
561     notifier_iter->second.last_value_ack = true;
562     self->MaybeNotifyNextValue(notifier_id);
563   });
564 }
565 
OnCharacteristicNotifierError(NotifierId notifier_id,bt::gatt::CharacteristicHandle char_handle,bt::gatt::IdType handler_id)566 void Gatt2RemoteServiceServer::OnCharacteristicNotifierError(
567     NotifierId notifier_id,
568     bt::gatt::CharacteristicHandle char_handle,
569     bt::gatt::IdType handler_id) {
570   auto self = weak_self_.GetWeakPtr();
571   service_->DisableNotifications(
572       char_handle, handler_id, [self, notifier_id](auto /*status*/) {
573         if (!self.is_alive()) {
574           return;
575         }
576         // Clear the notifier regardless of status. Wait until this callback is
577         // called in order to prevent the value callback from being called for
578         // an erased notifier.
579         self->characteristic_notifiers_.erase(notifier_id);
580       });
581 }
582 
583 }  // namespace bthost
584