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

Commit 7bdb3e58 authored by howardchung's avatar howardchung
Browse files

Floss: Implement set allowed services in BluetoothAdmin

This API disables the services that is not in the allowed list. A
profile is disabled by calling |disable| in topshim layer which should
call |cleanup| to the libbluetooth interface. Similarly, a profile is
re-enabled by calling |enable| in topshim and |init| of the libbluetooth
interface will be called subsequently.

However, the cleanup function of HFP and A2DP in topshim-cpp layer
haven't been implemented. By testing locally, the consequence is that
when a2dp and hfp are disabled by policy, the audio headset can still be
connected and stay connected, but there is no audio stream to the
headset nor the chromebook. Additionally, the headset could adjust the
volumn via HFP.

Bug: 239470589
Test: test it along with the top of this chain
Tag: #floss
Change-Id: Id4eba16f298599dad8d469bf719bc5ef2211c639
parent 5664351d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -334,8 +334,8 @@ impl CommandHandler {
                    let multi_adv_supported = adapter_dbus.is_multi_advertisement_supported();
                    let le_ext_adv_supported = adapter_dbus.is_le_extended_advertising_supported();
                    let wbs_supported = adapter_dbus.is_wbs_supported();
                    let enabled_profiles = UuidHelper::get_enabled_profiles();
                    let connected_profiles: Vec<Profile> = enabled_profiles
                    let supported_profiles = UuidHelper::get_supported_profiles();
                    let connected_profiles: Vec<Profile> = supported_profiles
                        .iter()
                        .filter(|&&prof| adapter_dbus.get_profile_connection_state(prof) > 0)
                        .cloned()
+84 −13
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@ use bt_topshim::btif::{
    BaseCallbacks, BaseCallbacksDispatcher, BluetoothInterface, BluetoothProperty, BtAclState,
    BtBondState, BtConnectionState, BtDeviceType, BtDiscoveryState, BtHciErrorCode, BtPinCode,
    BtPropertyType, BtScanMode, BtSspVariant, BtState, BtStatus, BtTransport, BtVendorProductInfo,
    RawAddress, Uuid, Uuid128Bit,
    RawAddress, ToggleableProfile, Uuid, Uuid128Bit,
};
use bt_topshim::{
    metrics,
@@ -394,29 +394,100 @@ impl Bluetooth {
        }
    }

    fn disable_profile(&mut self, profile: &Profile) {
        if !UuidHelper::is_profile_supported(profile) {
            return;
        }

        match profile {
            Profile::Hid => {
                self.hh.as_mut().unwrap().disable();
            }

            Profile::A2dpSource | Profile::Hfp => {
                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().enable();
            }

            Profile::A2dpSource | Profile::Hfp => {
                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.is_none() && self.hh.as_ref().unwrap().is_enabled()),

            Profile::A2dpSource | Profile::Hfp => {
                self.bluetooth_media.lock().unwrap().is_profile_enabled(profile)
            }
            // Ignore profiles that we don't connect.
            _ => None,
        }
    }

    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);
                }
            }
        }
    }

    pub fn init_profiles(&mut self) {
        let hhtx = self.tx.clone();
        self.hh = Some(HidHost::new(&self.intf.lock().unwrap()));
        self.hh.as_mut().unwrap().initialize(HHCallbacksDispatcher {
        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 = hhtx.clone();
                let txl = sdptx.clone();
                topstack::get_runtime().spawn(async move {
                    let _ = txl.send(Message::HidHost(cb)).await;
                    let _ = txl.send(Message::Sdp(cb)).await;
                });
            }),
        });

        let sdptx = self.tx.clone();
        self.sdp = Some(Sdp::new(&self.intf.lock().unwrap()));
        self.sdp.as_mut().unwrap().initialize(SdpCallbacksDispatcher {
        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 = sdptx.clone();
                let txl = hhtx.clone();
                topstack::get_runtime().spawn(async move {
                    let _ = txl.send(Message::Sdp(cb)).await;
                    let _ = txl.send(Message::HidHost(cb)).await;
                });
            }),
        });

        self.toggle_enabled_profiles(&vec![]);
        // Mark profiles as ready
        self.profiles_ready = true;
    }
