| //use fuchsia_async::{self as fasync, TimeoutExt}; |
| // 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; |
| use fidl_fuchsia_bluetooth_control::ControlRequest; |
| use fidl_fuchsia_bluetooth_host::HostEvent; |
| //use failure::Error; |
| //use fuchsia_async::{self as fasync, TimeoutExt}; |
| //use fuchsia_syslog::{fx_log, fx_log_err, fx_log_info, fx_log_warn}; |
| use fuchsia_syslog::fx_log_info; |
| //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::{LocalWaker, Waker}, Future, Poll, TryFutureExt}; |
| //use parking_lot::RwLock; |
| use std::collections::HashMap; |
| //use std::marker::Unpin; |
| //use std::sync::{Arc, Weak}; |
| //use futures::channel::mpsc; |
| |
| use crate::actor::{Actor, ActorHandle, ActorContext}; |
| use crate::adapters::AdapterEvent; |
| use crate::adapters::AdapterEvent::*; |
| //use crate::host::*; |
| use crate::host::{Host, FidlHost}; |
| use crate::types::bt; |
| use crate::types::*; |
| use crate::types::DiscoveryState::*; |
| |
| /// --- THE NEW BT-GAP --- |
| |
| /// The HostDispatcher acts as a proxy aggregating multiple Hosts |
| /// It appears as a Host to higher level systems, and is responsible for |
| /// routing commands to the appropriate Host |
| |
| pub struct HostDispatcher<H: Host> { |
| /// Bluetooth Hosts under management |
| hosts: HashMap<HostId, H>, |
| |
| /// Currently 'active' host |
| active_host: Option<H>, |
| |
| // Discovery |
| discovery: DiscoveryState, |
| discovery_clients: Vec<DiscoveryClient>, |
| |
| //waiting_for_discovery: Vec<Oneshot::Sender<Result<()>>> |
| |
| //subscribers: Vec<Listener>; |
| } |
| |
| //pub struct HostDispatcherHandle { |
| //channel: mpsc::unbounded::Sender<HostDispatcherMsg> |
| //} |
| |
| pub struct DiscoveryClient {} |
| //pub struct HostDispatcherHandle {} |
| |
| /// A token returned to someone who has requested discovery |
| pub struct DiscoverySession<H: Host> { |
| dispatcher: ActorHandle<HostDispatcherMsg<H>> |
| } |
| |
| impl<H: Host> Drop for DiscoverySession<H> { |
| fn drop(&mut self) { |
| self.dispatcher.send(HostDispatcherMsg::StopDiscovery); |
| } |
| } |
| |
| impl<H: Host> HostDispatcher<H> { |
| pub fn new() -> HostDispatcher<H> { |
| HostDispatcher { |
| hosts: HashMap::new(), |
| active_host: None, |
| discovery: NotDiscovering, |
| discovery_clients: vec![], |
| } |
| } |
| |
| fn handle(&self) -> ActorHandle<HostDispatcherMsg<H>> { |
| unreachable!() |
| } |
| |
| //fn add_host(&mut self, id: HostId, host: AdapterInfo) { |
| //panic!("NYI"); |
| //} |
| |
| //fn rm_host(&mut self, id: HostId, host: H) { |
| //panic!("NYI"); |
| //} |
| |
| // TODO - what should this type signature be |
| // pub fn start_discovery(&mut self) -> bt::Result<DiscoverySession> { |
| // |
| // We don't necessarily want to just be async, as we might not want to close |
| // over the host dispatcher - instead send messages? |
| // |
| // We could just wait for a DiscoveryStarted message |
| // What does the control fidl service method looks like? |
| // They need to wait for a response - perhaps register a listener? |
| pub fn start_discovery(&mut self) -> bt::Result<DiscoverySession<H>> { |
| if !self.hosts.is_empty() { |
| let session = DiscoverySession { dispatcher: self.handle() }; // TODO - need access to our sender! |
| if self.discovery == NotDiscovering { |
| self.enable_discovery() |
| } |
| Ok(session) |
| } else { |
| Err(bt::Error::NoAdapter) |
| } |
| } |
| |
| fn enable_discovery(&mut self) { |
| // TODO - this should probably fail if *all* hosts fail to start_discovery |
| // as long as one succeeds, report success |
| for (_,host) in self.hosts.iter_mut() { host.start_discovery(); } |
| self.discovery = Discovering; |
| } |
| |
| fn disable_discovery(&mut self) { |
| for (_,host) in self.hosts.iter_mut() { host.stop_discovery(); } |
| self.discovery = NotDiscovering; |
| } |
| |
| pub fn stop_discovery(&mut self) { |
| //assert!( self.discovery == Discovering || !self.discovery_clients.contains(client) ); |
| |
| //self.discovery_clients -= client; |
| if self.discovery_clients.is_empty() { self.disable_discovery() } |
| } |
| } |
| |
| impl HostDispatcher<FidlHost> { |
| /// Adds an adapter to the host dispatcher. Called by the watch_hosts device watcher |
| pub async fn add_adapter(hd: HostDispatcher<FidlHost>, host: FidlHost) -> Result<(), Error> { |
| fx_log_info!("Adding Host Device: {:?}", host.path()); |
| //let info = host.get_info(); |
| // TODO |
| //await!(try_restore_bonds(host.clone(), self.clone(), &info.address.clone()))?; |
| |
| //hd.adapters.push(info.identifier, host); |
| |
| // Start listening to Host interface events. |
| //fasync::spawn(host::run(self.clone(), host.clone()).map(|_| ())); |
| |
| Ok(()) |
| } |
| |
| //fn activate_host(host) { |
| // // Initialize bt-gap as this host's pairing delegate. |
| // start_pairing_delegate(self.clone(), host.clone())?; |
| // // TODO(NET-1445): Only the active host should be made connectable and scanning in the background. |
| // await!(host.read().set_connectable(true)).map_err(|_| err_msg("failed to set connectable"))?; |
| // host.read().enable_background_scan(true).map_err(|_| err_msg("failed to enable background scan"))?; |
| //} |
| } |
| |
| |
| |
| pub enum HostDispatcherMsg<H: Host> { |
| StartDiscovery(ActorHandle<bt::Result<DiscoverySession<H>>>), |
| StopDiscovery, |
| AdapterMsg(AdapterEvent), |
| HostMsg(HostEvent), |
| ControlMsg(ControlRequest), |
| } |
| |
| impl<H: Host + Send + Sync> Actor for HostDispatcher<H> { |
| type Message = HostDispatcherMsg<H>; |
| |
| fn update(&mut self, msg: Self::Message, _cx: ActorContext<HostDispatcher<H>>) { |
| match msg { |
| HostDispatcherMsg::StartDiscovery(mut sender) => { |
| let session = self.start_discovery(); |
| let _todo = sender.send(session); |
| } |
| HostDispatcherMsg::StopDiscovery => { |
| self.stop_discovery(); |
| } |
| HostDispatcherMsg::HostMsg(_) => panic!("NYI"), |
| HostDispatcherMsg::AdapterMsg(AdapterAdded(_)) => panic!("NYI"), |
| HostDispatcherMsg::AdapterMsg(AdapterRemoved(_)) => panic!("NYI"), |
| HostDispatcherMsg::ControlMsg(ControlRequest::RequestDiscovery{ .. }) => panic!("NYI"), |
| HostDispatcherMsg::ControlMsg(_) => panic!("NYI"), |
| } |
| } |
| } |
| |
| /* |
| #[cfg(test)] |
| mod test { |
| |
| #[test] |
| fn test_discovery() { |
| let dispatcher = HostDispatcher::new::<Fake::Host>(); |
| dispatcher.add_host(Fake::Host::new()); |
| let discovery = dispatcher.start_discovery(); |
| assert!(dispatcher.discovery == Discovering); |
| Drop::drop(discovery); |
| assert!(dispatcher.discovery == NotDiscovering); |
| } |
| |
| /* |
| #[test] |
| fn test_multi_discovery() { |
| let dispatcher = HostDispatcher::new(); |
| dispatcher.add_host(Host::fake()); |
| let discoveries = [1..3].map(|_| dispatcher.start_discovery()); |
| let (head, tail) = discoveries.randomize().split_at(1); |
| for d in tail { |
| Drop::drop(d); |
| assert!(dispatcher.discovery == Discovering); |
| } |
| Drop::drop(head); |
| assert!(dispatcher.discovery == NotDiscovering); |
| } |
| */ |
| } |
| */ |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| /* |
| impl HostDispatcher { |
| // Adapter Management |
| pub fn hosts(&self) -> Vec<AdapterInfo>; |
| pub fn active_adapter(&self) -> Option<AdapterInfo>; |
| pub fn set_active_adapter(&mut self, id: HostId) -> bt::Result<()>; |
| |
| // Device listing |
| pub fn remote_devices(&self) -> Vec<RemoteDevice>; |
| |
| // Device Control |
| pub async fn connect(&mut self, device_id: String) -> bt::Result<()>; |
| pub async fn disconnect(&mut self, device_id: String) -> bt::Result<()>; |
| pub async fn forget(&mut self, _device_id: String) -> bt::Result<()>; |
| |
| // Adapter Control |
| pub async fn set_name(&mut self, name: Option<String>) -> bt::Result<()>; |
| pub async fn start_discovery(&mut self) -> bt::Result<DiscoverySession>; |
| pub async fn set_discoverable(&mut self) -> DiscoverableRequest; |
| |
| // System control |
| pub fn subscribe_device_events(&mut self, handle: Weak<ControlControlHandle>); // TODO can we take a channel instead of fidl handle? |
| // pub fn subscribe_device_events(&mut self, chan: Channel<RemoteDeviceEvent>); |
| pub fn request_host_service(mut self, chan: fasync::Channel, service: HostService); |
| pub fn set_pairing_delegate(&mut self, delegate: Option<PairingDelegateProxy>) -> bool; |
| pub fn set_io_capability(&mut self, input: InputCapabilityType, output: OutputCapabilityType); |
| |
| // Bonding |
| pub fn store_bond(&mut self, bond: BondingData) -> Result<(),Error>; |
| } |
| */ |
| |
| /* |
| pub fn rm_adapter(&mut self, adapter: Adapter) { |
| hosts -= adapter; |
| |
| if hosts.empty() { |
| // terminate discovery |
| disable_discovery(); |
| discovery_clients.notify(DiscoveryTerminated); |
| } |
| } |
| |
| pub fn start_discoverable(&Mut self) -> DiscoverableRequest { |
| DiscoverableRequest{ self } |
| } |
| |
| /// Set the active adapter for this HostDispatcher |
| pub fn set_active_adapter(&mut self, id: HostId) -> Result<()> { |
| if self.hosts.contains_key(id) { |
| let current = self.active_adapter(); |
| if current != id { |
| self.deactivate(current); |
| } |
| self.activate(id); |
| Ok(()) |
| } else { |
| Err(MissingAdapter) |
| } |
| } |
| |
| /// 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. |
| // TODO - is this the right return type? Perhaps Result instead? |
| // Should we be returning false if we successfully cleared the delegate? |
| pub fn set_pairing_delegate(&mut self, delegate: Option<PairingDelegateProxy>) -> bool { |
| if self.pairing_delegate.exists(|d| !d.is_closed()) { |
| false |
| } else { |
| self.pairing_delegate = delegate; |
| delegate.is_some() |
| } |
| } |
| |
| /// Add a new host to the dispatcher |
| fn add_host(&mut self, id: HostId, host: Adapter) { |
| fx_log_info!("Host added: {:?}", host.read().get_info().identifier); |
| let info = host.get_info(); |
| self.hosts.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)); |
| }); |
| } |
| |
| /// Remove a host from the dispatcher |
| fn rm_host(&mut self, id: HostId, host: Host) { |
| fx_log_info!("Host removed: {:?}", host.get_info().identifier); |
| } |
| */ |