blob: b1fe3ef22fd9d02f9e272df0e8b84753ad4847ad [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 crate::host_device::{self, HostDevice};
use crate::services;
use crate::util;
use failure::Error;
use fidl;
use fidl::encoding::OutOfLine;
use fidl::endpoints::ServerEnd;
use fidl_fuchsia_bluetooth;
use fidl_fuchsia_bluetooth_control::{
AdapterInfo, BondingControlHandle, ControlControlHandle, PairingDelegateMarker,
PairingDelegateProxy, InputCapabilityType, OutputCapabilityType
};
use fidl_fuchsia_bluetooth_bredr::ProfileMarker;
use fidl_fuchsia_bluetooth_gatt::Server_Marker;
use fidl_fuchsia_bluetooth_host::HostProxy;
use fidl_fuchsia_bluetooth_le::{CentralProxy, CentralMarker, PeripheralMarker};
use fuchsia_bluetooth::{
self as bt, bt_fidl_status, error::Error as BTError, util::clone_host_info,
};
use fuchsia_async::{self as fasync, TimeoutExt};
use fuchsia_syslog::{fx_log, fx_log_err, fx_log_info, fx_log_warn};
use fuchsia_vfs_watcher as vfs_watcher;
use fuchsia_vfs_watcher::{WatchEvent, WatchMessage};
use fuchsia_zircon as zx;
use fuchsia_zircon::Duration;
use futures::TryStreamExt;
use futures::{task, Future, Poll, TryFutureExt};
use parking_lot::RwLock;
use slab::Slab;
use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::marker::Unpin;
use std::path::PathBuf;
use std::sync::{Arc, Weak};
pub static HOST_INIT_TIMEOUT: i64 = 5; // Seconds
static BT_HOST_DIR: &'static str = "/dev/class/bt-host";
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 {
fn drop(&mut self) {
if let Some(host) = self.adap.upgrade() {
host.write().stop_discovery();
}
}
}
pub struct DiscoverableRequestToken {
adap: Weak<RwLock<HostDevice>>,
}
impl Drop for DiscoverableRequestToken {
fn drop(&mut self) {
if let Some(host) = self.adap.upgrade() {
let mut host = host.write();
host.set_discoverable(false);
}
}
}
/// 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>,
// GAP state
name: String,
discovery: Option<Weak<DiscoveryRequestToken>>,
discoverable: Option<Weak<DiscoverableRequestToken>>,
pub input: InputCapabilityType,
pub output: OutputCapabilityType,
pub pairing_delegate: Option<PairingDelegateProxy>,
pub bonding_events: Option<BondingControlHandle>,
pub event_listeners: Vec<ControlControlHandle>,
// Pending requests to obtain a Host.
host_requests: Slab<task::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>>) {
self.host_devices.insert(id, host);
}
/// 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() {
for events in self.event_listeners.iter() {
let _res = events.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()))
}
}
#[derive(Clone)]
pub struct HostDispatcher {
state: Arc<RwLock<HostDispatcherState>>,
}
impl HostDispatcher {
pub fn new() -> HostDispatcher {
let hd = HostDispatcherState {
active_id: None,
host_devices: HashMap::new(),
name: DEFAULT_NAME.to_string(),
input: InputCapabilityType::None,
output: OutputCapabilityType::None,
discovery: None,
discoverable: None,
pairing_delegate: None,
bonding_events: 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 connect_le_central(&mut self) -> fidl::Result<Option<CentralProxy>> {
let adapter = await!(self.on_adapters_found())?;
let mut adapter = adapter.state.write();
match adapter.get_active_host() {
Some(host) => host.write().connect_le_central().map(|central| Some(central)),
None => Ok(None),
}
}
pub async fn connect(
&mut self, device_id: String,
) -> fidl::Result<fidl_fuchsia_bluetooth::Status> {
let central = await!(self.connect_le_central())?;
let central = match central {
Some(c) => c,
None => return Ok(bt_fidl_status!(BluetoothNotAvailable, "No Adapter found"))
};
let (service_local, service_remote) = fidl::endpoints::create_proxy().unwrap();
let connected = await!(central.connect_peripheral(device_id.as_str(), service_remote));
// TODO(NET-1092): We want this as a host.fidl API
match await!(self.get_active_adapter())? {
Some(adapter) => {
adapter
.write()
.store_gatt(device_id, central, service_local);
connected
}
None => Ok(bt_fidl_status!(BluetoothNotAvailable, "Adapter went away")),
}
}
pub async fn forget(
&mut self, device_id: String,
) -> fidl::Result<fidl_fuchsia_bluetooth::Status> {
// TODO(NET-1148): Implement correctly
let id = device_id.clone();
let adapter = await!(self.get_active_adapter())?;
let status = match adapter {
Some(adapter) => {
adapter.write().rm_gatt(device_id);
bt_fidl_status!()
}
None => bt_fidl_status!(BluetoothNotAvailable, "Adapter went away"),
};
let event = &self.state.read().bonding_events;
match event {
Some(events) => {
let _res = events
.send_on_delete_bond(id.as_str())
.map_err(|e| fx_log_err!("Failed to send device updated event: {:?}", e));
Ok(status)
}
None => Ok(bt_fidl_status!(BluetoothNotAvailable, "Adapter went away")),
}
}
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(&mut self, input: InputCapabilityType, output: OutputCapabilityType) {
let mut state = self.state.write();
state.input = input;
state.output = output;
}
pub fn add_event_listener(&mut self, handle: ControlControlHandle) {
self.state.write().event_listeners.push(handle);
}
pub fn event_listeners(&self) -> Vec<ControlControlHandle> {
self.state.read().event_listeners.clone()
}
pub fn set_bonding_listener(&mut self, handle: Option<BondingControlHandle>) {
self.state.write().bonding_events = handle;
}
pub fn bonding_listener(&self) -> Option<BondingControlHandle> {
self.state.read().bonding_events.clone()
}
/// 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> {
self.state.write().pairing_delegate()
}
}
/// 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::PinMut<Self>, ctx: &mut task::Context) -> 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(ctx.waker().clone()));
}
Poll::Pending
} else {
self.remove_waker();
Poll::Ready(Ok(self.hd.clone()))
}
}
}
/// Adds an adapter to the host dispatcher. Called by the watch_hosts device
/// watcher
async fn add_adapter(hd: HostDispatcher, host_path: PathBuf) -> Result<(), Error> {
fx_log_info!("Adding Adapter: {:?}", host_path);
// 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);
// TODO(NET-1445): Only the active host should be made connectable.
await!(host.set_connectable(true)).map_err(|_| BTError::new("failed to set connectable"))?;
// 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"))?;
let id = adapter_info.identifier.clone();
let host_device = Arc::new(RwLock::new(HostDevice::new(host_path, host, adapter_info)));
// 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)),
);
fx_log_info!("Host added: {:?}", host_device.read().get_info().identifier);
hd.state.write().add_host(id, host_device.clone());
// Notify Control interface clients about the new device.
// TODO(armansito): This layering isn't quite right. It's better to do this in
// HostDispatcher::add_host instead.
for listener in hd.state.read().event_listeners.iter() {
let _res =
listener.send_on_adapter_updated(&mut clone_host_info(host_device.read().get_info()));
}
// Resolve pending adapter futures.
hd.state.write().resolve_host_requests();
// Start listening to Host interface events.
await!(host_device::run(hd.clone(), host_device.clone()))
.map_err(|_| BTError::new("Host interface event stream error").into())
}
pub fn rm_adapter(hd: HostDispatcher, host_path: PathBuf) -> Result<(), Error> {
fx_log_info!("Host removed: {:?}", host_path);
let mut hd = hd.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(())
}
fn bluetooth_device_path(msg: &WatchMessage) -> PathBuf {
PathBuf::from(format!(
"{}/{}",
BT_HOST_DIR,
msg.filename.to_string_lossy()
))
}
pub async fn watch_hosts(hd: HostDispatcher) -> Result<(), Error> {
let dev = File::open(&BT_HOST_DIR);
let watcher = vfs_watcher::Watcher::new(&dev.unwrap()).unwrap();
await!(watcher.try_for_each(|msg| handle_device(hd.clone(), msg))).map_err(|e| e.into())
}
pub async fn handle_device(hd: HostDispatcher, msg: WatchMessage) -> Result<(), io::Error> {
let path = bluetooth_device_path(&msg);
match msg.event {
WatchEvent::EXISTING | WatchEvent::ADD_FILE => {
fx_log_info!("Adding device from {:?}", path);
await!(
add_adapter(hd, path)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
)
}
WatchEvent::REMOVE_FILE => {
fx_log_info!("Removing device from {:?}", path);
rm_adapter(hd, path).map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
}
WatchEvent::IDLE => {
fx_log_info!("HostDispatcher is IDLE");
Ok(())
}
e => {
fx_log_warn!("Unrecognized host watch event: {:?}", e);
Ok(())
}
}
}