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

Commit 868b8824 authored by howardchung's avatar howardchung
Browse files

Floss: Implement admin policy callbacks

Implement the following callbacks for client.

- OnServiceAllowlistChanged:
Get the updated service allowlist

- OnDevicePolicyEffectChanged:
Get the updated policy effect, which includes
(1) list of services that are disallowed by the current policy
(2) boolean value to indicate if the device has an adapter-supported
profile that is blocked by the policy.

Bug: 239470493
Test: test it along with the next patch
Tag: #floss
Change-Id: Idb770f9a7a34401d2d58421fb18b07013330cb09
parent aa529fbd
Loading
Loading
Loading
Loading
+38 −3
Original line number Diff line number Diff line
use btstack::bluetooth_admin::{IBluetoothAdmin, PolicyEffect};
use btstack::bluetooth_admin::{IBluetoothAdmin, IBluetoothAdminPolicyCallback, PolicyEffect};

use dbus::arg::RefArg;
use dbus_macros::{dbus_method, dbus_propmap, generate_dbus_exporter};
use dbus::Path;
use dbus_macros::{dbus_method, dbus_propmap, dbus_proxy_obj, generate_dbus_exporter};

use dbus_projection::dbus_generated;
use dbus_projection::{dbus_generated, DisconnectWatcher};

use crate::dbus_arg::{DBusArg, DBusArgError, RefArgToRust};

use bt_topshim::btif::Uuid128Bit;

use btstack::bluetooth::BluetoothDevice;
use btstack::RPCProxy;

struct IBluetoothAdminDBus {}

struct IBluetoothAdminPolicyCallbackDBus {}

#[dbus_propmap(PolicyEffect)]
pub struct PolicyEffectDBus {
    pub service_blocked: Vec<Uuid128Bit>,
    pub affected: bool,
}

#[dbus_proxy_obj(AdminPolicyCallback, "org.chromium.bluetooth.AdminPolicyCallback")]
impl IBluetoothAdminPolicyCallback for IBluetoothAdminPolicyCallbackDBus {
    #[dbus_method("OnServiceAllowlistChanged")]
    fn on_service_allowlist_changed(&self, allowlist: Vec<Uuid128Bit>) {
        dbus_generated!()
    }

    #[dbus_method("OnDevicePolicyEffectChanged")]
    fn on_device_policy_effect_changed(
        &self,
        device: BluetoothDevice,
        new_policy_effect: Option<PolicyEffect>,
    ) {
        dbus_generated!()
    }
}

