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

Commit 380a28e0 authored by Sonny Sasaka's avatar Sonny Sasaka
Browse files

Export callback object as a D-Bus object

This adds the first example of how to export a callback object as a
D-Bus object implementing an interface. Exporting the callback may also
utilize the existing D-Bus projection utility that is used by the
server.

Bug: 188718349
Tag: #floss
Test: manual - Build floss on Linux

Change-Id: Ibbb0a115e7671a32eb04db370148005b3693bad8
parent 1ef05f42
Loading
Loading
Loading
Loading
+71 −14
Original line number Diff line number Diff line
//! D-Bus proxy implementations of the APIs.

use btstack::bluetooth::{BluetoothDevice, BluetoothTransport, IBluetooth, IBluetoothCallback};
use btstack::RPCProxy;

use dbus::arg::{AppendAll, RefArg};
use dbus::nonblock::SyncConnection;

use dbus_projection::impl_dbus_arg_enum;
use dbus_crossroads::Crossroads;

use dbus_macros::dbus_propmap;
use dbus_projection::{impl_dbus_arg_enum, DisconnectWatcher};

use dbus_macros::{dbus_method, dbus_propmap, generate_dbus_exporter};

use num_traits::{FromPrimitive, ToPrimitive};

use std::sync::Arc;
use std::sync::{Arc, Mutex};

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

