xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/fuchsia/host/fidl/host_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/host_server.h"
16 
17 #include <cpp-string/string_printf.h>
18 #include <lib/fpromise/result.h>
19 #include <zircon/errors.h>
20 
21 #include <utility>
22 #include <variant>
23 
24 #include "fuchsia/bluetooth/host/cpp/fidl.h"
25 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/gatt2_server_server.h"
26 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/gatt_server_server.h"
27 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/helpers.h"
28 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/low_energy_central_server.h"
29 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/low_energy_peripheral_server.h"
30 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/profile_server.h"
31 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
32 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h"
33 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
34 #include "pw_bluetooth_sapphire/internal/host/gap/adapter.h"
35 #include "pw_bluetooth_sapphire/internal/host/gap/bonding_data.h"
36 #include "pw_bluetooth_sapphire/internal/host/gap/bredr_connection_manager.h"
37 #include "pw_bluetooth_sapphire/internal/host/gap/bredr_discovery_manager.h"
38 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h"
39 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_discovery_manager.h"
40 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
41 #include "pw_bluetooth_sapphire/internal/host/sm/util.h"
42 
43 namespace bthost {
44 
45 namespace fbt = fuchsia::bluetooth;
46 namespace fsys = fuchsia::bluetooth::sys;
47 namespace fhost = fuchsia::bluetooth::host;
48 
49 using bt::PeerId;
50 using bt::gap::BrEdrSecurityModeToString;
51 using bt::gap::LeSecurityModeToString;
52 using bt::sm::IOCapability;
53 using fidl_helpers::BrEdrSecurityModeFromFidl;
54 using fidl_helpers::HostErrorToFidl;
55 using fidl_helpers::LeSecurityModeFromFidl;
56 using fidl_helpers::NewFidlError;
57 using fidl_helpers::PeerIdFromString;
58 using fidl_helpers::ResultToFidl;
59 using fidl_helpers::SecurityLevelFromFidl;
60 
HostServer(zx::channel channel,const bt::gap::Adapter::WeakPtr & adapter,bt::gatt::GATT::WeakPtr gatt)61 HostServer::HostServer(zx::channel channel,
62                        const bt::gap::Adapter::WeakPtr& adapter,
63                        bt::gatt::GATT::WeakPtr gatt)
64     : AdapterServerBase(adapter, this, std::move(channel)),
65       pairing_delegate_(nullptr),
66       gatt_(std::move(gatt)),
67       requesting_background_scan_(false),
68       requesting_discoverable_(false),
69       io_capability_(IOCapability::kNoInputNoOutput),
70       weak_self_(this),
71       weak_pairing_(this) {
72   PW_CHECK(gatt_.is_alive());
73 
74   auto self = weak_self_.GetWeakPtr();
75   adapter->peer_cache()->set_peer_bonded_callback([self](const auto& peer) {
76     if (self.is_alive()) {
77       self->OnPeerBonded(peer);
78     }
79   });
80   adapter->set_auto_connect_callback([self](auto conn_ref) {
81     if (self.is_alive()) {
82       self->RegisterLowEnergyConnection(std::move(conn_ref),
83                                         /*auto_connect=*/true);
84     }
85   });
86 
87   // Watch for changes in LE address.
88   adapter->le()->register_address_changed_callback([self]() {
89     if (self.is_alive()) {
90       self->NotifyInfoChange();
91     }
92   });
93 
94   // Initialize the HostInfo getter with the initial state.
95   NotifyInfoChange();
96 }
97 
~HostServer()98 HostServer::~HostServer() { Shutdown(); }
99 
RequestProtocol(fhost::ProtocolRequest request)100 void HostServer::RequestProtocol(fhost::ProtocolRequest request) {
101   switch (request.Which()) {
102     case fhost::ProtocolRequest::Tag::kCentral:
103       BindServer<LowEnergyCentralServer>(
104           adapter()->AsWeakPtr(), std::move(request.central()), gatt_);
105       break;
106     case fhost::ProtocolRequest::Tag::kPeripheral:
107       BindServer<LowEnergyPeripheralServer>(
108           adapter()->AsWeakPtr(), gatt_, std::move(request.peripheral()));
109       break;
110     case fhost::ProtocolRequest::Tag::kGattServer:
111       BindServer<GattServerServer>(gatt_->GetWeakPtr(),
112                                    std::move(request.gatt_server()));
113       break;
114     case fhost::ProtocolRequest::Tag::kGatt2Server:
115       BindServer<Gatt2ServerServer>(gatt_->GetWeakPtr(),
116                                     std::move(request.gatt2_server()));
117       break;
118     case fhost::ProtocolRequest::Tag::kProfile:
119       BindServer<ProfileServer>(adapter()->AsWeakPtr(),
120                                 std::move(request.profile()));
121       break;
122     case fhost::ProtocolRequest::Tag::kPrivilegedPeripheral:
123       BindServer<LowEnergyPrivilegedPeripheralServer>(
124           adapter()->AsWeakPtr(),
125           gatt_,
126           std::move(request.privileged_peripheral()));
127       break;
128     default:
129       bt_log(WARN, "fidl", "received unknown protocol request");
130       // The unknown protocol will be closed when `request` is destroyed.
131       break;
132   }
133 }
134 
WatchState(WatchStateCallback callback)135 void HostServer::WatchState(WatchStateCallback callback) {
136   info_getter_.Watch([cb = std::move(callback)](fsys::HostInfo info) {
137     cb(fhost::Host_WatchState_Result::WithResponse(
138         fhost::Host_WatchState_Response(std::move(info))));
139   });
140 }
141 
SetLocalData(fsys::HostData host_data)142 void HostServer::SetLocalData(fsys::HostData host_data) {
143   if (host_data.has_irk()) {
144     bt_log(DEBUG, "fidl", "assign IRK");
145     if (adapter()->le()) {
146       adapter()->le()->set_irk(host_data.irk().value);
147     }
148   }
149 }
150 
SetPeerWatcher(::fidl::InterfaceRequest<::fuchsia::bluetooth::host::PeerWatcher> peer_watcher)151 void HostServer::SetPeerWatcher(
152     ::fidl::InterfaceRequest<::fuchsia::bluetooth::host::PeerWatcher>
153         peer_watcher) {
154   if (peer_watcher_server_.has_value()) {
155     peer_watcher.Close(ZX_ERR_ALREADY_BOUND);
156     return;
157   }
158   peer_watcher_server_.emplace(
159       std::move(peer_watcher), adapter()->peer_cache(), this);
160 }
161 
SetLocalName(::std::string local_name,SetLocalNameCallback callback)162 void HostServer::SetLocalName(::std::string local_name,
163                               SetLocalNameCallback callback) {
164   PW_DCHECK(!local_name.empty());
165   adapter()->SetLocalName(std::move(local_name),
166                           [self = weak_self_.GetWeakPtr(),
167                            callback = std::move(callback)](auto status) {
168                             // Send adapter state update on success and if the
169                             // connection is still open.
170                             if (status.is_ok() && self.is_alive()) {
171                               self->NotifyInfoChange();
172                             }
173                             callback(ResultToFidl(status));
174                           });
175 }
176 
177 // TODO(fxbug.dev/42110379): Add a unit test for this method.
SetDeviceClass(fbt::DeviceClass device_class,SetDeviceClassCallback callback)178 void HostServer::SetDeviceClass(fbt::DeviceClass device_class,
179                                 SetDeviceClassCallback callback) {
180   // Device Class values must only contain data in the lower 3 bytes.
181   if (device_class.value >= 1 << 24) {
182     callback(fpromise::error(fsys::Error::INVALID_ARGUMENTS));
183     return;
184   }
185   bt::DeviceClass dev_class(device_class.value);
186   adapter()->SetDeviceClass(dev_class,
187                             [callback = std::move(callback)](auto status) {
188                               callback(ResultToFidl(status));
189                             });
190 }
191 
StartLEDiscovery()192 void HostServer::StartLEDiscovery() {
193   if (!adapter()->le()) {
194     StopDiscovery(ZX_ERR_INTERNAL);
195     return;
196   }
197 
198   adapter()->le()->StartDiscovery(
199       /*active=*/true, [self = weak_self_.GetWeakPtr()](auto session) {
200         // End the new session if this AdapterServer got destroyed in the
201         // meantime (e.g. because the client disconnected).
202         if (!self.is_alive() || self->discovery_session_servers_.empty()) {
203           return;
204         }
205 
206         if (!session) {
207           bt_log(ERROR, "fidl", "failed to start active LE discovery session");
208           self->StopDiscovery(ZX_ERR_INTERNAL);
209           return;
210         }
211 
212         // Set up a general-discovery filter for connectable devices.
213         // NOTE(armansito): This currently has no effect since peer updates
214         // are driven by PeerCache events. |session|'s "result callback" is
215         // unused.
216         session->filter()->set_connectable(true);
217         session->filter()->SetGeneralDiscoveryFlags();
218 
219         self->le_discovery_session_ = std::move(session);
220 
221         // Send the adapter state update.
222         self->NotifyInfoChange();
223       });
224 }
225 
StartDiscovery(::fuchsia::bluetooth::host::HostStartDiscoveryRequest request)226 void HostServer::StartDiscovery(
227     ::fuchsia::bluetooth::host::HostStartDiscoveryRequest request) {
228   bt_log(DEBUG, "fidl", "%s", __FUNCTION__);
229   PW_DCHECK(adapter().is_alive());
230 
231   if (!request.has_token()) {
232     bt_log(WARN, "fidl", "missing Discovery token");
233     return;
234   }
235   fidl::InterfaceRequest<fhost::DiscoverySession> token =
236       std::move(*request.mutable_token());
237 
238   std::unique_ptr<DiscoverySessionServer> server =
239       std::make_unique<DiscoverySessionServer>(std::move(token), this);
240   DiscoverySessionServer* server_ptr = server.get();
241   discovery_session_servers_.emplace(server_ptr, std::move(server));
242 
243   // If there were existing sessions, then discovery is already
244   // starting/started.
245   if (discovery_session_servers_.size() != 1) {
246     return;
247   }
248 
249   if (!adapter()->bredr()) {
250     StartLEDiscovery();
251     return;
252   }
253   // TODO(jamuraa): start these in parallel instead of sequence
254   adapter()->bredr()->RequestDiscovery([self = weak_self_.GetWeakPtr(),
255                                         func = __FUNCTION__](
256                                            bt::hci::Result<> result,
257                                            auto session) mutable {
258     if (!self.is_alive() || self->discovery_session_servers_.empty()) {
259       return;
260     }
261 
262     if (result.is_error() || !session) {
263       bt_log(
264           ERROR, "fidl", "%s: failed to start BR/EDR discovery session", func);
265       self->StopDiscovery(ZX_ERR_INTERNAL);
266       return;
267     }
268 
269     self->bredr_discovery_session_ = std::move(session);
270     self->StartLEDiscovery();
271   });
272 }
273 
StopDiscovery(zx_status_t epitaph,bool notify_info_change)274 void HostServer::StopDiscovery(zx_status_t epitaph, bool notify_info_change) {
275   bool discovering = le_discovery_session_ || bredr_discovery_session_;
276   bredr_discovery_session_ = nullptr;
277   le_discovery_session_ = nullptr;
278   for (auto& [server, _] : discovery_session_servers_) {
279     server->Close(epitaph);
280   }
281   discovery_session_servers_.clear();
282 
283   if (discovering && notify_info_change) {
284     NotifyInfoChange();
285   }
286 }
287 
OnDiscoverySessionServerClose(DiscoverySessionServer * server)288 void HostServer::OnDiscoverySessionServerClose(DiscoverySessionServer* server) {
289   server->Close(ZX_ERR_CANCELED);
290   discovery_session_servers_.erase(server);
291   if (discovery_session_servers_.empty()) {
292     StopDiscovery(ZX_ERR_CANCELED);
293   }
294 }
295 
SetConnectable(bool connectable,SetConnectableCallback callback)296 void HostServer::SetConnectable(bool connectable,
297                                 SetConnectableCallback callback) {
298   bt_log(INFO, "fidl", "%s: %s", __FUNCTION__, connectable ? "true" : "false");
299 
300   auto classic = adapter()->bredr();
301   if (!classic) {
302     callback(fpromise::error(fsys::Error::NOT_SUPPORTED));
303     return;
304   }
305   classic->SetConnectable(connectable,
306                           [callback = std::move(callback)](const auto& result) {
307                             callback(ResultToFidl(result));
308                           });
309 }
310 
RestoreBonds(::std::vector<fsys::BondingData> bonds,fhost::BondingDelegate::RestoreBondsCallback callback)311 void HostServer::RestoreBonds(
312     ::std::vector<fsys::BondingData> bonds,
313     fhost::BondingDelegate::RestoreBondsCallback callback) {
314   bt_log(INFO, "fidl", "%s", __FUNCTION__);
315 
316   if (bonds.empty()) {
317     // Nothing to do. Reply with an empty list.
318     callback(fhost::BondingDelegate_RestoreBonds_Result::WithResponse(
319         fhost::BondingDelegate_RestoreBonds_Response()));
320     return;
321   }
322 
323   std::vector<fsys::BondingData> errors;
324   for (auto& bond : bonds) {
325     if (!bond.has_identifier() || !bond.has_address() ||
326         !(bond.has_le_bond() || bond.has_bredr_bond())) {
327       bt_log(ERROR,
328              "fidl",
329              "%s: BondingData mandatory fields missing!",
330              __FUNCTION__);
331       errors.push_back(std::move(bond));
332       continue;
333     }
334 
335     auto address = fidl_helpers::AddressFromFidlBondingData(bond);
336     if (!address) {
337       bt_log(ERROR,
338              "fidl",
339              "%s: BondingData address missing or invalid!",
340              __FUNCTION__);
341       errors.push_back(std::move(bond));
342       continue;
343     }
344 
345     bt::gap::BondingData bd;
346     bd.identifier = bt::PeerId{bond.identifier().value};
347     bd.address = *address;
348     if (bond.has_name()) {
349       bd.name = {bond.name()};
350     }
351 
352     if (bond.has_le_bond()) {
353       bd.le_pairing_data =
354           fidl_helpers::LePairingDataFromFidl(*address, bond.le_bond());
355     }
356     if (bond.has_bredr_bond()) {
357       bd.bredr_link_key = fidl_helpers::BredrKeyFromFidl(bond.bredr_bond());
358       bd.bredr_services =
359           fidl_helpers::BredrServicesFromFidl(bond.bredr_bond());
360     }
361 
362     // TODO(fxbug.dev/42137736): Convert bond.bredr.services to
363     // BondingData::bredr_services
364     if (!adapter()->AddBondedPeer(bd)) {
365       bt_log(ERROR,
366              "fidl",
367              "%s: failed to restore bonding data entry",
368              __FUNCTION__);
369       errors.push_back(std::move(bond));
370     }
371   }
372 
373   callback(fhost::BondingDelegate_RestoreBonds_Result::WithResponse(
374       fhost::BondingDelegate_RestoreBonds_Response(std::move(errors))));
375 }
376 
OnPeerBonded(const bt::gap::Peer & peer)377 void HostServer::OnPeerBonded(const bt::gap::Peer& peer) {
378   bt_log(DEBUG, "fidl", "%s", __FUNCTION__);
379   if (bonding_delegate_server_) {
380     bonding_delegate_server_->OnNewBondingData(peer);
381   }
382 }
383 
RegisterLowEnergyConnection(std::unique_ptr<bt::gap::LowEnergyConnectionHandle> conn_ref,bool auto_connect)384 void HostServer::RegisterLowEnergyConnection(
385     std::unique_ptr<bt::gap::LowEnergyConnectionHandle> conn_ref,
386     bool auto_connect) {
387   PW_DCHECK(conn_ref);
388 
389   bt::PeerId id = conn_ref->peer_identifier();
390   auto iter = le_connections_.find(id);
391   if (iter != le_connections_.end()) {
392     bt_log(
393         WARN,
394         "fidl",
395         "%s: peer already connected; connection reference dropped (peer: %s)",
396         __FUNCTION__,
397         bt_str(id));
398     return;
399   }
400 
401   bt_log(DEBUG,
402          "fidl",
403          "LE peer connected (%s): %s ",
404          (auto_connect ? "auto" : "direct"),
405          bt_str(id));
406   conn_ref->set_closed_callback([self = weak_self_.GetWeakPtr(), id] {
407     if (self.is_alive())
408       self->le_connections_.erase(id);
409   });
410   le_connections_[id] = std::move(conn_ref);
411 }
412 
SetDiscoverable(bool discoverable,SetDiscoverableCallback callback)413 void HostServer::SetDiscoverable(bool discoverable,
414                                  SetDiscoverableCallback callback) {
415   bt_log(INFO, "fidl", "%s(%s)", __FUNCTION__, discoverable ? "true" : "false");
416   // TODO(fxbug.dev/42177512): advertise LE here
417   if (!discoverable) {
418     bredr_discoverable_session_ = nullptr;
419     NotifyInfoChange();
420     callback(fpromise::ok());
421     return;
422   }
423   if (discoverable && requesting_discoverable_) {
424     bt_log(DEBUG, "fidl", "%s already in progress", __FUNCTION__);
425     callback(fpromise::error(fsys::Error::IN_PROGRESS));
426     return;
427   }
428   requesting_discoverable_ = true;
429   if (!adapter()->bredr()) {
430     callback(fpromise::error(fsys::Error::FAILED));
431     return;
432   }
433   adapter()->bredr()->RequestDiscoverable([self = weak_self_.GetWeakPtr(),
434                                            callback = std::move(callback),
435                                            func = __FUNCTION__](
436                                               bt::hci::Result<> result,
437                                               auto session) {
438     if (!self.is_alive()) {
439       callback(fpromise::error(fsys::Error::FAILED));
440       return;
441     }
442 
443     if (!self->requesting_discoverable_) {
444       callback(fpromise::error(fsys::Error::CANCELED));
445       return;
446     }
447 
448     if (result.is_error() || !session) {
449       bt_log(ERROR, "fidl", "%s: failed (result: %s)", func, bt_str(result));
450       fpromise::result<void, fsys::Error> fidl_result = ResultToFidl(result);
451       if (result.is_ok()) {
452         PW_CHECK(session == nullptr);
453         fidl_result = fpromise::error(fsys::Error::FAILED);
454       }
455       self->requesting_discoverable_ = false;
456       callback(std::move(fidl_result));
457       return;
458     }
459 
460     self->bredr_discoverable_session_ = std::move(session);
461     self->requesting_discoverable_ = false;
462     self->NotifyInfoChange();
463     callback(fpromise::ok());
464   });
465 }
466 
EnableBackgroundScan(bool enabled)467 void HostServer::EnableBackgroundScan(bool enabled) {
468   bt_log(INFO, "fidl", "%s background scan", (enabled ? "enable" : "disable"));
469   if (!adapter()->le()) {
470     bt_log(ERROR, "fidl", "%s: adapter does not support LE", __FUNCTION__);
471     return;
472   }
473 
474   if (!enabled) {
475     requesting_background_scan_ = false;
476     le_background_scan_ = nullptr;
477     return;
478   }
479 
480   // If a scan is already starting or is in progress, there is nothing to do to
481   // enable the scan.
482   if (requesting_background_scan_ || le_background_scan_) {
483     return;
484   }
485 
486   requesting_background_scan_ = true;
487   adapter()->le()->StartDiscovery(
488       /*active=*/false, [self = weak_self_.GetWeakPtr()](auto session) {
489         if (!self.is_alive()) {
490           return;
491         }
492 
493         // Background scan may have been disabled while discovery was starting.
494         if (!self->requesting_background_scan_) {
495           return;
496         }
497 
498         if (!session) {
499           bt_log(ERROR, "fidl", "failed to start LE background scan");
500           self->le_background_scan_ = nullptr;
501           self->requesting_background_scan_ = false;
502           return;
503         }
504 
505         self->le_background_scan_ = std::move(session);
506         self->requesting_background_scan_ = false;
507       });
508 }
509 
EnablePrivacy(bool enabled)510 void HostServer::EnablePrivacy(bool enabled) {
511   bt_log(INFO,
512          "fidl",
513          "%s: %s LE privacy",
514          __FUNCTION__,
515          (enabled ? "enable" : "disable"));
516   if (adapter()->le()) {
517     adapter()->le()->EnablePrivacy(enabled);
518   }
519 }
520 
SetBrEdrSecurityMode(fsys::BrEdrSecurityMode mode)521 void HostServer::SetBrEdrSecurityMode(fsys::BrEdrSecurityMode mode) {
522   std::optional<bt::gap::BrEdrSecurityMode> gap_mode =
523       BrEdrSecurityModeFromFidl(mode);
524   if (!gap_mode.has_value()) {
525     bt_log(WARN, "fidl", "%s: Unrecognized BR/EDR security mode", __FUNCTION__);
526     return;
527   }
528 
529   bt_log(INFO,
530          "fidl",
531          "%s: %s",
532          __FUNCTION__,
533          BrEdrSecurityModeToString(gap_mode.value()));
534   if (adapter()->bredr()) {
535     adapter()->bredr()->SetBrEdrSecurityMode(gap_mode.value());
536   }
537 }
538 
SetLeSecurityMode(fsys::LeSecurityMode mode)539 void HostServer::SetLeSecurityMode(fsys::LeSecurityMode mode) {
540   bt::gap::LESecurityMode gap_mode = LeSecurityModeFromFidl(mode);
541   bt_log(
542       INFO, "fidl", "%s: %s", __FUNCTION__, LeSecurityModeToString(gap_mode));
543   if (adapter()->le()) {
544     adapter()->le()->SetLESecurityMode(gap_mode);
545   }
546 }
547 
SetPairingDelegate(fsys::InputCapability input,fsys::OutputCapability output,::fidl::InterfaceHandle<fsys::PairingDelegate> delegate)548 void HostServer::SetPairingDelegate(
549     fsys::InputCapability input,
550     fsys::OutputCapability output,
551     ::fidl::InterfaceHandle<fsys::PairingDelegate> delegate) {
552   bool cleared = !delegate;
553   pairing_delegate_.Bind(std::move(delegate));
554 
555   if (cleared) {
556     bt_log(INFO, "fidl", "%s: PairingDelegate cleared", __FUNCTION__);
557     ResetPairingDelegate();
558     return;
559   }
560 
561   io_capability_ = fidl_helpers::IoCapabilityFromFidl(input, output);
562   bt_log(INFO,
563          "fidl",
564          "%s: PairingDelegate assigned (I/O capability: %s)",
565          __FUNCTION__,
566          bt::sm::util::IOCapabilityToString(io_capability_).c_str());
567 
568   auto pairing = weak_pairing_.GetWeakPtr();
569   auto self = weak_self_.GetWeakPtr();
570   adapter()->SetPairingDelegate(pairing);
571   pairing_delegate_.set_error_handler([self, func = __FUNCTION__](
572                                           zx_status_t status) {
573     bt_log(
574         INFO, "fidl", "%s error handler: PairingDelegate disconnected", func);
575     if (self.is_alive()) {
576       self->ResetPairingDelegate();
577     }
578   });
579 }
580 
581 // Attempt to connect to peer identified by |peer_id|. The peer must be
582 // in our peer cache. We will attempt to connect technologies (LowEnergy,
583 // Classic or Dual-Mode) as the peer claims to support when discovered
Connect(fbt::PeerId peer_id,ConnectCallback callback)584 void HostServer::Connect(fbt::PeerId peer_id, ConnectCallback callback) {
585   bt::PeerId id{peer_id.value};
586   bt_log(INFO, "fidl", "%s: (peer: %s)", __FUNCTION__, bt_str(id));
587 
588   auto peer = adapter()->peer_cache()->FindById(id);
589   if (!peer) {
590     // We don't support connecting to peers that are not in our cache
591     bt_log(WARN,
592            "fidl",
593            "%s: peer not found in peer cache (peer: %s)",
594            __FUNCTION__,
595            bt_str(id));
596     callback(fpromise::error(fsys::Error::PEER_NOT_FOUND));
597     return;
598   }
599 
600   // TODO(fxbug.dev/42075069): Dual-mode currently not supported; if the peer
601   // supports BR/EDR we prefer BR/EDR. If a dual-mode peer, we should attempt to
602   // connect both protocols.
603   if (peer->bredr()) {
604     ConnectBrEdr(id, std::move(callback));
605     return;
606   }
607 
608   ConnectLowEnergy(id, std::move(callback));
609 }
610 
611 // Attempt to disconnect the peer identified by |peer_id| from all transports.
612 // If the peer is already not connected, return success. If the peer is
613 // disconnected succesfully, return success.
Disconnect(fbt::PeerId peer_id,DisconnectCallback callback)614 void HostServer::Disconnect(fbt::PeerId peer_id, DisconnectCallback callback) {
615   bt::PeerId id{peer_id.value};
616   bt_log(INFO, "fidl", "%s: (peer: %s)", __FUNCTION__, bt_str(id));
617 
618   bool le_disc = adapter()->le() ? adapter()->le()->Disconnect(id) : true;
619   bool bredr_disc = adapter()->bredr()
620                         ? adapter()->bredr()->Disconnect(
621                               id, bt::gap::DisconnectReason::kApiRequest)
622                         : true;
623   if (le_disc && bredr_disc) {
624     callback(fpromise::ok());
625   } else {
626     bt_log(WARN, "fidl", "%s: failed (peer: %s)", __FUNCTION__, bt_str(id));
627     callback(fpromise::error(fsys::Error::FAILED));
628   }
629 }
630 
ConnectLowEnergy(PeerId peer_id,ConnectCallback callback)631 void HostServer::ConnectLowEnergy(PeerId peer_id, ConnectCallback callback) {
632   auto self = weak_self_.GetWeakPtr();
633   auto on_complete = [self,
634                       callback = std::move(callback),
635                       peer_id,
636                       func = __FUNCTION__](auto result) {
637     if (result.is_error()) {
638       bt_log(INFO,
639              "fidl",
640              "%s: failed to connect LE transport to peer (peer: %s)",
641              func,
642              bt_str(peer_id));
643       callback(fpromise::error(HostErrorToFidl(result.error_value())));
644       return;
645     }
646 
647     // We must be connected and to the right peer
648     auto connection = std::move(result).value();
649     PW_CHECK(connection);
650     PW_CHECK(peer_id == connection->peer_identifier());
651 
652     callback(fpromise::ok());
653 
654     if (self.is_alive())
655       self->RegisterLowEnergyConnection(std::move(connection),
656                                         /*auto_connect=*/false);
657   };
658 
659   adapter()->le()->Connect(
660       peer_id, std::move(on_complete), bt::gap::LowEnergyConnectionOptions());
661 }
662 
663 // Initiate an outgoing Br/Edr connection, unless already connected
664 // Br/Edr connections are host-wide, and stored in BrEdrConnectionManager
ConnectBrEdr(PeerId peer_id,ConnectCallback callback)665 void HostServer::ConnectBrEdr(PeerId peer_id, ConnectCallback callback) {
666   auto on_complete = [callback = std::move(callback),
667                       peer_id,
668                       func = __FUNCTION__](auto status, auto connection) {
669     if (status.is_error()) {
670       PW_CHECK(!connection);
671       bt_log(INFO,
672              "fidl",
673              "%s: failed to connect BR/EDR transport to peer (peer: %s)",
674              func,
675              bt_str(peer_id));
676       callback(fpromise::error(HostErrorToFidl(status.error_value())));
677       return;
678     }
679 
680     // We must be connected and to the right peer
681     PW_CHECK(connection);
682     PW_CHECK(peer_id == connection->peer_id());
683 
684     callback(fpromise::ok());
685   };
686 
687   if (!adapter()->bredr()->Connect(peer_id, std::move(on_complete))) {
688     bt_log(
689         INFO,
690         "fidl",
691         "%s: failed to initiate BR/EDR transport connection to peer (peer: %s)",
692         __FUNCTION__,
693         bt_str(peer_id));
694     callback(fpromise::error(fsys::Error::FAILED));
695   }
696 }
697 
Forget(fbt::PeerId peer_id,ForgetCallback callback)698 void HostServer::Forget(fbt::PeerId peer_id, ForgetCallback callback) {
699   bt::PeerId id{peer_id.value};
700   auto peer = adapter()->peer_cache()->FindById(id);
701   if (!peer) {
702     bt_log(DEBUG, "fidl", "peer %s to forget wasn't found", bt_str(id));
703     callback(fpromise::ok());
704     return;
705   }
706 
707   const bool le_disconnected =
708       adapter()->le() ? adapter()->le()->Disconnect(id) : true;
709   const bool bredr_disconnected =
710       adapter()->bredr() ? adapter()->bredr()->Disconnect(
711                                id, bt::gap::DisconnectReason::kApiRequest)
712                          : true;
713   const bool peer_removed = adapter()->peer_cache()->RemoveDisconnectedPeer(id);
714 
715   if (!le_disconnected || !bredr_disconnected) {
716     const auto message =
717         bt_lib_cpp_string::StringPrintf("link(s) failed to close:%s%s",
718                                         le_disconnected ? "" : " LE",
719                                         bredr_disconnected ? "" : " BR/EDR");
720     callback(fpromise::error(fsys::Error::FAILED));
721   } else {
722     PW_CHECK(peer_removed);
723     callback(fpromise::ok());
724   }
725 }
726 
Pair(fbt::PeerId id,fsys::PairingOptions options,PairCallback callback)727 void HostServer::Pair(fbt::PeerId id,
728                       fsys::PairingOptions options,
729                       PairCallback callback) {
730   auto peer_id = bt::PeerId(id.value);
731   auto peer = adapter()->peer_cache()->FindById(peer_id);
732   if (!peer) {
733     bt_log(WARN, "fidl", "%s: unknown peer %s", __FUNCTION__, bt_str(peer_id));
734     // We don't support pairing to peers that are not in our cache
735     callback(fpromise::error(fsys::Error::PEER_NOT_FOUND));
736     return;
737   }
738   // If options specifies a transport preference for LE or BR/EDR, we use that.
739   // Otherwise, we use whatever transport exists, defaulting to LE for dual-mode
740   // connections.
741   bool pair_bredr = !peer->le();
742   if (options.has_transport() &&
743       options.transport() != fsys::TechnologyType::DUAL_MODE) {
744     pair_bredr = (options.transport() == fsys::TechnologyType::CLASSIC);
745   }
746   if (pair_bredr) {
747     PairBrEdr(peer_id, std::move(callback));
748     return;
749   }
750   PairLowEnergy(peer_id, std::move(options), std::move(callback));
751 }
752 
PairLowEnergy(PeerId peer_id,fsys::PairingOptions options,PairCallback callback)753 void HostServer::PairLowEnergy(PeerId peer_id,
754                                fsys::PairingOptions options,
755                                PairCallback callback) {
756   std::optional<bt::sm::SecurityLevel> security_level;
757   if (options.has_le_security_level()) {
758     security_level = SecurityLevelFromFidl(options.le_security_level());
759     if (!security_level.has_value()) {
760       bt_log(WARN,
761              "fidl",
762              "%s: pairing options missing LE security level (peer: %s)",
763              __FUNCTION__,
764              bt_str(peer_id));
765       callback(fpromise::error(fsys::Error::INVALID_ARGUMENTS));
766       return;
767     }
768   } else {
769     security_level = bt::sm::SecurityLevel::kAuthenticated;
770   }
771   bt::sm::BondableMode bondable_mode = bt::sm::BondableMode::Bondable;
772   if (options.has_bondable_mode() &&
773       options.bondable_mode() == fsys::BondableMode::NON_BONDABLE) {
774     bondable_mode = bt::sm::BondableMode::NonBondable;
775   }
776   auto on_complete = [peer_id,
777                       callback = std::move(callback),
778                       func = __FUNCTION__](bt::sm::Result<> status) {
779     if (status.is_error()) {
780       bt_log(
781           WARN, "fidl", "%s: failed to pair (peer: %s)", func, bt_str(peer_id));
782       callback(fpromise::error(HostErrorToFidl(status.error_value())));
783     } else {
784       callback(fpromise::ok());
785     }
786   };
787   PW_CHECK(adapter()->le());
788   adapter()->le()->Pair(
789       peer_id, *security_level, bondable_mode, std::move(on_complete));
790 }
791 
PairBrEdr(PeerId peer_id,PairCallback callback)792 void HostServer::PairBrEdr(PeerId peer_id, PairCallback callback) {
793   auto on_complete = [peer_id,
794                       callback = std::move(callback),
795                       func = __FUNCTION__](bt::hci::Result<> status) {
796     if (status.is_error()) {
797       bt_log(
798           WARN, "fidl", "%s: failed to pair (peer: %s)", func, bt_str(peer_id));
799       callback(fpromise::error(HostErrorToFidl(status.error_value())));
800     } else {
801       callback(fpromise::ok());
802     }
803   };
804   // TODO(fxbug.dev/42135898): Add security parameter to Pair and use that here
805   // instead of hardcoding default.
806   bt::gap::BrEdrSecurityRequirements security{.authentication = false,
807                                               .secure_connections = false};
808   PW_CHECK(adapter()->bredr());
809   adapter()->bredr()->Pair(peer_id, security, std::move(on_complete));
810 }
811 
Shutdown()812 void HostServer::Shutdown() {
813   bt_log(INFO, "fidl", "closing FIDL handles");
814 
815   // Invalidate all weak pointers. This will guarantee that all pending tasks
816   // that reference this HostServer will return early if they run in the future.
817   weak_self_.InvalidatePtrs();
818 
819   // Destroy all FIDL bindings.
820   servers_.clear();
821 
822   // Cancel pending requests.
823   requesting_discoverable_ = false;
824   requesting_background_scan_ = false;
825 
826   le_background_scan_ = nullptr;
827   bredr_discoverable_session_ = nullptr;
828 
829   StopDiscovery(ZX_ERR_CANCELED, /*notify_info_change=*/false);
830 
831   // Drop all connections that are attached to this HostServer.
832   le_connections_.clear();
833 
834   if (adapter()->le()) {
835     // Stop background scan if enabled.
836     adapter()->le()->EnablePrivacy(false);
837     adapter()->le()->set_irk(std::nullopt);
838   }
839 
840   // Disallow future pairing.
841   pairing_delegate_ = nullptr;
842   ResetPairingDelegate();
843 
844   // Send adapter state change.
845   if (binding()->is_bound()) {
846     NotifyInfoChange();
847   }
848 }
849 
SetBondingDelegate(::fidl::InterfaceRequest<::fuchsia::bluetooth::host::BondingDelegate> request)850 void HostServer::SetBondingDelegate(
851     ::fidl::InterfaceRequest<::fuchsia::bluetooth::host::BondingDelegate>
852         request) {
853   if (bonding_delegate_server_.has_value()) {
854     request.Close(ZX_ERR_ALREADY_BOUND);
855     return;
856   }
857   bonding_delegate_server_.emplace(std::move(request), this);
858 }
859 
handle_unknown_method(uint64_t ordinal,bool method_has_response)860 void HostServer::handle_unknown_method(uint64_t ordinal,
861                                        bool method_has_response) {
862   bt_log(WARN, "fidl", "Received unknown method with ordinal: %lu", ordinal);
863 }
864 
Stop()865 void HostServer::DiscoverySessionServer::Stop() {
866   host_->OnDiscoverySessionServerClose(this);
867 }
868 
handle_unknown_method(uint64_t ordinal,bool method_has_response)869 void HostServer::DiscoverySessionServer::handle_unknown_method(
870     uint64_t ordinal, bool method_has_response) {
871   bt_log(WARN, "fidl", "Received unknown method with ordinal: %lu", ordinal);
872 }
873 
DiscoverySessionServer(fidl::InterfaceRequest<::fuchsia::bluetooth::host::DiscoverySession> request,HostServer * host)874 HostServer::DiscoverySessionServer::DiscoverySessionServer(
875     fidl::InterfaceRequest<::fuchsia::bluetooth::host::DiscoverySession>
876         request,
877     HostServer* host)
878     : ServerBase(this, std::move(request)), host_(host) {
879   binding()->set_error_handler([this, host](zx_status_t /*status*/) {
880     host->OnDiscoverySessionServerClose(this);
881   });
882 }
883 
PeerWatcherServer(::fidl::InterfaceRequest<::fuchsia::bluetooth::host::PeerWatcher> request,bt::gap::PeerCache * peer_cache,HostServer * host)884 HostServer::PeerWatcherServer::PeerWatcherServer(
885     ::fidl::InterfaceRequest<::fuchsia::bluetooth::host::PeerWatcher> request,
886     bt::gap::PeerCache* peer_cache,
887     HostServer* host)
888     : ServerBase(this, std::move(request)),
889       peer_cache_(peer_cache),
890       host_(host),
891       weak_self_(this) {
892   auto self = weak_self_.GetWeakPtr();
893 
894   peer_updated_callback_id_ =
895       peer_cache_->add_peer_updated_callback([self](const auto& peer) {
896         if (self.is_alive()) {
897           self->OnPeerUpdated(peer);
898         }
899       });
900   peer_cache_->set_peer_removed_callback([self](const auto& identifier) {
901     if (self.is_alive()) {
902       self->OnPeerRemoved(identifier);
903     }
904   });
905 
906   // Initialize the peer watcher with all known connectable peers that are in
907   // the cache.
908   peer_cache_->ForEach(
909       [this](const bt::gap::Peer& peer) { OnPeerUpdated(peer); });
910 
911   binding()->set_error_handler(
912       [this](zx_status_t /*status*/) { host_->peer_watcher_server_.reset(); });
913 }
914 
~PeerWatcherServer()915 HostServer::PeerWatcherServer::~PeerWatcherServer() {
916   // Unregister PeerCache callbacks.
917   peer_cache_->remove_peer_updated_callback(peer_updated_callback_id_);
918   peer_cache_->set_peer_removed_callback(nullptr);
919 }
920 
OnPeerUpdated(const bt::gap::Peer & peer)921 void HostServer::PeerWatcherServer::OnPeerUpdated(const bt::gap::Peer& peer) {
922   if (!peer.connectable()) {
923     return;
924   }
925 
926   updated_.insert(peer.identifier());
927   removed_.erase(peer.identifier());
928   MaybeCallCallback();
929 }
930 
OnPeerRemoved(bt::PeerId id)931 void HostServer::PeerWatcherServer::OnPeerRemoved(bt::PeerId id) {
932   updated_.erase(id);
933   removed_.insert(id);
934   MaybeCallCallback();
935 }
936 
MaybeCallCallback()937 void HostServer::PeerWatcherServer::MaybeCallCallback() {
938   if (!callback_) {
939     return;
940   }
941 
942   if (!removed_.empty()) {
943     Removed removed_fidl;
944     for (const bt::PeerId& id : removed_) {
945       removed_fidl.push_back(fbt::PeerId{id.value()});
946     }
947     removed_.clear();
948     callback_(fhost::PeerWatcher_GetNext_Result::WithResponse(
949         fhost::PeerWatcher_GetNext_Response::WithRemoved(
950             std::move(removed_fidl))));
951     callback_ = nullptr;
952     return;
953   }
954 
955   if (!updated_.empty()) {
956     Updated updated_fidl;
957     for (const bt::PeerId& id : updated_) {
958       bt::gap::Peer* peer = peer_cache_->FindById(id);
959       // All ids in |updated_| are assumed to be valid as they would otherwise
960       // be in |removed_|.
961       PW_CHECK(peer);
962       updated_fidl.push_back(fidl_helpers::PeerToFidl(*peer));
963     }
964     updated_.clear();
965     callback_(fhost::PeerWatcher_GetNext_Result::WithResponse(
966         fhost::PeerWatcher_GetNext_Response::WithUpdated(
967             std::move(updated_fidl))));
968     callback_ = nullptr;
969     return;
970   }
971 }
972 
GetNext(::fuchsia::bluetooth::host::PeerWatcher::GetNextCallback callback)973 void HostServer::PeerWatcherServer::GetNext(
974     ::fuchsia::bluetooth::host::PeerWatcher::GetNextCallback callback) {
975   if (callback_) {
976     binding()->Close(ZX_ERR_BAD_STATE);
977     host_->peer_watcher_server_.reset();
978     return;
979   }
980   callback_ = std::move(callback);
981   MaybeCallCallback();
982 }
983 
handle_unknown_method(uint64_t ordinal,bool method_has_response)984 void HostServer::PeerWatcherServer::handle_unknown_method(
985     uint64_t ordinal, bool method_has_response) {
986   bt_log(WARN,
987          "fidl",
988          "PeerWatcher received unknown method with ordinal %lu",
989          ordinal);
990 }
991 
BondingDelegateServer(::fidl::InterfaceRequest<::fuchsia::bluetooth::host::BondingDelegate> request,HostServer * host)992 HostServer::BondingDelegateServer::BondingDelegateServer(
993     ::fidl::InterfaceRequest<::fuchsia::bluetooth::host::BondingDelegate>
994         request,
995     HostServer* host)
996     : ServerBase(this, std::move(request)), host_(host) {
997   binding()->set_error_handler(
998       [this](zx_status_t status) { host_->bonding_delegate_server_.reset(); });
999   // Initialize the peer watcher with all known bonded peers that are in the
1000   // cache.
1001   host_->adapter()->peer_cache()->ForEach([this](const bt::gap::Peer& peer) {
1002     if (peer.bonded()) {
1003       OnNewBondingData(peer);
1004     }
1005   });
1006 }
1007 
OnNewBondingData(const bt::gap::Peer & peer)1008 void HostServer::BondingDelegateServer::OnNewBondingData(
1009     const bt::gap::Peer& peer) {
1010   updated_.push(
1011       fidl_helpers::PeerToFidlBondingData(host_->adapter().get(), peer));
1012   MaybeNotifyWatchBonds();
1013 }
1014 
RestoreBonds(::std::vector<::fuchsia::bluetooth::sys::BondingData> bonds,RestoreBondsCallback callback)1015 void HostServer::BondingDelegateServer::RestoreBonds(
1016     ::std::vector<::fuchsia::bluetooth::sys::BondingData> bonds,
1017     RestoreBondsCallback callback) {
1018   host_->RestoreBonds(std::move(bonds), std::move(callback));
1019 }
WatchBonds(WatchBondsCallback callback)1020 void HostServer::BondingDelegateServer::WatchBonds(
1021     WatchBondsCallback callback) {
1022   if (watch_bonds_cb_) {
1023     binding()->Close(ZX_ERR_ALREADY_EXISTS);
1024     host_->bonding_delegate_server_.reset();
1025     return;
1026   }
1027   watch_bonds_cb_ = std::move(callback);
1028   MaybeNotifyWatchBonds();
1029 }
1030 
handle_unknown_method(uint64_t ordinal,bool method_has_response)1031 void HostServer::BondingDelegateServer::handle_unknown_method(
1032     uint64_t ordinal, bool method_has_response) {
1033   bt_log(WARN,
1034          "fidl",
1035          "BondingDelegate received unknown method with ordinal %lu",
1036          ordinal);
1037 }
1038 
1039 // TODO(fxbug.dev/42158854): Support notifying removed bonds.
MaybeNotifyWatchBonds()1040 void HostServer::BondingDelegateServer::MaybeNotifyWatchBonds() {
1041   if (!watch_bonds_cb_ || updated_.empty()) {
1042     return;
1043   }
1044 
1045   watch_bonds_cb_(fhost::BondingDelegate_WatchBonds_Result::WithResponse(
1046       ::fuchsia::bluetooth::host::BondingDelegate_WatchBonds_Response::
1047           WithUpdated(std::move(updated_.front()))));
1048   updated_.pop();
1049 }
1050 
io_capability() const1051 bt::sm::IOCapability HostServer::io_capability() const {
1052   bt_log(DEBUG,
1053          "fidl",
1054          "I/O capability: %s",
1055          bt::sm::util::IOCapabilityToString(io_capability_).c_str());
1056   return io_capability_;
1057 }
1058 
CompletePairing(PeerId id,bt::sm::Result<> status)1059 void HostServer::CompletePairing(PeerId id, bt::sm::Result<> status) {
1060   bt_log(DEBUG,
1061          "fidl",
1062          "pairing complete for peer: %s, status: %s",
1063          bt_str(id),
1064          bt_str(status));
1065   PW_DCHECK(pairing_delegate_);
1066   pairing_delegate_->OnPairingComplete(fbt::PeerId{id.value()}, status.is_ok());
1067 }
1068 
ConfirmPairing(PeerId id,ConfirmCallback confirm)1069 void HostServer::ConfirmPairing(PeerId id, ConfirmCallback confirm) {
1070   bt_log(
1071       DEBUG, "fidl", "pairing confirmation request for peer: %s", bt_str(id));
1072   DisplayPairingRequest(
1073       id, std::nullopt, fsys::PairingMethod::CONSENT, std::move(confirm));
1074 }
1075 
DisplayPasskey(PeerId id,uint32_t passkey,DisplayMethod method,ConfirmCallback confirm)1076 void HostServer::DisplayPasskey(PeerId id,
1077                                 uint32_t passkey,
1078                                 DisplayMethod method,
1079                                 ConfirmCallback confirm) {
1080   auto fidl_method = fsys::PairingMethod::PASSKEY_DISPLAY;
1081   if (method == DisplayMethod::kComparison) {
1082     bt_log(
1083         DEBUG, "fidl", "compare passkey %06u on peer: %s", passkey, bt_str(id));
1084     fidl_method = fsys::PairingMethod::PASSKEY_COMPARISON;
1085   } else {
1086     bt_log(
1087         DEBUG, "fidl", "enter passkey %06u on peer: %s", passkey, bt_str(id));
1088   }
1089   DisplayPairingRequest(id, passkey, fidl_method, std::move(confirm));
1090 }
1091 
RequestPasskey(PeerId id,PasskeyResponseCallback respond)1092 void HostServer::RequestPasskey(PeerId id, PasskeyResponseCallback respond) {
1093   bt_log(DEBUG, "fidl", "passkey request for peer: %s", bt_str(id));
1094   auto found_peer = adapter()->peer_cache()->FindById(id);
1095   PW_CHECK(found_peer);
1096   auto peer = fidl_helpers::PeerToFidl(*found_peer);
1097 
1098   PW_CHECK(pairing_delegate_);
1099   pairing_delegate_->OnPairingRequest(
1100       std::move(peer),
1101       fsys::PairingMethod::PASSKEY_ENTRY,
1102       0u,
1103       [respond = std::move(respond), id, func = __FUNCTION__](
1104           const bool accept, uint32_t entered_passkey) mutable {
1105         if (!respond) {
1106           bt_log(WARN,
1107                  "fidl",
1108                  "%s: The PairingDelegate invoked the Pairing Request callback "
1109                  "more than once, which "
1110                  "should not happen (peer: %s)",
1111                  func,
1112                  bt_str(id));
1113           return;
1114         }
1115         bt_log(INFO,
1116                "fidl",
1117                "%s: got PairingDelegate response: %s with passkey code \"%u\" "
1118                "(peer: %s)",
1119                func,
1120                accept ? "accept" : "reject",
1121                entered_passkey,
1122                bt_str(id));
1123         if (!accept) {
1124           respond(-1);
1125         } else {
1126           respond(entered_passkey);
1127         }
1128       });
1129 }
1130 
DisplayPairingRequest(bt::PeerId id,std::optional<uint32_t> passkey,fsys::PairingMethod method,ConfirmCallback confirm)1131 void HostServer::DisplayPairingRequest(bt::PeerId id,
1132                                        std::optional<uint32_t> passkey,
1133                                        fsys::PairingMethod method,
1134                                        ConfirmCallback confirm) {
1135   auto found_peer = adapter()->peer_cache()->FindById(id);
1136   PW_CHECK(found_peer);
1137   auto peer = fidl_helpers::PeerToFidl(*found_peer);
1138 
1139   PW_CHECK(pairing_delegate_);
1140   uint32_t displayed_passkey = passkey ? *passkey : 0u;
1141   pairing_delegate_->OnPairingRequest(
1142       std::move(peer),
1143       method,
1144       displayed_passkey,
1145       [confirm = std::move(confirm), id, func = __FUNCTION__](
1146           const bool accept, uint32_t entered_passkey) mutable {
1147         if (!confirm) {
1148           bt_log(WARN,
1149                  "fidl",
1150                  "%s: The PairingDelegate invoked the Pairing Request callback "
1151                  "more than once, which "
1152                  "should not happen (peer: %s)",
1153                  func,
1154                  bt_str(id));
1155           return;
1156         }
1157         bt_log(INFO,
1158                "fidl",
1159                "%s: got PairingDelegate response: %s, \"%u\" (peer: %s)",
1160                func,
1161                accept ? "accept" : "reject",
1162                entered_passkey,
1163                bt_str(id));
1164         confirm(accept);
1165       });
1166 }
1167 
OnConnectionError(Server * server)1168 void HostServer::OnConnectionError(Server* server) {
1169   PW_DCHECK(server);
1170   servers_.erase(server);
1171 }
1172 
ResetPairingDelegate()1173 void HostServer::ResetPairingDelegate() {
1174   io_capability_ = IOCapability::kNoInputNoOutput;
1175   adapter()->SetPairingDelegate(PairingDelegate::WeakPtr());
1176 }
1177 
NotifyInfoChange()1178 void HostServer::NotifyInfoChange() {
1179   info_getter_.Set(fidl_helpers::HostInfoToFidl(adapter().get()));
1180 }
1181 
1182 }  // namespace bthost
1183