//! Anything related to the adapter API (IBluetooth). use bt_topshim::btif::ffi; use bt_topshim::btif::{BluetoothCallbacks, BluetoothInterface, BtState}; use bt_topshim::topstack; use btif_macros::btif_callbacks_generator; use btif_macros::stack_message; use num_traits::cast::ToPrimitive; use num_traits::FromPrimitive; use std::fmt::Debug; 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); /// 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; } /// 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); } /// Implementation of the adapter API. pub struct Bluetooth { intf: Arc>, state: BtState, callbacks: Vec<(u32, Box)>, callbacks_last_id: u32, tx: Sender, local_address: Option, } impl Bluetooth { /// Constructs the IBluetooth implementation. pub fn new(tx: Sender, intf: Arc>) -> Bluetooth { Bluetooth { tx, intf, state: BtState::Off, callbacks: vec![], callbacks_last_id: 0, local_address: None, } } fn update_local_address(&mut self, raw: &Vec) { self.local_address = Some(BDAddr::from_byte_vec(raw)); for callback in &self.callbacks { callback.1.on_bluetooth_address_changed(self.local_address.unwrap().to_string()); } } pub(crate) fn callback_disconnected(&mut self, id: u32) { self.callbacks.retain(|x| x.0 != id); } } #[btif_callbacks_generator(btif_bluetooth_callbacks, BluetoothCallbacks)] pub(crate) trait BtifBluetoothCallbacks { #[stack_message(BluetoothAdapterStateChanged)] fn adapter_state_changed(&mut self, state: BtState); #[stack_message(BluetoothAdapterPropertiesChanged)] fn adapter_properties_changed( &mut self, status: i32, num_properties: i32, properties: Vec, ); } #[derive(FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] #[repr(i32)] #[derive(Debug)] enum PropertyType { BDName = 0x01, BDAddr, Uuids, ClassOfDevice, TypeOfDevice, ServiceRecord, AdapterScanMode, AdapterBondedDevices, AdapterDiscoverableTimeout, RemoteFriendlyName, RemoteRssi, RemoteVersionInfo, RemoteLocalLeFeatures, RemoteDynamicAudioBuffer = 0x10, Unknown = 0x100, } impl BtifBluetoothCallbacks for Bluetooth { fn adapter_state_changed(&mut self, state: BtState) { for callback in &self.callbacks { callback .1 .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: i32, num_properties: i32, properties: Vec, ) { if status != 0 { return; } for prop in properties { let prop_type = PropertyType::from_i32(prop.prop_type); if prop_type.is_none() { continue; } match prop_type.unwrap() { PropertyType::BDAddr => { self.update_local_address(&prop.val); } _ => {} } } } } // TODO: Add unit tests for this implementation impl IBluetooth for Bluetooth { fn register_callback(&mut self, mut callback: Box) { 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(), } } }