blob: 0bcbcd0eaaa2ef9f9938628cb76af5c1ac7393dc [file] [log] [blame]
// Copyright 2019 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::Error;
use fidl::endpoints::RequestStream;
use fidl_fuchsia_bluetooth::PeerId;
use fidl_fuchsia_bluetooth_control::{
ControlMarker, ControlProxy, InputCapabilityType, OutputCapabilityType, PairingDelegateMarker,
PairingDelegateRequest, PairingDelegateRequestStream, PairingMethod, PairingOptions,
PairingSecurityLevel, TechnologyType,
};
use fuchsia_async::{self as fasync, DurationExt, TimeoutExt};
use fuchsia_component as component;
use fuchsia_syslog::macros::*;
use fuchsia_zircon::{self as zx, DurationNum};
use parking_lot::RwLock;
use std::collections::HashMap;
use crate::bluetooth::types::CustomRemoteDevice;
use crate::common_utils::common::macros::{fx_err_and_bail, with_line};
use crate::common_utils::error::Sl4fError;
use futures::channel::mpsc;
use futures::stream::StreamExt;
static ERR_NO_CONTROL_PROXY_DETECTED: &'static str = "No Bluetooth Control Proxy detected.";
#[derive(Debug)]
struct InnerBluetoothControlFacade {
/// The current Bluetooth Control Interface Proxy
control_interface_proxy: Option<ControlProxy>,
/// The MPSC Sender object for sending the pin to the pairing delegate.
client_pin_sender: Option<mpsc::Sender<String>>,
/// The MPSC Receiver object for sending the pin out from the pairing delegate.
client_pin_receiver: Option<mpsc::Receiver<String>>,
/// Discovered device list
discovered_device_list: HashMap<String, CustomRemoteDevice>,
}
#[derive(Debug)]
pub struct BluetoothControlFacade {
inner: RwLock<InnerBluetoothControlFacade>,
}
/// Perform Bluetooth Control operations.
///
/// Note this object is shared among all threads created by server.
///
impl BluetoothControlFacade {
pub fn new() -> BluetoothControlFacade {
BluetoothControlFacade {
inner: RwLock::new(InnerBluetoothControlFacade {
control_interface_proxy: None,
client_pin_sender: None,
client_pin_receiver: None,
discovered_device_list: HashMap::new(),
}),
}
}
pub fn create_control_interface_proxy(&self) -> Result<ControlProxy, Error> {
let tag = "BluetoothControlFacade::create_control_interface_proxy";
match self.inner.read().control_interface_proxy.clone() {
Some(control_interface_proxy) => {
fx_log_info!(
tag: &with_line!(tag),
"Current control interface proxy: {:?}",
control_interface_proxy
);
Ok(control_interface_proxy)
}
None => {
fx_log_info!(tag: &with_line!(tag), "Setting new control interface proxy");
let control_interface_proxy =
component::client::connect_to_service::<ControlMarker>();
if let Err(err) = control_interface_proxy {
fx_err_and_bail!(
&with_line!(tag),
format_err!("Failed to create control interface proxy: {:?}", err)
);
}
control_interface_proxy
}
}
}
pub async fn monitor_pairing_delegate_request_stream(
mut stream: PairingDelegateRequestStream,
mut pin_receiver: mpsc::Receiver<String>,
mut pin_sender: mpsc::Sender<String>,
) -> Result<(), Error> {
let tag = "BluetoothControlFacade::monitor_pairing_delegate_request_stream";
while let Some(request) = stream.next().await {
match request {
Ok(r) => match r {
PairingDelegateRequest::OnPairingComplete {
device_id,
status,
control_handle: _,
} => {
fx_log_info!(
tag: &with_line!(tag),
"Pairing complete for peer (id: {}, status: {})",
device_id,
match status.error {
None => format!("{:?}", "success"),
Some(error) => format!("{:?}", error),
}
);
}
PairingDelegateRequest::OnPairingRequest {
device,
method,
displayed_passkey,
responder,
} => {
if let Some(key) = displayed_passkey.clone() {
let _res = pin_sender.try_send(key);
}
fx_log_info!(
tag: &with_line!(tag),
"Pairing request from peer: {}",
match &device.name {
Some(name) => format!("{} ({})", name, &device.address),
None => device.address,
}
);
let consent = true;
let default_passkey = "000000".to_string();
let (confirm, entered_passkey) = match method {
PairingMethod::Consent => (consent, None),
PairingMethod::PasskeyComparison => (consent, None),
PairingMethod::PasskeyDisplay => {
if let Some(key) = displayed_passkey.clone() {
fx_log_info!(
"Passkey {:?} provided for 'Passkey Display`.",
key
);
(true, None)
} else {
fx_log_info!("No passkey provided for 'Passkey Display`.");
(false, None)
}
}
PairingMethod::PasskeyEntry => {
let timeout = 10.seconds();
let pin = match pin_receiver
.next()
.on_timeout(timeout.after_now(), || None)
.await
{
Some(p) => p,
_ => {
fx_log_err!(
tag: &with_line!(tag),
"No pairing pin found from remote host."
);
default_passkey
}
};
(consent, Some(pin))
}
};
let _ =
responder.send(confirm, entered_passkey.as_ref().map(String::as_ref));
}
PairingDelegateRequest::OnRemoteKeypress {
device_id,
keypress,
control_handle: _,
} => {
fx_log_info!(
tag: &with_line!(tag),
"Unhandled OnRemoteKeypress for Device: {} | {:?}",
device_id,
keypress
);
}
},
Err(r) => return Err(format_err!("Error during handling request stream: {:?}", r)),
};
}
Ok(())
}
pub fn set_io_capabilities(&self, input: &str, output: &str) -> Result<(), Error> {
let tag = "BluetoothControlFacade::set_io_capabilities";
let input_capability = match input {
"NONE" => InputCapabilityType::None,
"CONFIRMATION" => InputCapabilityType::Confirmation,
"KEYBOARD" => InputCapabilityType::Keyboard,
_ => {
fx_err_and_bail!(&with_line!(tag), format!("Invalid Input Capability {:?}", input))
}
};
let output_capability = match output {
"NONE" => OutputCapabilityType::None,
"DISPLAY" => OutputCapabilityType::Display,
_ => fx_err_and_bail!(
&with_line!(tag),
format!("Invalid Output Capability {:?}", output)
),
};
fx_log_info!(
tag: &with_line!(tag),
"Setting IO capabilities to input: {:?} output: {:?}",
input_capability,
output_capability
);
match &self.inner.read().control_interface_proxy {
Some(p) => {
let _ = p.set_io_capabilities(input_capability, output_capability);
Ok(())
}
None => {
fx_err_and_bail!(&with_line!(tag), "No Bluetooth Control Interface Proxy Set.");
}
}
}
pub async fn accept_pairing(&self) -> Result<(), Error> {
let tag = "BluetoothControlFacade::accept_pairing";
fx_log_info!(tag: &with_line!(tag), "Accepting 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);
let pairing_delegate_result = match &self.inner.read().control_interface_proxy {
Some(p) => p.set_pairing_delegate(Some(delegate_ptr)),
None => {
fx_err_and_bail!(&with_line!(tag), "No Bluetooth Control Interface Proxy Set.");
}
};
let delegate_request_stream = PairingDelegateRequestStream::from_channel(delegate_local);
let (sender, pin_receiver) = mpsc::channel(10);
let (pin_sender, receiever) = mpsc::channel(10);
let pairing_delegate_fut = BluetoothControlFacade::monitor_pairing_delegate_request_stream(
delegate_request_stream,
pin_receiver,
pin_sender,
);
self.inner.write().client_pin_sender = Some(sender);
self.inner.write().client_pin_receiver = Some(receiever);
let monitor_pairing_delegate_future = async {
let result = pairing_delegate_result.await;
if let Err(err) = result {
fx_log_err!(
tag: &with_line!("BluetoothControlFacade::accept_pairing"),
"Failed to take ownership of Bluetooth Pairing: {:?}",
err
);
}
};
fasync::Task::spawn(monitor_pairing_delegate_future).detach();
let fut = async {
let result = pairing_delegate_fut.await;
if let Err(err) = result {
fx_log_err!(
tag: &with_line!("BluetoothControlFacade::accept_pairing"),
"Failed to create or monitor the pairing service delegate: {:?}",
err
);
}
};
fasync::Task::spawn(fut).detach();
Ok(())
}
/// Sets a control proxy to use if one is not already in use.
pub async fn init_control_interface_proxy(&self) -> Result<(), Error> {
self.inner.write().control_interface_proxy = Some(self.create_control_interface_proxy()?);
Ok(())
}
pub async fn input_pairing_pin(&self, pin: String) -> Result<(), Error> {
let tag = "BluetoothControlFacade::input_pairing_pin";
match self.inner.read().client_pin_sender.clone() {
Some(mut sender) => sender.try_send(pin)?,
None => {
let err_msg = "No sender setup for pairing delegate.".to_string();
fx_err_and_bail!(&with_line!(tag), err_msg)
}
};
Ok(())
}
pub async fn get_pairing_pin(&self) -> Result<String, Error> {
let tag = "BluetoothControlFacade::get_pairing_pin";
let pin = match &mut self.inner.write().client_pin_receiver {
Some(receiever) => match receiever.try_next() {
Ok(value) => match value {
Some(v) => v,
None => return Err(format_err!("Error getting pin from pairing delegate.")),
},
Err(_e) => {
let err_msg = "No pairing pin sent from the pairing delegate.".to_string();
fx_err_and_bail!(&with_line!(tag), err_msg)
}
},
None => {
let err_str = "No receiever setup for pairing delegate.".to_string();
fx_log_err!(tag: &with_line!(tag), "{}", err_str);
bail!(err_str)
}
};
Ok(pin)
}
/// Sets the current control proxy to be discoverable.
///
/// # Arguments
/// * 'discoverable' - A bool object for setting Bluetooth device discoverable or not.
pub async fn set_discoverable(&self, discoverable: bool) -> Result<(), Error> {
let tag = "BluetoothControlFacade::set_discoverable";
match &self.inner.read().control_interface_proxy {
Some(proxy) => {
let resp = proxy.set_discoverable(discoverable).await?;
match resp.error {
Some(err) => {
let err_msg = format!("Error: {:?}", Sl4fError::from(*err));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(()),
}
}
None => fx_err_and_bail!(
&with_line!(tag),
format!("{:?}", ERR_NO_CONTROL_PROXY_DETECTED.to_string())
),
}
}
/// Sets the current control proxy name.
///
/// # Arguments
/// * 'name' - A String object representing the name to set.
pub async fn set_name(&self, name: String) -> Result<(), Error> {
let tag = "BluetoothControlFacade::set_name";
match &self.inner.read().control_interface_proxy {
Some(proxy) => {
let resp = proxy.set_name(Some(&name)).await?;
match resp.error {
Some(err) => {
let err_msg = format!("Error: {:?}", Sl4fError::from(*err));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(()),
}
}
None => fx_err_and_bail!(
&with_line!(tag),
format!("{:?}", ERR_NO_CONTROL_PROXY_DETECTED.to_string())
),
}
}
/// Requests discovery on the Bluetooth Control Proxy.
///
/// # Arguments
/// * 'discovery' - A bool representing starting and stopping discovery.
pub async fn request_discovery(&self, discovery: bool) -> Result<(), Error> {
let tag = "BluetoothControlFacade::request_discovery";
match &self.inner.read().control_interface_proxy {
Some(proxy) => {
let resp = proxy.request_discovery(discovery).await?;
match resp.error {
Some(err) => {
let err_msg = format!("Error: {:?}", Sl4fError::from(*err));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(()),
}
}
None => fx_err_and_bail!(
&with_line!(tag),
format!("{:?}", ERR_NO_CONTROL_PROXY_DETECTED.to_string())
),
}
}
/// Returns a hash of the known devices on the Bluetooth Control proxy.
pub async fn get_known_remote_devices(
&self,
) -> Result<HashMap<String, CustomRemoteDevice>, Error> {
let tag = "BluetoothControlFacade::get_known_remote_devices";
&self.inner.write().discovered_device_list.clear();
let discovered_devices = match &self.inner.read().control_interface_proxy {
Some(proxy) => proxy.get_known_remote_devices().await?,
None => fx_err_and_bail!(
&with_line!(tag),
format!("{:?}", ERR_NO_CONTROL_PROXY_DETECTED.to_string())
),
};
self.inner.write().discovered_device_list =
discovered_devices.iter().map(|d| (d.identifier.clone(), d.into())).collect();
Ok(self.inner.read().discovered_device_list.clone())
}
/// Forgets (Unbonds) an input device ID.
///
/// # Arguments
/// * `id` - A String representing the device ID.
pub async fn forget(&self, id: String) -> Result<(), Error> {
let tag = "BluetoothControlFacade::forget";
match &self.inner.read().control_interface_proxy {
Some(proxy) => {
let resp = proxy.forget(&id).await?;
match resp.error {
Some(err) => {
let err_msg = format!("Error: {:?}", Sl4fError::from(*err));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(()),
}
}
None => fx_err_and_bail!(
&with_line!(tag),
format!("{:?}", ERR_NO_CONTROL_PROXY_DETECTED.to_string())
),
}
}
/// Connects over BR/EDR to an input device ID.
///
/// # Arguments
/// * `id` - A String representing the device ID.
pub async fn connect(&self, id: String) -> Result<(), Error> {
let tag = "BluetoothControlFacade::connect";
match &self.inner.read().control_interface_proxy {
Some(proxy) => {
let resp = proxy.connect(&id).await?;
match resp.error {
Some(err) => {
let err_msg = format!("Error: {:?}", Sl4fError::from(*err));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(()),
}
}
None => fx_err_and_bail!(
&with_line!(tag),
format!("{:?}", ERR_NO_CONTROL_PROXY_DETECTED.to_string())
),
}
}
/// Sends an outgoing pairing request over BR/EDR or LE to an input device ID.
///
/// # Arguments
/// * `id` - A String representing the device ID.
/// * `pairing_security_level_value` - The security level required for this pairing request
/// represented as a u64. (Only for LE pairing)
/// Available Values
/// 1 - ENCRYPTED: Encrypted without MITM protection (unauthenticated)
/// 2 - AUTHENTICATED: Encrypted with MITM protection (authenticated).
/// None: Used for BR/EDR
/// * `non_bondable` - A bool representing whether the pairing mode is bondable or not. None is
/// also accepted. False if bondable, True if non-bondable.
/// * `transport_value` - A u64 representing the transport type.
/// Available Values
/// 1 - BREDR: Classic BR/EDR transport
/// 2 - LE: Bluetooth Low Energy Transport
pub async fn pair(
&self,
id: String,
pairing_security_level_value: Option<u64>,
non_bondable: Option<bool>,
transport_value: u64,
) -> Result<(), Error> {
let tag = "BluetoothControlFacade::pair";
let peer_id = match u64::from_str_radix(&id, 16) {
Ok(value) => value,
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("Unable to pair - invalid peer id: {:?}", e)
),
};
let pairing_security_level = match pairing_security_level_value {
Some(value) => match value {
1 => Some(PairingSecurityLevel::Encrypted),
2 => Some(PairingSecurityLevel::Authenticated),
_ => fx_err_and_bail!(
&with_line!(tag),
format!(
"Invalid pairing security level provided: {:?}",
pairing_security_level_value
)
),
},
None => None,
};
let transport = match transport_value {
1 => TechnologyType::Classic,
2 => TechnologyType::LowEnergy,
_ => fx_err_and_bail!(
&with_line!(tag),
format!("Invalid transport provided: {:?}", transport_value)
),
};
let pairing_options = PairingOptions {
le_security_level: pairing_security_level,
non_bondable: non_bondable,
transport: Some(transport),
};
let proxy = match &self.inner.read().control_interface_proxy {
Some(p) => p.clone(),
None => fx_err_and_bail!(
&with_line!(tag),
format!("{:?}", ERR_NO_CONTROL_PROXY_DETECTED.to_string())
),
};
let fut = async move {
let result = proxy.pair(&mut PeerId { value: peer_id }, pairing_options).await;
if let Err(err) = result {
fx_log_err!(
tag: &with_line!("BluetoothControlFacade::pair"),
"Failed to pair with: {:?}",
err
);
}
};
fasync::Task::spawn(fut).detach();
Ok(())
}
/// Disconnects an active BR/EDR connection by input device ID.
///
/// # Arguments
/// * `id` - A String representing the device ID.
pub async fn disconnect(&self, id: String) -> Result<(), Error> {
let tag = "BluetoothControlFacade::disconnect";
match &self.inner.read().control_interface_proxy {
Some(proxy) => {
let resp = proxy.disconnect(&id).await?;
match resp.error {
Some(err) => {
let err_msg = format!("Error: {:?}", Sl4fError::from(*err));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(()),
}
}
None => fx_err_and_bail!(
&with_line!(tag),
format!("{:?}", ERR_NO_CONTROL_PROXY_DETECTED.to_string())
),
}
}
/// Returns the current Active Adapter's Address.
pub async fn get_active_adapter_address(&self) -> Result<String, Error> {
let tag = "BluetoothControlFacade::get_active_adapter_address";
let result = match &self.inner.read().control_interface_proxy {
Some(proxy) => {
if let Some(adapter) = proxy.get_active_adapter_info().await? {
adapter.address
} else {
fx_err_and_bail!(&with_line!(tag), "No active adapter.")
}
}
None => fx_err_and_bail!(
&with_line!(tag),
format!("{:?}", ERR_NO_CONTROL_PROXY_DETECTED.to_string())
),
};
Ok(result)
}
/// Cleans up objects in use.
pub fn cleanup(&self) {
self.inner.write().control_interface_proxy = None;
self.inner.write().discovered_device_list.clear();
}
/// Prints useful information.
pub fn print(&self) {
let tag = "BluetoothControlFacade::print:";
fx_log_info!(
tag: &with_line!(tag),
"control_interface_proxy: {:?}",
self.inner.read().control_interface_proxy
);
fx_log_info!(
tag: &with_line!(tag),
"discovered_device_list: {:?}",
self.inner.read().discovered_device_list
);
}
}