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

Commit a0c5c54a authored by Yun-Hao Chung's avatar Yun-Hao Chung
Browse files

Floss: Refactor DBus intf and only expose after interal API is ready

This does 2 things.

1. refactor the work of DBus interface registration to a separated
struct, i.e. InterfaceManager.

2. delay the DBus interface registration until the api is ready.

The reason why (2) is needed is because in many implementations, our
APIs assume the underlying libbluetooth API is ready when the DBus
method is called. If we register the API to DBus before it is ready,
there is a tiny window that clients could poke the API and cause crash
or get an unexpected result of it.

(1) is just to make implement (2) easier.

Bug: 299431339
Test: mma -j 32
Test: verify adapter/media/battery interfaces are there via gdbus
Test: perform mouse, kbd, headset CUJ on brya/skolas
Tag: #floss
Change-Id: If532032bf29b796e4fc46a68b90fa9ebc6a0756e
parent 64b6ac66
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ use dbus::message::MatchRule;
use dbus::nonblock::SyncConnection;
use dbus_crossroads::Crossroads;
use tokio::sync::mpsc;
use tokio::time::timeout;
use tokio::time::{sleep, timeout};

use crate::bt_adv::AdvSet;
use crate::bt_gatt::GattClientContext;
@@ -246,6 +246,9 @@ impl ClientContext {
        let fg = self.fg.clone();
        tokio::spawn(async move {
            let adapter = String::from(format!("adapter{}", idx));
            // Floss won't export the interface until it is ready to be used.
            // Wait 1 second before registering the callbacks.
            sleep(Duration::from_millis(1000)).await;
            let _ = fg.send(ForegroundActions::RegisterAdapterCallback(adapter)).await;
        });
    }
+1 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ impl_dbus_arg_from_into!(BtStatus, u32);

/// A mixin of the several interfaces. The naming of the fields in the mixin must match
/// what is listed in the `generate_dbus_exporter` invocation.
#[derive(Clone)]
pub struct BluetoothMixin {
    pub adapter: Arc<Mutex<Box<Bluetooth>>>,
    pub qa: Arc<Mutex<Box<Bluetooth>>>,
+231 −0
Original line number Diff line number Diff line
use dbus::{channel::MatchingReceiver, message::MatchRule, nonblock::SyncConnection};
use dbus_crossroads::Crossroads;
use dbus_projection::DisconnectWatcher;

use std::sync::{Arc, Mutex};
use tokio::sync::mpsc::{channel, Receiver, Sender};

use btstack::{
    battery_manager::BatteryManager, battery_provider_manager::BatteryProviderManager,
    bluetooth::Bluetooth, bluetooth_admin::BluetoothAdmin, bluetooth_gatt::BluetoothGatt,
    bluetooth_logging::BluetoothLogging, bluetooth_media::BluetoothMedia,
    bluetooth_qa::BluetoothQA, socket_manager::BluetoothSocketManager, suspend::Suspend,
    APIMessage, BluetoothAPI,
};

use crate::iface_battery_manager;
use crate::iface_battery_provider_manager;
use crate::iface_bluetooth;
use crate::iface_bluetooth_admin;
use crate::iface_bluetooth_gatt;
use crate::iface_bluetooth_media;
use crate::iface_bluetooth_qa;
use crate::iface_bluetooth_telephony;
use crate::iface_logging;

pub(crate) struct InterfaceManager {}

impl InterfaceManager {
    fn make_object_name(idx: i32, name: &str) -> String {
        String::from(format!("/org/chromium/bluetooth/hci{}/{}", idx, name))
    }

    /// Creates an mpsc channel for passing messages to the main dispatch loop.
    pub fn create_channel() -> (Sender<APIMessage>, Receiver<APIMessage>) {
        channel::<APIMessage>(1)
    }

