blob: a62ec4ca5497c8956f0c2c4dee9a020581143fdc [file] [log] [blame]
//! Anything related to the adapter API (IBluetooth).
use bt_topshim::btif::{
BaseCallbacks, BaseCallbacksDispatcher, BluetoothInterface, BluetoothProperty, BtAclState,
BtBondState, BtConnectionDirection, BtConnectionState, BtDeviceType, BtDiscMode,
BtDiscoveryState, BtHciErrorCode, BtPinCode, BtPropertyType, BtScanMode, BtSspVariant, BtState,
BtStatus, BtThreadEvent, BtTransport, BtVendorProductInfo, DisplayAddress, RawAddress,
ToggleableProfile, Uuid, Uuid128Bit,
};
use bt_topshim::{
metrics,
profiles::gatt::GattStatus,
profiles::hid_host::{
BthhConnectionState, BthhHidInfo, BthhProtocolMode, BthhReportType, BthhStatus,
HHCallbacks, HHCallbacksDispatcher, HidHost,
},
profiles::sdp::{BtSdpRecord, Sdp, SdpCallbacks, SdpCallbacksDispatcher},
profiles::ProfileConnectionState,
topstack,
};
use bt_utils::array_utils;
use bt_utils::cod::{is_cod_hid_combo, is_cod_hid_keyboard};
use btif_macros::{btif_callback, btif_callbacks_dispatcher};
use log::{debug, warn};
use num_traits::cast::ToPrimitive;
use num_traits::pow;
use std::collections::HashMap;
use std::convert::TryInto;
use std::fs::File;
use std::hash::Hash;
use std::io::Write;
use std::process;
use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;
use std::time::Instant;
use tokio::sync::mpsc::Sender;
use tokio::task::JoinHandle;
use tokio::time;
use crate::battery_service::BatteryServiceActions;
use crate::bluetooth_admin::{BluetoothAdmin, IBluetoothAdmin};
use crate::bluetooth_gatt::{BluetoothGatt, IBluetoothGatt, IScannerCallback, ScanResult};
use crate::bluetooth_media::{BluetoothMedia, IBluetoothMedia, MediaActions};
use crate::callbacks::Callbacks;
use crate::uuid::{Profile, UuidHelper, HOGP};
use crate::{Message, RPCProxy, SuspendMode};
pub(crate) const FLOSS_VER: u16 = 0x0001;
const DEFAULT_DISCOVERY_TIMEOUT_MS: u64 = 12800;
const MIN_ADV_INSTANCES_FOR_MULTI_ADV: u8 = 5;
/// Devices that were last seen longer than this duration are considered stale
/// if they haven't already bonded or connected. Once this duration expires, the
/// clear event should be sent to clients.
const FOUND_DEVICE_FRESHNESS: Duration = Duration::from_secs(30);
/// This is the value returned from Bluetooth Interface calls.
// TODO(241930383): Add enum to topshim
const BTM_SUCCESS: i32 = 0;
const PID_DIR: &str = "/var/run/bluetooth";
/// 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>) -> u32;
/// Removes registered callback.
fn unregister_callback(&mut self, callback_id: u32) -> bool;
/// 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;
/// Inits the bluetooth interface. Should always be called before enable.
fn init(&mut self, init_flags: Vec<String>) -> 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;
/// Cleans up the bluetooth interface. Should always be called after disable.
fn cleanup(&mut self);
/// 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;
/// Gets the bluetooth class.
fn get_bluetooth_class(&self) -> u32;
/// Sets the bluetooth class.
fn set_bluetooth_class(&self, cod: u32) -> bool;
/// Returns whether the adapter is discoverable.
fn get_discoverable(&self) -> bool;
/// Returns the adapter discoverable timeout.
fn get_discoverable_timeout(&self) -> u32;
/// Sets discoverability. If discoverable, limits the duration with given value.
fn set_discoverable(&mut self, mode: BtDiscMode, duration: u32) -> bool;
/// Returns whether multi-advertisement is supported.
/// A minimum number of 5 advertising instances is required for multi-advertisment support.
fn is_multi_advertisement_supported(&self) -> bool;
/// Returns whether LE extended advertising is supported.
fn is_le_extended_advertising_supported(&self) -> bool;
/// Starts BREDR Inquiry.
fn start_discovery(&mut self) -> bool;
/// Cancels BREDR Inquiry.
fn cancel_discovery(&mut 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(&mut 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) -> BtBondState;
/// Set pin on bonding device.
fn set_pin(&self, device: BluetoothDevice, accept: bool, pin_code: Vec<u8>) -> bool;
/// Set passkey on bonding device.
fn set_passkey(&self, device: BluetoothDevice, accept: bool, 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 name of the remote device.
fn get_remote_name(&self, device: BluetoothDevice) -> String;
/// Gets the type of the remote device.
fn get_remote_type(&self, device: BluetoothDevice) -> BtDeviceType;
/// Gets the alias of the remote device.
fn get_remote_alias(&self, device: BluetoothDevice) -> String;
/// Sets the alias of the remote device.
fn set_remote_alias(&mut self, device: BluetoothDevice, new_alias: String);
/// Gets the class of the remote device.
fn get_remote_class(&self, device: BluetoothDevice) -> u32;
/// Gets the appearance of the remote device.
fn get_remote_appearance(&self, device: BluetoothDevice) -> u16;
/// Gets whether the remote device is connected.
fn get_remote_connected(&self, device: BluetoothDevice) -> bool;
/// Gets whether the remote device can wake the system.
fn get_remote_wake_allowed(&self, device: BluetoothDevice) -> bool;
/// Gets the vendor and product information of the remote device.
fn get_remote_vendor_product_info(&self, device: BluetoothDevice) -> BtVendorProductInfo;
/// Returns a list of connected devices.
fn get_connected_devices(&self) -> Vec<BluetoothDevice>;
/// Gets the connection state of a single device.
fn get_connection_state(&self, device: BluetoothDevice) -> BtConnectionState;
/// Gets the connection state of a specific profile.
fn get_profile_connection_state(&self, profile: Uuid128Bit) -> ProfileConnectionState;
/// 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;
/// Creates a new SDP record.
fn create_sdp_record(&mut self, sdp_record: BtSdpRecord) -> bool;
/// Removes the SDP record associated with the provided handle.
fn remove_sdp_record(&self, handle: i32) -> bool;
/// Connect all profiles supported by device and enabled on adapter.
fn connect_all_enabled_profiles(&mut self, device: BluetoothDevice) -> bool;
/// Disconnect all profiles supported by device and enabled on adapter.
fn disconnect_all_enabled_profiles(&mut self, device: BluetoothDevice) -> bool;
/// Returns whether WBS is supported.
fn is_wbs_supported(&self) -> bool;
/// Returns whether SWB is supported.
fn is_swb_supported(&self) -> bool;
}
/// Adapter API for Bluetooth qualification and verification.
///
/// This interface is provided for testing and debugging.
/// Clients should not use this interface for production.
pub trait IBluetoothQALegacy {
/// Returns whether the adapter is connectable.
fn get_connectable(&self) -> bool;
/// Sets connectability. Returns true on success, false otherwise.
fn set_connectable(&mut self, mode: bool) -> bool;
/// Returns the adapter's Bluetooth friendly name.
fn get_alias(&self) -> String;
/// Returns the adapter's Device ID information in modalias format
/// used by the kernel and udev.
fn get_modalias(&self) -> String;
/// Gets HID report on the peer.
fn get_hid_report(
&mut self,
addr: String,
report_type: BthhReportType,
report_id: u8,
) -> BtStatus;
/// Sets HID report to the peer.
fn set_hid_report(
&mut self,
addr: String,
report_type: BthhReportType,
report: String,
) -> BtStatus;
/// Snd HID data report to the peer.
fn send_hid_data(&mut self, addr: String, data: String) -> BtStatus;
}
/// Delayed actions from adapter events.
pub enum DelayedActions {
/// Check whether the current set of found devices are still fresh.
DeviceFreshnessCheck,
/// Connect to all supported profiles on target device.
ConnectAllProfiles(BluetoothDevice),
/// Scanner for BLE discovery is registered with given status and scanner id.
BleDiscoveryScannerRegistered(Uuid128Bit, u8, GattStatus),
/// Scanner for BLE discovery is reporting a result.
BleDiscoveryScannerResult(ScanResult),
}
/// Serializable device used in various apis.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
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 {
/// Transport type reported by ACL connection (if completed).
pub acl_reported_transport: BtTransport,
pub acl_state: BtAclState,
pub bond_state: BtBondState,
pub info: BluetoothDevice,
pub last_seen: Instant,
pub properties: HashMap<BtPropertyType, BluetoothProperty>,
/// Keep track of whether services have been resolved.
pub services_resolved: bool,
/// If supported UUIDs weren't available in EIR, wait for services to be
/// resolved to connect.
pub wait_to_connect: bool,
}
impl BluetoothDeviceContext {
pub(crate) fn new(
bond_state: BtBondState,
acl_state: BtAclState,
info: BluetoothDevice,
last_seen: Instant,
properties: Vec<BluetoothProperty>,
) -> BluetoothDeviceContext {
let mut device = BluetoothDeviceContext {
acl_reported_transport: BtTransport::Auto,
acl_state,
bond_state,
info,
last_seen,
properties: HashMap::new(),
services_resolved: false,
wait_to_connect: false,
};
device.update_properties(&properties);
device
}
pub(crate) fn update_properties(&mut self, in_properties: &Vec<BluetoothProperty>) {
for prop in in_properties {
// Handle merging of certain properties.
match &prop {
BluetoothProperty::BdAddr(bdaddr) => {
self.info.address = bdaddr.to_string();
self.properties.insert(prop.get_type(), prop.clone());
}
BluetoothProperty::BdName(bdname) => {
if !bdname.is_empty() {
self.info.name = bdname.clone();
self.properties.insert(prop.get_type(), prop.clone());
}
}
_ => {
self.properties.insert(prop.get_type(), prop.clone());
}
}
}
}
/// Mark this device as seen.
pub(crate) fn seen(&mut self) {
self.last_seen = Instant::now();
}
}
/// Structure to track all the signals for SIGTERM.
pub struct SigData {
pub enabled: Mutex<bool>,
pub enabled_notify: Condvar,
pub thread_attached: Mutex<bool>,
pub thread_notify: Condvar,
}
/// The interface for adapter callbacks registered through `IBluetooth::register_callback`.
pub trait IBluetoothCallback: RPCProxy {
/// When any adapter property changes.
fn on_adapter_property_changed(&mut self, prop: BtPropertyType);
/// When any device properties change.
fn on_device_properties_changed(
&mut self,
remote_device: BluetoothDevice,
props: Vec<BtPropertyType>,
);
/// When any of the adapter local address is changed.
fn on_address_changed(&mut self, addr: String);
/// When the adapter name is changed.
fn on_name_changed(&mut self, name: String);
/// When the adapter's discoverable mode is changed.
fn on_discoverable_changed(&mut self, discoverable: bool);
/// When a device is found via discovery.
fn on_device_found(&mut self, remote_device: BluetoothDevice);
/// When a device is cleared from discovered devices cache.
fn on_device_cleared(&mut self, remote_device: BluetoothDevice);
/// When the discovery state is changed.
fn on_discovering_changed(&mut self, discovering: bool);
/// When there is a pairing/bonding process and requires agent to display the event to UI.
fn on_ssp_request(
&mut self,
remote_device: BluetoothDevice,
cod: u32,
variant: BtSspVariant,
passkey: u32,
);
/// When there is a pin request to display the event to client.
fn on_pin_request(&mut self, remote_device: BluetoothDevice, cod: u32, min_16_digit: bool);
/// When there is a auto-gen pin to display the event to client.
fn on_pin_display(&mut self, remote_device: BluetoothDevice, pincode: String);
/// When a bonding attempt has completed.
fn on_bond_state_changed(&mut self, status: u32, device_address: String, state: u32);
/// When an SDP search has completed.
fn on_sdp_search_complete(
&mut self,
remote_device: BluetoothDevice,
searched_uuid: Uuid128Bit,
sdp_records: Vec<BtSdpRecord>,
);
/// When an SDP record has been successfully created.
fn on_sdp_record_created(&mut self, record: BtSdpRecord, handle: i32);
}
/// An interface for other modules to track found remote devices.
pub trait IBluetoothDeviceCallback {
/// When a device is found via discovery.
fn on_device_found(&mut self, remote_device: BluetoothDevice);
/// When a device is cleared from discovered devices cache.
fn on_device_cleared(&mut self, remote_device: BluetoothDevice);
/// When a device property is changed.
fn on_remote_device_properties_changed(
&mut self,
remote_device: BluetoothDevice,
properties: Vec<BluetoothProperty>,
);
}
pub trait IBluetoothConnectionCallback: RPCProxy {
/// Notification sent when a remote device completes HCI connection.
fn on_device_connected(&mut self, remote_device: BluetoothDevice);
/// Notification sent when a remote device completes HCI disconnection.
fn on_device_disconnected(&mut self, remote_device: BluetoothDevice);
}
/// Implementation of the adapter API.
pub struct Bluetooth {
intf: Arc<Mutex<BluetoothInterface>>,
adapter_index: i32,
hci_index: i32,
bonded_devices: HashMap<String, BluetoothDeviceContext>,
ble_scanner_id: Option<u8>,
ble_scanner_uuid: Option<Uuid128Bit>,
bluetooth_admin: Arc<Mutex<Box<BluetoothAdmin>>>,
bluetooth_gatt: Arc<Mutex<Box<BluetoothGatt>>>,
bluetooth_media: Arc<Mutex<Box<BluetoothMedia>>>,
callbacks: Callbacks<dyn IBluetoothCallback + Send>,
connection_callbacks: Callbacks<dyn IBluetoothConnectionCallback + Send>,
discovering_started: Instant,
hh: Option<HidHost>,
is_connectable: bool,
is_discovering: bool,
is_discovering_before_suspend: bool,
is_discovery_paused: bool,
discovery_suspend_mode: SuspendMode,
local_address: Option<RawAddress>,
pending_discovery: bool,
properties: HashMap<BtPropertyType, BluetoothProperty>,
profiles_ready: bool,
found_devices: HashMap<String, BluetoothDeviceContext>,
freshness_check: Option<JoinHandle<()>>,
sdp: Option<Sdp>,
state: BtState,
tx: Sender<Message>,
// Internal API members
discoverable_timeout: Option<JoinHandle<()>>,
/// Used to notify signal handler that we have turned off the stack.
sig_notifier: Arc<SigData>,
}
impl Bluetooth {
/// Constructs the IBluetooth implementation.
pub fn new(
adapter_index: i32,
hci_index: i32,
tx: Sender<Message>,
sig_notifier: Arc<SigData>,
intf: Arc<Mutex<BluetoothInterface>>,
bluetooth_admin: Arc<Mutex<Box<BluetoothAdmin>>>,
bluetooth_gatt: Arc<Mutex<Box<BluetoothGatt>>>,
bluetooth_media: Arc<Mutex<Box<BluetoothMedia>>>,
) -> Bluetooth {
Bluetooth {
adapter_index,
hci_index,
bonded_devices: HashMap::new(),
callbacks: Callbacks::new(tx.clone(), Message::AdapterCallbackDisconnected),
connection_callbacks: Callbacks::new(
tx.clone(),
Message::ConnectionCallbackDisconnected,
),
hh: None,
ble_scanner_id: None,
ble_scanner_uuid: None,
bluetooth_admin,
bluetooth_gatt,
bluetooth_media,
discovering_started: Instant::now(),
intf,
is_connectable: false,
is_discovering: false,
is_discovering_before_suspend: false,
is_discovery_paused: false,
discovery_suspend_mode: SuspendMode::Normal,
local_address: None,
pending_discovery: false,
properties: HashMap::new(),
profiles_ready: false,
found_devices: HashMap::new(),
freshness_check: None,
sdp: None,
state: BtState::Off,
tx,
// Internal API members
discoverable_timeout: None,
sig_notifier,
}
}
fn disable_profile(&mut self, profile: &Profile) {
if !UuidHelper::is_profile_supported(profile) {
return;
}
match profile {
Profile::Hid => {
self.hh.as_mut().unwrap().activate_hidp(false);
}
Profile::Hogp => {
self.hh.as_mut().unwrap().activate_hogp(false);
}
Profile::A2dpSource | Profile::Hfp | Profile::AvrcpTarget => {
self.bluetooth_media.lock().unwrap().disable_profile(profile);
}
// Ignore profiles that we don't connect.
_ => (),
}
}
fn enable_profile(&mut self, profile: &Profile) {
if !UuidHelper::is_profile_supported(profile) {
return;
}
match profile {
Profile::Hid => {
self.hh.as_mut().unwrap().activate_hidp(true);
}
Profile::Hogp => {
self.hh.as_mut().unwrap().activate_hogp(true);
}
Profile::A2dpSource | Profile::Hfp | Profile::AvrcpTarget => {
self.bluetooth_media.lock().unwrap().enable_profile(profile);
}
// Ignore profiles that we don't connect.
_ => (),
}
}
fn is_profile_enabled(&self, profile: &Profile) -> Option<bool> {
if !UuidHelper::is_profile_supported(profile) {
return None;
}
match profile {
Profile::Hid => Some(self.hh.as_ref().unwrap().is_hidp_activated),
Profile::Hogp => Some(self.hh.as_ref().unwrap().is_hogp_activated),
Profile::A2dpSource | Profile::Hfp | Profile::AvrcpTarget => {
self.bluetooth_media.lock().unwrap().is_profile_enabled(profile)
}
// Ignore profiles that we don't connect.
_ => None,
}
}
pub(crate) fn get_hci_index(&self) -> u16 {
self.hci_index as u16
}
pub fn toggle_enabled_profiles(&mut self, allowed_services: &Vec<Uuid128Bit>) {
for profile in UuidHelper::get_supported_profiles().clone() {
// Only toggle initializable profiles.
if let Some(enabled) = self.is_profile_enabled(&profile) {
let allowed = allowed_services.len() == 0
|| allowed_services.contains(&UuidHelper::get_profile_uuid(&profile).unwrap());
if allowed && !enabled {
debug!("Enabling profile {}", &profile);
self.enable_profile(&profile);
} else if !allowed && enabled {
debug!("Disabling profile {}", &profile);
self.disable_profile(&profile);
}
}
}
if self.hh.as_mut().unwrap().configure_enabled_profiles() {
self.hh.as_mut().unwrap().disable();
let txl = self.tx.clone();
tokio::spawn(async move {
// Wait 100 milliseconds to prevent race condition caused by quick disable then
// enable.
// TODO: (b/272191117): don't enable until we're sure disable is done.
tokio::time::sleep(Duration::from_millis(100)).await;
let _ = txl.send(Message::HidHostEnable).await;
});
}
}
pub fn enable_hidhost(&mut self) {
self.hh.as_mut().unwrap().enable();
}
pub fn init_profiles(&mut self) {
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;
});
}),
});
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 allowed_profiles = self.bluetooth_admin.lock().unwrap().get_allowed_services();
self.toggle_enabled_profiles(&allowed_profiles);
// Mark profiles as ready
self.profiles_ready = true;
}
fn update_local_address(&mut self, addr: &RawAddress) {
self.local_address = Some(addr.clone());
self.callbacks.for_all_callbacks(|callback| {
callback.on_address_changed(addr.to_string());
});
}
pub(crate) fn adapter_callback_disconnected(&mut self, id: u32) {
self.callbacks.remove_callback(id);
}
pub(crate) fn connection_callback_disconnected(&mut self, id: u32) {
self.connection_callbacks.remove_callback(id);
}
fn get_remote_device_if_found(&self, address: &str) -> Option<&BluetoothDeviceContext> {
self.bonded_devices.get(address).or_else(|| self.found_devices.get(address))
}
fn get_remote_device_if_found_mut(
&mut self,
address: &str,
) -> Option<&mut BluetoothDeviceContext> {
match self.bonded_devices.get_mut(address) {
None => self.found_devices.get_mut(address),
some => some,
}
}
fn get_remote_device_info_if_found(&self, remote_address: &str) -> Option<BluetoothDevice> {
self.get_remote_device_if_found(remote_address)
.map(|device_context| device_context.info.clone())
}
fn get_remote_device_property(
&self,
device: &BluetoothDevice,
property_type: &BtPropertyType,
) -> Option<BluetoothProperty> {
self.get_remote_device_if_found(&device.address)
.and_then(|d| d.properties.get(property_type).and_then(|p| Some(p.clone())))
}
fn set_remote_device_property(
&mut self,
device: &BluetoothDevice,
property_type: BtPropertyType,
property: BluetoothProperty,
) -> Result<(), ()> {
let remote_device = match self.get_remote_device_if_found_mut(&device.address) {
Some(d) => d,
None => {
return Err(());
}
};
let mut addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
return Err(());
}
let addr = addr.as_mut().unwrap();
// TODO: Determine why a callback isn't invoked to do this.
remote_device.properties.insert(property_type, property.clone());
self.intf.lock().unwrap().set_remote_device_property(addr, property);
Ok(())
}
/// Returns whether the adapter is connectable.
pub(crate) fn get_connectable_internal(&self) -> bool {
match self.properties.get(&BtPropertyType::AdapterScanMode) {
Some(prop) => match prop {
BluetoothProperty::AdapterScanMode(mode) => match *mode {
BtScanMode::Connectable | BtScanMode::ConnectableDiscoverable => true,
_ => false,
},
_ => false,
},
_ => false,
}
}
/// Sets the adapter's connectable mode for classic connections.
pub(crate) fn set_connectable_internal(&mut self, mode: bool) -> bool {
self.is_connectable = mode;
if mode && self.get_discoverable() {
return true;
}
self.intf.lock().unwrap().set_adapter_property(BluetoothProperty::AdapterScanMode(
if mode { BtScanMode::Connectable } else { BtScanMode::None_ },
)) == 0
}
/// Returns adapter's discoverable mode.
pub fn get_discoverable_mode_internal(&self) -> BtDiscMode {
let off_mode = BtDiscMode::NonDiscoverable;
match self.properties.get(&BtPropertyType::AdapterScanMode) {
Some(prop) => match prop {
BluetoothProperty::AdapterScanMode(mode) => match *mode {
BtScanMode::ConnectableDiscoverable => BtDiscMode::GeneralDiscoverable,
BtScanMode::ConnectableLimitedDiscoverable => BtDiscMode::LimitedDiscoverable,
_ => off_mode,
},
_ => off_mode,
},
_ => off_mode,
}
}
/// Returns adapter's alias.
pub(crate) fn get_alias_internal(&self) -> String {
let name = self.get_name();
if !name.is_empty() {
return name;
}
// If the adapter name is empty, generate one based on local BDADDR
// so that test programs can have a friendly name for the adapter.
match self.local_address {
None => "floss_0000".to_string(),
Some(addr) => format!("floss_{:02X}{:02X}", addr.address[4], addr.address[5]),
}
}
pub(crate) fn get_hid_report_internal(
&mut self,
addr: String,
report_type: BthhReportType,
report_id: u8,
) -> BtStatus {
if let Some(mut addr) = RawAddress::from_string(addr) {
self.hh.as_mut().unwrap().get_report(&mut addr, report_type, report_id, 128)
} else {
BtStatus::InvalidParam
}
}
pub(crate) fn set_hid_report_internal(
&mut self,
addr: String,
report_type: BthhReportType,
report: String,
) -> BtStatus {
if let Some(mut addr) = RawAddress::from_string(addr) {
let mut rb = report.clone().into_bytes();
self.hh.as_mut().unwrap().set_report(&mut addr, report_type, rb.as_mut_slice())
} else {
BtStatus::InvalidParam
}
}
pub(crate) fn send_hid_data_internal(&mut self, addr: String, data: String) -> BtStatus {
if let Some(mut addr) = RawAddress::from_string(addr) {
let mut rb = data.clone().into_bytes();
self.hh.as_mut().unwrap().send_data(&mut addr, rb.as_mut_slice())
} else {
BtStatus::InvalidParam
}
}
/// Returns all bonded and connected devices.
pub(crate) fn get_bonded_and_connected_devices(&mut self) -> Vec<BluetoothDevice> {
self.bonded_devices
.values()
.filter(|v| v.acl_state == BtAclState::Connected && v.bond_state == BtBondState::Bonded)
.map(|v| v.info.clone())
.collect()
}
/// Gets the bond state of a single device with its address.
pub fn get_bond_state_by_addr(&self, addr: &String) -> BtBondState {
match self.bonded_devices.get(addr) {
Some(device) => device.bond_state.clone(),
None => BtBondState::NotBonded,
}
}
/// Check whether found devices are still fresh. If they're outside the
/// freshness window, send a notification to clear the device from clients.
fn trigger_freshness_check(&mut self) {
if let Some(ref handle) = self.freshness_check {
// Abort and drop the previous JoinHandle.
handle.abort();
self.freshness_check = None;
}
// A found device is considered fresh if:
// * It was last seen less than |FOUND_DEVICE_FRESHNESS| ago.
// * It is currently connected.
fn is_fresh(d: &BluetoothDeviceContext, now: &Instant) -> bool {
let fresh_at = d.last_seen + FOUND_DEVICE_FRESHNESS;
now < &fresh_at || d.acl_state == BtAclState::Connected
}
let now = Instant::now();
let stale_devices: Vec<BluetoothDevice> = self
.found_devices
.iter()
.filter(|(_, d)| !is_fresh(d, &now))
.map(|(_, d)| d.info.clone())
.collect();
// Retain only devices that are fresh.
self.found_devices.retain(|_, d| is_fresh(d, &now));
for d in stale_devices {
self.callbacks.for_all_callbacks(|callback| {
callback.on_device_cleared(d.clone());
});
self.bluetooth_admin.lock().unwrap().on_device_cleared(&d);
}
// If we have any fresh devices remaining, re-queue a freshness check.
if self.found_devices.len() > 0 {
let txl = self.tx.clone();
self.freshness_check = Some(tokio::spawn(async move {
time::sleep(FOUND_DEVICE_FRESHNESS).await;
let _ = txl
.send(Message::DelayedAdapterActions(DelayedActions::DeviceFreshnessCheck))
.await;
}));
}
}
/// Makes an LE_RAND call to the Bluetooth interface.
pub fn le_rand(&mut self) -> bool {
self.intf.lock().unwrap().le_rand() == BTM_SUCCESS
}
fn send_metrics_remote_device_info(device: &BluetoothDeviceContext) {
if device.bond_state != BtBondState::Bonded && device.acl_state != BtAclState::Connected {
return;
}
let addr = RawAddress::from_string(device.info.address.clone()).unwrap();
let mut class_of_device = 0u32;
let mut device_type = BtDeviceType::Unknown;
let mut appearance = 0u16;
let mut vpi =
BtVendorProductInfo { vendor_id_src: 0, vendor_id: 0, product_id: 0, version: 0 };
for prop in device.properties.values() {
match prop {
BluetoothProperty::TypeOfDevice(p) => device_type = p.clone(),
BluetoothProperty::ClassOfDevice(p) => class_of_device = p.clone(),
BluetoothProperty::Appearance(p) => appearance = p.clone(),
BluetoothProperty::VendorProductInfo(p) => vpi = p.clone(),
_ => (),
}
}
metrics::device_info_report(
addr,
device_type,
class_of_device,
appearance,
vpi.vendor_id,
vpi.vendor_id_src,
vpi.product_id,
vpi.version,
);
}
/// Handle some delayed and recurring actions within the adapter.
pub(crate) fn handle_delayed_actions(&mut self, action: DelayedActions) {
match action {
DelayedActions::DeviceFreshnessCheck => {
self.trigger_freshness_check();
}
DelayedActions::ConnectAllProfiles(device) => {
self.connect_all_enabled_profiles(device);
}
DelayedActions::BleDiscoveryScannerRegistered(uuid, scanner_id, status) => {
if let Some(app_uuid) = self.ble_scanner_uuid {
if app_uuid == uuid {
if status == GattStatus::Success {
self.ble_scanner_id = Some(scanner_id);
} else {
log::error!("BLE discovery scanner failed to register: {:?}", status);
}
}
}
}
DelayedActions::BleDiscoveryScannerResult(result) => {
let addr = RawAddress::from_string(result.address);
let properties = match addr {
Some(v) => {
let mut props = vec![];
props.push(BluetoothProperty::BdName(result.name.clone()));
props.push(BluetoothProperty::BdAddr(v.clone()));
if result.service_uuids.len() > 0 {
props.push(BluetoothProperty::Uuids(
result
.service_uuids
.iter()
.map(|&v| Uuid::from(v.clone()))
.collect(),
));
}
props.push(BluetoothProperty::RemoteRssi(result.rssi));
props
}
None => {
return;
}
};
// Generate a vector of properties from ScanResult.
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);
existing.seen();
} else {
let device_with_props = BluetoothDeviceContext::new(
BtBondState::NotBonded,
BtAclState::Disconnected,
device,
Instant::now(),
properties,
);
self.found_devices.insert(address.clone(), device_with_props);
}
}
}
}
/// Creates a file to notify btmanagerd the adapter is enabled.
fn create_pid_file(&self) -> std::io::Result<()> {
let file_name = format!("{}/bluetooth{}.pid", PID_DIR, self.adapter_index);
let mut f = File::create(&file_name)?;
f.write_all(process::id().to_string().as_bytes())?;
Ok(())
}
/// Removes the file to notify btmanagerd the adapter is disabled.
fn remove_pid_file(&self) -> std::io::Result<()> {
let file_name = format!("{}/bluetooth{}.pid", PID_DIR, self.adapter_index);
std::fs::remove_file(&file_name)?;
Ok(())
}
/// Set the suspend mode.
pub fn set_discovery_suspend_mode(&mut self, suspend_mode: SuspendMode) {
if suspend_mode != self.discovery_suspend_mode {
self.discovery_suspend_mode = suspend_mode;
}
}
/// Gets current suspend mode.
pub fn get_discovery_suspend_mode(&self) -> SuspendMode {
self.discovery_suspend_mode.clone()
}
/// Enters the suspend mode for discovery.
pub fn discovery_enter_suspend(&mut self) -> BtStatus {
if self.get_discovery_suspend_mode() != SuspendMode::Normal {
return BtStatus::Busy;
}
self.set_discovery_suspend_mode(SuspendMode::Suspending);
if self.is_discovering {
self.is_discovering_before_suspend = true;
self.cancel_discovery();
}
self.set_discovery_suspend_mode(SuspendMode::Suspended);
return BtStatus::Success;
}
/// Exits the suspend mode for discovery.
pub fn discovery_exit_suspend(&mut self) -> BtStatus {
if self.get_discovery_suspend_mode() != SuspendMode::Suspended {
return BtStatus::Busy;
}
self.set_discovery_suspend_mode(SuspendMode::Resuming);
if self.is_discovering_before_suspend {
self.is_discovering_before_suspend = false;
self.start_discovery();
}
self.set_discovery_suspend_mode(SuspendMode::Normal);
return BtStatus::Success;
}
/// Temporarily stop the discovery process and mark it as paused so that clients cannot restart
/// it.
fn pause_discovery(&mut self) {
self.cancel_discovery();
self.is_discovery_paused = true;
}
/// Remove the paused flag to allow clients to begin discovery, and if there is already a
/// pending request, start discovery.
fn resume_discovery(&mut self) {
self.is_discovery_paused = false;
if self.pending_discovery {
self.pending_discovery = false;
self.start_discovery();
}
}
}
#[btif_callbacks_dispatcher(dispatch_base_callbacks, BaseCallbacks)]
#[allow(unused_variables)]
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,
conn_direction: BtConnectionDirection,
acl_handle: u16,
) {
}
#[btif_callback(LeRandCallback)]
fn le_rand_cb(&mut self, random: u64) {}
#[btif_callback(PinRequest)]
fn pin_request(
&mut self,
remote_addr: RawAddress,
remote_name: String,
cod: u32,
min_16_digit: bool,
) {
}
#[btif_callback(ThreadEvent)]
fn thread_event(&mut self, event: BtThreadEvent) {}
}
#[btif_callbacks_dispatcher(dispatch_hid_host_callbacks, HHCallbacks)]
pub(crate) trait BtifHHCallbacks {
#[btif_callback(ConnectionState)]
fn connection_state(&mut self, address: RawAddress, state: BthhConnectionState);
#[btif_callback(HidInfo)]
fn hid_info(&mut self, address: RawAddress, info: BthhHidInfo);
#[btif_callback(ProtocolMode)]
fn protocol_mode(&mut self, address: RawAddress, status: BthhStatus, mode: BthhProtocolMode);
#[btif_callback(IdleTime)]
fn idle_time(&mut self, address: RawAddress, status: BthhStatus, idle_rate: i32);
#[btif_callback(GetReport)]
fn get_report(&mut self, address: RawAddress, status: BthhStatus, data: Vec<u8>, size: i32);
#[btif_callback(Handshake)]
fn handshake(&mut self, address: RawAddress, status: BthhStatus);
}
#[btif_callbacks_dispatcher(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;
metrics::adapter_state_changed(self.state.clone());
// If it's the same state as before, no further action
if self.state == prev_state {
return;
}
match self.state {
BtState::Off => {
self.properties.clear();
match self.remove_pid_file() {
Err(err) => warn!("remove_pid_file() error: {}", err),
_ => (),
}
// Let the signal notifier know we are turned off.
*self.sig_notifier.enabled.lock().unwrap() = false;
self.sig_notifier.enabled_notify.notify_all();
}
BtState::On => {
// Initialize media
self.bluetooth_media.lock().unwrap().initialize();
// Trigger properties update
self.intf.lock().unwrap().get_adapter_properties();
// Also need to manually request some properties
self.intf.lock().unwrap().get_adapter_property(BtPropertyType::ClassOfDevice);
// Initialize the BLE scanner for discovery.
let callback_id = self.bluetooth_gatt.lock().unwrap().register_scanner_callback(
Box::new(BleDiscoveryCallbacks::new(self.tx.clone())),
);
self.ble_scanner_uuid =
Some(self.bluetooth_gatt.lock().unwrap().register_scanner(callback_id));
// Ensure device is connectable so that disconnected device can reconnect
self.set_connectable(true);
// Notify the signal notifier that we are turned on.
*self.sig_notifier.enabled.lock().unwrap() = true;
self.sig_notifier.enabled_notify.notify_all();
// Signal that the stack is up and running.
match self.create_pid_file() {
Err(err) => warn!("create_pid_file() error: {}", err),
_ => (),
}
// Inform the rest of the stack we're ready.
let txl = self.tx.clone();
tokio::spawn(async move {
let _ = txl.send(Message::AdapterReady).await;
});
}
}
}
#[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 {
self.properties.insert(prop.get_type(), prop.clone());
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()),
Instant::now(),
vec![],
));
}
}
BluetoothProperty::BdName(bdname) => {
self.callbacks.for_all_callbacks(|callback| {
callback.on_name_changed(bdname.clone());
});
}
BluetoothProperty::AdapterScanMode(mode) => {
self.callbacks.for_all_callbacks(|callback| {
callback
.on_discoverable_changed(*mode == BtScanMode::ConnectableDiscoverable);
});
}
_ => {}
}
self.callbacks.for_all_callbacks(|callback| {
callback.on_adapter_property_changed(prop.get_type());
});
}
}
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);
existing.seen();
} else {
let device_with_props = BluetoothDeviceContext::new(
BtBondState::NotBonded,
BtAclState::Disconnected,
device,
Instant::now(),
properties,
);
self.found_devices.insert(address.clone(), device_with_props);
}
let device = self.found_devices.get(&address).unwrap();
self.callbacks.for_all_callbacks(|callback| {
callback.on_device_found(device.info.clone());
});
self.bluetooth_admin.lock().unwrap().on_device_found(&device.info);
}
fn discovery_state(&mut self, state: BtDiscoveryState) {
let is_discovering = &state == &BtDiscoveryState::Started;
// No-op if we're updating the state to the same value again.
if &is_discovering == &self.is_discovering {
return;
}
// Cache discovering state
self.is_discovering = &state == &BtDiscoveryState::Started;
if self.is_discovering {
self.discovering_started = Instant::now();
}
// Prevent sending out discovering changes or freshness checks when
// suspending. Clients don't need to be notified of discovery pausing
// during suspend. They will probably try to restore it and fail.
let discovery_suspend_mode = self.get_discovery_suspend_mode();
if discovery_suspend_mode != SuspendMode::Normal
&& discovery_suspend_mode != SuspendMode::Resuming
{
return;
}
self.callbacks.for_all_callbacks(|callback| {
callback.on_discovering_changed(state == BtDiscoveryState::Started);
});
// Stopped discovering and no freshness check is active. Immediately do
// freshness check which will schedule a recurring future until all
// entries are cleared.
if !is_discovering && self.freshness_check.is_none() {
self.trigger_freshness_check();
}
// Start or stop BLE scanning based on discovering state
if let Some(scanner_id) = self.ble_scanner_id {
if is_discovering {
self.bluetooth_gatt.lock().unwrap().start_active_scan(scanner_id);
} else {
self.bluetooth_gatt.lock().unwrap().stop_active_scan(scanner_id);
}
}
}
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(b/274706838): We need a way to select the default agent.
self.callbacks.for_all_callbacks(|callback| {
callback.on_ssp_request(
BluetoothDevice::new(remote_addr.to_string(), remote_name.clone()),
cod,
variant.clone(),
passkey,
);
});
}
fn pin_request(
&mut self,
remote_addr: RawAddress,
remote_name: String,
cod: u32,
min_16_digit: bool,
) {
let device = BluetoothDevice::new(remote_addr.to_string(), remote_name.clone());
let digits = match min_16_digit {
true => 16,
false => 6,
};
if is_cod_hid_keyboard(cod) || is_cod_hid_combo(cod) {
debug!("auto gen pin for device {} (cod={:#x})", DisplayAddress(&remote_addr), cod);
// generate a random pin code to display.
let pin = rand::random::<u64>() % pow(10, digits);
let display_pin = format!("{:06}", pin);
// Currently this supports many agent because we accept many callbacks.
// TODO(b/274706838): We need a way to select the default agent.
self.callbacks.for_all_callbacks(|callback| {
callback.on_pin_display(device.clone(), display_pin.clone());
});
let pin_vec = display_pin.chars().map(|d| d.try_into().unwrap()).collect::<Vec<u8>>();
self.set_pin(device, true, pin_vec);
} else {
debug!(
"sending pin request for device {} (cod={:#x}) to clients",
DisplayAddress(&remote_addr),
cod
);
// Currently this supports many agent because we accept many callbacks.
// TODO(b/274706838): We need a way to select the default agent.
self.callbacks.for_all_callbacks(|callback| {
callback.on_pin_request(device.clone(), cod, min_16_digit);
});
}
}
fn bond_state(
&mut self,
status: BtStatus,
addr: RawAddress,
bond_state: BtBondState,
fail_reason: i32,
) {
let address = addr.to_string();
// Get the device type before the device is potentially deleted.
let device_type =
self.get_remote_type(BluetoothDevice::new(address.clone(), "".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. Immediately insert that into the bonded list.
let mut 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()),
Instant::now(),
vec![],
),
};
let device_info = device.info.clone();
// Since this is a newly bonded device, we also need to trigger SDP
// on it.
device.services_resolved = false;
self.bonded_devices.insert(address.clone(), device);
self.fetch_remote_uuids(device_info);
} 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());
}
// Resume discovery once the bonding process is complete. Discovery was paused before the
// bond request to avoid ACL connection from interfering with active inquiry.
if &bond_state == &BtBondState::NotBonded || &bond_state == &BtBondState::Bonded {
self.resume_discovery();
}
// Send bond state changed notifications
self.callbacks.for_all_callbacks(|callback| {
callback.on_bond_state_changed(
status.to_u32().unwrap(),
address.clone(),
bond_state.to_u32().unwrap(),
);
});
metrics::bond_state_changed(addr, device_type, status, bond_state, fail_reason);
}
fn remote_device_properties_changed(
&mut self,
_status: BtStatus,
addr: RawAddress,
_num_properties: i32,
properties: Vec<BluetoothProperty>,
) {
let address = addr.to_string();
let txl = self.tx.clone();
let device = match self.get_remote_device_if_found_mut(&address) {
None => {
self.found_devices.insert(
address.clone(),
BluetoothDeviceContext::new(
BtBondState::NotBonded,
BtAclState::Disconnected,
BluetoothDevice::new(address.clone(), String::from("")),
Instant::now(),
vec![],
),
);
self.found_devices.get_mut(&address)
}
some => some,
};
match device {
Some(d) => {
d.update_properties(&properties);
d.seen();
Bluetooth::send_metrics_remote_device_info(d);
let info = d.info.clone();
if !d.services_resolved {
let has_uuids = properties.iter().any(|prop| match prop {
BluetoothProperty::Uuids(uu) => uu.len() > 0,
_ => false,
});
// Services are resolved when uuids are fetched.
d.services_resolved |= has_uuids;
}
if d.wait_to_connect && d.services_resolved {
d.wait_to_connect = false;
let sent_info = info.clone();
tokio::spawn(async move {
let _ = txl
.send(Message::DelayedAdapterActions(
DelayedActions::ConnectAllProfiles(sent_info),
))
.await;
});
}
let info = &d.info.clone();
self.callbacks.for_all_callbacks(|callback| {
callback.on_device_properties_changed(
info.clone(),
properties.clone().into_iter().map(|x| x.get_type()).collect(),
);
});
self.bluetooth_admin
.lock()
.unwrap()
.on_remote_device_properties_changed(&info, &properties);
}
None => (),
}
}
fn acl_state(
&mut self,
status: BtStatus,
addr: RawAddress,
state: BtAclState,
link_type: BtTransport,
hci_reason: BtHciErrorCode,
conn_direction: BtConnectionDirection,
_acl_handle: u16,
) {
// If discovery was previously paused at connect_all_enabled_profiles to avoid an outgoing
// ACL connection colliding with an ongoing inquiry, resume it.
self.resume_discovery();
if status != BtStatus::Success {
warn!(
"Connection to [{}] failed. Status: {:?}, Reason: {:?}",
DisplayAddress(&addr),
status,
hci_reason
);
metrics::acl_connection_state_changed(
addr,
link_type,
status,
BtAclState::Disconnected,
conn_direction,
hci_reason,
);
return;
}
let address = addr.to_string();
let device = match self.get_remote_device_if_found_mut(&address) {
None => {
self.found_devices.insert(
address.clone(),
BluetoothDeviceContext::new(
BtBondState::NotBonded,
BtAclState::Disconnected,
BluetoothDevice::new(address.clone(), String::from("")),
Instant::now(),
vec![],
),
);
self.found_devices.get_mut(&address)
}
some => some,
};
match device {
Some(found) => {
// Only notify if there's been a change in state
let prev_state = &found.acl_state;
if prev_state != &state {
let device = found.info.clone();
found.acl_state = state.clone();
found.acl_reported_transport = link_type;
metrics::acl_connection_state_changed(
addr,
link_type,
BtStatus::Success,
state.clone(),
conn_direction,
hci_reason,
);
match state {
BtAclState::Connected => {
let bluetooth_device = found.info.clone();
let acl_reported_transport = found.acl_reported_transport.clone();
Bluetooth::send_metrics_remote_device_info(found);
self.connection_callbacks.for_all_callbacks(|callback| {
callback.on_device_connected(device.clone());
});
let tx = self.tx.clone();
let transport = match self.get_remote_type(bluetooth_device.clone()) {
BtDeviceType::Bredr => BtTransport::Bredr,
BtDeviceType::Ble => BtTransport::Le,
_ => acl_reported_transport,
};
tokio::spawn(async move {
let _ = tx
.send(Message::OnAclConnected(bluetooth_device, transport))
.await;
});
}
BtAclState::Disconnected => {
self.connection_callbacks.for_all_callbacks(|callback| {
callback.on_device_disconnected(device.clone());
});
let tx = self.tx.clone();
tokio::spawn(async move {
let _ = tx.send(Message::OnAclDisconnected(device.clone())).await;
});
}
};
}
}
None => (),
};
}
fn thread_event(&mut self, event: BtThreadEvent) {
match event {
BtThreadEvent::Associate => {
// Let the signal notifier know stack is initialized.
*self.sig_notifier.thread_attached.lock().unwrap() = true;
self.sig_notifier.thread_notify.notify_all();
}
BtThreadEvent::Disassociate => {
// Let the signal notifier know stack is done.
*self.sig_notifier.thread_attached.lock().unwrap() = false;
self.sig_notifier.thread_notify.notify_all();
}
}
}
}
struct BleDiscoveryCallbacks {
tx: Sender<Message>,
}
impl BleDiscoveryCallbacks {
fn new(tx: Sender<Message>) -> Self {
Self { tx }
}
}
// Handle BLE scanner results.
impl IScannerCallback for BleDiscoveryCallbacks {
fn on_scanner_registered(&mut self, uuid: Uuid128Bit, scanner_id: u8, status: GattStatus) {
let tx = self.tx.clone();
tokio::spawn(async move {
let _ = tx
.send(Message::DelayedAdapterActions(
DelayedActions::BleDiscoveryScannerRegistered(uuid, scanner_id, status),
))
.await;
});
}
fn on_scan_result(&mut self, scan_result: ScanResult) {
let tx = self.tx.clone();
tokio::spawn(async move {
let _ = tx
.send(Message::DelayedAdapterActions(DelayedActions::BleDiscoveryScannerResult(
scan_result,
)))
.await;
});
}
fn on_advertisement_found(&mut self, _scanner_id: u8, _scan_result: ScanResult) {}
fn on_advertisement_lost(&mut self, _scanner_id: u8, _scan_result: ScanResult) {}
fn on_suspend_mode_change(&mut self, _suspend_mode: SuspendMode) {}
}
impl RPCProxy for BleDiscoveryCallbacks {
fn get_object_id(&self) -> String {
"BLE Discovery Callback".to_string()
}
}
// TODO: Add unit tests for this implementation
impl IBluetooth for Bluetooth {
fn register_callback(&mut self, callback: Box<dyn IBluetoothCallback + Send>) -> u32 {
self.callbacks.add_callback(callback)
}
fn unregister_callback(&mut self, callback_id: u32) -> bool {
self.callbacks.remove_callback(callback_id)
}
fn register_connection_callback(
&mut self,
callback: Box<dyn IBluetoothConnectionCallback + Send>,
) -> u32 {
self.connection_callbacks.add_callback(callback)
}
fn unregister_connection_callback(&mut self, callback_id: u32) -> bool {
self.connection_callbacks.remove_callback(callback_id)
}
fn init(&mut self, init_flags: Vec<String>) -> bool {
self.intf.lock().unwrap().initialize(get_bt_dispatcher(self.tx.clone()), init_flags)
}
fn enable(&mut self) -> bool {
self.intf.lock().unwrap().enable() == 0
}
fn disable(&mut self) -> bool {
self.intf.lock().unwrap().disable() == 0
}
fn cleanup(&mut self) {
self.intf.lock().unwrap().cleanup();
}
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 get_bluetooth_class(&self) -> u32 {
match self.properties.get(&BtPropertyType::ClassOfDevice) {
Some(prop) => match prop {
BluetoothProperty::ClassOfDevice(cod) => cod.clone(),
_ => 0,
},
_ => 0,
}
}
fn set_bluetooth_class(&self, cod: u32) -> bool {
self.intf.lock().unwrap().set_adapter_property(BluetoothProperty::ClassOfDevice(cod)) == 0
}
fn get_discoverable(&self) -> bool {
match self.properties.get(&BtPropertyType::AdapterScanMode) {
Some(prop) => match prop {
BluetoothProperty::AdapterScanMode(mode) => match mode {
BtScanMode::ConnectableDiscoverable => true,
_ => false,
},
_ => false,
},
_ => false,
}
}
fn get_discoverable_timeout(&self) -> u32 {
match self.properties.get(&BtPropertyType::AdapterDiscoverableTimeout) {
Some(prop) => match prop {
BluetoothProperty::AdapterDiscoverableTimeout(timeout) => timeout.clone(),
_ => 0,
},
_ => 0,
}
}
fn set_discoverable(&mut self, mode: BtDiscMode, duration: u32) -> bool {
let intf = self.intf.lock().unwrap();
// Checks if the duration is valid.
if mode == BtDiscMode::LimitedDiscoverable && (duration > 60 || duration <= 0) {
warn!("Invalid duration for setting the device into limited discoverable mode. The valid duration is 1~60 seconds.");
return false;
}
let off_mode =
if self.is_connectable { BtScanMode::Connectable } else { BtScanMode::None_ };
let new_mode = match mode {
BtDiscMode::LimitedDiscoverable => BtScanMode::ConnectableLimitedDiscoverable,
BtDiscMode::GeneralDiscoverable => BtScanMode::ConnectableDiscoverable,
BtDiscMode::NonDiscoverable => off_mode.clone(),
};
// The old timer should be overwritten regardless of what the new mode is.
if let Some(ref handle) = self.discoverable_timeout {
handle.abort();
self.discoverable_timeout = None;
}
if intf.set_adapter_property(BluetoothProperty::AdapterDiscoverableTimeout(duration)) != 0
|| intf.set_adapter_property(BluetoothProperty::AdapterScanMode(new_mode)) != 0
{
return false;
}
if (mode != BtDiscMode::NonDiscoverable) && (duration != 0) {
let intf_clone = self.intf.clone();
self.discoverable_timeout = Some(tokio::spawn(async move {
time::sleep(Duration::from_secs(duration.into())).await;
intf_clone
.lock()
.unwrap()
.set_adapter_property(BluetoothProperty::AdapterScanMode(off_mode));
}));
}
true
}
fn is_multi_advertisement_supported(&self) -> bool {
match self.properties.get(&BtPropertyType::LocalLeFeatures) {
Some(prop) => match prop {
BluetoothProperty::LocalLeFeatures(llf) => {
llf.max_adv_instance >= MIN_ADV_INSTANCES_FOR_MULTI_ADV
}
_ => false,
},
_ => false,
}
}
fn is_le_extended_advertising_supported(&self) -> bool {
match self.properties.get(&BtPropertyType::LocalLeFeatures) {
Some(prop) => match prop {
BluetoothProperty::LocalLeFeatures(llf) => llf.le_extended_advertising_supported,
_ => false,
},
_ => false,
}
}
fn start_discovery(&mut self) -> bool {
// Short-circuit to avoid sending multiple start discovery calls.
if self.is_discovering {
return true;
}
// Short-circuit if paused and add the discovery intent to the queue.
if self.is_discovery_paused {
self.pending_discovery = true;
debug!("Queue the discovery request during paused state");
return true;
}
let discovery_suspend_mode = self.get_discovery_suspend_mode();
if discovery_suspend_mode != SuspendMode::Normal
&& discovery_suspend_mode != SuspendMode::Resuming
{
log::warn!("start_discovery is not allowed when suspending or suspended.");
return false;
}
self.intf.lock().unwrap().start_discovery() == 0
}
fn cancel_discovery(&mut self) -> bool {
// Client no longer want to discover, clear the request
if self.is_discovery_paused {
self.pending_discovery = false;
debug!("Cancel the discovery request during paused state");
}
// Reject the cancel discovery request if the underlying stack is not in a discovering
// state. For example, previous start discovery was enqueued for ongoing discovery.
if !self.is_discovering {
debug!("Reject cancel_discovery as it's not in discovering state.");
return false;
}
let discovery_suspend_mode = self.get_discovery_suspend_mode();
if discovery_suspend_mode != SuspendMode::Normal
&& discovery_suspend_mode != SuspendMode::Suspending
{
log::warn!("cancel_discovery is not allowed when resuming or suspended.");
return false;
}
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;
}
let elapsed_ms = self.discovering_started.elapsed().as_millis() as u64;
if elapsed_ms >= DEFAULT_DISCOVERY_TIMEOUT_MS {
0
} else {
DEFAULT_DISCOVERY_TIMEOUT_MS - elapsed_ms
}
}
fn create_bond(&mut self, device: BluetoothDevice, transport: BtTransport) -> bool {
let addr = RawAddress::from_string(device.address.clone());
if addr.is_none() {
metrics::bond_create_attempt(RawAddress::default(), BtDeviceType::Unknown);
metrics::bond_state_changed(
RawAddress::default(),
BtDeviceType::Unknown,
BtStatus::InvalidParam,
BtBondState::NotBonded,
0,
);
warn!("Can't create bond. Address {} is not valid", device.address);
return false;
}
let address = addr.unwrap();
let device_type = match transport {
BtTransport::Bredr => BtDeviceType::Bredr,
BtTransport::Le => BtDeviceType::Ble,
_ => self.get_remote_type(device.clone()),
};
// We explicitly log the attempt to start the bonding separate from logging the bond state.
// The start of the attempt is critical to help identify a bonding/pairing session.
metrics::bond_create_attempt(address, device_type.clone());
// BREDR connection won't work when Inquiry is in progress.
self.pause_discovery();
let status = self.intf.lock().unwrap().create_bond(&address, transport);
if status != 0 {
metrics::bond_state_changed(
address,
device_type,
BtStatus::from(status as u32),
BtBondState::NotBonded,
0,
);
return false;
}
// Creating bond automatically create ACL connection as well, therefore also log metrics
// ACL connection attempt here.
let is_connected = self
.get_remote_device_if_found(&device.address)
.map_or(false, |d| d.acl_state == BtAclState::Connected);
if !is_connected {
metrics::acl_connect_attempt(address, BtAclState::Connected);
}
return true;
}
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();
debug!("Removing bond for {}", DisplayAddress(&address));
let status = self.intf.lock().unwrap().remove_bond(&address);
if status != 0 {
return false;
}
// Removing bond also disconnects the ACL if is connected. Therefore, also log ACL
// disconnection attempt here.
let is_connected = self
.get_remote_device_if_found(&device.address)
.map_or(false, |d| d.acl_state == BtAclState::Connected);
if is_connected {
metrics::acl_connect_attempt(address, BtAclState::Disconnected);
}
return true;
}
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) -> BtBondState {
self.get_bond_state_by_addr(&device.address)
}
fn set_pin(&self, device: BluetoothDevice, accept: bool, pin_code: Vec<u8>) -> bool {
let addr = if let Some(addr) = RawAddress::from_string(device.address.clone()) {
addr
} else {
warn!("Can't set pin. 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 pin. Device {} isn't bonding.", DisplayAddress(&addr));
return false;
}
let mut btpin = BtPinCode { pin: array_utils::to_sized_array(&pin_code) };
self.intf.lock().unwrap().pin_reply(&addr, accept as u8, pin_code.len() as u8, &mut btpin)
== 0
}
fn set_passkey(&self, device: BluetoothDevice, accept: bool, passkey: Vec<u8>) -> bool {
let addr = if let Some(addr) = RawAddress::from_string(device.address.clone()) {
addr
} else {
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.", DisplayAddress(&addr));
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,
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;
}
self.intf.lock().unwrap().ssp_reply(
&addr.unwrap(),
BtSspVariant::PasskeyConfirmation,
accept as u8,
0,
) == 0
}
fn get_remote_name(&self, device: BluetoothDevice) -> String {
match self.get_remote_device_property(&device, &BtPropertyType::BdName) {
Some(BluetoothProperty::BdName(name)) => return name.clone(),
_ => return "".to_string(),
}
}
fn get_remote_type(&self, device: BluetoothDevice) -> BtDeviceType {
match self.get_remote_device_property(&device, &BtPropertyType::TypeOfDevice) {
Some(BluetoothProperty::TypeOfDevice(device_type)) => return device_type,
_ => return BtDeviceType::Unknown,
}
}
fn get_remote_alias(&self, device: BluetoothDevice) -> String {
match self.get_remote_device_property(&device, &BtPropertyType::RemoteFriendlyName) {
Some(BluetoothProperty::RemoteFriendlyName(name)) => return name.clone(),
_ => "".to_string(),
}
}
fn set_remote_alias(&mut self, device: BluetoothDevice, new_alias: String) {
let _ = self.set_remote_device_property(
&device,
BtPropertyType::RemoteFriendlyName,
BluetoothProperty::RemoteFriendlyName(new_alias),
);
}
fn get_remote_class(&self, device: BluetoothDevice) -> u32 {
match self.get_remote_device_property(&device, &BtPropertyType::ClassOfDevice) {
Some(BluetoothProperty::ClassOfDevice(class)) => return class,
_ => 0,
}
}
fn get_remote_appearance(&self, device: BluetoothDevice) -> u16 {
match self.get_remote_device_property(&device, &BtPropertyType::Appearance) {
Some(BluetoothProperty::Appearance(appearance)) => appearance,
_ => 0,
}
}
fn get_remote_connected(&self, device: BluetoothDevice) -> bool {
self.get_connection_state(device) != BtConnectionState::NotConnected
}
fn get_remote_wake_allowed(&self, device: BluetoothDevice) -> bool {
// Wake is allowed if the device supports HIDP or HOGP only.
match self.get_remote_device_property(&device, &BtPropertyType::Uuids) {
Some(BluetoothProperty::Uuids(uuids)) => {
return uuids.iter().any(|&x| {
UuidHelper::is_known_profile(&x.uu).map_or(false, |profile| {
profile == Profile::Hid || profile == Profile::Hogp
})
});
}
_ => false,
}
}
fn get_remote_vendor_product_info(&self, device: BluetoothDevice) -> BtVendorProductInfo {
match self.get_remote_device_property(&device, &BtPropertyType::VendorProductInfo) {
Some(BluetoothProperty::VendorProductInfo(p)) => p.clone(),
_ => BtVendorProductInfo { vendor_id_src: 0, vendor_id: 0, product_id: 0, version: 0 },
}
}
fn get_connected_devices(&self) -> Vec<BluetoothDevice> {
let bonded_connected: HashMap<String, BluetoothDevice> = self
.bonded_devices
.iter()
.filter(|(_, v)| v.acl_state == BtAclState::Connected)
.map(|(k, v)| (k.clone(), v.info.clone()))
.collect();
let mut found_connected: Vec<BluetoothDevice> = self
.found_devices
.iter()
.filter(|(k, v)| {
v.acl_state == BtAclState::Connected
&& !bonded_connected.contains_key(&k.to_string())
})
.map(|(_, v)| v.info.clone())
.collect();
let mut all =
bonded_connected.iter().map(|(_, v)| v.clone()).collect::<Vec<BluetoothDevice>>();
all.append(&mut found_connected);
all
}
fn get_connection_state(&self, device: BluetoothDevice) -> BtConnectionState {
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 BtConnectionState::NotConnected;
}
// The underlying api adds whether this is ENCRYPTED_BREDR or ENCRYPTED_LE.
// As long as it is non-zero, it is connected.
self.intf.lock().unwrap().get_connection_state(&addr.unwrap())
}
fn get_profile_connection_state(&self, profile: Uuid128Bit) -> ProfileConnectionState {
if let Some(known) = UuidHelper::is_known_profile(&profile) {
match known {
Profile::A2dpSink | Profile::A2dpSource => {
self.bluetooth_media.lock().unwrap().get_a2dp_connection_state()
}
Profile::Hfp | Profile::HfpAg => {
self.bluetooth_media.lock().unwrap().get_hfp_connection_state()
}
// TODO: (b/223431229) Profile::Hid and Profile::Hogp
_ => ProfileConnectionState::Disconnected,
}
} else {
ProfileConnectionState::Disconnected
}
}
fn get_remote_uuids(&self, device: BluetoothDevice) -> Vec<Uuid128Bit> {
match self.get_remote_device_property(&device, &BtPropertyType::Uuids) {
Some(BluetoothProperty::Uuids(uuids)) => {
return uuids.iter().map(|&x| x.uu.clone()).collect::<Vec<Uuid128Bit>>()
}
_ => return vec![],
}
}
fn fetch_remote_uuids(&self, remote_device: BluetoothDevice) -> bool {
let device = match self.get_remote_device_if_found(&remote_device.address) {
Some(v) => v,
None => {
warn!("Won't fetch UUIDs on unknown device");
return false;
}
};
let mut addr = match RawAddress::from_string(device.info.address.clone()) {
Some(v) => v,
None => {
warn!("Can't fetch UUIDs. Address {} is not valid.", device.info.address);
return false;
}
};
let transport = match self.get_remote_type(device.info.clone()) {
BtDeviceType::Bredr => BtTransport::Bredr,
BtDeviceType::Ble => BtTransport::Le,
_ => device.acl_reported_transport,
};
self.intf.lock().unwrap().get_remote_services(&mut addr, transport) == 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::from(uuid);
self.sdp.as_ref().unwrap().sdp_search(&mut addr.unwrap(), &uu) == BtStatus::Success
}
fn create_sdp_record(&mut self, sdp_record: BtSdpRecord) -> bool {
let mut handle: i32 = -1;
let mut sdp_record = sdp_record;
match self.sdp.as_ref().unwrap().create_sdp_record(&mut sdp_record, &mut handle) {
BtStatus::Success => {
let record_clone = sdp_record.clone();
self.callbacks.for_all_callbacks(|callback| {
callback.on_sdp_record_created(record_clone.clone(), handle);
});
true
}
_ => false,
}
}
fn remove_sdp_record(&self, handle: i32) -> bool {
self.sdp.as_ref().unwrap().remove_sdp_record(handle) == BtStatus::Success
}
fn connect_all_enabled_profiles(&mut self, device: BluetoothDevice) -> bool {
// Profile init must be complete before this api is callable
if !self.profiles_ready {
return false;
}
let mut addr = match RawAddress::from_string(device.address.clone()) {
Some(v) => v,
None => {
warn!("Can't connect profiles on invalid address [{}]", &device.address);
return false;
}
};
let is_connected = self
.get_remote_device_if_found(&device.address)
.map_or(false, |d| d.acl_state == BtAclState::Connected);
if !is_connected {
// log ACL connection attempt if it's not already connected.
metrics::acl_connect_attempt(addr, BtAclState::Connected);
// Pause discovery before connecting, or the ACL connection request may conflict with
// the ongoing inquiry.
self.pause_discovery();
}
// Check all remote uuids to see if they match enabled profiles and connect them.
let mut has_enabled_uuids = false;
let mut has_media_profile = false;
let mut has_supported_profile = false;
let uuids = self.get_remote_uuids(device.clone());
for uuid in uuids.iter() {
match UuidHelper::is_known_profile(uuid) {
Some(p) => {
if UuidHelper::is_profile_supported(&p) {
match p {
Profile::Hid | Profile::Hogp => {
has_supported_profile = true;
let status = self.hh.as_ref().unwrap().connect(&mut addr);
metrics::profile_connection_state_changed(
addr,
p as u32,
BtStatus::Success,
BthhConnectionState::Connecting as u32,
);
if status != BtStatus::Success {
metrics::profile_connection_state_changed(
addr,
p as u32,
status,
BthhConnectionState::Disconnected as u32,
);
}
}
Profile::A2dpSink | Profile::A2dpSource | Profile::Hfp
if !has_media_profile =>
{
has_supported_profile = true;
has_media_profile = true;
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;
});
}
Profile::Bas => {
has_supported_profile = true;
let tx = self.tx.clone();
let transport =
match self.get_remote_device_if_found(&device.address) {
Some(context) => context.acl_reported_transport,
None => return false,
};
let device_to_send = device.clone();
let transport = match self.get_remote_type(device.clone()) {
BtDeviceType::Bredr => BtTransport::Bredr,
BtDeviceType::Ble => BtTransport::Le,
_ => transport,
};
topstack::get_runtime().spawn(async move {
let _ = tx
.send(Message::BatteryService(
BatteryServiceActions::Connect(
device_to_send,
transport,
),
))
.await;
});
}
// We don't connect most profiles
_ => (),
}
}
has_enabled_uuids = true;
}
_ => {}
}
}
// If SDP isn't completed yet, we wait for it to complete and retry the connection again.
// Otherwise, this connection request is done, no retry is required.
if !has_enabled_uuids {
warn!("[{}] SDP hasn't completed for device, wait to connect.", DisplayAddress(&addr));
if let Some(d) = self.get_remote_device_if_found_mut(&device.address) {
if uuids.len() == 0 || !d.services_resolved {
d.wait_to_connect = true;
}
}
}
// If the SDP has not been completed or the device does not have a profile that we are
// interested in connecting to, resume discovery now. Other cases will be handled in the
// ACL connection state or bond state callbacks.
if !has_enabled_uuids || !has_supported_profile {
self.resume_discovery();
}
return true;
}
fn disconnect_all_enabled_profiles(&mut 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;
}
// log ACL disconnection attempt if it's not already disconnected.
let is_connected = self
.get_remote_device_if_found(&device.address)
.map_or(false, |d| d.acl_state == BtAclState::Connected);
if is_connected {
metrics::acl_connect_attempt(addr.unwrap(), BtAclState::Disconnected);
}
let uuids = self.get_remote_uuids(device.clone());
let mut has_media_profile = false;
for uuid in uuids.iter() {
match UuidHelper::is_known_profile(uuid) {
Some(p) => {
if UuidHelper::is_profile_supported(&p) {
match p {
Profile::Hid | Profile::Hogp => {
self.hh.as_ref().unwrap().disconnect(&mut addr.unwrap());
}
Profile::A2dpSink
| Profile::A2dpSource
| Profile::Hfp
| Profile::AvrcpController
if !has_media_profile =>
{
has_media_profile = true;
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;
});
}
Profile::Bas => {
let tx = self.tx.clone();
let device_to_send = device.clone();
topstack::get_runtime().spawn(async move {
let _ = tx
.send(Message::BatteryService(
BatteryServiceActions::Disconnect(device_to_send),
))
.await;
});
}
// We don't connect most profiles
_ => (),
}
}
}
_ => {}
}
}
return true;
}
fn is_wbs_supported(&self) -> bool {
self.intf.lock().unwrap().get_wbs_supported()
}
fn is_swb_supported(&self) -> bool {
self.intf.lock().unwrap().get_swb_supported()
}
}
impl BtifSdpCallbacks for Bluetooth {
fn sdp_search(
&mut self,
status: BtStatus,
address: RawAddress,
uuid: Uuid,
_count: i32,
records: Vec<BtSdpRecord>,
) {
let uuid_to_send = match UuidHelper::from_string(uuid.to_string()) {
Some(uu) => uu,
None => return,
};
let device_info = match self.get_remote_device_info_if_found(&address.to_string()) {
Some(info) => info,
None => BluetoothDevice::new(address.to_string(), "".to_string()),
};
// The SDP records we get back do not populate the UUID so we populate it ourselves before
// sending them on.
let mut records = records;
records.iter_mut().for_each(|record| {
match record {
BtSdpRecord::HeaderOverlay(header) => header.uuid = uuid.clone(),
BtSdpRecord::MapMas(record) => record.hdr.uuid = uuid.clone(),
BtSdpRecord::MapMns(record) => record.hdr.uuid = uuid.clone(),
BtSdpRecord::PbapPse(record) => record.hdr.uuid = uuid.clone(),
BtSdpRecord::PbapPce(record) => record.hdr.uuid = uuid.clone(),
BtSdpRecord::OppServer(record) => record.hdr.uuid = uuid.clone(),
BtSdpRecord::SapServer(record) => record.hdr.uuid = uuid.clone(),
BtSdpRecord::Dip(record) => record.hdr.uuid = uuid.clone(),
BtSdpRecord::Mps(record) => record.hdr.uuid = uuid.clone(),
};
});
self.callbacks.for_all_callbacks(|callback| {
callback.on_sdp_search_complete(device_info.clone(), uuid_to_send, records.clone());
});
debug!(
"Sdp search result found: Status({:?}) Address({}) Uuid({:?})",
status,
DisplayAddress(&address),
uuid
);
}
}
impl BtifHHCallbacks for Bluetooth {
fn connection_state(&mut self, mut address: RawAddress, state: BthhConnectionState) {
debug!(
"Hid host connection state updated: Address({}) State({:?})",
DisplayAddress(&address),
state
);
// HID or HOG is not differentiated by the hid host when callback this function. Assume HOG
// if the device is LE only and HID if classic only. And assume HOG if UUID said so when
// device type is dual or unknown.
let device = BluetoothDevice::new(address.to_string(), "".to_string());
let profile = match self.get_remote_type(device.clone()) {
BtDeviceType::Ble => Profile::Hogp,
BtDeviceType::Bredr => Profile::Hid,
_ => {
if self.get_remote_uuids(device).contains(&UuidHelper::from_string(HOGP).unwrap()) {
Profile::Hogp
} else {
Profile::Hid
}
}
};
metrics::profile_connection_state_changed(
address,
profile as u32,
BtStatus::Success,
state as u32,
);
if BtBondState::Bonded != self.get_bond_state_by_addr(&address.to_string()) {
warn!(
"[{}]: Rejecting a unbonded device's attempt to connect to HID/HOG profiles",
DisplayAddress(&address)
);
self.hh.as_ref().unwrap().disconnect(&mut address);
}
}
fn hid_info(&mut self, address: RawAddress, info: BthhHidInfo) {
debug!("Hid host info updated: Address({}) Info({:?})", DisplayAddress(&address), info);
}
fn protocol_mode(&mut self, address: RawAddress, status: BthhStatus, mode: BthhProtocolMode) {
debug!(
"Hid host protocol mode updated: Address({}) Status({:?}) Mode({:?})",
DisplayAddress(&address),
status,
mode
);
}
fn idle_time(&mut self, address: RawAddress, status: BthhStatus, idle_rate: i32) {
debug!(
"Hid host idle time updated: Address({}) Status({:?}) Idle Rate({:?})",
DisplayAddress(&address),
status,
idle_rate
);
}
fn get_report(
&mut self,
mut address: RawAddress,
status: BthhStatus,
mut data: Vec<u8>,
size: i32,
) {
debug!(
"Hid host got report: Address({}) Status({:?}) Report Size({:?})",
DisplayAddress(&address),
status,
size
);
self.hh.as_ref().unwrap().get_report_reply(&mut address, status, &mut data, size as u16);
}
fn handshake(&mut self, address: RawAddress, status: BthhStatus) {
debug!("Hid host handshake: Address({}) Status({:?})", DisplayAddress(&address), status);
}
}
// TODO(b/261143122): Remove these once we migrate to BluetoothQA entirely
impl IBluetoothQALegacy for Bluetooth {
fn get_connectable(&self) -> bool {
self.get_connectable_internal()
}
fn set_connectable(&mut self, mode: bool) -> bool {
self.set_connectable_internal(mode)
}
fn get_alias(&self) -> String {
self.get_alias_internal()
}
fn get_modalias(&self) -> String {
format!("bluetooth:v00E0pC405d{:04x}", FLOSS_VER)
}
fn get_hid_report(
&mut self,
addr: String,
report_type: BthhReportType,
report_id: u8,
) -> BtStatus {
self.get_hid_report_internal(addr, report_type, report_id)
}
fn set_hid_report(
&mut self,
addr: String,
report_type: BthhReportType,
report: String,
) -> BtStatus {
self.set_hid_report_internal(addr, report_type, report)
}
fn send_hid_data(&mut self, addr: String, data: String) -> BtStatus {
self.send_hid_data_internal(addr, data)
}
}