Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 448f8984 authored by Abhishek Pandit-Subedi's avatar Abhishek Pandit-Subedi
Browse files

floss: Trigger service discovery after bond

Devices need to trigger service resolution after bonding. This updates
the bonding and connection state machine so that this happens properly.

Bug: 261732772
Tag: #floss
Test: Manual test with Airpods on Zork
Change-Id: I42a12087c0095553142edb6daa68064a4cf1289f
parent 67a4fcc0
Loading
Loading
Loading
Loading
+115 −43
Original line number Original line Diff line number Diff line
@@ -4,7 +4,8 @@ use bt_topshim::btif::{
    BaseCallbacks, BaseCallbacksDispatcher, BluetoothInterface, BluetoothProperty, BtAclState,
    BaseCallbacks, BaseCallbacksDispatcher, BluetoothInterface, BluetoothProperty, BtAclState,
    BtBondState, BtConnectionDirection, BtConnectionState, BtDeviceType, BtDiscoveryState,
    BtBondState, BtConnectionDirection, BtConnectionState, BtDeviceType, BtDiscoveryState,
    BtHciErrorCode, BtPinCode, BtPropertyType, BtScanMode, BtSspVariant, BtState, BtStatus,
    BtHciErrorCode, BtPinCode, BtPropertyType, BtScanMode, BtSspVariant, BtState, BtStatus,
    BtTransport, BtVendorProductInfo, RawAddress, ToggleableProfile, Uuid, Uuid128Bit,
    BtTransport, BtVendorProductInfo, DisplayAddress, RawAddress, ToggleableProfile, Uuid,
    Uuid128Bit,
};
};
use bt_topshim::{
use bt_topshim::{
    metrics,
    metrics,
@@ -206,6 +207,15 @@ pub trait IBluetoothQA {
    fn set_connectable(&mut self, mode: bool) -> bool;
    fn set_connectable(&mut self, mode: bool) -> bool;
}
}


/// 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),
}

