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

Commit 3af158ea authored by Archie Pusaka's avatar Archie Pusaka Committed by Gerrit Code Review
Browse files

Merge "floss: Connect to newly discovered profiles if it is user initiated" into main

parents db88f358 31eca2cc
Loading
Loading
Loading
Loading
+197 −163
Original line number Diff line number Diff line
@@ -316,6 +316,9 @@ pub enum DelayedActions {
    /// Connect to all supported profiles on target device.
    ConnectAllProfiles(BluetoothDevice),

    /// Connect to the specified profiles on target device.
    ConnectProfiles(Vec<Uuid>, BluetoothDevice),

    /// Scanner for BLE discovery is registered with given status and scanner id.
    BleDiscoveryScannerRegistered(Uuid, u8, GattStatus),

@@ -373,12 +376,9 @@ struct BluetoothDeviceContext {
    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,
    /// If user wants to connect to all profiles, when new profiles are discovered we will also try
    /// to connect them.
    pub connect_to_new_profiles: bool,
}

impl BluetoothDeviceContext {
@@ -398,8 +398,7 @@ impl BluetoothDeviceContext {
            info,
            last_seen,
            properties: HashMap::new(),
            services_resolved: false,
            wait_to_connect: false,
            connect_to_new_profiles: false,
        };
        device.update_properties(&properties);
        device
@@ -1104,6 +1103,10 @@ impl Bluetooth {
                self.connect_all_enabled_profiles(device);
            }

            DelayedActions::ConnectProfiles(uuids, device) => {
                self.connect_profiles_internal(&uuids, device);
            }

            DelayedActions::BleDiscoveryScannerRegistered(uuid, scanner_id, status) => {
                if let Some(app_uuid) = self.ble_scanner_uuid {
                    if app_uuid == uuid {
@@ -1140,6 +1143,10 @@ impl Bluetooth {
                };

                let device_info = BluetoothDevice::from_properties(&properties);
                self.check_new_property_and_potentially_connect_profiles(
                    result.address,
                    &properties,
                );

                self.remote_devices
                    .entry(device_info.address)
@@ -1285,6 +1292,177 @@ impl Bluetooth {
            || self.active_pairing_address.is_some()
            || self.pending_create_bond.is_some()
    }

    /// Checks whether the list of device properties contains some UUID we should connect now
    /// This function also connects those UUIDs.
    fn check_new_property_and_potentially_connect_profiles(
        &self,
        addr: RawAddress,
        properties: &Vec<BluetoothProperty>,
    ) {
        // Return early if no need to connect new profiles
        if !self.remote_devices.get(&addr).map_or(false, |d| d.connect_to_new_profiles) {
            return;
        }

        // Get the reported UUIDs, if any. Otherwise return early.
        let mut new_uuids: Vec<Uuid> = vec![];
        for prop in properties.iter() {
            if let BluetoothProperty::Uuids(value) = prop {
                new_uuids.extend(value);
            }
        }
        if new_uuids.is_empty() {
            return;
        }

        // Only connect if the UUID is not seen before and it's supported
        let device = BluetoothDevice::new(addr, "".to_string());
        let current_uuids = self.get_remote_uuids(device.clone());
        new_uuids.retain(|uuid| !current_uuids.contains(uuid));

        let profile_known_and_supported = new_uuids.iter().any(|uuid| {
            if let Some(profile) = UuidHelper::is_known_profile(uuid) {
                return UuidHelper::is_profile_supported(&profile);
            }
            return false;
        });
        if !profile_known_and_supported {
            return;
        }

        log::info!("[{}]: Connecting to newly discovered profiles", DisplayAddress(&addr));
        let tx = self.tx.clone();
        tokio::spawn(async move {
            let _ = tx
                .send(Message::DelayedAdapterActions(DelayedActions::ConnectProfiles(
                    new_uuids, device,
                )))
                .await;
        });
    }

    /// Connect these profiles of a peripheral device
    fn connect_profiles_internal(&mut self, uuids: &Vec<Uuid>, device: BluetoothDevice) {
        let addr = device.address;
        if !self.get_acl_state_by_addr(&addr) {
            // 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();
        }

        let mut has_supported_profile = false;
        let mut has_le_media_profile = false;
        let mut has_classic_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 => {
                                has_supported_profile = true;
                                // TODO(b/328675014): Use BtAddrType
                                // and BtTransport from
                                // BluetoothDevice instead of default
                                let status = self.hh.as_ref().unwrap().connect(
                                    &mut addr.clone(),
                                    BtAddrType::Public,
                                    BtTransport::Auto,
                                );
                                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,
                                    );
                                }
                            }

                            // TODO(b/317682584): implement policy to connect to LEA, VC, and CSIS
                            Profile::LeAudio | Profile::VolumeControl | Profile::CoordinatedSet
                                if !has_le_media_profile =>
                            {
                                has_le_media_profile = true;
                                let txl = self.tx.clone();
                                topstack::get_runtime().spawn(async move {
                                    let _ = txl
                                        .send(Message::Media(
                                            MediaActions::ConnectLeaGroupByMemberAddress(addr),
                                        ))
                                        .await;
                                });
                            }

                            Profile::A2dpSink | Profile::A2dpSource | Profile::Hfp
                                if !has_classic_media_profile =>
                            {
                                has_supported_profile = true;
                                has_classic_media_profile = true;
                                let txl = self.tx.clone();
                                topstack::get_runtime().spawn(async move {
                                    let _ =
                                        txl.send(Message::Media(MediaActions::Connect(addr))).await;
                                });
                            }

                            Profile::Bas => {
                                has_supported_profile = true;
                                let tx = self.tx.clone();
                                let device_context = match self.remote_devices.get(&addr) {
                                    Some(context) => context,
                                    None => continue,
                                };

                                let acl_state = device_context.ble_acl_state.clone();
                                let bond_state = device_context.bond_state.clone();
                                let device_to_send = device.clone();

                                let transport = match self.get_remote_type(device.clone()) {
                                    BtDeviceType::Bredr => BtTransport::Bredr,
                                    BtDeviceType::Ble => BtTransport::Le,
                                    _ => device_context.acl_reported_transport.clone(),
                                };
                                topstack::get_runtime().spawn(async move {
                                    let _ = tx
                                        .send(Message::BatteryService(
                                            BatteryServiceActions::Connect(
                                                device_to_send,
                                                acl_state,
                                                bond_state,
                                                transport,
                                            ),
                                        ))
                                        .await;
                                });
                            }

                            // We don't connect most profiles
                            _ => (),
                        }
                    }
                }
                _ => {}
            }
        }

        // If 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_supported_profile {
            self.resume_discovery();
        }
    }
}

