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

Commit 56fa0870 authored by Hansong Zhang's avatar Hansong Zhang
Browse files

AdapterStateChange and HciDeviceChange callback

 * Move observers to main.
 * Add dbus callback util.
 * Use UpstartInvoker instead of NativeSubprocess.
 * Fix multiple issues.

Bug: 189501676
Tag: #floss
Test: cargo test
Test: run btmanagerd and try sending dbus commands
Change-Id: Ie0a678a6f5622221f5d12bb2b8b82a72971ab949
parent 01d1a322
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ dbus-tokio = "0.7.3"
dbus-crossroads = "0.3.0"
inotify = "*"
nix = "*"
regex = "1.5"
serde_json = "1.0"
tokio = { version = "1.0", features = ["fs", "macros", "rt-multi-thread", "sync"] }

+6 −2
Original line number Diff line number Diff line
@@ -96,7 +96,11 @@ fn modify_hci_n_enabled_internal(config: String, n: i32, enabled: bool) -> Optio
    }
}

pub fn list_hci_devices() -> Vec<String> {
pub fn list_hci_devices() -> Vec<i32> {
    hci_devices_string_to_int(list_hci_devices_string())
}

fn list_hci_devices_string() -> Vec<String> {
    match std::fs::read_dir(HCI_DEVICES_DIR) {
        Ok(entries) => entries
            .map(|e| e.unwrap().path().file_name().unwrap().to_str().unwrap().to_string())
@@ -105,7 +109,7 @@ pub fn list_hci_devices() -> Vec<String> {
    }
}

pub fn hci_devices_string_to_int(devices: Vec<String>) -> Vec<i32> {
fn hci_devices_string_to_int(devices: Vec<String>) -> Vec<i32> {
    devices
        .into_iter()
        .filter_map(|e| if e.starts_with("hci") { e[3..].parse::<i32>().ok() } else { None })
+71 −0
Original line number Diff line number Diff line
use dbus::nonblock::{Proxy, SyncConnection};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;

#[derive(Clone)]
pub struct DbusCallbackUtil {
    dbus_connection: Arc<SyncConnection>,
    state_change_observers: Arc<Mutex<Vec<String>>>,
    hci_device_change_observer: Arc<Mutex<Vec<String>>>,
}

impl DbusCallbackUtil {
    pub fn new(
        dbus_connection: Arc<SyncConnection>,
        state_change_observers: Arc<Mutex<Vec<String>>>,
        hci_device_change_observer: Arc<Mutex<Vec<String>>>,
    ) -> Self {
        DbusCallbackUtil {
            dbus_connection: dbus_connection,
            state_change_observers: state_change_observers,
            hci_device_change_observer: hci_device_change_observer,
        }
    }

    pub async fn send_hci_device_change_callback(
        &self,
        hci_device: i32,
        present: bool,
    ) -> Result<(), Box<dyn std::error::Error>> {
        for path in &*self.hci_device_change_observer.lock().await {
            let proxy = Proxy::new(
                "org.chromium.bluetooth.Manager",
                path,
                Duration::from_secs(2),
                self.dbus_connection.clone(),
            );
            proxy
                .method_call(
                    "org.chromium.bluetooth.Manager",
                    "HciDeviceChangeCallback",
                    (hci_device, present),
                )
                .await?;
        }
        Ok(())
    }

    pub async fn send_adapter_state_change_callback(
        &self,
        hci_device: i32,
        state: i32,
    ) -> Result<(), Box<dyn std::error::Error>> {
        for path in &*self.state_change_observers.lock().await {
            let proxy = Proxy::new(
                "org.chromium.bluetooth.Manager",
                path,
                Duration::from_secs(2),
                self.dbus_connection.clone(),
            );
            proxy
                .method_call(
                    "org.chromium.bluetooth.Manager",
                    "AdapterStateChangeCallback",
                    (hci_device, state),
                )
                .await?;
        }
        Ok(())
    }
}
+80 −28
Original line number Diff line number Diff line
mod config_util;
mod dbus_callback_util;
mod state_machine;

use dbus::channel::MatchingReceiver;
use dbus::message::MatchRule;
use dbus::nonblock::SyncConnection;
use dbus_crossroads::Crossroads;
use dbus_tokio::connection;
use std::process::Command;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::sync::Mutex;

const BLUEZ_INIT_TARGET: &str = "bluetoothd";

@@ -15,6 +18,9 @@ const BLUEZ_INIT_TARGET: &str = "bluetoothd";
struct ManagerContext {
    proxy: state_machine::StateMachineProxy,
    floss_enabled: Arc<AtomicBool>,
    dbus_connection: Arc<SyncConnection>,
    state_change_observer: Arc<Mutex<Vec<String>>>,
    hci_device_change_observer: Arc<Mutex<Vec<String>>>,
}

#[tokio::main]
@@ -22,15 +28,26 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize config util
    config_util::fix_config_file_format();

    // Connect to the D-Bus system bus (this is blocking, unfortunately).
    let (resource, conn) = connection::new_system_sync()?;

    let context = state_machine::start_new_state_machine_context();
    let proxy = context.get_proxy();
    let state_change_observer = Arc::new(Mutex::new(Vec::new()));
    let hci_device_change_observer = Arc::new(Mutex::new(Vec::new()));
    let manager_context = ManagerContext {
        proxy: proxy,
        floss_enabled: Arc::new(AtomicBool::new(config_util::is_floss_enabled())),
        dbus_connection: conn.clone(),
        state_change_observer: state_change_observer.clone(),
        hci_device_change_observer: hci_device_change_observer.clone(),
    };

    // Connect to the D-Bus system bus (this is blocking, unfortunately).
    let (resource, c) = connection::new_system_sync()?;
    let dbus_callback_util = dbus_callback_util::DbusCallbackUtil::new(
        conn.clone(),
        state_change_observer.clone(),
        hci_device_change_observer.clone(),
    );

    // The resource is a task that should be spawned onto a tokio compatible
    // reactor ASAP. If the resource ever finishes, you lost connection to D-Bus.
@@ -40,7 +57,7 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
    });

    // Let's request a name on the bus, so that clients can find us.
    c.request_name("org.chromium.bluetooth.Manager", false, true, false).await?;
    conn.request_name("org.chromium.bluetooth.Manager", false, true, false).await?;

    // Create a new crossroads instance.
    // The instance is configured so that introspection and properties interfaces
@@ -49,7 +66,7 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {

    // Enable async support for the crossroads instance.
    cr.set_async_support(Some((
        c.clone(),
        conn.clone(),
        Box::new(|x| {
            tokio::spawn(x);
        }),
@@ -101,30 +118,20 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
            let proxy = cr.data_mut::<ManagerContext>(ctx.path()).unwrap().proxy.clone();
            async move {
                let state = proxy.get_state().await;
                let result = match state {
                    state_machine::State::Off => 0,
                    state_machine::State::TurningOn => 1,
                    state_machine::State::On => 2,
                    state_machine::State::TurningOff => 3,
                };
                let result = state_machine::state_to_i32(state);
                ctx.reply(Ok((result,)))
            }
        });
        // Register AdapterStateChangeCallback(int hci_device, int state) on specified object_path
        b.method_with_cr_async(
            "RegisterStateChangeObserver",
            ("object_path",),
            (),
            |mut ctx, cr, (object_path,): (String,)| {
                let proxy = cr.data_mut::<ManagerContext>(ctx.path()).unwrap().proxy.clone();
                let manager_context = cr.data_mut::<ManagerContext>(ctx.path()).unwrap().clone();
                async move {
                    let result = proxy.register_state_change_observer(object_path.clone()).await;
                    match result {
                        Ok(()) => ctx.reply(Ok(())),
                        Err(_) => ctx.reply(Err(dbus_crossroads::MethodErr::failed(&format!(
                            "cannot register {}",
                            object_path
                        )))),
                    }
                    manager_context.state_change_observer.lock().await.push(object_path.clone());
                    ctx.reply(Ok(()))
                }
            },
        );
@@ -133,12 +140,15 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
            ("object_path",),
            (),
            |mut ctx, cr, (object_path,): (String,)| {
                let proxy = cr.data_mut::<ManagerContext>(ctx.path()).unwrap().proxy.clone();
                let manager_context = cr.data_mut::<ManagerContext>(ctx.path()).unwrap().clone();
                async move {
                    let result = proxy.unregister_state_change_observer(object_path.clone()).await;
                    match result {
                        Ok(()) => ctx.reply(Ok(())),
                        Err(_) => ctx.reply(Err(dbus_crossroads::MethodErr::failed(&format!(
                    let mut observers = manager_context.state_change_observer.lock().await;
                    match observers.iter().position(|x| *x == object_path) {
                        Some(index) => {
                            observers.remove(index);
                            ctx.reply(Ok(()))
                        }
                        _ => ctx.reply(Err(dbus_crossroads::MethodErr::failed(&format!(
                            "cannot unregister {}",
                            object_path
                        )))),
@@ -176,9 +186,13 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
                            .args(&["stop", BLUEZ_INIT_TARGET])
                            .output()
                            .expect("failed to stop bluetoothd");
                        let _ = proxy.start_bluetooth(0).await;
                        // TODO: Implement multi-hci case
                        let default_device = config_util::list_hci_devices()[0];
                        let _ = proxy.start_bluetooth(default_device).await;
                    } else if prev != enabled {
                        let _ = proxy.stop_bluetooth(0).await;
                        // TODO: Implement multi-hci case
                        let default_device = config_util::list_hci_devices()[0];
                        let _ = proxy.stop_bluetooth(default_device).await;
                        Command::new("initctl")
                            .args(&["start", BLUEZ_INIT_TARGET])
                            .output()
@@ -188,6 +202,44 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
                }
            },
        );
        b.method_with_cr_async("ListHciDevices", (), ("devices",), |mut ctx, _cr, ()| {
            let devices = config_util::list_hci_devices();
            async move { ctx.reply(Ok((devices,))) }
        });
        // Register AdapterStateChangeCallback(int hci_device, int state) on specified object_path
        b.method_with_cr_async(
            "RegisterHciDeviceChangeObserver",
            ("object_path",),
            (),
            |mut ctx, cr, (object_path,): (String,)| {
                let manager_context = cr.data_mut::<ManagerContext>(ctx.path()).unwrap().clone();
                async move {
                    manager_context.hci_device_change_observer.lock().await.push(object_path);
                    ctx.reply(Ok(()))
                }
            },
        );
        b.method_with_cr_async(
            "UnregisterHciDeviceChangeObserver",
            ("object_path",),
            (),
            |mut ctx, cr, (object_path,): (String,)| {
                let manager_context = cr.data_mut::<ManagerContext>(ctx.path()).unwrap().clone();
                async move {
                    let mut observers = manager_context.hci_device_change_observer.lock().await;
                    match observers.iter().position(|x| *x == object_path) {
                        Some(index) => {
                            observers.remove(index);
                            ctx.reply(Ok(()))
                        }
                        _ => ctx.reply(Err(dbus_crossroads::MethodErr::failed(&format!(
                            "cannot unregister {}",
                            object_path
                        )))),
                    }
                }
            },
        );
    });

    // Let's add the "/org/chromium/bluetooth/Manager" path, which implements the org.chromium.bluetooth.Manager interface,
@@ -195,7 +247,7 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
    cr.insert("/org/chromium/bluetooth/Manager", &[iface_token], manager_context);

    // We add the Crossroads instance to the connection so that incoming method calls will be handled.
    c.start_receive(
    conn.start_receive(
        MatchRule::new_method_call(),
        Box::new(move |msg, conn| {
            cr.handle_message(msg, conn).unwrap();
@@ -204,7 +256,7 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
    );

    tokio::spawn(async move {
        state_machine::mainloop(context).await;
        state_machine::mainloop(context, dbus_callback_util).await;
    });

    std::future::pending::<()>().await;
+115 −74

File changed.

Preview size limit exceeded, changes collapsed.