/// Serializable device used in various apis.
/// Serializable device used in various apis.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct BluetoothDevice {
pub struct BluetoothDevice {
@@ -240,11 +250,21 @@ impl BluetoothDevice {


/// Internal data structure that keeps a map of cached properties for a remote device.
/// Internal data structure that keeps a map of cached properties for a remote device.
struct BluetoothDeviceContext {
struct BluetoothDeviceContext {
    pub bond_state: BtBondState,
    /// Transport type reported by ACL connection (if completed).
    pub acl_reported_transport: BtTransport,

    pub acl_state: BtAclState,
    pub acl_state: BtAclState,
    pub bond_state: BtBondState,
    pub info: BluetoothDevice,
    pub info: BluetoothDevice,
    pub last_seen: Instant,
    pub last_seen: Instant,
    pub properties: HashMap<BtPropertyType, BluetoothProperty>,
    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 {
impl BluetoothDeviceContext {
@@ -256,11 +276,14 @@ impl BluetoothDeviceContext {
        properties: Vec<BluetoothProperty>,
        properties: Vec<BluetoothProperty>,
    ) -> BluetoothDeviceContext {
    ) -> BluetoothDeviceContext {
        let mut device = BluetoothDeviceContext {
        let mut device = BluetoothDeviceContext {
            bond_state,
            acl_reported_transport: BtTransport::Auto,
            acl_state,
            acl_state,
            bond_state,
            info,
            info,
            last_seen,
            last_seen,
            properties: HashMap::new(),
            properties: HashMap::new(),
            services_resolved: false,
            wait_to_connect: false,
        };
        };
        device.update_properties(&properties);
        device.update_properties(&properties);
        device
        device
@@ -369,8 +392,6 @@ pub struct Bluetooth {
    sdp: Option<Sdp>,
    sdp: Option<Sdp>,
    state: BtState,
    state: BtState,
    tx: Sender<Message>,
    tx: Sender<Message>,
    /// Used to delay connection until we have SDP results.
    wait_to_connect: bool,
    // Internal API members
    // Internal API members
    discoverable_timeout: Option<JoinHandle<()>>,
    discoverable_timeout: Option<JoinHandle<()>>,


@@ -409,7 +430,6 @@ impl Bluetooth {
            sdp: None,
            sdp: None,
            state: BtState::Off,
            state: BtState::Off,
            tx,
            tx,
            wait_to_connect: false,
            // Internal API members
            // Internal API members
            discoverable_timeout: None,
            discoverable_timeout: None,
            sig_notifier,
            sig_notifier,
@@ -580,7 +600,7 @@ impl Bluetooth {


    /// Check whether found devices are still fresh. If they're outside the
    /// Check whether found devices are still fresh. If they're outside the
    /// freshness window, send a notification to clear the device from clients.
    /// freshness window, send a notification to clear the device from clients.
    pub(crate) fn trigger_freshness_check(&mut self) {
    fn trigger_freshness_check(&mut self) {
        if let Some(ref handle) = self.freshness_check {
        if let Some(ref handle) = self.freshness_check {
            // Abort and drop the previous JoinHandle.
            // Abort and drop the previous JoinHandle.
            handle.abort();
            handle.abort();
@@ -620,7 +640,9 @@ impl Bluetooth {


            self.freshness_check = Some(tokio::spawn(async move {
            self.freshness_check = Some(tokio::spawn(async move {
                time::sleep(FOUND_DEVICE_FRESHNESS).await;
                time::sleep(FOUND_DEVICE_FRESHNESS).await;
                let _ = txl.send(Message::DeviceFreshnessCheck).await;
                let _ = txl
                    .send(Message::DelayedAdapterActions(DelayedActions::DeviceFreshnessCheck))
                    .await;
            }));
            }));
        }
        }
    }
    }
@@ -663,6 +685,19 @@ impl Bluetooth {
            vpi.version,
            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);
            }
        }
    }
}
}


#[btif_callbacks_dispatcher(dispatch_base_callbacks, BaseCallbacks)]
#[btif_callbacks_dispatcher(dispatch_base_callbacks, BaseCallbacks)]
@@ -970,8 +1005,8 @@ impl BtifBluetoothCallbacks for Bluetooth {
        else if &bond_state == &BtBondState::Bonded && !self.bonded_devices.contains_key(&address)
        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
            // We either need to construct a new BluetoothDeviceContext or grab it from the found
            // devices map
            // devices map. Immediately insert that into the bonded list.
            let device = match self.found_devices.remove(&address) {
            let mut device = match self.found_devices.remove(&address) {
                Some(mut v) => {
                Some(mut v) => {
                    v.bond_state = bond_state.clone();
                    v.bond_state = bond_state.clone();
                    v
                    v
@@ -984,8 +1019,13 @@ impl BtifBluetoothCallbacks for Bluetooth {
                    vec![],
                    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.bonded_devices.insert(address.clone(), device);
            self.fetch_remote_uuids(device_info);
        } else {
        } else {
            // If we're bonding, we need to update the found devices list
            // If we're bonding, we need to update the found devices list
            self.found_devices
            self.found_devices
@@ -1013,6 +1053,7 @@ impl BtifBluetoothCallbacks for Bluetooth {
        properties: Vec<BluetoothProperty>,
        properties: Vec<BluetoothProperty>,
    ) {
    ) {
        let address = addr.to_string();
        let address = addr.to_string();
        let txl = self.tx.clone();
        let device = match self.get_remote_device_if_found_mut(&address) {
        let device = match self.get_remote_device_if_found_mut(&address) {
            None => {
            None => {
                self.found_devices.insert(
                self.found_devices.insert(
@@ -1036,12 +1077,30 @@ impl BtifBluetoothCallbacks for Bluetooth {
                d.update_properties(&properties);
                d.update_properties(&properties);
                d.seen();
                d.seen();


                // Services are resolved.
                d.services_resolved = true;

                Bluetooth::send_metrics_remote_device_info(d);
                Bluetooth::send_metrics_remote_device_info(d);


                let info = d.info.clone();
                let info = d.info.clone();
                let uuids = self.get_remote_uuids(info.clone());
                let has_uuids = d
                if self.wait_to_connect && uuids.len() > 0 {
                    .properties
                    self.connect_all_enabled_profiles(info.clone());
                    .get(&BtPropertyType::Uuids)
                    .and_then(|prop| match prop {
                        BluetoothProperty::Uuids(uu) => Some(uu.len() > 0),
                        _ => None,
                    })
                    .map_or(false, |v| v);

                if d.wait_to_connect && has_uuids {
                    d.wait_to_connect = false;

                    let sent_info = info.clone();
                    tokio::spawn(async move {
                        let _ = txl.send(Message::DelayedAdapterActions(
                            DelayedActions::ConnectAllProfiles(sent_info),
                        ));
                    });
                }
                }


                self.bluetooth_admin
                self.bluetooth_admin
@@ -1050,7 +1109,7 @@ impl BtifBluetoothCallbacks for Bluetooth {
                    .on_remote_device_properties_changed(&info, &properties);
                    .on_remote_device_properties_changed(&info, &properties);
            }
            }
            None => (),
            None => (),
        };
        }
    }
    }


    fn acl_state(
    fn acl_state(
@@ -1106,6 +1165,7 @@ impl BtifBluetoothCallbacks for Bluetooth {
                if prev_state != &state {
                if prev_state != &state {
                    let device = found.info.clone();
                    let device = found.info.clone();
                    found.acl_state = state.clone();
                    found.acl_state = state.clone();
                    found.acl_reported_transport = link_type;


                    metrics::acl_connection_state_changed(
                    metrics::acl_connection_state_changed(
                        addr,
                        addr,
@@ -1645,18 +1705,30 @@ impl IBluetooth for Bluetooth {
        }
        }
    }
    }


    fn fetch_remote_uuids(&self, device: BluetoothDevice) -> bool {
    fn fetch_remote_uuids(&self, remote_device: BluetoothDevice) -> bool {
        if self.get_remote_device_if_found(&device.address).is_none() {
        let device = match self.get_remote_device_if_found(&remote_device.address) {
            warn!("Won't fetch UUIDs on unknown device {}", device.address);
            Some(v) => v,
            None => {
                warn!("Won't fetch UUIDs on unknown device {}", remote_device.address);
                return false;
                return false;
            }
            }
        };


        let addr = RawAddress::from_string(device.address.clone());
        let mut addr = match RawAddress::from_string(device.info.address.clone()) {
        if addr.is_none() {
            Some(v) => v,
            warn!("Can't fetch UUIDs. Address {} is not valid.", device.address);
            None => {
                warn!("Can't fetch UUIDs. Address {} is not valid.", device.info.address);
                return false;
                return false;
            }
            }
        self.intf.lock().unwrap().get_remote_services(&mut addr.unwrap(), BtTransport::Auto) == 0
        };

        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 {
    fn sdp_search(&self, device: BluetoothDevice, uuid: Uuid128Bit) -> bool {
@@ -1681,18 +1753,20 @@ impl IBluetooth for Bluetooth {
            return false;
            return false;
        }
        }


        let addr = RawAddress::from_string(device.address.clone());
        let mut addr = match RawAddress::from_string(device.address.clone()) {
        if addr.is_none() {
            Some(v) => v,
            None => {
                warn!("Can't connect profiles on invalid address [{}]", &device.address);
                warn!("Can't connect profiles on invalid address [{}]", &device.address);
                return false;
                return false;
            }
            }
        };


        // log ACL connection attempt if it's not already connected.
        // log ACL connection attempt if it's not already connected.
        let is_connected = self
        let is_connected = self
            .get_remote_device_if_found(&device.address)
            .get_remote_device_if_found(&device.address)
            .map_or(false, |d| d.acl_state == BtAclState::Connected);
            .map_or(false, |d| d.acl_state == BtAclState::Connected);
        if !is_connected {
        if !is_connected {
            metrics::acl_connect_attempt(addr.unwrap(), BtAclState::Connected);
            metrics::acl_connect_attempt(addr, BtAclState::Connected);
        }
        }


        // Check all remote uuids to see if they match enabled profiles and connect them.
        // Check all remote uuids to see if they match enabled profiles and connect them.
@@ -1704,9 +1778,9 @@ impl IBluetooth for Bluetooth {
                    if UuidHelper::is_profile_supported(&p) {
                    if UuidHelper::is_profile_supported(&p) {
                        match p {
                        match p {
                            Profile::Hid | Profile::Hogp => {
                            Profile::Hid | Profile::Hogp => {
                                let status = self.hh.as_ref().unwrap().connect(&mut addr.unwrap());
                                let status = self.hh.as_ref().unwrap().connect(&mut addr);
                                metrics::profile_connection_state_changed(
                                metrics::profile_connection_state_changed(
                                    addr.unwrap(),
                                    addr,
                                    p as u32,
                                    p as u32,
                                    BtStatus::Success,
                                    BtStatus::Success,
                                    BthhConnectionState::Connecting as u32,
                                    BthhConnectionState::Connecting as u32,
@@ -1714,7 +1788,7 @@ impl IBluetooth for Bluetooth {


                                if status != BtStatus::Success {
                                if status != BtStatus::Success {
                                    metrics::profile_connection_state_changed(
                                    metrics::profile_connection_state_changed(
                                        addr.unwrap(),
                                        addr,
                                        p as u32,
                                        p as u32,
                                        status,
                                        status,
                                        BthhConnectionState::Disconnected as u32,
                                        BthhConnectionState::Disconnected as u32,
@@ -1759,21 +1833,19 @@ impl IBluetooth for Bluetooth {


        // If SDP isn't completed yet, we wait for it to complete and retry the connection again.
        // 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.
        // Otherwise, this connection request is done, no retry is required.
        self.wait_to_connect = !has_enabled_uuids;
        if !has_enabled_uuids {
        if self.wait_to_connect {
            warn!("[{}] SDP hasn't completed for device, wait to connect.", DisplayAddress(addr));
            warn!(
            if let Some(d) = self.get_remote_device_if_found_mut(&device.address) {
                "[{}] SDP hasn't completed for device, wait to connect.",
                if uuids.len() == 0 || !d.services_resolved {
                addr.unwrap().to_string()
                    d.wait_to_connect = true;
            );
                }
            }
        }
        }


        return true;
        return true;
    }
    }


    fn disconnect_all_enabled_profiles(&mut self, device: BluetoothDevice) -> bool {
    fn disconnect_all_enabled_profiles(&mut self, device: BluetoothDevice) -> bool {
        // No need to retry connection as we are going to disconnect all enabled profiles.
        self.wait_to_connect = false;

        if !self.profiles_ready {
        if !self.profiles_ready {
            return false;
            return false;
        }
        }
+5 −5
Original line number Original line Diff line number Diff line
@@ -32,7 +32,7 @@ use crate::battery_provider_manager::BatteryProviderManager;
use crate::battery_service::{BatteryService, BatteryServiceActions};
use crate::battery_service::{BatteryService, BatteryServiceActions};
use crate::bluetooth::{
use crate::bluetooth::{
    dispatch_base_callbacks, dispatch_hid_host_callbacks, dispatch_sdp_callbacks, Bluetooth,
    dispatch_base_callbacks, dispatch_hid_host_callbacks, dispatch_sdp_callbacks, Bluetooth,
    BluetoothDevice, IBluetooth,
    BluetoothDevice, DelayedActions, IBluetooth,
};
};
use crate::bluetooth_admin::{BluetoothAdmin, IBluetoothAdmin};
use crate::bluetooth_admin::{BluetoothAdmin, IBluetoothAdmin};
use crate::bluetooth_gatt::{
use crate::bluetooth_gatt::{
@@ -79,8 +79,8 @@ pub enum Message {
    AdapterCallbackDisconnected(u32),
    AdapterCallbackDisconnected(u32),
    ConnectionCallbackDisconnected(u32),
    ConnectionCallbackDisconnected(u32),


    // Update list of found devices and remove old instances.
    // Some delayed actions for the adapter.
    DeviceFreshnessCheck,
    DelayedAdapterActions(DelayedActions),


    // Follows IBluetooth's on_device_(dis)connected callback but doesn't require depending on
    // Follows IBluetooth's on_device_(dis)connected callback but doesn't require depending on
    // Bluetooth.
    // Bluetooth.
@@ -232,8 +232,8 @@ impl Stack {
                    bluetooth.lock().unwrap().connection_callback_disconnected(id);
                    bluetooth.lock().unwrap().connection_callback_disconnected(id);
                }
                }


                Message::DeviceFreshnessCheck => {
                Message::DelayedAdapterActions(action) => {
                    bluetooth.lock().unwrap().trigger_freshness_check();
                    bluetooth.lock().unwrap().handle_delayed_actions(action);
                }
                }


                // Any service needing an updated list of devices can have an
                // Any service needing an updated list of devices can have an
+8 −0
Original line number Original line Diff line number Diff line
@@ -814,6 +814,14 @@ impl RawAddress {
    }
    }
}
}


/// Address that is safe to display in logs.
pub struct DisplayAddress(pub RawAddress);
impl Display for DisplayAddress {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "xx:xx:xx:xx:{:2X}:{:2X}", &self.0.address[4], &self.0.address[5])
    }
}

/// An enum representing `bt_callbacks_t` from btif.
/// An enum representing `bt_callbacks_t` from btif.
#[derive(Clone, Debug)]
#[derive(Clone, Debug)]
pub enum BaseCallbacks {
pub enum BaseCallbacks {