    pub async fn dispatch(
        mut rx: Receiver<APIMessage>,
        adapter_index: i32,
        conn: Arc<SyncConnection>,
        disconnect_watcher: Arc<Mutex<DisconnectWatcher>>,
        bluetooth: Arc<Mutex<Box<Bluetooth>>>,
        bluetooth_admin: Arc<Mutex<Box<BluetoothAdmin>>>,
        bluetooth_gatt: Arc<Mutex<Box<BluetoothGatt>>>,
        battery_manager: Arc<Mutex<Box<BatteryManager>>>,
        battery_provider_manager: Arc<Mutex<Box<BatteryProviderManager>>>,
        bluetooth_media: Arc<Mutex<Box<BluetoothMedia>>>,
        bluetooth_qa: Arc<Mutex<Box<BluetoothQA>>>,
        bt_sock_mgr: Arc<Mutex<Box<BluetoothSocketManager>>>,
        suspend: Arc<Mutex<Box<Suspend>>>,
        logging: Arc<Mutex<Box<BluetoothLogging>>>,
    ) {
        // Prepare D-Bus interfaces.
        let cr = Arc::new(Mutex::new(Crossroads::new()));
        cr.lock().unwrap().set_async_support(Some((
            conn.clone(),
            Box::new(|x| {
                tokio::spawn(x);
            }),
        )));

        // Announce the exported adapter objects so that clients can properly detect the readiness
        // of the adapter APIs.
        cr.lock().unwrap().set_object_manager_support(Some(conn.clone()));
        let object_manager = cr.lock().unwrap().object_manager();
        cr.lock().unwrap().insert("/", &[object_manager], {});

        // Set up handling of D-Bus methods. This must be done before exporting interfaces so that
        // clients that rely on InterfacesAdded signal can rely on us being ready to handle methods
        // on those exported interfaces.
        let cr_clone = cr.clone();
        conn.start_receive(
            MatchRule::new_method_call(),
            Box::new(move |msg, conn| {
                cr_clone.lock().unwrap().handle_message(msg, conn).unwrap();
                true
            }),
        );

        // Register D-Bus method handlers of IBluetooth.
        let adapter_iface = iface_bluetooth::export_bluetooth_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );
        let qa_iface = iface_bluetooth_qa::export_bluetooth_qa_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );
        let qa_legacy_iface = iface_bluetooth::export_bluetooth_qa_legacy_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );
        let socket_mgr_iface = iface_bluetooth::export_socket_mgr_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );
        let suspend_iface = iface_bluetooth::export_suspend_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );
        let logging_iface = iface_logging::export_bluetooth_logging_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        // Register D-Bus method handlers of IBluetoothGatt.
        let gatt_iface = iface_bluetooth_gatt::export_bluetooth_gatt_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        let media_iface = iface_bluetooth_media::export_bluetooth_media_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        let telephony_iface = iface_bluetooth_telephony::export_bluetooth_telephony_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        let battery_provider_manager_iface =
            iface_battery_provider_manager::export_battery_provider_manager_dbus_intf(
                conn.clone(),
                &mut cr.lock().unwrap(),
                disconnect_watcher.clone(),
            );

        let battery_manager_iface = iface_battery_manager::export_battery_manager_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        let admin_iface = iface_bluetooth_admin::export_bluetooth_admin_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        // Create mixin object for Bluetooth + Suspend interfaces.
        let mixin = Box::new(iface_bluetooth::BluetoothMixin {
            adapter: bluetooth.clone(),
            qa: bluetooth.clone(),
            suspend: suspend.clone(),
            socket_mgr: bt_sock_mgr.clone(),
        });

        loop {
            let m = rx.recv().await;

            if m.is_none() {
                eprintln!("APIMessage dispatch loop quit");
                break;
            }

            match m.unwrap() {
                APIMessage::IsReady(api) => match api {
                    BluetoothAPI::Adapter => {
                        cr.lock().unwrap().insert(
                            Self::make_object_name(adapter_index, "adapter"),
                            &[adapter_iface, qa_legacy_iface, socket_mgr_iface, suspend_iface],
                            mixin.clone(),
                        );

                        cr.lock().unwrap().insert(
                            Self::make_object_name(adapter_index, "admin"),
                            &[admin_iface],
                            bluetooth_admin.clone(),
                        );

                        cr.lock().unwrap().insert(
                            Self::make_object_name(adapter_index, "logging"),
                            &[logging_iface],
                            logging.clone(),
                        );

                        cr.lock().unwrap().insert(
                            Self::make_object_name(adapter_index, "qa"),
                            &[qa_iface],
                            bluetooth_qa.clone(),
                        );
                    }
                    BluetoothAPI::Gatt => {
                        cr.lock().unwrap().insert(
                            Self::make_object_name(adapter_index, "gatt"),
                            &[gatt_iface],
                            bluetooth_gatt.clone(),
                        );
                    }
                    BluetoothAPI::Media => {
                        cr.lock().unwrap().insert(
                            Self::make_object_name(adapter_index, "media"),
                            &[media_iface],
                            bluetooth_media.clone(),
                        );

                        cr.lock().unwrap().insert(
                            Self::make_object_name(adapter_index, "telephony"),
                            &[telephony_iface],
                            bluetooth_media.clone(),
                        );
                    }
                    BluetoothAPI::Battery => {
                        cr.lock().unwrap().insert(
                            Self::make_object_name(adapter_index, "battery_provider_manager"),
                            &[battery_provider_manager_iface],
                            battery_provider_manager.clone(),
                        );

                        cr.lock().unwrap().insert(
                            Self::make_object_name(adapter_index, "battery_manager"),
                            &[battery_manager_iface],
                            battery_manager.clone(),
                        );
                    }
                },
            }
        }
    }
}
+17 −162
Original line number Diff line number Diff line
use btstack::bluetooth_qa::BluetoothQA;
use clap::{App, AppSettings, Arg};
use dbus::{channel::MatchingReceiver, message::MatchRule};
use dbus_crossroads::Crossroads;
use dbus_projection::DisconnectWatcher;
use dbus_tokio::connection;
use futures::future;
use lazy_static::lazy_static;
@@ -9,6 +7,7 @@ use nix::sys::signal;
use std::error::Error;
use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;
use tokio::sync::mpsc::Sender;
use tokio::time;

