blob: 188ee58e2e3730b6424714e1ce581bec094a0cca [file] [log] [blame]
// 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(())
}