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