blob: 9e95b36f491fd8bf97272c7041daeadc467fdf51 [file] [log] [blame]
//! Anything related to the adapter API (IBluetooth).
use bt_topshim::btif::{
BaseCallbacks, BaseCallbacksDispatcher, BluetoothInterface, BluetoothProperty, BtAclState,
BtBondState, BtDiscoveryState, BtHciErrorCode, BtPinCode, BtPropertyType, BtSspVariant,
BtState, BtStatus, BtTransport, RawAddress, Uuid, Uuid128Bit,
};
use bt_topshim::{
profiles::hid_host::{HHCallbacksDispatcher, HidHost},
profiles::sdp::{BtSdpRecord, Sdp, SdpCallbacks, SdpCallbacksDispatcher},
topstack,
};
use btif_macros::{btif_callback, btif_callbacks_dispatcher};
use log::{debug, warn};
use num_traits::cast::ToPrimitive;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Instant;
use tokio::sync::mpsc::Sender;
use crate::bluetooth_media::{BluetoothMedia, IBluetoothMedia, MediaActions};
use crate::uuid::{Profile, UuidHelper};
use crate::{BluetoothCallbackType, 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>);
/// Adds a callback from a client who wishes to observe connection events.
fn register_connection_callback(
&mut self,
callback: Box<dyn IBluetoothConnectionCallback + Send>,
) -> u32;
/// Removes registered callback.
fn unregister_connection_callback(&mut self, callback_id: u32) -> bool;
/// 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;
/// Gets supported UUIDs by the local adapter.
fn get_uuids(&self) -> Vec<Uuid128Bit>;
/// Gets the local adapter name.
fn get_name(&self) -> String;
/// Sets the local adapter name.
fn set_name(&self, name: String) -> bool;
/// Starts BREDR Inquiry.
fn start_discovery(&self) -> bool;
/// Cancels BREDR Inquiry.
fn cancel_discovery(&self) -> bool;
/// Checks if discovery is started.
fn is_discovering(&self) -> bool;
/// Checks when discovery ends in milliseconds from now.
fn get_discovery_end_millis(&self) -> u64;
/// Initiates pairing to a remote device. Triggers connection if not already started.
fn create_bond(&self, device: BluetoothDevice, transport: BtTransport) -> bool;
/// Cancels any pending bond attempt on given device.
fn cancel_bond_process(&self, device: BluetoothDevice) -> bool;
/// Removes pairing for given device.
fn remove_bond(&self, device: BluetoothDevice) -> bool;
/// Returns a list of known bonded devices.
fn get_bonded_devices(&self) -> Vec<BluetoothDevice>;
/// Gets the bond state of a single device.
fn get_bond_state(&self, device: BluetoothDevice) -> u32;
/// Set pin on bonding device.
fn set_pin(&self, device: BluetoothDevice, accept: bool, len: u32, pin_code: Vec<u8>) -> bool;
/// Set passkey on bonding device.
fn set_passkey(
&self,
device: BluetoothDevice,
accept: bool,
len: u32,
passkey: Vec<u8>,
) -> bool;
/// Confirm that a pairing should be completed on a bonding device.
fn set_pairing_confirmation(&self, device: BluetoothDevice, accept: bool) -> bool;
/// Gets the connection state of a single device.
fn get_connection_state(&self, device: BluetoothDevice) -> u32;
/// Returns the cached UUIDs of a remote device.
fn get_remote_uuids(&self, device: BluetoothDevice) -> Vec<Uuid128Bit>;
/// Triggers SDP to get UUIDs of a remote device.
fn fetch_remote_uuids(&self, device: BluetoothDevice) -> bool;
/// Triggers SDP and searches for a specific UUID on a remote device.
fn sdp_search(&self, device: BluetoothDevice, uuid: Uuid128Bit) -> bool;
/// Connect all profiles supported by device and enabled on adapter.
fn connect_all_enabled_profiles(&self, device: BluetoothDevice) -> bool;
/// Disconnect all profiles supported by device and enabled on adapter.
fn disconnect_all_enabled_profiles(&self, device: BluetoothDevice) -> bool;
}
/// Serializable device used in various apis.
#[derive(Clone, Debug, Default)]
pub struct BluetoothDevice {
pub address: String,
pub name: String,
}
impl BluetoothDevice {
pub(crate) fn new(address: String, name: String) -> BluetoothDevice {
BluetoothDevice { address, name }
}
pub(crate) fn from_properties(in_properties: &Vec<BluetoothProperty>) -> BluetoothDevice {
let mut address = String::from("");
let mut name = String::from("");
for prop in in_properties {
match &prop {
BluetoothProperty::BdAddr(bdaddr) => {
address = bdaddr.to_string();
}
BluetoothProperty::BdName(bdname) => {
name = bdname.clone();
}
_ => {}
}
}
BluetoothDevice::new(address, name)
}
}
/// Internal data structure that keeps a map of cached properties for a remote device.
struct BluetoothDeviceContext {
pub bond_state: BtBondState,
pub acl_state: BtAclState,
pub info: BluetoothDevice,
pub properties: HashMap<BtPropertyType, BluetoothProperty>,
}
impl BluetoothDeviceContext {
pub(crate) fn new(
bond_state: BtBondState,
acl_state: BtAclState,
info: BluetoothDevice,
properties: Vec<BluetoothProperty>,
) -> BluetoothDeviceContext {
let mut device =
BluetoothDeviceContext { bond_state, acl_state, info, properties: HashMap::new() };
device.update_properties(properties);
device
}
pub(crate) fn update_properties(&mut self, in_properties: Vec<BluetoothProperty>) {
for prop in in_properties {
match &prop {
BluetoothProperty::BdAddr(bdaddr) => {
self.info.address = bdaddr.to_string();
}
BluetoothProperty::BdName(bdname) => {
self.info.name = bdname.clone();
}
_ => {}
}
self.properties.insert(prop.get_type(), prop);
}
}
}
/// The interface for adapter callbacks registered through `IBluetooth::register_callback`.
pub trait IBluetoothCallback: RPCProxy {
/// When any of the adapter local address is changed.
fn on_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,
);
/// When a bonding attempt has completed.
fn on_bond_state_changed(&self, status: u32, device_address: String, state: u32);
}
pub trait IBluetoothConnectionCallback: RPCProxy {
/// Notification sent when a remote device completes HCI connection.
fn on_device_connected(&self, remote_device: BluetoothDevice);
/// Notification sent when a remote device completes HCI disconnection.
fn on_device_disconnected(&self, remote_device: BluetoothDevice);
}
/// Implementation of the adapter API.
pub struct Bluetooth {
intf: Arc<Mutex<BluetoothInterface>>,
bonded_devices: HashMap<String, BluetoothDeviceContext>,
bluetooth_media: Arc<Mutex<Box<BluetoothMedia>>>,
callbacks: HashMap<u32, Box<dyn IBluetoothCallback + Send>>,
connection_callbacks: HashMap<u32, Box<dyn IBluetoothConnectionCallback + Send>>,
discovering_started: Instant,
hh: Option<HidHost>,
is_discovering: bool,
local_address: Option<RawAddress>,
properties: HashMap<BtPropertyType, BluetoothProperty>,
profiles_ready: bool,
found_devices: HashMap<String, BluetoothDeviceContext>,
sdp: Option<Sdp>,
state: BtState,
tx: Sender<Message>,
uuid_helper: UuidHelper,
}
impl Bluetooth {
/// Constructs the IBluetooth implementation.
pub fn new(
tx: Sender<Message>,
intf: Arc<Mutex<BluetoothInterface>>,
bluetooth_media: Arc<Mutex<Box<BluetoothMedia>>>,
) -> Bluetooth {
Bluetooth {
bonded_devices: HashMap::new(),
callbacks: HashMap::new(),
connection_callbacks: HashMap::new(),
hh: None,
bluetooth_media,
discovering_started: Instant::now(),
intf,
is_discovering: false,
local_address: None,
properties: HashMap::new(),
profiles_ready: false,
found_devices: HashMap::new(),
sdp: None,
state: BtState::Off,
tx,
uuid_helper: UuidHelper::new(),
}
}
pub fn init_profiles(&mut self) {
let hhtx = self.tx.clone();
self.hh = Some(HidHost::new(&self.intf.lock().unwrap()));
self.hh.as_mut().unwrap().initialize(HHCallbacksDispatcher {
dispatch: Box::new(move |cb| {
let txl = hhtx.clone();
topstack::get_runtime().spawn(async move {
let _ = txl.send(Message::HidHost(cb)).await;
});
}),
});
let sdptx = self.tx.clone();
self.sdp = Some(Sdp::new(&self.intf.lock().unwrap()));
self.sdp.as_mut().unwrap().initialize(SdpCallbacksDispatcher {
dispatch: Box::new(move |cb| {
let txl = sdptx.clone();
topstack::get_runtime().spawn(async move {
let _ = txl.send(Message::Sdp(cb)).await;
});
}),
});
// Mark profiles as ready
self.profiles_ready = true;
}
fn update_local_address(&mut self, addr: &RawAddress) {
self.local_address = Some(*addr);
self.for_all_callbacks(|callback| {
callback.on_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.iter() {
f(&callback);
}
}
fn for_all_connection_callbacks<F: Fn(&Box<dyn IBluetoothConnectionCallback + Send>)>(
&self,
f: F,
) {
for (_, callback) in self.connection_callbacks.iter() {
f(&callback);
}
}
pub(crate) fn callback_disconnected(&mut self, id: u32, cb_type: BluetoothCallbackType) {
match cb_type {
BluetoothCallbackType::Adapter => {
self.callbacks.remove(&id);
}
BluetoothCallbackType::Connection => {
self.connection_callbacks.remove(&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<BluetoothProperty>,
);
#[btif_callback(DeviceFound)]
fn device_found(&mut self, n: i32, properties: Vec<BluetoothProperty>);
#[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,
fail_reason: i32,
);
#[btif_callback(RemoteDeviceProperties)]
fn remote_device_properties_changed(
&mut self,
status: BtStatus,
addr: RawAddress,
num_properties: i32,
properties: Vec<BluetoothProperty>,
);
#[btif_callback(AclState)]
fn acl_state(
&mut self,
status: BtStatus,
addr: RawAddress,
state: BtAclState,
link_type: BtTransport,
hci_reason: BtHciErrorCode,
);
}
#[btif_callbacks_dispatcher(Bluetooth, dispatch_sdp_callbacks, SdpCallbacks)]
pub(crate) trait BtifSdpCallbacks {
#[btif_callback(SdpSearch)]
fn sdp_search(
&mut self,
status: BtStatus,
address: RawAddress,
uuid: Uuid,
count: i32,
records: Vec<BtSdpRecord>,
);
}
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) {
let prev_state = self.state.clone();
self.state = state;
// If it's the same state as before, no further action
if self.state == prev_state {
return;
}
if self.state == BtState::On {
self.bluetooth_media.lock().unwrap().initialize();
}
if self.state == BtState::Off {
self.properties.clear();
} else {
// Trigger properties update
self.intf.lock().unwrap().get_adapter_properties();
}
}
#[allow(unused_variables)]
fn adapter_properties_changed(
&mut self,
status: BtStatus,
num_properties: i32,
properties: Vec<BluetoothProperty>,
) {
if status != BtStatus::Success {
return;
}
// Update local property cache
for prop in properties {
match &prop {
BluetoothProperty::BdAddr(bdaddr) => {
self.update_local_address(&bdaddr);
}
BluetoothProperty::AdapterBondedDevices(bondlist) => {
for addr in bondlist.iter() {
let address = addr.to_string();
// Update bonded state if already in the list. Otherwise create a new
// context with empty properties and name.
self.bonded_devices
.entry(address.clone())
.and_modify(|d| d.bond_state = BtBondState::Bonded)
.or_insert(BluetoothDeviceContext::new(
BtBondState::Bonded,
BtAclState::Disconnected,
BluetoothDevice::new(address.clone(), "".to_string()),
vec![],
));
}
}
_ => {}
}
self.properties.insert(prop.get_type(), prop);
}
}
fn device_found(&mut self, _n: i32, properties: Vec<BluetoothProperty>) {
let device = BluetoothDevice::from_properties(&properties);
let address = device.address.clone();
if let Some(existing) = self.found_devices.get_mut(&address) {
existing.update_properties(properties);
} else {
let device_with_props = BluetoothDeviceContext::new(
BtBondState::NotBonded,
BtAclState::Disconnected,
device,
properties,
);
self.found_devices.insert(address.clone(), device_with_props);
}
let device = self.found_devices.get(&address).unwrap();
self.for_all_callbacks(|callback| {
callback.on_device_found(device.info.clone());
});
}
fn discovery_state(&mut self, state: BtDiscoveryState) {
// Clear found devices when discovery session starts
if !self.is_discovering && &state == &BtDiscoveryState::Started {
self.found_devices.clear();
}
// Cache discovering state
self.is_discovering = &state == &BtDiscoveryState::Started;
if self.is_discovering {
self.discovering_started = Instant::now();
}
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::new(remote_addr.to_string(), remote_name.clone()),
cod,
variant.clone(),
passkey,
);
});
}
fn bond_state(
&mut self,
status: BtStatus,
addr: RawAddress,
bond_state: BtBondState,
_fail_reason: i32,
) {
let address = addr.to_string();
// Easy case of not bonded -- we remove the device from the bonded list and change the bond
// state in the found list (in case it was previously bonding).
if &bond_state == &BtBondState::NotBonded {
self.bonded_devices.remove(&address);
self.found_devices
.entry(address.clone())
.and_modify(|d| d.bond_state = bond_state.clone());
}
// We will only insert into the bonded list after bonding is complete
else if &bond_state == &BtBondState::Bonded && !self.bonded_devices.contains_key(&address)
{
// We either need to construct a new BluetoothDeviceContext or grab it from the found
// devices map
let device = match self.found_devices.remove(&address) {
Some(mut v) => {
v.bond_state = bond_state.clone();
v
}
None => BluetoothDeviceContext::new(
bond_state.clone(),
BtAclState::Disconnected,
BluetoothDevice::new(address.clone(), "".to_string()),
vec![],
),
};
self.bonded_devices.insert(address.clone(), device);
} else {
// If we're bonding, we need to update the found devices list
self.found_devices
.entry(address.clone())
.and_modify(|d| d.bond_state = bond_state.clone());
}
// Send bond state changed notifications
self.for_all_callbacks(|callback| {
callback.on_bond_state_changed(
status.to_u32().unwrap(),
address.clone(),
bond_state.to_u32().unwrap(),
);
});
}
fn remote_device_properties_changed(
&mut self,
_status: BtStatus,
addr: RawAddress,
_num_properties: i32,
properties: Vec<BluetoothProperty>,
) {
let address = addr.to_string();
// Device should be in either found devices or bonded devices
// If it isn't in either, create it and put it found devices.
let device = if self.bonded_devices.contains_key(&address) {
self.bonded_devices.get_mut(&address)
} else if self.found_devices.contains_key(&address) {
self.found_devices.get_mut(&address)
} else {
self.found_devices.insert(
address.clone(),
BluetoothDeviceContext::new(
BtBondState::NotBonded,
BtAclState::Disconnected,
BluetoothDevice::new(address.clone(), String::from("")),
vec![],
),
);
self.found_devices.get_mut(&address)
};
device.unwrap().update_properties(properties);
}
fn acl_state(
&mut self,
status: BtStatus,
addr: RawAddress,
state: BtAclState,
_link_type: BtTransport,
_hci_reason: BtHciErrorCode,
) {
if status != BtStatus::Success {
warn!("Connection to [{}] failed. Status: {:?}", addr.to_string(), status);
return;
}
let address = addr.to_string();
let found = if self.bonded_devices.contains_key(&address) {
self.bonded_devices.get_mut(&address)
} else if self.found_devices.contains_key(&address) {
self.found_devices.get_mut(&address)
} else {
self.found_devices.insert(
address.clone(),
BluetoothDeviceContext::new(
BtBondState::NotBonded,
BtAclState::Disconnected,
BluetoothDevice::new(address.clone(), String::from("")),
vec![],
),
);
self.found_devices.get_mut(&address)
};
// Only notify if there's been a change in state
let prev_state = &found.as_ref().unwrap().acl_state;
if prev_state != &state {
let device = found.as_ref().unwrap().info.clone();
found.unwrap().acl_state = state.clone();
match state {
BtAclState::Connected => {
self.for_all_connection_callbacks(|callback| {
callback.on_device_connected(device.clone());
});
}
BtAclState::Disconnected => {
self.for_all_connection_callbacks(|callback| {
callback.on_device_disconnected(device.clone());
});
}
};
}
}
}
// 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();
let id = callback.register_disconnect(Box::new(move |cb_id| {
let tx = tx.clone();
tokio::spawn(async move {
let _result = tx
.send(Message::BluetoothCallbackDisconnected(
cb_id,
BluetoothCallbackType::Adapter,
))
.await;
});
}));
self.callbacks.insert(id, callback);
}
fn register_connection_callback(
&mut self,
mut callback: Box<dyn IBluetoothConnectionCallback + Send>,
) -> u32 {
let tx = self.tx.clone();
let id = callback.register_disconnect(Box::new(move |cb_id| {
let tx = tx.clone();
tokio::spawn(async move {
let _ = tx
.send(Message::BluetoothCallbackDisconnected(
cb_id,
BluetoothCallbackType::Connection,
))
.await;
});
}));
self.connection_callbacks.insert(id, callback);
id
}
fn unregister_connection_callback(&mut self, callback_id: u32) -> bool {
match self.connection_callbacks.get_mut(&callback_id) {
Some(cb) => cb.unregister(callback_id),
None => false,
}
}
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 get_uuids(&self) -> Vec<Uuid128Bit> {
match self.properties.get(&BtPropertyType::Uuids) {
Some(prop) => match prop {
BluetoothProperty::Uuids(uuids) => {
uuids.iter().map(|&x| x.uu.clone()).collect::<Vec<Uuid128Bit>>()
}
_ => vec![],
},
_ => vec![],
}
}
fn get_name(&self) -> String {
match self.properties.get(&BtPropertyType::BdName) {
Some(prop) => match prop {
BluetoothProperty::BdName(name) => name.clone(),
_ => String::new(),
},
_ => String::new(),
}
}
fn set_name(&self, name: String) -> bool {
self.intf.lock().unwrap().set_adapter_property(BluetoothProperty::BdName(name)) == 0
}
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 is_discovering(&self) -> bool {
self.is_discovering
}
fn get_discovery_end_millis(&self) -> u64 {
if !self.is_discovering {
return 0;
}
match self.properties.get(&BtPropertyType::AdapterDiscoveryTimeout) {
Some(variant) => match variant {
BluetoothProperty::AdapterDiscoveryTimeout(timeout) => {
let seconds: u64 = (*timeout).into();
let elapsed = self.discovering_started.elapsed();
if elapsed.as_secs() >= seconds {
0
} else {
seconds * 1000 - elapsed.as_millis() as u64
}
}
_ => 0,
},
_ => 0,
}
}
fn create_bond(&self, device: BluetoothDevice, transport: BtTransport) -> bool {
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
warn!("Can't create bond. Address {} is not valid", device.address);
return false;
}
let address = addr.unwrap();
self.intf.lock().unwrap().create_bond(&address, transport) == 0
}
fn cancel_bond_process(&self, device: BluetoothDevice) -> bool {
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
warn!("Can't cancel bond. Address {} is not valid.", device.address);
return false;
}
let address = addr.unwrap();
self.intf.lock().unwrap().cancel_bond(&address) == 0
}
fn remove_bond(&self, device: BluetoothDevice) -> bool {
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
warn!("Can't remove bond. Address {} is not valid.", device.address);
return false;
}
let address = addr.unwrap();
self.intf.lock().unwrap().remove_bond(&address) == 0
}
fn get_bonded_devices(&self) -> Vec<BluetoothDevice> {
let mut devices: Vec<BluetoothDevice> = vec![];
for (_, device) in self.bonded_devices.iter() {
devices.push(device.info.clone());
}
devices
}
fn get_bond_state(&self, device: BluetoothDevice) -> u32 {
match self.bonded_devices.get(&device.address) {
Some(device) => device.bond_state.to_u32().unwrap(),
None => BtBondState::NotBonded.to_u32().unwrap(),
}
}
fn set_pin(&self, device: BluetoothDevice, accept: bool, len: u32, pin_code: Vec<u8>) -> bool {
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
warn!("Can't set pin. Address {} is not valid.", device.address);
return false;
}
if len as usize != pin_code.len() || len > 16 {
warn!("Invalid pin code length: {}", len);
return false;
}
let is_bonding = match self.found_devices.get(&device.address) {
Some(d) => d.bond_state == BtBondState::Bonding,
None => false,
};
if !is_bonding {
warn!("Can't set pin. Device {} isn't bonding.", device.address);
return false;
}
let mut btpin: BtPinCode = BtPinCode { pin: [0; 16] };
btpin.pin.copy_from_slice(pin_code.as_slice());
self.intf.lock().unwrap().pin_reply(&addr.unwrap(), accept as u8, len as u8, &mut btpin)
== 0
}
fn set_passkey(
&self,
device: BluetoothDevice,
accept: bool,
len: u32,
passkey: Vec<u8>,
) -> bool {
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
warn!("Can't set passkey. Address {} is not valid.", device.address);
return false;
}
let is_bonding = match self.found_devices.get(&device.address) {
Some(d) => d.bond_state == BtBondState::Bonding,
None => false,
};
if !is_bonding {
warn!("Can't set passkey. Device {} isn't bonding.", device.address);
return false;
}
if len as usize != passkey.len() || len != 4 {
warn!("Invalid passkey length: {}", len);
return false;
}
let mut tmp: [u8; 4] = [0; 4];
tmp.copy_from_slice(passkey.as_slice());
let passkey = u32::from_ne_bytes(tmp);
self.intf.lock().unwrap().ssp_reply(
&addr.unwrap(),
BtSspVariant::PasskeyEntry,
accept as u8,
passkey,
) == 0
}
fn set_pairing_confirmation(&self, device: BluetoothDevice, accept: bool) -> bool {
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
warn!("Can't set pairing confirmation. Address {} is not valid.", device.address);
return false;
}
let is_bonding = match self.found_devices.get(&device.address) {
Some(d) => d.bond_state == BtBondState::Bonding,
None => false,
};
if !is_bonding {
warn!("Can't set pairing confirmation. Device {} isn't bonding.", device.address);
return false;
}
self.intf.lock().unwrap().ssp_reply(
&addr.unwrap(),
BtSspVariant::PasskeyConfirmation,
accept as u8,
0,
) == 0
}
fn get_connection_state(&self, device: BluetoothDevice) -> u32 {
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
warn!("Can't check connection state. Address {} is not valid.", device.address);
return 0;
}
self.intf.lock().unwrap().get_connection_state(&addr.unwrap())
}
fn get_remote_uuids(&self, device: BluetoothDevice) -> Vec<Uuid128Bit> {
// Device must exist in either bonded or found list
let found = self
.bonded_devices
.get(&device.address)
.or_else(|| self.found_devices.get(&device.address));
// Extract property from the device
return found
.and_then(|d| {
if let Some(u) = d.properties.get(&BtPropertyType::Uuids) {
match u {
BluetoothProperty::Uuids(uuids) => {
return Some(
uuids.iter().map(|&x| x.uu.clone()).collect::<Vec<Uuid128Bit>>(),
);
}
_ => (),
}
}
None
})
.unwrap_or(vec![]);
}
fn fetch_remote_uuids(&self, device: BluetoothDevice) -> bool {
if !self.bonded_devices.contains_key(&device.address)
&& !self.found_devices.contains_key(&device.address)
{
warn!("Won't fetch UUIDs on unknown device {}", device.address);
return false;
}
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
warn!("Can't fetch UUIDs. Address {} is not valid.", device.address);
return false;
}
self.intf.lock().unwrap().get_remote_services(&mut addr.unwrap(), BtTransport::Auto) == 0
}
fn sdp_search(&self, device: BluetoothDevice, uuid: Uuid128Bit) -> bool {
if self.sdp.is_none() {
warn!("SDP is not initialized. Can't do SDP search.");
return false;
}
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
warn!("Can't SDP search. Address {} is not valid.", device.address);
return false;
}
let uu = Uuid { uu: uuid };
self.sdp.as_ref().unwrap().sdp_search(&mut addr.unwrap(), &uu) == BtStatus::Success
}
fn connect_all_enabled_profiles(&self, device: BluetoothDevice) -> bool {
// Profile init must be complete before this api is callable
if !self.profiles_ready {
return false;
}
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
warn!("Can't connect profiles on invalid address [{}]", &device.address);
return false;
}
// Check all remote uuids to see if they match enabled profiles and connect them.
let uuids = self.get_remote_uuids(device.clone());
for uuid in uuids.iter() {
match self.uuid_helper.is_known_profile(uuid) {
Some(p) => {
if self.uuid_helper.is_profile_enabled(&p) {
match p {
Profile::Hid | Profile::Hogp => {
self.hh.as_ref().unwrap().connect(&mut addr.unwrap());
}
Profile::A2dpSink | Profile::A2dpSource => {
let txl = self.tx.clone();
let address = device.address.clone();
topstack::get_runtime().spawn(async move {
let _ = txl
.send(Message::Media(MediaActions::Connect(address)))
.await;
});
}
// We don't connect most profiles
_ => (),
}
}
}
_ => {}
}
}
return true;
}
fn disconnect_all_enabled_profiles(&self, device: BluetoothDevice) -> bool {
if !self.profiles_ready {
return false;
}
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
warn!("Can't connect profiles on invalid address [{}]", &device.address);
return false;
}
let uuids = self.get_remote_uuids(device.clone());
for uuid in uuids.iter() {
match self.uuid_helper.is_known_profile(uuid) {
Some(p) => {
if self.uuid_helper.is_profile_enabled(&p) {
match p {
Profile::Hid | Profile::Hogp => {
self.hh.as_ref().unwrap().disconnect(&mut addr.unwrap());
}
Profile::A2dpSink | Profile::A2dpSource => {
let txl = self.tx.clone();
let address = device.address.clone();
topstack::get_runtime().spawn(async move {
let _ = txl
.send(Message::Media(MediaActions::Disconnect(address)))
.await;
});
}
// We don't connect most profiles
_ => (),
}
}
}
_ => {}
}
}
return true;
}
}
impl BtifSdpCallbacks for Bluetooth {
fn sdp_search(
&mut self,
status: BtStatus,
address: RawAddress,
uuid: Uuid,
_count: i32,
_records: Vec<BtSdpRecord>,
) {
debug!(
"Sdp search result found: Status({:?}) Address({:?}) Uuid({:?})",
status, address, uuid
);
}
}