@@ -1543,7 +1614,7 @@ impl IBluetooth for Bluetooth {
        for uuid in uuids.iter() {
            match UuidHelper::is_known_profile(uuid) {
                Some(p) => {
                    if UuidHelper::is_profile_enabled(&p) {
                    if UuidHelper::is_profile_supported(&p) {
                        match p {
                            Profile::Hid | Profile::Hogp => {
                                let status = self.hh.as_ref().unwrap().connect(&mut addr.unwrap());
@@ -1607,7 +1678,7 @@ impl IBluetooth for Bluetooth {
        for uuid in uuids.iter() {
            match UuidHelper::is_known_profile(uuid) {
                Some(p) => {
                    if UuidHelper::is_profile_enabled(&p) {
                    if UuidHelper::is_profile_supported(&p) {
                        match p {
                            Profile::Hid | Profile::Hogp => {
                                self.hh.as_ref().unwrap().disconnect(&mut addr.unwrap());
+7 −2
Original line number Diff line number Diff line
@@ -63,8 +63,13 @@ impl IBluetoothAdmin for BluetoothAdmin {
            self.allowed_services.insert(service.clone());
        }

        // TODO: Toggle profiles here.
        true
        if let Some(adapter) = &self.adapter {
            let allowed_services = self.get_allowed_services();
            adapter.lock().unwrap().toggle_enabled_profiles(&allowed_services);
            return true;
        }

        false
    }

    fn get_allowed_services(&self) -> Vec<Uuid128Bit> {
+86 −1
Original line number Diff line number Diff line
//! Anything related to audio and media API.

use bt_topshim::btif::{BluetoothInterface, BtConnectionDirection, BtStatus, RawAddress};
use bt_topshim::btif::{
    BluetoothInterface, BtConnectionDirection, BtStatus, RawAddress, ToggleableProfile,
};
use bt_topshim::profiles::a2dp::{
    A2dp, A2dpCallbacks, A2dpCallbacksDispatcher, A2dpCodecBitsPerSample, A2dpCodecChannelMode,
    A2dpCodecConfig, A2dpCodecSampleRate, BtavAudioState, BtavConnectionState,
@@ -175,6 +177,7 @@ pub struct BluetoothMedia {
    device_added_tasks: Arc<Mutex<HashMap<RawAddress, Option<(JoinHandle<()>, Instant)>>>>,
    absolute_volume: bool,
    uinput: UInput,
    delay_enable_profiles: HashSet<uuid::Profile>,
    connected_profiles: HashMap<RawAddress, HashSet<uuid::Profile>>,
    disconnecting_devices: HashSet<RawAddress>,
}
@@ -203,6 +206,7 @@ impl BluetoothMedia {
            device_added_tasks: Arc::new(Mutex::new(HashMap::new())),
            absolute_volume: false,
            uinput: UInput::new(),
            delay_enable_profiles: HashSet::new(),
            connected_profiles: HashMap::new(),
            disconnecting_devices: HashSet::new(),
        }
@@ -250,6 +254,78 @@ impl BluetoothMedia {
        self.adapter = Some(adapter);
    }

    pub fn enable_profile(&mut self, profile: &Profile) {
        match profile {
            &Profile::A2dpSource => {
                if let Some(a2dp) = &mut self.a2dp {
                    a2dp.enable();
                }
            }
            &Profile::AvrcpTarget => {
                if let Some(avrcp) = &mut self.avrcp {
                    avrcp.enable();
                }
            }
            &Profile::Hfp => {
                if let Some(hfp) = &mut self.hfp {
                    hfp.enable();
                }
            }
            _ => {
                warn!("Tried to enable {} in bluetooth_media", profile);
                return;
            }
        }

        if self.is_profile_enabled(profile).unwrap() {
            self.delay_enable_profiles.remove(profile);
        } else {
            self.delay_enable_profiles.insert(profile.clone());
        }
    }

    pub fn disable_profile(&mut self, profile: &Profile) {
        match profile {
            &Profile::A2dpSource => {
                if let Some(a2dp) = &mut self.a2dp {
                    a2dp.disable();
                }
            }
            &Profile::AvrcpTarget => {
                if let Some(avrcp) = &mut self.avrcp {
                    avrcp.disable();
                }
            }
            &Profile::Hfp => {
                if let Some(hfp) = &mut self.hfp {
                    hfp.disable();
                }
            }
            _ => {
                warn!("Tried to disable {} in bluetooth_media", profile);
                return;
            }
        }

        self.delay_enable_profiles.remove(profile);
    }

    pub fn is_profile_enabled(&self, profile: &Profile) -> Option<bool> {
        match profile {
            &Profile::A2dpSource => {
                Some(self.a2dp.as_ref().map_or(false, |a2dp| a2dp.is_enabled()))
            }
            &Profile::AvrcpTarget => {
                Some(self.avrcp.as_ref().map_or(false, |avrcp| avrcp.is_enabled()))
            }
            &Profile::Hfp => Some(self.hfp.as_ref().map_or(false, |hfp| hfp.is_enabled())),
            _ => {
                warn!("Tried to query enablement status of {} in bluetooth_media", profile);
                None
            }
        }
    }

    pub fn dispatch_a2dp_callbacks(&mut self, cb: A2dpCallbacks) {
        match cb {
            A2dpCallbacks::ConnectionState(addr, state, error) => {
@@ -763,6 +839,15 @@ impl IBluetoothMedia for BluetoothMedia {
        self.hfp = Some(Hfp::new(&self.intf.lock().unwrap()));
        self.hfp.as_mut().unwrap().initialize(hfp_dispatcher);

        for profile in self.delay_enable_profiles.clone() {
            self.enable_profile(&profile);
        }

        // Default to enable AVRCP since btadapterd will crash when connecting a headset while
        // avrcp is disabled.
        // TODO: fix b/251692015
        self.enable_profile(&Profile::AvrcpTarget);

        true
    }

+22 −6
Original line number Diff line number Diff line
//! Collection of Profile UUIDs and helpers to use them.

use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
use std::fmt::{Debug, Display, Formatter};

use bt_topshim::btif::{Uuid, Uuid128Bit};

@@ -76,6 +76,12 @@ pub enum Profile {
pub const BASE_UUID_NUM: u128 = 0x0000000000001000800000805f9b34fbu128;
pub const BASE_UUID_MASK: u128 = !(0xffffffffu128 << 96);

impl Display for Profile {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        Debug::fmt(self, f)
    }
}

/// Wraps a reference of Uuid128Bit, which is the raw array of bytes of UUID.
/// This is useful in implementing standard Rust traits which can't be implemented directly on
/// built-in types (Rust's Orphan Rule).
@@ -99,7 +105,7 @@ impl<'a> Display for KnownUuidWrapper<'a> {
pub struct UuidHelper {}

lazy_static! {
    static ref ENABLED_PROFILES: HashSet<Profile> = [
    static ref SUPPORTED_PROFILES: HashSet<Profile> = [
        Profile::A2dpSink,
        Profile::A2dpSource,
        Profile::Hsp,
@@ -154,10 +160,15 @@ lazy_static! {
    .collect();
}

lazy_static! {
    static ref PROFILES_UUIDS: HashMap<Profile, Uuid128Bit> =
        PROFILES.iter().map(|(k, v)| (v.clone(), k.clone())).collect();
}

impl UuidHelper {
    /// Checks whether a UUID corresponds to a currently enabled profile.
    pub fn is_profile_enabled(profile: &Profile) -> bool {
        ENABLED_PROFILES.contains(profile)
    pub fn is_profile_supported(profile: &Profile) -> bool {
        SUPPORTED_PROFILES.contains(profile)
    }

    /// Converts a UUID to a known profile enum.
@@ -165,8 +176,13 @@ impl UuidHelper {
        PROFILES.get(uuid).cloned()
    }

    pub fn get_enabled_profiles() -> HashSet<Profile> {
        ENABLED_PROFILES.clone()
    pub fn get_supported_profiles() -> HashSet<Profile> {
        SUPPORTED_PROFILES.clone()
    }

    /// Converts a profile enum to its UUID if known.
    pub fn get_profile_uuid(profile: &Profile) -> Option<&Uuid128Bit> {
        PROFILES_UUIDS.get(profile)
    }

    /// Converts a UUID byte array into a formatted string.