// Necessary to link right entries.
@@ -25,13 +24,12 @@ use btstack::{
    bluetooth_gatt::BluetoothGatt,
    bluetooth_logging::BluetoothLogging,
    bluetooth_media::BluetoothMedia,
    bluetooth_qa::BluetoothQA,
    dis::DeviceInformation,
    socket_manager::BluetoothSocketManager,
    suspend::Suspend,
    Message, Stack,
};
use dbus_projection::DisconnectWatcher;
use tokio::sync::mpsc::Sender;

mod dbus_arg;
mod iface_battery_manager;
@@ -43,6 +41,7 @@ mod iface_bluetooth_media;
mod iface_bluetooth_qa;
mod iface_bluetooth_telephony;
mod iface_logging;
mod interface_manager;

const DBUS_SERVICE_NAME: &str = "org.chromium.bluetooth";
const ADMIN_SETTINGS_FILE_PATH: &str = "/var/lib/bluetooth/admin_policy.json";
@@ -61,10 +60,6 @@ const VERBOSE_ONLY_LOG_TAGS: &[&str] = &[
    "uipc",      // Userspace IPC implementation
];

fn make_object_name(idx: i32, name: &str) -> String {
    String::from(format!("/org/chromium/bluetooth/hci{}/{}", idx, name))
}