@@ -24,25 +27,31 @@ pub struct BluetoothDeviceDBus {

pub(crate) struct BluetoothDBus {
    conn: Arc<SyncConnection>,
    cr: Arc<Mutex<Crossroads>>,
}

impl BluetoothDBus {
    pub(crate) fn new(conn: Arc<SyncConnection>) -> BluetoothDBus {
        BluetoothDBus { conn: conn.clone() }
    pub(crate) fn new(conn: Arc<SyncConnection>, cr: Arc<Mutex<Crossroads>>) -> BluetoothDBus {
        BluetoothDBus { conn: conn.clone(), cr: cr }
    }

    fn method<A: AppendAll, T: 'static + dbus::arg::Arg + for<'z> dbus::arg::Get<'z>>(
        &self,
        member: &str,
        args: A,
    ) -> T {
    fn create_proxy(&self) -> dbus::nonblock::Proxy<Arc<SyncConnection>> {
        let conn = self.conn.clone();
        let proxy = dbus::nonblock::Proxy::new(
        // TODO: Adapter path should have hci number, e.g. /org/chromium/bluetooth/adapter/hci0.
        dbus::nonblock::Proxy::new(
            "org.chromium.bluetooth",
            "/org/chromium/bluetooth/adapter",
            std::time::Duration::from_secs(2),
            conn,
        );
        )
    }

    fn method<A: AppendAll, T: 'static + dbus::arg::Arg + for<'z> dbus::arg::Get<'z>>(
        &self,
        member: &str,
        args: A,
    ) -> T {
        let proxy = self.create_proxy();
        // We know that all APIs return immediately, so we can block on it for simplicity.
        let (ret,): (T,) = futures::executor::block_on(async {
            proxy.method_call("org.chromium.bluetooth.Bluetooth", member, args).await
@@ -51,12 +60,60 @@ impl BluetoothDBus {

        return ret;
    }

    fn method_noreturn<A: AppendAll>(&self, member: &str, args: A) {
        let proxy = self.create_proxy();
        // We know that all APIs return immediately, so we can block on it for simplicity.
        let _: () = futures::executor::block_on(async {
            proxy.method_call("org.chromium.bluetooth.Bluetooth", member, args).await
        })
        .unwrap();
    }
}

#[allow(dead_code)]
struct IBluetoothCallbackDBus {}

impl RPCProxy for IBluetoothCallbackDBus {
    // Dummy implementations just to satisfy impl RPCProxy requirements.
    fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {}
    fn get_object_id(&self) -> String {
        String::from("")
    }
}

#[generate_dbus_exporter(
    export_bluetooth_callback_dbus_obj,
    "org.chromium.bluetooth.BluetoothCallback"
)]
impl IBluetoothCallback for IBluetoothCallbackDBus {
    #[dbus_method("OnBluetoothStateChanged")]
    fn on_bluetooth_state_changed(&self, prev_state: u32, new_state: u32) {}

    #[dbus_method("OnBluetoothAddressChanged")]
    fn on_bluetooth_address_changed(&self, addr: String) {}

    #[dbus_method("OnDeviceFound")]
    fn on_device_found(&self, remote_device: BluetoothDevice) {}

    #[dbus_method("OnDiscoveringChanged")]
    fn on_discovering_changed(&self, discovering: bool) {}
}

// TODO: These are boilerplate codes, consider creating a macro to generate.
impl IBluetooth for BluetoothDBus {
    fn register_callback(&mut self, _callback: Box<dyn IBluetoothCallback + Send>) {
        // TODO: Implement callback object export.
    fn register_callback(&mut self, callback: Box<dyn IBluetoothCallback + Send>) {
        let path_string = callback.get_object_id();
        let path = dbus::Path::new(path_string.clone()).unwrap();
        export_bluetooth_callback_dbus_obj(
            path_string,
            self.conn.clone(),
            &mut self.cr.lock().unwrap(),
            Arc::new(Mutex::new(callback)),
            Arc::new(Mutex::new(DisconnectWatcher::new())),
        );

        self.method_noreturn("RegisterCallback", (path,))
    }

    fn enable(&mut self) -> bool {
+35 −7
Original line number Diff line number Diff line
@@ -8,6 +8,10 @@ use btstack::bluetooth::{

use btstack::{RPCProxy, Stack};

use dbus::channel::MatchingReceiver;

use dbus::message::MatchRule;

use dbus::nonblock::SyncConnection;

use std::sync::{Arc, Mutex};
@@ -16,6 +20,8 @@ use crate::command_handler::CommandHandler;
use crate::dbus_iface::BluetoothDBus;
use crate::editor::AsyncEditor;

use dbus_crossroads::Crossroads;

mod command_handler;
mod console;
mod dbus_arg;
@@ -24,6 +30,7 @@ mod editor;

struct BtCallback {
    disconnect_callbacks: Arc<Mutex<Vec<Box<dyn Fn() + Send>>>>,
    objpath: String,
}

impl IBluetoothCallback for BtCallback {
@@ -48,6 +55,10 @@ impl RPCProxy for BtCallback {
    fn register_disconnect(&mut self, f: Box<dyn Fn() + Send>) {
        self.disconnect_callbacks.lock().unwrap().push(f);
    }

    fn get_object_id(&self) -> String {
        self.objpath.clone()
    }
}

struct API<T: IBluetooth> {
@@ -74,8 +85,8 @@ fn create_api_embedded() -> API<Bluetooth> {
}

// This creates the API implementations over D-Bus.
fn create_api_dbus(conn: Arc<SyncConnection>) -> API<BluetoothDBus> {
    let bluetooth = Arc::new(Mutex::new(Box::new(BluetoothDBus::new(conn.clone()))));
fn create_api_dbus(conn: Arc<SyncConnection>, cr: Arc<Mutex<Crossroads>>) -> API<BluetoothDBus> {
    let bluetooth = Arc::new(Mutex::new(Box::new(BluetoothDBus::new(conn.clone(), cr))));

    API { bluetooth }
}
@@ -95,13 +106,30 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
            panic!("Lost connection to D-Bus: {}", err);
        });

        let api = create_api_dbus(conn);
        // Sets up Crossroads for receiving callbacks.
        let cr = Arc::new(Mutex::new(Crossroads::new()));
        cr.lock().unwrap().set_async_support(Some((
            conn.clone(),
            Box::new(|x| {
                topstack::get_runtime().spawn(x);
            }),
        )));
        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
            }),
        );

        let api = create_api_dbus(conn, cr);

        let dc_callbacks = Arc::new(Mutex::new(vec![]));
        api.bluetooth
            .lock()
            .unwrap()
            .register_callback(Box::new(BtCallback { disconnect_callbacks: dc_callbacks.clone() }));
        api.bluetooth.lock().unwrap().register_callback(Box::new(BtCallback {
            disconnect_callbacks: dc_callbacks.clone(),
            objpath: String::from("/org/chromium/bluetooth/client/bluetooth_callback"),
        }));

        let handler = CommandHandler::<BluetoothDBus>::new(api.bluetooth.clone());

+7 −0
Original line number Diff line number Diff line
@@ -426,6 +426,9 @@ pub fn dbus_proxy_obj(attr: TokenStream, item: TokenStream) -> TokenStream {

        impl RPCProxy for #self_ty {
            fn register_disconnect(&mut self, _disconnect_callback: Box<dyn Fn() + Send>) {}
            fn get_object_id(&self) -> String {
                String::from("")
            }
        }

        struct #struct_ident {
@@ -443,6 +446,10 @@ pub fn dbus_proxy_obj(attr: TokenStream, item: TokenStream) -> TokenStream {
            fn register_disconnect(&mut self, disconnect_callback: Box<dyn Fn() + Send>) {
                self.disconnect_watcher.lock().unwrap().add(self.remote.clone(), disconnect_callback);
            }

            fn get_object_id(&self) -> String {
                self.objpath.to_string().clone()
            }
        }

        impl DBusArg for Box<dyn #trait_ + Send> {
+4 −0
Original line number Diff line number Diff line
@@ -125,5 +125,9 @@ impl Stack {
/// RPC object. Therefore the object may be disconnected and thus should implement
/// `register_disconnect` to let others observe the disconnection event.
pub trait RPCProxy {
    /// Registers disconnect observer that will be notified when the remote object is disconnected.
    fn register_disconnect(&mut self, f: Box<dyn Fn() + Send>);

    /// Returns the ID of the object. For example this would be an object path in D-Bus RPC.
    fn get_object_id(&self) -> String;
}