blob: 0c79fb6531c99086132b5dace57896af9a80a40e [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 {
failure::Error,
fidl::{
self,
encoding::OutOfLine,
endpoints::ServerEnd,
},
fidl_fuchsia_bluetooth,
fidl_fuchsia_bluetooth_control::{
AdapterInfo,
ControlControlHandle,
PairingDelegateMarker,
PairingDelegateProxy,
InputCapabilityType,
OutputCapabilityType,
RemoteDevice
},
fidl_fuchsia_bluetooth_bredr::ProfileMarker,
fidl_fuchsia_bluetooth_gatt::Server_Marker,
fidl_fuchsia_bluetooth_host::HostProxy,
fidl_fuchsia_bluetooth_le::{CentralMarker, PeripheralMarker},
fuchsia_bluetooth::{
self as bt,
bt_fidl_status,
error::Error as BTError,
util::clone_host_info,
util::clone_remote_device
},
fuchsia_async::{self as fasync, TimeoutExt},
fuchsia_syslog::{fx_log_err, fx_log_info, fx_vlog},
fuchsia_zircon::{self as zx, Duration},
futures::{
task::{LocalWaker, Waker},
Future,
Poll,
FutureExt,
TryFutureExt
},
parking_lot::RwLock,
slab::Slab,
std::collections::HashMap,
std::fs::File,
std::marker::Unpin,
std::path::PathBuf,
std::sync::{Arc, Weak},
};
use crate::{
host_device::{self, HostDevice},
services, store::stash::Stash, util
};
pub static HOST_INIT_TIMEOUT: i64 = 5; // Seconds
static DEFAULT_NAME: &'static str = "fuchsia";
/// Available FIDL services that can be provided by a particular Host
pub enum HostService {
LeCentral,
LePeripheral,
LeGatt,
Profile
}
// We use tokens to track the reference counting for discovery/discoverable states
// As long as at least one user maintains an Arc<> to the token, the state persists
// Once all references are dropped, the `Drop` trait on the token causes the state
// to be terminated.
pub struct DiscoveryRequestToken {
adap: Weak<RwLock<HostDevice>>,
}
impl Drop for DiscoveryRequestToken {
#[allow(unused_must_use)] // FIXME(BT-643)
fn drop(&mut self) {
fx_vlog!(1, "DiscoveryRequestToken dropped");
if let Some(host) = self.adap.upgrade() {
// FIXME(nickpollard) this should be `await!`ed, but not while holding the lock
host.write().stop_discovery();
}
}
}
pub struct DiscoverableRequestToken {
adap: Weak<RwLock<HostDevice>>,
}
impl Drop for DiscoverableRequestToken {
#[allow(unused_must_use)] // FIXME(nickpollard)
fn drop(&mut self) {
if let Some(host) = self.adap.upgrade() {
// FIXME(BT-643) this should be `await!`ed, but not while holding the lock
let host = host.write();
host.set_discoverable(false);
}
}
}
type DeviceId = String;
/// The HostDispatcher acts as a proxy aggregating multiple HostAdapters
/// It appears as a Host to higher level systems, and is responsible for
/// routing commands to the appropriate HostAdapter
struct HostDispatcherState {
host_devices: HashMap<String, Arc<RwLock<HostDevice>>>,
active_id: Option<String>,
// Component storage.
pub stash: Stash,
// GAP state
name: String,
discovery: Option<Weak<DiscoveryRequestToken>>,
discoverable: Option<Weak<DiscoverableRequestToken>>,
pub input: InputCapabilityType,
pub output: OutputCapabilityType,
remote_devices: HashMap<DeviceId, RemoteDevice>,
pub pairing_delegate: Option<PairingDelegateProxy>,
pub event_listeners: Vec<Weak<ControlControlHandle>>,
// Pending requests to obtain a Host.
host_requests: Slab<Waker>,
}
impl HostDispatcherState {
/// Set the active adapter for this HostDispatcher
pub fn set_active_adapter(&mut self, adapter_id: String) -> fidl_fuchsia_bluetooth::Status {
if let Some(ref id) = self.active_id {
if *id == adapter_id {
return bt_fidl_status!(Already, "Adapter already active");
}
// Shut down the previously active host.
let _ = self.host_devices[id].write().close();
}
if self.host_devices.contains_key(&adapter_id) {
self.set_active_id(Some(adapter_id));
bt_fidl_status!()
} else {
bt_fidl_status!(NotFound, "Attempting to activate an unknown adapter")
}
}
/// Used to set the pairing delegate. If there is a prior pairing delegate connected to the
/// host it will fail. It checks if the existing stored connection is closed, and will
/// overwrite it if so.
pub fn set_pairing_delegate(&mut self, delegate: Option<PairingDelegateProxy>) -> bool {
match delegate {
Some(delegate) => {
let assign = match self.pairing_delegate {
None => true,
Some(ref pd) => pd.is_closed(),
};
if assign {
self.pairing_delegate = Some(delegate);
}
assign
}
None => {
self.pairing_delegate = None;
false
}
}
}
/// Returns the current pairing delegate proxy if it exists and has not been closed. Clears the
/// if the handle is closed.
pub fn pairing_delegate(&mut self) -> Option<PairingDelegateProxy> {
if let Some(delegate) = &self.pairing_delegate {
if delegate.is_closed() {
self.pairing_delegate = None;
}
}
self.pairing_delegate.clone()
}
/// Return the active id. If the ID is currently not set,
/// it will make the first ID in it's host_devices active
fn get_active_id(&mut self) -> Option<String> {
match self.active_id {
None => match self.host_devices.keys().next() {
None => None,
Some(id) => {
self.set_active_id(Some(id.clone()));
self.active_id.clone()
}
},
ref id => id.clone(),
}
}
/// Return the active host. If the Host is currently not set,
/// it will make the first ID in it's host_devices active
fn get_active_host(&mut self) -> Option<Arc<RwLock<HostDevice>>> {
self.get_active_id()
.as_ref()
.and_then(|id| self.host_devices.get(id))
.map(|host| host.clone())
}
/// Resolves all pending OnAdapterFuture's. Called when we leave the init period (by seeing the
/// first host device or when the init timer expires).
fn resolve_host_requests(&mut self) {
for waker in &self.host_requests {
waker.1.wake();
}
}
fn add_host(&mut self, id: String, host: Arc<RwLock<HostDevice>>) {
fx_log_info!("Host added: {:?}", host.read().get_info().identifier);
let info = clone_host_info(host.read().get_info());
self.host_devices.insert(id, host);
// Notify Control interface clients about the new device.
self.notify_event_listeners(|l| {
let _res = l.send_on_adapter_updated(&mut clone_host_info(&info));
});
// Resolve pending adapter futures.
self.resolve_host_requests();
}
/// Updates the active adapter and sends a FIDL event.
fn set_active_id(&mut self, id: Option<String>) {
fx_log_info!("New active adapter: {:?}", id);
self.active_id = id;
if let Some(ref mut adapter_info) = self.get_active_adapter_info() {
self.notify_event_listeners(|listener| {
let _res = listener.send_on_active_adapter_changed(Some(OutOfLine(adapter_info)));
})
}
}
pub fn get_active_adapter_info(&mut self) -> Option<AdapterInfo> {
self.get_active_host().map(|host| util::clone_host_info(host.read().get_info()))
}
pub fn notify_event_listeners<F>(&mut self, mut f: F) where F: FnMut(&ControlControlHandle) -> () {
self.event_listeners.retain(|listener| {
match listener.upgrade() {
Some(listener_) => {
f(&listener_);
true
}
None => false
}
})
}
}
#[derive(Clone)]
pub struct HostDispatcher {
state: Arc<RwLock<HostDispatcherState>>,
}
impl HostDispatcher {
pub fn new(stash: Stash) -> HostDispatcher {
let hd = HostDispatcherState {
active_id: None,
host_devices: HashMap::new(),
name: DEFAULT_NAME.to_string(),
input: InputCapabilityType::None,
output: OutputCapabilityType::None,
remote_devices: HashMap::new(),
stash: stash,
discovery: None,
discoverable: None,
pairing_delegate: None,
event_listeners: vec![],
host_requests: Slab::new(),
};
HostDispatcher {
state: Arc::new(RwLock::new(hd)),
}
}
pub fn get_active_adapter_info(&mut self) -> Option<AdapterInfo> {
self.state.write().get_active_adapter_info()
}
pub async fn on_adapters_found(&self) -> fidl::Result<HostDispatcher> {
await!(OnAdaptersFound::new(self.clone()))
}
pub async fn set_name(
&mut self, name: Option<String>,
) -> fidl::Result<fidl_fuchsia_bluetooth::Status> {
self.state.write().name = name.unwrap_or(DEFAULT_NAME.to_string());
match await!(self.get_active_adapter())? {
Some(adapter) => await!(adapter.write().set_name(self.state.read().name.clone())),
None => Ok(bt_fidl_status!(BluetoothNotAvailable, "No Adapter found")),
}
}
/// Set the active adapter for this HostDispatcher
pub fn set_active_adapter(&mut self, adapter_id: String) -> fidl_fuchsia_bluetooth::Status {
self.state.write().set_active_adapter(adapter_id)
}
pub fn set_pairing_delegate(&mut self, delegate: Option<PairingDelegateProxy>) -> bool {
self.state.write().set_pairing_delegate(delegate)
}
pub async fn start_discovery(
&mut self,
) -> fidl::Result<(
fidl_fuchsia_bluetooth::Status,
Option<Arc<DiscoveryRequestToken>>,
)> {
let strong_current_token = self
.state
.read()
.discovery
.as_ref()
.and_then(|token| token.upgrade());
if let Some(token) = strong_current_token {
return Ok((bt_fidl_status!(), Some(Arc::clone(&token))));
}
match await!(self.get_active_adapter())? {
Some(adapter) => {
let weak_adapter = Arc::downgrade(&adapter);
let resp = await!(adapter.write().start_discovery())?;
match resp.error {
Some(_) => Ok((resp, None)),
None => {
let token = Arc::new(DiscoveryRequestToken { adap: weak_adapter });
self.state.write().discovery = Some(Arc::downgrade(&token));
Ok((resp, Some(token)))
}
}
}
None => Ok((
bt_fidl_status!(BluetoothNotAvailable, "No Adapter found"),
None,
)),
}
}
pub async fn set_discoverable(
&mut self,
) -> fidl::Result<(
fidl_fuchsia_bluetooth::Status,
Option<Arc<DiscoverableRequestToken>>,
)> {
let strong_current_token = self
.state
.read()
.discoverable
.as_ref()
.and_then(|token| token.upgrade());
if let Some(token) = strong_current_token {
return Ok((bt_fidl_status!(), Some(Arc::clone(&token))));
}
match await!(self.get_active_adapter())? {
Some(adapter) => {
let weak_adapter = Arc::downgrade(&adapter);
let resp = await!(adapter.write().set_discoverable(true))?;
match resp.error {
Some(_) => Ok((resp, None)),
None => {
let token = Arc::new(DiscoverableRequestToken { adap: weak_adapter });
self.state.write().discoverable = Some(Arc::downgrade(&token));
Ok((resp, Some(token)))
}
}
}
None => Ok((
bt_fidl_status!(BluetoothNotAvailable, "No Adapter found"),
None,
)),
}
}
pub async fn forget(
&mut self, _device_id: String
) -> fidl::Result<fidl_fuchsia_bluetooth::Status> {
// TODO(NET-1148): This function should perform the following:
// 1. Remove the device from bt-gap's in-memory list of devices, once it exists.
// 2. Remove bonding data from store::Stash.
// 3. Call Host.Forget(), once it exists.
Ok(bt_fidl_status!(NotSupported, "Operation not supported"))
}
pub async fn disconnect(
&mut self, device_id: String,
) -> fidl::Result<fidl_fuchsia_bluetooth::Status> {
let adapter = await!(self.get_active_adapter())?;
match adapter {
Some(adapter) => await!(adapter.write().rm_gatt(device_id)),
None => Ok(bt_fidl_status!(BluetoothNotAvailable, "Adapter went away")),
}
}
pub async fn get_active_adapter(&mut self) -> fidl::Result<Option<Arc<RwLock<HostDevice>>>> {
let adapter = await!(self.on_adapters_found())?;
let mut wstate = adapter.state.write();
Ok(wstate.get_active_host())
}
pub async fn get_adapters(&mut self) -> fidl::Result<Vec<AdapterInfo>> {
let _ = await!(self.on_adapters_found());
let mut result = vec![];
for host in self.state.read().host_devices.values() {
let host = host.read();
result.push(util::clone_host_info(host.get_info()));
}
Ok(result)
}
pub async fn request_host_service(mut self, chan: fasync::Channel, service: HostService) {
let adapter = await!(self.get_active_adapter());
match adapter {
Ok(Some(adapter)) => {
let adapter = adapter.read();
let host = adapter.get_host();
match service {
HostService::LeCentral => {
let remote = ServerEnd::<CentralMarker>::new(chan.into());
let _ = host.request_low_energy_central(remote);
}
HostService::LePeripheral => {
let remote = ServerEnd::<PeripheralMarker>::new(chan.into());
let _ = host.request_low_energy_peripheral(remote);
}
HostService::LeGatt => {
let remote = ServerEnd::<Server_Marker>::new(chan.into());
let _ = host.request_gatt_server_(remote);
}
HostService::Profile => {
let remote = ServerEnd::<ProfileMarker>::new(chan.into());
let _ = host.request_profile(remote);
}
}
},
Ok(None) => eprintln!("Failed to spawn, no active adapter"),
Err(e) => eprintln!("Failed to spawn, error resolving adapter {:?}", e),
}
}
pub fn set_io_capability(&self, input: InputCapabilityType, output: OutputCapabilityType) {
let mut state = self.state.write();
state.input = input;
state.output = output;
}
pub fn add_event_listener(&self, handle: Weak<ControlControlHandle>) {
self.state.write().event_listeners.push(handle);
}
pub fn notify_event_listeners<F>(&self, f: F) where F: FnMut(&ControlControlHandle) -> () {
self.state.write().notify_event_listeners(f);
}
/// Returns the current pairing delegate proxy if it exists and has not been closed. Clears the
/// if the handle is closed.
pub fn pairing_delegate(&self) -> Option<PairingDelegateProxy> {
self.state.write().pairing_delegate()
}
pub fn store_bond(&self, bond_data: fidl_fuchsia_bluetooth_host::BondingData) -> Result<(),Error> {
self.state.write().stash.store_bond(bond_data)
}
pub fn on_device_updated(&self, mut device: RemoteDevice ) {
// TODO(NET-1297): generic method for this pattern
self.notify_event_listeners(|listener| {
let _res = listener
.send_on_device_updated(&mut device)
.map_err(|e| fx_log_err!("Failed to send device updated event: {:?}", e));
});
let _drop_old_value = self.state.write().remote_devices.insert(device.identifier.clone(), device);
}
pub fn on_device_removed( &self, identifier: String ) {
self.state.write().remote_devices.remove(&identifier);
self.notify_event_listeners(|listener| {
let _res = listener
.send_on_device_removed(&identifier)
.map_err(|e| fx_log_err!("Failed to send device removed event: {:?}", e));
})
}
pub fn get_remote_devices(&self) -> Vec<RemoteDevice> {
self.state.read().remote_devices.values().map(|d| clone_remote_device(d)).collect()
}
/// Adds an adapter to the host dispatcher. Called by the watch_hosts device
/// watcher
pub async fn add_adapter(self, host_path: PathBuf) -> Result<(), Error> {
fx_log_info!("Adding Adapter: {:?}", host_path);
let host_device = await!(init_host(host_path))?;
let address = host_device.read().get_info().address.clone();
await!(try_restore_bonds(host_device.clone(), self.clone(), &address))?;
// TODO(NET-1445): Only the active host should be made connectable and scanning in the background.
await!(host_device.read().set_connectable(true)).map_err(|_| BTError::new("failed to set connectable"))?;
host_device.read().enable_background_scan(true).map_err(|_| BTError::new("failed to enable background scan"))?;
// Initialize bt-gap as this host's pairing delegate.
start_pairing_delegate(self.clone(), host_device.clone())?;
let id = host_device.read().get_info().identifier.clone();
self.state.write().add_host(id, host_device.clone());
// Start listening to Host interface events.
fasync::spawn(host_device::run(self.clone(), host_device.clone()).map(|_| ()));
Ok(())
}
pub fn rm_adapter(self, host_path: PathBuf) -> Result<(), Error> {
fx_log_info!("Host removed: {:?}", host_path);
let mut hd = self.state.write();
let active_id = hd.active_id.clone();
// Get the host IDs that match `host_path`.
let ids: Vec<String> = hd
.host_devices
.iter()
.filter(|(_, ref host)| host.read().path == host_path)
.map(|(k, _)| k.clone())
.collect();
for id in &ids {
hd.host_devices.remove(id);
}
// Reset the active ID if it got removed.
if let Some(active_id) = active_id {
if ids.contains(&active_id) {
hd.active_id = None;
}
}
// Try to assign a new active adapter. This may send an "OnActiveAdapterChanged" event.
if hd.active_id.is_none() {
let _ = hd.get_active_id();
}
Ok(())
}
pub async fn connect(
&mut self, device_id: String,
) -> fidl::Result<fidl_fuchsia_bluetooth::Status> {
let adapter = await!(self.get_active_adapter())?;
match adapter {
Some(adapter) => await!(adapter.write().connect(device_id)),
None => Ok(bt_fidl_status!(BluetoothNotAvailable, "Adapter went away")),
}
}
}
/// A future that completes when at least one adapter is available.
#[must_use = "futures do nothing unless polled"]
struct OnAdaptersFound {
hd: HostDispatcher,
waker_key: Option<usize>,
}
impl OnAdaptersFound {
// Constructs an OnAdaptersFound that completes at the latest after HOST_INIT_TIMEOUT seconds.
fn new(hd: HostDispatcher) -> impl Future<Output = fidl::Result<HostDispatcher>> {
OnAdaptersFound {
hd: hd.clone(),
waker_key: None,
}.on_timeout(
Duration::from_seconds(HOST_INIT_TIMEOUT).after_now(),
move || {
{
let mut inner = hd.state.write();
if inner.host_devices.len() == 0 {
fx_log_info!("No bt-host devices found");
inner.resolve_host_requests();
}
}
Ok(hd)
},
)
}
fn remove_waker(&mut self) {
if let Some(key) = self.waker_key {
self.hd.state.write().host_requests.remove(key);
}
self.waker_key = None;
}
}
impl Drop for OnAdaptersFound {
fn drop(&mut self) {
self.remove_waker()
}
}
impl Unpin for OnAdaptersFound {}
impl Future for OnAdaptersFound {
type Output = fidl::Result<HostDispatcher>;
fn poll(mut self: ::std::pin::Pin<&mut Self>, lw: &LocalWaker) -> Poll<Self::Output> {
if self.hd.state.read().host_devices.len() == 0 {
let hd = self.hd.clone();
if self.waker_key.is_none() {
self.waker_key = Some(hd.state.write().host_requests.insert(lw.clone().into_waker()));
}
Poll::Pending
} else {
self.remove_waker();
Poll::Ready(Ok(self.hd.clone()))
}
}
}
/// Initialize a HostDevice
async fn init_host(host_path: PathBuf) -> Result<Arc<RwLock<HostDevice>>, Error> {
// Connect to the host device.
let host = File::open(host_path.clone())
.map_err(|_| BTError::new("failed to open bt-host device"))?;
let handle = bt::host::open_host_channel(&host)?;
let handle = fasync::Channel::from_channel(handle.into())?;
let host = HostProxy::new(handle);
// Obtain basic information and create and entry in the disptacher's map.
let adapter_info = await!(host.get_info())
.map_err(|_| BTError::new("failed to obtain bt-host information"))?;
Ok(Arc::new(RwLock::new(HostDevice::new(host_path, host, adapter_info))))
}
async fn try_restore_bonds(host_device: Arc<RwLock<HostDevice>>, hd: HostDispatcher, address: &str) -> Result<(), Error> {
// Load bonding data that use this host's `address` as their "local identity address".
if let Some(iter) = hd.state.read().stash.list_bonds(address) {
let res = await!( host_device.read().restore_bonds(iter.map(|bd| util::clone_bonding_data(&bd)).collect()));
match res {
Ok(_) => Ok(()),
Err(e) => {
fx_log_err!("failed to restore bonding data for host: {}", e);
Err(e.into())
}
}
} else {
Ok(())
}
}
fn start_pairing_delegate(hd: HostDispatcher, host_device: Arc<RwLock<HostDevice>>)-> Result<(),Error> {
// Initialize bt-gap as this host's pairing delegate.
// TODO(NET-1445): Do this only for the active host. This will make sure that non-active hosts
// always reject pairing.
let (delegate_local, delegate_remote) = zx::Channel::create()?;
let delegate_local = fasync::Channel::from_channel(delegate_local)?;
let delegate_ptr = fidl::endpoints::ClientEnd::<PairingDelegateMarker>::new(delegate_remote);
host_device.read().set_host_pairing_delegate(hd.state.read().input, hd.state.read().output, delegate_ptr);
fasync::spawn(
services::start_pairing_delegate(hd.clone(), delegate_local)
.unwrap_or_else(|e| eprintln!("Failed to spawn {:?}", e)),
);
Ok(())
}