/// Runs the Bluetooth daemon serving D-Bus IPC.
fn main() -> Result<(), Box<dyn Error>> {
    let matches = App::new("Bluetooth Adapter Daemon")
@@ -137,6 +132,7 @@ fn main() -> Result<(), Box<dyn Error>> {
    init_flags.push(String::from("INIT_classic_discovery_only=true"));

    let (tx, rx) = Stack::create_channel();
    let (api_tx, api_rx) = interface_manager::InterfaceManager::create_channel();
    let sig_notifier = Arc::new(SigData {
        enabled: Mutex::new(false),
        enabled_notify: Condvar::new(),
@@ -171,6 +167,7 @@ fn main() -> Result<(), Box<dyn Error>> {
        adapter_index,
        hci_index,
        tx.clone(),
        api_tx.clone(),
        sig_notifier.clone(),
        intf.clone(),
        bluetooth_admin.clone(),
@@ -207,21 +204,6 @@ fn main() -> Result<(), Box<dyn Error>> {
        // Request a service name and quit if not able to.
        conn.request_name(DBUS_SERVICE_NAME, false, true, false).await?;

        // Prepare D-Bus interfaces.
        let cr = Arc::new(Mutex::new(Crossroads::new()));
        cr.lock().unwrap().set_async_support(Some((
            conn.clone(),
            Box::new(|x| {
                tokio::spawn(x);
            }),
        )));

        // Announce the exported adapter objects so that clients can properly detect the readiness
        // of the adapter APIs.
        cr.lock().unwrap().set_object_manager_support(Some(conn.clone()));
        let object_manager = cr.lock().unwrap().object_manager();
        cr.lock().unwrap().insert("/", &[object_manager], {});

        // Run the stack main dispatch loop.
        topstack::get_runtime().spawn(Stack::dispatch(
            rx,
@@ -242,149 +224,22 @@ fn main() -> Result<(), Box<dyn Error>> {
        let disconnect_watcher = Arc::new(Mutex::new(DisconnectWatcher::new()));
        disconnect_watcher.lock().unwrap().setup_watch(conn.clone()).await;

        // Set up handling of D-Bus methods. This must be done before exporting interfaces so that
        // clients that rely on InterfacesAdded signal can rely on us being ready to handle methods
        // on those exported interfaces.
        let cr_clone = cr.clone();
        conn.start_receive(
            MatchRule::new_method_call(),
            Box::new(move |msg, conn| {
                cr_clone.lock().unwrap().handle_message(msg, conn).unwrap();
                true
            }),
        );

        // Register D-Bus method handlers of IBluetooth.
        let adapter_iface = iface_bluetooth::export_bluetooth_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );
        let qa_iface = iface_bluetooth_qa::export_bluetooth_qa_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );
        let qa_legacy_iface = iface_bluetooth::export_bluetooth_qa_legacy_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );
        let socket_mgr_iface = iface_bluetooth::export_socket_mgr_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );
        let suspend_iface = iface_bluetooth::export_suspend_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );
        let logging_iface = iface_logging::export_bluetooth_logging_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        // Register D-Bus method handlers of IBluetoothGatt.
        let gatt_iface = iface_bluetooth_gatt::export_bluetooth_gatt_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        let media_iface = iface_bluetooth_media::export_bluetooth_media_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        let telephony_iface = iface_bluetooth_telephony::export_bluetooth_telephony_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        let battery_provider_manager_iface =
            iface_battery_provider_manager::export_battery_provider_manager_dbus_intf(
                conn.clone(),
                &mut cr.lock().unwrap(),
                disconnect_watcher.clone(),
            );

        let battery_manager_iface = iface_battery_manager::export_battery_manager_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        let admin_iface = iface_bluetooth_admin::export_bluetooth_admin_dbus_intf(
        tokio::spawn(interface_manager::InterfaceManager::dispatch(
            api_rx,
            adapter_index,
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        // Create mixin object for Bluetooth + Suspend interfaces.
        let mixin = Box::new(iface_bluetooth::BluetoothMixin {
            adapter: bluetooth.clone(),
            qa: bluetooth.clone(),
            suspend: suspend.clone(),
            socket_mgr: bt_sock_mgr.clone(),
        });

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "adapter"),
            &[adapter_iface, qa_legacy_iface, socket_mgr_iface, suspend_iface],
            mixin,
        );

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "gatt"),
            &[gatt_iface],
            bluetooth.clone(),
            bluetooth_admin.clone(),
            bluetooth_gatt.clone(),
        );

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "media"),
            &[media_iface],
            bluetooth_media.clone(),
        );

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "telephony"),
            &[telephony_iface],
            bluetooth_media.clone(),
        );

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "battery_provider_manager"),
            &[battery_provider_manager_iface],
            battery_provider_manager.clone(),
        );

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "battery_manager"),
            &[battery_manager_iface],
            battery_manager.clone(),
        );

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "admin"),
            &[admin_iface],
            bluetooth_admin.clone(),
        );

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "logging"),
            &[logging_iface],
            logging.clone(),
        );

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "qa"),
            &[qa_iface],
            battery_provider_manager.clone(),
            bluetooth_media.clone(),
            bluetooth_qa.clone(),
        );
            bt_sock_mgr.clone(),
            suspend.clone(),
            logging.clone(),
        ));

        // Hold locks and initialize all interfaces. This must be done AFTER DBus is
        // initialized so DBus can properly enforce user policies.
