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

Commit ba0ec1b0 authored by Sonny Sasaka's avatar Sonny Sasaka
Browse files

Add D-Bus implementations of command line APIs

This adds a minimal D-Bus implementations for some APIs to be used as
proxies by the command line.

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

Change-Id: I40678ae6880bd1e376df879364f067c59ee9bbf9
parent 0434be67
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -9,6 +9,13 @@ bt_topshim = { path = "../../topshim" }
bt_shim = { path = "../../shim" }
btstack = { path = "../stack" }

dbus = "0.9.2"
dbus-crossroads = "0.3.0"
dbus-tokio = "0.7.3"

dbus_projection = { path = "../dbus_projection" }
dbus_macros = { path = "../dbus_projection/dbus_macros" }

futures = "0.3.13"
num-traits = "*"
tokio = { version = "1", features = ['bytes', 'fs', 'io-util', 'libc', 'macros', 'memchr', 'mio', 'net', 'num_cpus', 'rt', 'rt-multi-thread', 'sync', 'time', 'tokio-macros'] }
+3 −0
Original line number Diff line number Diff line
use dbus_macros::generate_dbus_arg;

generate_dbus_arg!();
+93 −0
Original line number Diff line number Diff line
//! D-Bus proxy implementations of the APIs.

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

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

use dbus_projection::impl_dbus_arg_enum;

use dbus_macros::dbus_propmap;

use num_traits::{FromPrimitive, ToPrimitive};

use std::sync::Arc;

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

impl_dbus_arg_enum!(BluetoothTransport);

#[dbus_propmap(BluetoothDevice)]
pub struct BluetoothDeviceDBus {
    address: String,
}

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

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

    fn method<A: AppendAll, T: 'static + dbus::arg::Arg + for<'z> dbus::arg::Get<'z>>(
        &self,
        member: &str,
        args: A,
    ) -> T {
        let conn = self.conn.clone();
        let proxy = dbus::nonblock::Proxy::new(
            "org.chromium.bluetooth",
            "/org/chromium/bluetooth/adapter",
            std::time::Duration::from_secs(2),
            conn,
        );
        // 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
        })
        .unwrap();

        return ret;
    }
}

// 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 enable(&mut self) -> bool {
        // Not implemented by server
        true
    }

    fn disable(&mut self) -> bool {
        // Not implemented by server
        true
    }

    fn get_address(&self) -> String {
        self.method("GetAddress", ())
    }

    fn start_discovery(&self) -> bool {
        self.method("StartDiscovery", ())
    }

    fn cancel_discovery(&self) -> bool {
        self.method("CancelDiscovery", ())
    }

    fn create_bond(&self, device: BluetoothDevice, transport: BluetoothTransport) -> bool {
        self.method(
            "CreateBond",
            (
                BluetoothDevice::to_dbus(device).unwrap(),
                BluetoothTransport::to_dbus(transport).unwrap(),
            ),
        )
    }
}
+60 −34
Original line number Diff line number Diff line
@@ -8,13 +8,18 @@ use btstack::bluetooth::{

use btstack::{RPCProxy, Stack};

use dbus::nonblock::SyncConnection;

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

use crate::command_handler::CommandHandler;
use crate::dbus_iface::BluetoothDBus;
use crate::editor::AsyncEditor;

mod command_handler;
mod console;
mod dbus_arg;
mod dbus_iface;
mod editor;

struct BtCallback {
@@ -49,10 +54,8 @@ struct API {
    bluetooth: Arc<Mutex<dyn IBluetooth>>,
}

fn create_api_embedded() -> API {
// This creates the API implementations directly embedded to this client.
    // TODO: Add API implementations as proxy over D-Bus.

fn create_api_embedded() -> API {
    let (tx, rx) = Stack::create_channel();

    let intf = Arc::new(Mutex::new(get_btinterface().unwrap()));
@@ -67,11 +70,34 @@ fn create_api_embedded() -> API {
    API { bluetooth }
}

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

    API { bluetooth }
}

/// Runs a command line program that interacts with a Bluetooth stack.
fn main() -> Result<(), Box<dyn std::error::Error>> {
    // TODO: Process command line arguments.

    let api = create_api_embedded();
    topstack::get_runtime().block_on(async move {
        // Connect to D-Bus system bus.
        let (resource, conn) = dbus_tokio::connection::new_system_sync()?;

        // The `resource` is a task that should be spawned onto a tokio compatible
        // reactor ASAP. If the resource ever finishes, we lost connection to D-Bus.
        topstack::get_runtime().spawn(async {
            let err = resource.await;
            panic!("Lost connection to D-Bus: {}", err);
        });

        // TODO: Separate the embedded mode into its own binary.
        let api = if std::env::var("EMBEDDED_STACK").is_ok() {
            create_api_embedded()
        } else {
            create_api_dbus(conn)
        };

        let dc_callbacks = Arc::new(Mutex::new(vec![]));
        api.bluetooth
@@ -107,7 +133,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
        };

        let editor = AsyncEditor::new();
    topstack::get_runtime().block_on(async move {

        loop {
            let result = editor.readline().await;
            match result {