#[generate_dbus_exporter(export_bluetooth_admin_dbus_intf, "org.chromium.bluetooth.BluetoothAdmin")]
@@ -39,4 +61,17 @@ impl IBluetoothAdmin for IBluetoothAdminDBus {
    fn get_device_policy_effect(&self, device: BluetoothDevice) -> Option<PolicyEffect> {
        dbus_generated!()
    }

    #[dbus_method("RegisterAdminPolicyCallback")]
    fn register_admin_policy_callback(
        &mut self,
        callback: Box<dyn IBluetoothAdminPolicyCallback + Send>,
    ) -> u32 {
        dbus_generated!()
    }

    #[dbus_method("UnregisterAdminPolicyCallback")]
    fn unregister_admin_policy_callback(&mut self, callback_id: u32) -> bool {
        dbus_generated!()
    }
}
+6 −2
Original line number Diff line number Diff line
@@ -128,13 +128,16 @@ fn main() -> Result<(), Box<dyn Error>> {
        battery_provider_manager.clone(),
        tx.clone(),
    ))));
    let bluetooth_admin =
        Arc::new(Mutex::new(Box::new(BluetoothAdmin::new(String::from(ADMIN_SETTINGS_FILE_PATH)))));
    let bluetooth_admin = Arc::new(Mutex::new(Box::new(BluetoothAdmin::new(
        String::from(ADMIN_SETTINGS_FILE_PATH),
        tx.clone(),
    ))));
    let bluetooth = Arc::new(Mutex::new(Box::new(Bluetooth::new(
        tx.clone(),
        intf.clone(),
        bluetooth_media.clone(),
        sig_notifier.clone(),
        bluetooth_admin.clone(),
    ))));
    let suspend = Arc::new(Mutex::new(Box::new(Suspend::new(
        bluetooth.clone(),
@@ -185,6 +188,7 @@ fn main() -> Result<(), Box<dyn Error>> {
            bluetooth_media.clone(),
            suspend.clone(),
            bt_sock_mgr.clone(),
            bluetooth_admin.clone(),
        ));

        // Set up the disconnect watcher to monitor client disconnects.
+37 −7
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ use btif_macros::{btif_callback, btif_callbacks_dispatcher};
use log::{debug, warn};
use num_traits::cast::ToPrimitive;
use std::collections::HashMap;
use std::hash::Hash;
use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;
use std::time::Instant;
@@ -28,6 +29,7 @@ use tokio::sync::mpsc::Sender;
use tokio::task::JoinHandle;
use tokio::time;

use crate::bluetooth_admin::BluetoothAdmin;
use crate::bluetooth_media::{BluetoothMedia, IBluetoothMedia, MediaActions};
use crate::callbacks::Callbacks;
use crate::uuid::{Profile, UuidHelper, HOGP};
@@ -204,7 +206,7 @@ pub trait IBluetoothQA {
}

/// Serializable device used in various apis.
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct BluetoothDevice {
    pub address: String,
    pub name: String,
@@ -259,11 +261,11 @@ impl BluetoothDeviceContext {
            last_seen,
            properties: HashMap::new(),
        };
        device.update_properties(properties);
        device.update_properties(&properties);
        device
    }

    pub(crate) fn update_properties(&mut self, in_properties: Vec<BluetoothProperty>) {
    pub(crate) fn update_properties(&mut self, in_properties: &Vec<BluetoothProperty>) {
        for prop in in_properties {
            match &prop {
                BluetoothProperty::BdAddr(bdaddr) => {
@@ -275,7 +277,7 @@ impl BluetoothDeviceContext {
                _ => {}
            }

            self.properties.insert(prop.get_type(), prop);
            self.properties.insert(prop.get_type(), prop.clone());
        }
    }

@@ -321,6 +323,22 @@ pub trait IBluetoothCallback: RPCProxy {
    fn on_bond_state_changed(&self, status: u32, device_address: String, state: u32);
}

/// 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(&self, remote_device: BluetoothDevice);
@@ -335,6 +353,7 @@ pub struct Bluetooth {

    bonded_devices: HashMap<String, BluetoothDeviceContext>,
    bluetooth_media: Arc<Mutex<Box<BluetoothMedia>>>,
    bluetooth_admin: Arc<Mutex<Box<BluetoothAdmin>>>,
    callbacks: Callbacks<dyn IBluetoothCallback + Send>,
    connection_callbacks: Callbacks<dyn IBluetoothConnectionCallback + Send>,
    discovering_started: Instant,
@@ -365,6 +384,7 @@ impl Bluetooth {
        intf: Arc<Mutex<BluetoothInterface>>,
        bluetooth_media: Arc<Mutex<Box<BluetoothMedia>>>,
        sig_notifier: Arc<(Mutex<bool>, Condvar)>,
        bluetooth_admin: Arc<Mutex<Box<BluetoothAdmin>>>,
    ) -> Bluetooth {
        Bluetooth {
            bonded_devices: HashMap::new(),
@@ -375,6 +395,7 @@ impl Bluetooth {
            ),
            hh: None,
            bluetooth_media,
            bluetooth_admin,
            discovering_started: Instant::now(),
            intf,
            is_connectable: false,
@@ -588,6 +609,8 @@ impl Bluetooth {
            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.
@@ -852,7 +875,7 @@ impl BtifBluetoothCallbacks for Bluetooth {
        let address = device.address.clone();

        if let Some(existing) = self.found_devices.get_mut(&address) {
            existing.update_properties(properties);
            existing.update_properties(&properties);
            existing.seen();
        } else {
            let device_with_props = BluetoothDeviceContext::new(
@@ -870,6 +893,8 @@ impl BtifBluetoothCallbacks for Bluetooth {
        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) {
@@ -1014,7 +1039,7 @@ impl BtifBluetoothCallbacks for Bluetooth {

        match device {
            Some(d) => {
                d.update_properties(properties);
                d.update_properties(&properties);
                d.seen();

                Bluetooth::send_metrics_remote_device_info(d);
@@ -1022,8 +1047,13 @@ impl BtifBluetoothCallbacks for Bluetooth {
                let info = d.info.clone();
                let uuids = self.get_remote_uuids(info.clone());
                if self.wait_to_connect && uuids.len() > 0 {
                    self.connect_all_enabled_profiles(info);
                    self.connect_all_enabled_profiles(info.clone());
                }

                self.bluetooth_admin
                    .lock()
                    .unwrap()
                    .on_remote_device_properties_changed(&info, &properties);
            }
            None => (),
        };
+133 −16
Original line number Diff line number Diff line
//! Anything related to the Admin API (IBluetoothAdmin).

use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::{Read, Result, Write};
use std::sync::{Arc, Mutex};

use crate::bluetooth::{Bluetooth, BluetoothDevice, IBluetooth};
use crate::callbacks::Callbacks;
use crate::uuid::UuidHelper;
use bt_topshim::btif::Uuid128Bit;
use crate::{Message, RPCProxy};

use bt_topshim::btif::{BluetoothProperty, Uuid128Bit};
use log::{info, warn};
use serde_json::{json, Value};
use tokio::sync::mpsc::Sender;

/// Defines the Admin API
pub trait IBluetoothAdmin {
@@ -21,26 +25,56 @@ pub trait IBluetoothAdmin {
    fn get_allowed_services(&self) -> Vec<Uuid128Bit>;
    /// Get the PolicyEffect struct of a device
    fn get_device_policy_effect(&self, device: BluetoothDevice) -> Option<PolicyEffect>;
    /// Register client callback
    fn register_admin_policy_callback(
        &mut self,
        callback: Box<dyn IBluetoothAdminPolicyCallback + Send>,
    ) -> u32;
    /// Unregister client callback via callback ID
    fn unregister_admin_policy_callback(&mut self, callback_id: u32) -> bool;
}

/// Information of the effects to a remote device by the admin policies
#[derive(PartialEq, Clone)]
pub struct PolicyEffect {
    /// Array of services that are blocked by policy
    pub service_blocked: Vec<Uuid128Bit>,
    /// Indicate if the device has an adapter-supported profile that is blocked by the policy
    pub affected: bool,
}

pub trait IBluetoothAdminPolicyCallback: RPCProxy {
    /// This gets called when service allowlist changed.
    fn on_service_allowlist_changed(&self, allowlist: Vec<Uuid128Bit>);
    /// This gets called when
    /// 1. a new device is found by adapter
    /// 2. the policy effect to a device is changed due to
    ///    the remote services changed or
    ///    the service allowlist changed.
    fn on_device_policy_effect_changed(
        &self,
        device: BluetoothDevice,
        new_policy_effect: Option<PolicyEffect>,
    );
}

pub struct BluetoothAdmin {
    path: String,
    adapter: Option<Arc<Mutex<Box<Bluetooth>>>>,
    allowed_services: HashSet<Uuid128Bit>,
    callbacks: Callbacks<dyn IBluetoothAdminPolicyCallback + Send>,
    device_policy_affect_cache: HashMap<BluetoothDevice, Option<PolicyEffect>>,
}

impl BluetoothAdmin {
    pub fn new(path: String) -> BluetoothAdmin {
    pub fn new(path: String, tx: Sender<Message>) -> BluetoothAdmin {
        // default admin settings
        let mut admin = BluetoothAdmin {
            path,
            adapter: None,
            allowed_services: HashSet::new(), //empty means allowed all services
            callbacks: Callbacks::new(tx.clone(), Message::AdminCallbackDisconnected),
            device_policy_affect_cache: HashMap::new(),
        };

        if admin.load_config().is_err() {
@@ -50,7 +84,7 @@ impl BluetoothAdmin {
    }

    pub fn set_adapter(&mut self, adapter: Arc<Mutex<Box<Bluetooth>>>) {
        self.adapter = Some(adapter);
        self.adapter = Some(adapter.clone());
    }

    fn get_blocked_services(&self, remote_uuids: &Vec<Uuid128Bit>) -> Vec<Uuid128Bit> {
@@ -61,6 +95,17 @@ impl BluetoothAdmin {
            .collect::<Vec<Uuid128Bit>>()
    }

    fn get_affected_status(&self, blocked_services: &Vec<Uuid128Bit>) -> bool {
        // return true if a supported profile is in blocked services.
        blocked_services
            .iter()
            .find(|&uuid| {
                UuidHelper::is_known_profile(uuid)
                    .map_or(false, |p| UuidHelper::is_profile_supported(&p))
            })
            .is_some()
    }

    fn load_config(&mut self) -> Result<()> {
        let mut file = File::open(&self.path)?;
        let mut contents = String::new();
@@ -102,6 +147,50 @@ impl BluetoothAdmin {
        .ok()
        .unwrap()
    }

    fn new_device_policy_effect(&self, uuids: Option<Vec<Uuid128Bit>>) -> Option<PolicyEffect> {
        uuids.map(|uuids| {
            let service_blocked = self.get_blocked_services(&uuids);
            let affected = self.get_affected_status(&service_blocked);
            PolicyEffect { service_blocked, affected }
        })
    }

    pub fn on_device_found(&mut self, remote_device: &BluetoothDevice) {
        self.device_policy_affect_cache.insert(remote_device.clone(), None).or_else(|| {
            self.callbacks.for_all_callbacks(|cb| {
                cb.on_device_policy_effect_changed(remote_device.clone(), None);
            });
            None
        });
    }

    pub fn on_device_cleared(&mut self, remote_device: &BluetoothDevice) {
        self.device_policy_affect_cache.remove(remote_device);
    }

    pub fn on_remote_device_properties_changed(
        &mut self,
        remote_device: &BluetoothDevice,
        properties: &Vec<BluetoothProperty>,
    ) {
        let new_uuids = properties.iter().find_map(|p| match p {
            BluetoothProperty::Uuids(uuids) => {
                Some(uuids.iter().map(|&x| x.uu.clone()).collect::<Vec<Uuid128Bit>>())
            }
            _ => None,
        });

        let new_effect = self.new_device_policy_effect(new_uuids);
        let cur_effect = self.device_policy_affect_cache.get(remote_device);

        if cur_effect.is_none() || *cur_effect.unwrap() != new_effect.clone() {
            self.callbacks.for_all_callbacks(|cb| {
                cb.on_device_policy_effect_changed(remote_device.clone(), new_effect.clone())
            });
            self.device_policy_affect_cache.insert(remote_device.clone(), new_effect.clone());
        }
    }
}

impl IBluetoothAdmin for BluetoothAdmin {
@@ -110,6 +199,11 @@ impl IBluetoothAdmin for BluetoothAdmin {
    }

    fn set_allowed_services(&mut self, services: Vec<Uuid128Bit>) -> bool {
        if self.get_allowed_services() == services {
            // Allowlist is not changed.
            return true;
        }

        self.allowed_services.clear();

        for service in services.iter() {
@@ -122,6 +216,22 @@ impl IBluetoothAdmin for BluetoothAdmin {
            if self.write_config().is_err() {
                warn!("Failed to write config");
            }

            self.callbacks.for_all_callbacks(|cb| {
                cb.on_service_allowlist_changed(self.get_allowed_services());
            });

            for (device, effect) in self.device_policy_affect_cache.clone().iter() {
                let uuids = adapter.lock().unwrap().get_remote_uuids(device.clone());
                let new_effect = self.new_device_policy_effect(Some(uuids));

                if new_effect.clone() != *effect {
                    self.callbacks.for_all_callbacks(|cb| {
                        cb.on_device_policy_effect_changed(device.clone(), new_effect.clone())
                    });
                    self.device_policy_affect_cache.insert(device.clone(), new_effect.clone());
                }
            }
            return true;
        }

@@ -133,26 +243,31 @@ impl IBluetoothAdmin for BluetoothAdmin {
    }

    fn get_device_policy_effect(&self, device: BluetoothDevice) -> Option<PolicyEffect> {
        if let Some(adapter) = &self.adapter {
            let service_blocked =
                self.get_blocked_services(&adapter.lock().unwrap().get_remote_uuids(device));

            if service_blocked.is_empty() {
                None
            } else {
                Some(PolicyEffect { service_blocked })
            }
        if let Some(effect) = self.device_policy_affect_cache.get(&device) {
            effect.clone()
        } else {
            warn!("Adapter not found");
            warn!("Device not found in cache");
            None
        }
    }

    fn register_admin_policy_callback(
        &mut self,
        callback: Box<dyn IBluetoothAdminPolicyCallback + Send>,
    ) -> u32 {
        self.callbacks.add_callback(callback)
    }

    fn unregister_admin_policy_callback(&mut self, callback_id: u32) -> bool {
        self.callbacks.remove_callback(callback_id)
    }
}

#[cfg(test)]
mod tests {
    use crate::bluetooth_admin::{BluetoothAdmin, IBluetoothAdmin};
    use crate::uuid::UuidHelper;
    use crate::Stack;
    use bt_topshim::btif::Uuid128Bit;

    // A workaround needed for linking. For more details, check the comment in
@@ -163,7 +278,8 @@ mod tests {

    #[test]
    fn test_set_service_allowed() {
        let mut admin = BluetoothAdmin::new(String::from(""));
        let (tx, _) = Stack::create_channel();
        let mut admin = BluetoothAdmin::new(String::from(""), tx.clone());
        let uuid1: Uuid128Bit = [1; 16];
        let uuid2: Uuid128Bit = [2; 16];
        let uuid3: Uuid128Bit = [3; 16];
@@ -214,7 +330,8 @@ mod tests {

    #[test]
    fn test_config() {
        let mut admin = BluetoothAdmin::new(String::from(""));
        let (tx, _) = Stack::create_channel();
        let mut admin = BluetoothAdmin::new(String::from(""), tx.clone());
        let a2dp_sink = "0000110b-0000-1000-8000-00805f9b34fb";
        let a2dp_source = "0000110a-0000-1000-8000-00805f9b34fb";

+8 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ use crate::bluetooth::{
    dispatch_base_callbacks, dispatch_hid_host_callbacks, dispatch_sdp_callbacks, Bluetooth,
    BluetoothDevice, IBluetooth,
};
use crate::bluetooth_admin::{BluetoothAdmin, IBluetoothAdmin};
use crate::bluetooth_gatt::{
    dispatch_gatt_client_callbacks, dispatch_le_adv_callbacks, dispatch_le_scanner_callbacks,
    dispatch_le_scanner_inband_callbacks, BluetoothGatt,
@@ -108,6 +109,9 @@ pub enum Message {
    BatteryManagerCallbackDisconnected(u32),

    GattClientCallbackDisconnected(u32),

    // Admin policy related
    AdminCallbackDisconnected(u32),
}

/// Represents suspend mode of a module.
@@ -142,6 +146,7 @@ impl Stack {
        bluetooth_media: Arc<Mutex<Box<BluetoothMedia>>>,
        suspend: Arc<Mutex<Box<Suspend>>>,
        bluetooth_socketmgr: Arc<Mutex<Box<BluetoothSocketManager>>>,
        bluetooth_admin: Arc<Mutex<Box<BluetoothAdmin>>>,
    ) {
        loop {
            let m = rx.recv().await;
@@ -290,6 +295,9 @@ impl Stack {
                Message::GattClientCallbackDisconnected(id) => {
                    bluetooth_gatt.lock().unwrap().remove_client_callback(id);
                }
                Message::AdminCallbackDisconnected(id) => {
                    bluetooth_admin.lock().unwrap().unregister_admin_policy_callback(id);
                }
            }
        }
    }