blob: 773e1ce7919f45c27b67bbab830240b78b6556ea [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 {
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))
}
}
}