| // 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 { |
| anyhow::{format_err, Error}, |
| fidl::endpoints::RequestStream, |
| fidl_fuchsia_bluetooth_control::{self as control, ControlRequest, ControlRequestStream}, |
| fuchsia_bluetooth::{bt_fidl_status, types::PeerId}, |
| fuchsia_syslog::fx_log_warn, |
| futures::prelude::*, |
| std::sync::Arc, |
| }; |
| |
| use crate::{ |
| host_dispatcher::*, |
| types::{self, status_response}, |
| }; |
| |
| struct ControlSession { |
| discovery_token: Option<Arc<DiscoveryRequestToken>>, |
| discoverable_token: Option<Arc<DiscoverableRequestToken>>, |
| } |
| |
| impl ControlSession { |
| fn new() -> ControlSession { |
| ControlSession { discovery_token: None, discoverable_token: None } |
| } |
| } |
| |
| /// Build the ControlImpl to interact with fidl messages |
| /// State is stored in the HostDispatcher object |
| pub async fn start_control_service( |
| hd: HostDispatcher, |
| mut stream: ControlRequestStream, |
| ) -> Result<(), Error> { |
| let event_listener = Arc::new(stream.control_handle()); |
| hd.add_event_listener(Arc::downgrade(&event_listener)); |
| let mut session = ControlSession::new(); |
| |
| while let Some(event) = stream.next().await { |
| handler(hd.clone(), &mut session, event?).await?; |
| } |
| // event_listener will now be dropped, closing the listener |
| Ok(()) |
| } |
| |
| fn parse_peer_id(id: &str) -> Result<PeerId, types::Error> { |
| id.parse::<PeerId>() |
| .map_err(|_| types::Error::InternalError(format_err!("invalid peer ID: {}", id))) |
| } |
| |
| async fn handle_connect(hd: HostDispatcher, device_id: &str) -> types::Result<()> { |
| hd.connect(parse_peer_id(device_id)?).await |
| } |
| |
| async fn handle_forget(hd: HostDispatcher, device_id: &str) -> types::Result<()> { |
| hd.forget(parse_peer_id(device_id)?).await |
| } |
| |
| async fn handle_disconnect(hd: HostDispatcher, device_id: &str) -> types::Result<()> { |
| hd.disconnect(parse_peer_id(device_id)?).await |
| } |
| |
| async fn handler( |
| hd: HostDispatcher, |
| session: &mut ControlSession, |
| event: ControlRequest, |
| ) -> fidl::Result<()> { |
| match event { |
| ControlRequest::Connect { device_id, responder } => { |
| let result = handle_connect(hd, &device_id).await; |
| responder.send(&mut status_response(result)) |
| } |
| ControlRequest::Pair { id, options, responder } => { |
| let result = hd.pair(id.into(), options).await; |
| responder.send(&mut status_response(result)) |
| } |
| ControlRequest::SetDiscoverable { discoverable, responder } => { |
| let mut resp = if discoverable { |
| match hd.set_discoverable().await { |
| Ok(token) => { |
| session.discoverable_token = Some(token); |
| bt_fidl_status!() |
| } |
| Err(err) => err.as_status(), |
| } |
| } else { |
| session.discoverable_token = None; |
| bt_fidl_status!() |
| }; |
| responder.send(&mut resp) |
| } |
| ControlRequest::SetIoCapabilities { input, output, control_handle: _ } => { |
| hd.set_io_capability(input, output); |
| Ok(()) |
| } |
| ControlRequest::Forget { device_id, responder } => { |
| let result = handle_forget(hd, &device_id).await; |
| responder.send(&mut status_response(result)) |
| } |
| ControlRequest::Disconnect { device_id, responder } => { |
| let result = handle_disconnect(hd, &device_id).await; |
| responder.send(&mut status_response(result)) |
| } |
| ControlRequest::GetKnownRemoteDevices { responder } => { |
| let mut devices: Vec<_> = |
| hd.get_peers().into_iter().map(control::RemoteDevice::from).collect(); |
| responder.send(&mut devices.iter_mut()) |
| } |
| ControlRequest::IsBluetoothAvailable { responder } => { |
| let is_available = hd.get_active_host_info().is_some(); |
| responder.send(is_available) |
| } |
| ControlRequest::SetPairingDelegate { delegate, responder } => { |
| let status = match delegate.map(|d| d.into_proxy()) { |
| Some(Ok(proxy)) => hd.set_pairing_delegate(Some(proxy)), |
| Some(Err(err)) => { |
| fx_log_warn!( |
| "Invalid Pairing Delegate passed to SetPairingDelegate - ignoring: {}", |
| err |
| ); |
| false |
| } |
| None => hd.set_pairing_delegate(None), |
| }; |
| responder.send(status) |
| } |
| ControlRequest::GetAdapters { responder } => { |
| let mut adapters: Vec<_> = |
| hd.get_adapters().await.into_iter().map(control::AdapterInfo::from).collect(); |
| responder.send(Some(&mut adapters.iter_mut())) |
| } |
| ControlRequest::SetActiveAdapter { identifier, responder } => { |
| let result = hd.set_active_adapter(identifier.clone()); |
| responder.send(&mut status_response(result)) |
| } |
| ControlRequest::GetActiveAdapterInfo { responder } => { |
| let host_info = hd.get_active_host_info(); |
| responder.send(host_info.map(control::AdapterInfo::from).as_mut()) |
| } |
| ControlRequest::RequestDiscovery { discovery, responder } => { |
| let mut resp = if discovery { |
| match hd.start_discovery().await { |
| Ok(token) => { |
| session.discovery_token = Some(token); |
| bt_fidl_status!() |
| } |
| Err(err) => err.as_status(), |
| } |
| } else { |
| session.discovery_token = None; |
| bt_fidl_status!() |
| }; |
| responder.send(&mut resp) |
| } |
| ControlRequest::SetName { name, responder } => { |
| let result = hd.set_name(name.unwrap_or("".to_string())).await; |
| responder.send(&mut status_response(result)) |
| } |
| ControlRequest::SetDeviceClass { device_class, responder } => { |
| let device_class = fidl_fuchsia_bluetooth::DeviceClass { value: device_class.value }; |
| let result = hd.set_device_class(device_class).await; |
| responder.send(&mut status_response(result)) |
| } |
| } |
| } |