+14 −1
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@ use crate::bluetooth_media::{BluetoothMedia, IBluetoothMedia, MediaActions};
use crate::callbacks::Callbacks;
use crate::socket_manager::SocketActions;
use crate::uuid::{Profile, UuidHelper, HOGP};
use crate::{Message, RPCProxy, SuspendMode};
use crate::{APIMessage, BluetoothAPI, Message, RPCProxy, SuspendMode};

pub(crate) const FLOSS_VER: u16 = 0x0001;
const DEFAULT_DISCOVERY_TIMEOUT_MS: u64 = 12800;
@@ -507,6 +507,7 @@ pub struct Bluetooth {
    sdp: Option<Sdp>,
    state: BtState,
    tx: Sender<Message>,
    api_tx: Sender<APIMessage>,
    // Internal API members
    discoverable_timeout: Option<JoinHandle<()>>,
    cancelling_devices: HashSet<RawAddress>,
@@ -524,6 +525,7 @@ impl Bluetooth {
        adapter_index: i32,
        hci_index: i32,
        tx: Sender<Message>,
        api_tx: Sender<APIMessage>,
        sig_notifier: Arc<SigData>,
        intf: Arc<Mutex<BluetoothInterface>>,
        bluetooth_admin: Arc<Mutex<Box<BluetoothAdmin>>>,
@@ -561,6 +563,7 @@ impl Bluetooth {
            sdp: None,
            state: BtState::Off,
            tx,
            api_tx,
            // Internal API members
            discoverable_timeout: None,
            cancelling_devices: HashSet::new(),
@@ -1336,9 +1339,19 @@ impl BtifBluetoothCallbacks for Bluetooth {

                // Inform the rest of the stack we're ready.
                let txl = self.tx.clone();
                let api_txl = self.api_tx.clone();
                tokio::spawn(async move {
                    let _ = txl.send(Message::AdapterReady).await;
                });
                tokio::spawn(async move {
                    let _ = api_txl.send(APIMessage::IsReady(BluetoothAPI::Adapter)).await;
                    // TODO(b:300202052) make sure media interface is exposed after initialized
                    let _ = api_txl.send(APIMessage::IsReady(BluetoothAPI::Media)).await;
                    // TODO(b:300202055) make sure GATT interface is exposed after initialized
                    let _ = api_txl.send(APIMessage::IsReady(BluetoothAPI::Gatt)).await;
                    // TODO(b:300202503) make sure battery interface is exposed after initialized
                    let _ = api_txl.send(APIMessage::IsReady(BluetoothAPI::Battery)).await;
                });
            }
        }
    }
Loading