blob: 151321d712e60f9d86aea6bc45bfb05e3568471f [file] [log] [blame]
//! Anything related to the adapter API (IBluetooth).
use bt_topshim::btif::{
BaseCallbacks, BaseCallbacksDispatcher, BluetoothInterface, BtBondState, BtDiscoveryState,
BtProperty, BtPropertyType, BtSspVariant, BtState, BtStatus, BtTransport, RawAddress,
};
use bt_topshim::profiles::hid_host::{HHCallbacksDispatcher, HidHost};
use bt_topshim::topstack;
use btif_macros::{btif_callback, btif_callbacks_dispatcher};
use num_traits::cast::ToPrimitive;
use std::sync::Arc;
use std::sync::Mutex;
use tokio::sync::mpsc::Sender;
use crate::{BDAddr, Message, RPCProxy};
/// Defines the adapter API.
pub trait IBluetooth {
/// Adds a callback from a client who wishes to observe adapter events.
fn register_callback(&mut self, callback: Box<dyn IBluetoothCallback + Send>);
/// Enables the adapter.
///
/// Returns true if the request is accepted.
fn enable(&mut self) -> bool;
/// Disables the adapter.
///
/// Returns true if the request is accepted.
fn disable(&mut self) -> bool;
/// Returns the Bluetooth address of the local adapter.
fn get_address(&self) -> String;
/// Starts BREDR Inquiry.
fn start_discovery(&self) -> bool;
/// Cancels BREDR Inquiry.
fn cancel_discovery(&self) -> bool;
/// Initiates pairing to a remote device. Triggers connection if not already started.
fn create_bond(&self, device: BluetoothDevice, transport: BluetoothTransport) -> bool;
}
#[derive(Debug, FromPrimitive, ToPrimitive)]
#[repr(i32)]
pub enum BluetoothTransport {
Auto = 0,
Bredr = 1,
Le = 2,
}
#[derive(Debug, Default)]
pub struct BluetoothDevice {
pub address: String,
pub name: String,
}
impl BluetoothDevice {
pub(crate) fn from_properties(properties: &Vec<BtProperty>) -> BluetoothDevice {
let mut address = String::from("");
let mut name = String::from("");
for prop in properties {
match prop.prop_type {
BtPropertyType::BdAddr => {
if let Some(addr) = BDAddr::from_byte_vec(&prop.val) {
address = addr.to_string();
}
}
BtPropertyType::BdName => {
name = String::from_utf8(prop.val.clone()).unwrap();
}
_ => {}
}
}
BluetoothDevice { address, name }
}
}
/// The interface for adapter callbacks registered through `IBluetooth::register_callback`.
pub trait IBluetoothCallback: RPCProxy {
/// When any of the adapter states is changed.
fn on_bluetooth_state_changed(&self, prev_state: u32, new_state: u32);
/// When any of the adapter local address is changed.
fn on_bluetooth_address_changed(&self, addr: String);
/// When a device is found via discovery.
fn on_device_found(&self, remote_device: BluetoothDevice);
/// When the discovery state is changed.
fn on_discovering_changed(&self, discovering: bool);
/// When there is a pairing/bonding process and requires agent to display the event to UI.
fn on_ssp_request(
&self,
remote_device: BluetoothDevice,
cod: u32,
variant: BtSspVariant,
passkey: u32,
);
}
/// Implementation of the adapter API.
pub struct Bluetooth {
intf: Arc<Mutex<BluetoothInterface>>,
state: BtState,
callbacks: Vec<(u32, Box<dyn IBluetoothCallback + Send>)>,
callbacks_last_id: u32,
tx: Sender<Message>,
local_address: Option<BDAddr>,
hh: Option<HidHost>,
}
impl Bluetooth {
/// Constructs the IBluetooth implementation.
pub fn new(tx: Sender<Message>, intf: Arc<Mutex<BluetoothInterface>>) -> Bluetooth {
Bluetooth {
tx,
intf,
state: BtState::Off,
callbacks: vec![],
callbacks_last_id: 0,
local_address: None,
hh: None,
}
}
pub fn init_profiles(&mut self) {
self.hh = Some(HidHost::new(&self.intf.lock().unwrap()));
self.hh.as_mut().unwrap().initialize(HHCallbacksDispatcher {
dispatch: Box::new(move |_cb| {
// TODO("Implement the callbacks");
println!("received HH callback");
}),
});
}
fn update_local_address(&mut self, raw: &Vec<u8>) {
self.local_address = BDAddr::from_byte_vec(raw);
self.for_all_callbacks(|callback| {
callback.on_bluetooth_address_changed(self.local_address.unwrap().to_string());
});
}
fn for_all_callbacks<F: Fn(&Box<dyn IBluetoothCallback + Send>)>(&self, f: F) {
for callback in &self.callbacks {
f(&callback.1);
}
}
pub(crate) fn callback_disconnected(&mut self, id: u32) {
self.callbacks.retain(|x| x.0 != id);
}
}
#[btif_callbacks_dispatcher(Bluetooth, dispatch_base_callbacks, BaseCallbacks)]
pub(crate) trait BtifBluetoothCallbacks {
#[btif_callback(AdapterState)]
fn adapter_state_changed(&mut self, state: BtState);
#[btif_callback(AdapterProperties)]
fn adapter_properties_changed(
&mut self,
status: BtStatus,
num_properties: i32,
properties: Vec<BtProperty>,
);
#[btif_callback(DeviceFound)]
fn device_found(&mut self, n: i32, properties: Vec<BtProperty>);
#[btif_callback(DiscoveryState)]
fn discovery_state(&mut self, state: BtDiscoveryState);
#[btif_callback(SspRequest)]
fn ssp_request(
&mut self,
remote_addr: RawAddress,
remote_name: String,
cod: u32,
variant: BtSspVariant,
passkey: u32,
);
#[btif_callback(BondState)]
fn bond_state(&mut self, status: BtStatus, addr: RawAddress, bond_state: BtBondState);
}
pub fn get_bt_dispatcher(tx: Sender<Message>) -> BaseCallbacksDispatcher {
BaseCallbacksDispatcher {
dispatch: Box::new(move |cb| {
let txl = tx.clone();
topstack::get_runtime().spawn(async move {
let _ = txl.send(Message::Base(cb)).await;
});
}),
}
}
impl BtifBluetoothCallbacks for Bluetooth {
fn adapter_state_changed(&mut self, state: BtState) {
self.for_all_callbacks(|callback| {
callback
.on_bluetooth_state_changed(self.state.to_u32().unwrap(), state.to_u32().unwrap())
});
self.state = state;
}
#[allow(unused_variables)]
fn adapter_properties_changed(
&mut self,
status: BtStatus,
num_properties: i32,
properties: Vec<BtProperty>,
) {
if status != BtStatus::Success {
return;
}
for prop in properties {
match prop.prop_type {
BtPropertyType::BdAddr => {
self.update_local_address(&prop.val);
}
_ => {}
}
}
}
fn device_found(&mut self, _n: i32, properties: Vec<BtProperty>) {
self.for_all_callbacks(|callback| {
callback.on_device_found(BluetoothDevice::from_properties(&properties));
});
}
fn discovery_state(&mut self, state: BtDiscoveryState) {
self.for_all_callbacks(|callback| {
callback.on_discovering_changed(state == BtDiscoveryState::Started);
});
}
fn ssp_request(
&mut self,
remote_addr: RawAddress,
remote_name: String,
cod: u32,
variant: BtSspVariant,
passkey: u32,
) {
// Currently this supports many agent because we accept many callbacks.
// TODO: We need a way to select the default agent.
self.for_all_callbacks(|callback| {
callback.on_ssp_request(
BluetoothDevice {
address: BDAddr::from_byte_vec(&remote_addr.address.to_vec())
.unwrap()
.to_string(),
name: remote_name.clone(),
},
cod,
variant.clone(),
passkey,
);
});
// Immediately accept the pairing.
// TODO: Delegate the pairing confirmation to agent.
self.intf.lock().unwrap().ssp_reply(&remote_addr, variant, 1, passkey);
}
fn bond_state(&mut self, _status: BtStatus, mut addr: RawAddress, bond_state: BtBondState) {
if bond_state == BtBondState::Bonded {
// We are assuming that peer is a HID device and automatically connect to that profile.
// TODO: Only connect to enabled profiles on that device.
self.hh.as_ref().unwrap().connect(&mut addr);
}
}
}
// TODO: Add unit tests for this implementation
impl IBluetooth for Bluetooth {
fn register_callback(&mut self, mut callback: Box<dyn IBluetoothCallback + Send>) {
let tx = self.tx.clone();
// TODO: Refactor into a separate wrap-around id generator.
self.callbacks_last_id += 1;
let id = self.callbacks_last_id;
callback.register_disconnect(Box::new(move || {
let tx = tx.clone();
topstack::get_runtime().spawn(async move {
let _result = tx.send(Message::BluetoothCallbackDisconnected(id)).await;
});
}));
self.callbacks.push((id, callback))
}
fn enable(&mut self) -> bool {
self.intf.lock().unwrap().enable() == 0
}
fn disable(&mut self) -> bool {
self.intf.lock().unwrap().disable() == 0
}
fn get_address(&self) -> String {
match self.local_address {
None => String::from(""),
Some(addr) => addr.to_string(),
}
}
fn start_discovery(&self) -> bool {
self.intf.lock().unwrap().start_discovery() == 0
}
fn cancel_discovery(&self) -> bool {
self.intf.lock().unwrap().cancel_discovery() == 0
}
fn create_bond(&self, device: BluetoothDevice, transport: BluetoothTransport) -> bool {
let addr = BDAddr::from_string(device.address.clone());
if addr.is_none() {
println!("address {} is not valid", device.address);
return false;
}
let address = unsafe { RawAddress::new(&addr.unwrap().val) };
self.intf
.lock()
.unwrap()
.create_bond(&address, BtTransport::from(transport.to_i32().unwrap()))
== 0
}
}