| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| fidl::{self, endpoints::ClientEnd}, |
| fidl_fuchsia_bluetooth::{self, Status}, |
| fidl_fuchsia_bluetooth_control::{ |
| AdapterInfo, |
| InputCapabilityType, |
| OutputCapabilityType, |
| PairingDelegateMarker, |
| }, |
| fidl_fuchsia_bluetooth_gatt::ClientProxy, |
| fidl_fuchsia_bluetooth_host::{BondingData, HostEvent, HostProxy}, |
| fidl_fuchsia_bluetooth_le::CentralProxy, |
| fuchsia_bluetooth::bt_fidl_status, |
| fuchsia_syslog::{fx_log_err, fx_log_info}, |
| futures::{Future, StreamExt}, |
| parking_lot::RwLock, |
| std::collections::HashMap, |
| std::path::PathBuf, |
| std::sync::Arc, |
| }; |
| |
| use crate::{ |
| host_dispatcher::HostDispatcher, |
| util::clone_host_state, |
| }; |
| |
| pub struct HostDevice { |
| pub path: PathBuf, |
| host: HostProxy, |
| info: AdapterInfo, |
| gatt: HashMap<String, (CentralProxy, ClientProxy)>, |
| } |
| |
| |
| // Many HostDevice methods return impl Future rather than being implemented as `async`. This has an |
| // important behavioral difference in that the function body is triggered immediately when called. |
| // |
| // If they were instead declared async, the function body would not be executed until the first time |
| // the future was polled. |
| impl HostDevice { |
| pub fn new(path: PathBuf, host: HostProxy, info: AdapterInfo) -> Self { |
| HostDevice { |
| path, |
| host, |
| info, |
| gatt: HashMap::new(), |
| } |
| } |
| |
| pub fn get_host(&self) -> &HostProxy { |
| &self.host |
| } |
| |
| pub fn set_host_pairing_delegate( |
| &self, input: InputCapabilityType, output: OutputCapabilityType, |
| delegate: ClientEnd<PairingDelegateMarker>, |
| ) { |
| let _ = self |
| .host |
| .set_pairing_delegate(input, output, Some(delegate)); |
| } |
| |
| pub fn get_info(&self) -> &AdapterInfo { |
| &self.info |
| } |
| |
| pub fn rm_gatt(&mut self, id: String) -> impl Future<Output = fidl::Result<Status>> { |
| let gatt_entry = self.gatt.remove(&id); |
| async move { |
| if let Some((central, _)) = gatt_entry { |
| await!(central.disconnect_peripheral(id.as_str())) |
| } else { |
| Ok(bt_fidl_status!(BluetoothNotAvailable, "Unknown peripheral")) |
| } |
| } |
| } |
| |
| pub fn set_name(&self, mut name: String) -> impl Future<Output = fidl::Result<Status>> { |
| self.host.set_local_name(&mut name) |
| } |
| |
| pub fn start_discovery(&mut self) -> impl Future<Output = fidl::Result<Status>> { |
| self.host.start_discovery() |
| } |
| |
| pub fn connect(&mut self, device_id: String) -> impl Future<Output = Result<Status, fidl::Error>> { |
| self.host.connect(&device_id) |
| } |
| |
| pub fn close(&self) -> Result<(), fidl::Error> { |
| self.host.close() |
| } |
| |
| pub fn restore_bonds(&self, mut bonds: Vec<BondingData>) -> impl Future<Output = fidl::Result<Status>> { |
| self.host.add_bonded_devices(&mut bonds.iter_mut()) |
| } |
| |
| pub fn set_connectable(&self, value: bool) -> impl Future<Output = fidl::Result<Status>> { |
| self.host.set_connectable(value) |
| } |
| |
| pub fn stop_discovery(&self) -> impl Future<Output = fidl::Result<Status>> { |
| self.host.stop_discovery() |
| } |
| |
| pub fn set_discoverable(&self, discoverable: bool) -> impl Future<Output = fidl::Result<Status>> { |
| self.host.set_discoverable(discoverable) |
| } |
| |
| pub fn enable_background_scan(&self, enable: bool) -> fidl::Result<()> { |
| self.host.enable_background_scan(enable) |
| } |
| } |
| |
| pub async fn run(hd: HostDispatcher, host: Arc<RwLock<HostDevice>>) -> fidl::Result<()> { |
| let mut stream = host.read().host.take_event_stream(); |
| |
| while let Some(event) = await!(stream.next()) { |
| let host_ = host.clone(); |
| let dispatcher = hd.clone(); |
| match event? { |
| HostEvent::OnAdapterStateChanged { ref state } => { |
| host_.write().info.state = Some(Box::new(clone_host_state(&state))); |
| } |
| // TODO(NET-968): Add integration test for this. |
| HostEvent::OnDeviceUpdated { device } => hd.on_device_updated(device), |
| // TODO(NET-1038): Add integration test for this. |
| HostEvent::OnDeviceRemoved { identifier } => hd.on_device_removed(identifier), |
| HostEvent::OnNewBondingData { data } => { |
| fx_log_info!("Received bonding data"); |
| if let Err(e) = dispatcher.store_bond(data) { |
| fx_log_err!("Failed to persist bonding data: {:#?}", e); |
| } |
| } |
| }; |
| }; |
| Ok(()) |
| } |