#[btif_callbacks_dispatcher(dispatch_base_callbacks, BaseCallbacks)]
@@ -1576,6 +1754,7 @@ impl BtifBluetoothCallbacks for Bluetooth {

    fn device_found(&mut self, _n: i32, properties: Vec<BluetoothProperty>) {
        let device_info = BluetoothDevice::from_properties(&properties);
        self.check_new_property_and_potentially_connect_profiles(device_info.address, &properties);

        let device_info = self
            .remote_devices
@@ -1760,7 +1939,6 @@ impl BtifBluetoothCallbacks for Bluetooth {
                    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.fetch_remote_uuids(device_info.clone());
                    if self.get_wake_allowed_device_bonded() {
                        self.create_uhid_for_suspend_wakesource();
@@ -1821,8 +1999,7 @@ impl BtifBluetoothCallbacks for Bluetooth {
        _num_properties: i32,
        properties: Vec<BluetoothProperty>,
    ) {
        let txl = self.tx.clone();

        self.check_new_property_and_potentially_connect_profiles(addr, &properties);
        let device = self.remote_devices.entry(addr).or_insert(BluetoothDeviceContext::new(
            BtBondState::NotBonded,
            BtAclState::Disconnected,
@@ -1839,30 +2016,6 @@ impl BtifBluetoothCallbacks for Bluetooth {

        let info = device.info.clone();

        if !device.services_resolved {
            let has_uuids = properties.iter().any(|prop| match prop {
                BluetoothProperty::Uuids(uu) => !uu.is_empty(),
                _ => false,
            });

            // Services are resolved when uuids are fetched.
            device.services_resolved |= has_uuids;
        }

        if device.wait_to_connect && device.services_resolved {
            device.wait_to_connect = false;

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

        self.callbacks.for_all_callbacks(|callback| {
            callback.on_device_properties_changed(
                info.clone(),
@@ -1971,6 +2124,7 @@ impl BtifBluetoothCallbacks for Bluetooth {
                    self.connection_callbacks.for_all_callbacks(|callback| {
                        callback.on_device_disconnected(info.clone());
                    });
                    device.connect_to_new_profiles = false;
                }
                tokio::spawn(async move {
                    let _ = txl.send(Message::OnDeviceDisconnected(info)).await;
@@ -2617,138 +2771,14 @@ impl IBluetooth for Bluetooth {
        if !self.profiles_ready {
            return BtStatus::NotReady;
        }
        let addr = device.address;

        if !self.get_acl_state_by_addr(&addr) {
            // 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_classic_media_profile = false;
        let mut has_le_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;
                                // TODO(b/328675014): Use BtAddrType
                                // and BtTransport from
                                // BluetoothDevice instead of default
                                let status = self.hh.as_ref().unwrap().connect(
                                    &mut addr.clone(),
                                    BtAddrType::Public,
                                    BtTransport::Auto,
                                );
                                metrics::profile_connection_state_changed(
                                    addr,
                                    p as u32,
                                    BtStatus::Success,
                                    BthhConnectionState::Connecting as u32,
                                );
        self.connect_profiles_internal(&uuids, device.clone());

                                if status != BtStatus::Success {
                                    metrics::profile_connection_state_changed(
                                        addr,
                                        p as u32,
                                        status,
                                        BthhConnectionState::Disconnected as u32,
                                    );
                                }
                            }

                            // TODO(b/317682584): implement policy to connect to LEA, VC, and CSIS
                            Profile::LeAudio | Profile::VolumeControl | Profile::CoordinatedSet
                                if !has_le_media_profile =>
                            {
                                has_le_media_profile = true;
                                let txl = self.tx.clone();
                                topstack::get_runtime().spawn(async move {
                                    let _ = txl
                                        .send(Message::Media(
                                            MediaActions::ConnectLeaGroupByMemberAddress(addr),
                                        ))
                                        .await;
                                });
                            }

                            Profile::A2dpSink | Profile::A2dpSource | Profile::Hfp
                                if !has_classic_media_profile =>
                            {
                                has_supported_profile = true;
                                has_classic_media_profile = true;
                                let txl = self.tx.clone();
                                topstack::get_runtime().spawn(async move {
                                    let _ =
                                        txl.send(Message::Media(MediaActions::Connect(addr))).await;
                                });
                            }

                            Profile::Bas => {
                                has_supported_profile = true;
                                let tx = self.tx.clone();
                                let device_context = match self.remote_devices.get(&addr) {
                                    Some(context) => context,
                                    None => return BtStatus::RemoteDeviceDown,
                                };

                                let acl_state = device_context.ble_acl_state.clone();
                                let bond_state = device_context.bond_state.clone();
                                let device_to_send = device.clone();

                                let transport = match self.get_remote_type(device.clone()) {
                                    BtDeviceType::Bredr => BtTransport::Bredr,
                                    BtDeviceType::Ble => BtTransport::Le,
                                    _ => device_context.acl_reported_transport.clone(),
                                };
                                topstack::get_runtime().spawn(async move {
                                    let _ = tx
                                        .send(Message::BatteryService(
                                            BatteryServiceActions::Connect(
                                                device_to_send,
                                                acl_state,
                                                bond_state,
                                                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.remote_devices.get_mut(&addr) {
                if uuids.is_empty() || !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();
        // Also connect to profiles discovered in the future.
        if let Some(d) = self.remote_devices.get_mut(&device.address) {
            d.connect_to_new_profiles = true;
        }

        BtStatus::Success
@@ -2853,6 +2883,10 @@ impl IBluetooth for Bluetooth {
            let _ = txl.send(Message::GattActions(GattActions::Disconnect(device))).await;
        });

        if let Some(d) = self.remote_devices.get_mut(&addr) {
            d.connect_to_new_profiles = false;
        }

        true
    }