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

Commit 5004919f authored by Sonny Sasaka's avatar Sonny Sasaka Committed by Gerrit Code Review
Browse files

Merge "Floss: Improve D-Bus projection for client"

parents 1d33c5b0 5df9b191
Loading
Loading
Loading
Loading
+38 −14
Original line number Original line Diff line number Diff line
@@ -184,24 +184,36 @@ impl IBluetoothConnectionCallback for IBluetoothConnectionCallbackDBus {
    fn on_device_disconnected(&self, remote_device: BluetoothDevice) {}
    fn on_device_disconnected(&self, remote_device: BluetoothDevice) {}
}
}


// Implements RPC-friendly wrapper methods for calling IBluetooth, generated by
// `generate_dbus_interface_client` below.
pub(crate) struct BluetoothDBusRPC {
    client_proxy: ClientDBusProxy,
}

pub(crate) struct BluetoothDBus {
pub(crate) struct BluetoothDBus {
    client_proxy: ClientDBusProxy,
    client_proxy: ClientDBusProxy,
    pub rpc: BluetoothDBusRPC,
}
}


impl BluetoothDBus {
impl BluetoothDBus {
    pub(crate) fn new(conn: Arc<SyncConnection>, index: i32) -> BluetoothDBus {
    fn make_client_proxy(conn: Arc<SyncConnection>, index: i32) -> ClientDBusProxy {
        BluetoothDBus {
        ClientDBusProxy::new(
            client_proxy: ClientDBusProxy::new(
            conn.clone(),
            conn.clone(),
            String::from("org.chromium.bluetooth"),
            String::from("org.chromium.bluetooth"),
            make_object_path(index, "adapter"),
            make_object_path(index, "adapter"),
            String::from("org.chromium.bluetooth.Bluetooth"),
            String::from("org.chromium.bluetooth.Bluetooth"),
            ),
        )
    }

    pub(crate) fn new(conn: Arc<SyncConnection>, index: i32) -> BluetoothDBus {
        BluetoothDBus {
            client_proxy: Self::make_client_proxy(conn.clone(), index),
            rpc: BluetoothDBusRPC { client_proxy: Self::make_client_proxy(conn.clone(), index) },
        }
        }
    }
    }
}
}


#[generate_dbus_interface_client]
#[generate_dbus_interface_client(BluetoothDBusRPC)]
impl IBluetooth for BluetoothDBus {
impl IBluetooth for BluetoothDBus {
    #[dbus_method("RegisterCallback")]
    #[dbus_method("RegisterCallback")]
    fn register_callback(&mut self, callback: Box<dyn IBluetoothCallback + Send>) {
    fn register_callback(&mut self, callback: Box<dyn IBluetoothCallback + Send>) {
@@ -413,19 +425,31 @@ pub struct AdapterWithEnabledDbus {
    enabled: bool,
    enabled: bool,
}
}


// Implements RPC-friendly wrapper methods for calling IBluetoothManager, generated by
// `generate_dbus_interface_client` below.
pub(crate) struct BluetoothManagerDBusRPC {
    client_proxy: ClientDBusProxy,
}

pub(crate) struct BluetoothManagerDBus {
pub(crate) struct BluetoothManagerDBus {
    client_proxy: ClientDBusProxy,
    client_proxy: ClientDBusProxy,
    pub rpc: BluetoothManagerDBusRPC,
}
}


impl BluetoothManagerDBus {
impl BluetoothManagerDBus {
    pub(crate) fn new(conn: Arc<SyncConnection>) -> BluetoothManagerDBus {
    fn make_client_proxy(conn: Arc<SyncConnection>) -> ClientDBusProxy {
        BluetoothManagerDBus {
        ClientDBusProxy::new(
            client_proxy: ClientDBusProxy::new(
            conn,
                conn.clone(),
            String::from("org.chromium.bluetooth.Manager"),
            String::from("org.chromium.bluetooth.Manager"),
            dbus::Path::new("/org/chromium/bluetooth/Manager").unwrap(),
            dbus::Path::new("/org/chromium/bluetooth/Manager").unwrap(),
            String::from("org.chromium.bluetooth.Manager"),
            String::from("org.chromium.bluetooth.Manager"),
            ),
        )
    }

    pub(crate) fn new(conn: Arc<SyncConnection>) -> BluetoothManagerDBus {
        BluetoothManagerDBus {
            client_proxy: Self::make_client_proxy(conn.clone()),
            rpc: BluetoothManagerDBusRPC { client_proxy: Self::make_client_proxy(conn.clone()) },
        }
        }
    }
    }


@@ -435,7 +459,7 @@ impl BluetoothManagerDBus {
    }
    }
}
}


#[generate_dbus_interface_client]
#[generate_dbus_interface_client(BluetoothManagerDBusRPC)]
impl IBluetoothManager for BluetoothManagerDBus {
impl IBluetoothManager for BluetoothManagerDBus {
    #[dbus_method("Start")]
    #[dbus_method("Start")]
    fn start(&mut self, hci_interface: i32) {
    fn start(&mut self, hci_interface: i32) {
+29 −8
Original line number Original line Diff line number Diff line
@@ -226,7 +226,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
        if !context.lock().unwrap().manager_dbus.is_valid() {
        if !context.lock().unwrap().manager_dbus.is_valid() {
            println!("Bluetooth manager doesn't seem to be working correctly.");
            println!("Bluetooth manager doesn't seem to be working correctly.");
            println!("Check if service is running.");
            println!("Check if service is running.");
            println!("...");
            return Ok(());
        }
        }


        // TODO: Registering the callback should be done when btmanagerd is ready (detect with
        // TODO: Registering the callback should be done when btmanagerd is ready (detect with
@@ -241,8 +241,19 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
        // Check if the default adapter is enabled. If yes, we should create the adapter proxy
        // Check if the default adapter is enabled. If yes, we should create the adapter proxy
        // right away.
        // right away.
        let default_adapter = context.lock().unwrap().default_adapter;
        let default_adapter = context.lock().unwrap().default_adapter;
        if context.lock().unwrap().manager_dbus.get_adapter_enabled(default_adapter) {

            context.lock().unwrap().set_adapter_enabled(default_adapter, true);
        {
            let mut context_locked = context.lock().unwrap();
            match context_locked.manager_dbus.rpc.get_adapter_enabled(default_adapter).await {
                Ok(ret) => {
                    if ret {
                        context_locked.set_adapter_enabled(default_adapter, true);
                    }
                }
                Err(e) => {
                    panic!("Bluetooth Manager is not available. Exiting. D-Bus error: {}", e);
                }
            }
        }
        }


        let mut handler = CommandHandler::new(context.clone());
        let mut handler = CommandHandler::new(context.clone());
@@ -325,26 +336,36 @@ async fn start_interactive_shell(
                let dbus_connection = context.lock().unwrap().dbus_connection.clone();
                let dbus_connection = context.lock().unwrap().dbus_connection.clone();
                let dbus_crossroads = context.lock().unwrap().dbus_crossroads.clone();
                let dbus_crossroads = context.lock().unwrap().dbus_crossroads.clone();


                context.lock().unwrap().adapter_dbus.as_mut().unwrap().register_callback(Box::new(
                context
                    BtCallback::new(
                    .lock()
                    .unwrap()
                    .adapter_dbus
                    .as_mut()
                    .unwrap()
                    .rpc
                    .register_callback(Box::new(BtCallback::new(
                        cb_objpath.clone(),
                        cb_objpath.clone(),
                        context.clone(),
                        context.clone(),
                        dbus_connection.clone(),
                        dbus_connection.clone(),
                        dbus_crossroads.clone(),
                        dbus_crossroads.clone(),
                    ),
                    )))
                ));
                    .await
                    .expect("D-Bus error on IBluetooth::RegisterCallback");
                context
                context
                    .lock()
                    .lock()
                    .unwrap()
                    .unwrap()
                    .adapter_dbus
                    .adapter_dbus
                    .as_mut()
                    .as_mut()
                    .unwrap()
                    .unwrap()
                    .rpc
                    .register_connection_callback(Box::new(BtConnectionCallback::new(
                    .register_connection_callback(Box::new(BtConnectionCallback::new(
                        conn_cb_objpath,
                        conn_cb_objpath,
                        context.clone(),
                        context.clone(),
                        dbus_connection.clone(),
                        dbus_connection.clone(),
                        dbus_crossroads.clone(),
                        dbus_crossroads.clone(),
                    )));
                    )))
                    .await
                    .expect("D-Bus error on IBluetooth::RegisterConnectionCallback");


                // When adapter is ready, Suspend API is also ready. Register as an observer.
                // When adapter is ready, Suspend API is also ready. Register as an observer.
                // TODO(b/224606285): Implement suspend debug utils in btclient.
                // TODO(b/224606285): Implement suspend debug utils in btclient.
+83 −2
Original line number Original line Diff line number Diff line
@@ -221,11 +221,22 @@ pub fn generate_dbus_exporter(attr: TokenStream, item: TokenStream) -> TokenStre
/// Generates a client implementation of a D-Bus interface.
/// Generates a client implementation of a D-Bus interface.
///
///
/// Example:
/// Example:
///   #[generate_dbus_interface_client()]
///   #[generate_dbus_interface_client]
///
///
/// The impl containing #[dbus_method()] will contain a generated code to call the method via D-Bus.
/// The impl containing #[dbus_method()] will contain a generated code to call the method via D-Bus.
///
/// Example:
///   #[generate_dbus_interface_client(SomeRPC)]
///
/// When the RPC wrapper struct name is specified, it also generates the more RPC-friendly struct:
/// * All methods are async, allowing clients to await (yield) without blocking. Even methods that
///   are sync at the server side requires clients to "wait" for the return.
/// * All method returns are wrapped with `Result`, allowing clients to detect D-Bus level errors in
///   addition to API-level errors.
#[proc_macro_attribute]
#[proc_macro_attribute]
pub fn generate_dbus_interface_client(_attr: TokenStream, item: TokenStream) -> TokenStream {
pub fn generate_dbus_interface_client(attr: TokenStream, item: TokenStream) -> TokenStream {
    let rpc_struct_name = attr.to_string();

    let ast: ItemImpl = syn::parse(item.clone()).unwrap();
    let ast: ItemImpl = syn::parse(item.clone()).unwrap();
    let trait_path = ast.trait_.unwrap().1;
    let trait_path = ast.trait_.unwrap().1;
    let struct_path = match *ast.self_ty {
    let struct_path = match *ast.self_ty {
@@ -233,8 +244,12 @@ pub fn generate_dbus_interface_client(_attr: TokenStream, item: TokenStream) ->
        _ => panic!("Struct path not available"),
        _ => panic!("Struct path not available"),
    };
    };


    // Generated methods
    let mut methods = quote! {};
    let mut methods = quote! {};


    // Generated RPC-friendly methods (async and Result-wrapped).
    let mut rpc_methods = quote! {};

    // Iterate on every methods of a trait impl
    // Iterate on every methods of a trait impl
    for item in ast.items {
    for item in ast.items {
        if let ImplItem::Method(method) = item {
        if let ImplItem::Method(method) = item {
@@ -256,6 +271,22 @@ pub fn generate_dbus_interface_client(_attr: TokenStream, item: TokenStream) ->


            let sig = &method.sig;
            let sig = &method.sig;


            // For RPC-friendly method, copy the original signature but add public, async, and wrap
            // the return with Result.
            let mut rpc_sig = sig.clone();
            rpc_sig.asyncness = Some(<syn::Token![async]>::default());
            rpc_sig.output = match rpc_sig.output {
                syn::ReturnType::Default => {
                    syn::parse(quote! {-> Result<(), dbus::Error>}.into()).unwrap()
                }
                syn::ReturnType::Type(_arrow, path) => {
                    syn::parse(quote! {-> Result<#path, dbus::Error>}.into()).unwrap()
                }
            };
            let rpc_sig = quote! {
                pub #rpc_sig
            };

            let dbus_method_name = if let Meta::List(meta_list) = attr.parse_meta().unwrap() {
            let dbus_method_name = if let Meta::List(meta_list) = attr.parse_meta().unwrap() {
                Some(meta_list.nested[0].clone())
                Some(meta_list.nested[0].clone())
            } else {
            } else {
@@ -343,6 +374,26 @@ pub fn generate_dbus_interface_client(_attr: TokenStream, item: TokenStream) ->
                    }
                    }
                }
                }
            };
            };
            let rpc_body = match &method.sig.output {
                // Build the async method call to `self.client_proxy`.
                ReturnType::Default => {
                    quote! {
                        self.client_proxy
                            .async_method_noreturn(#dbus_method_name, #input_tuple)
                            .await
                    }
                }
                _ => {
                    quote! {
                        self.client_proxy
                            .async_method(#dbus_method_name, #input_tuple)
                            .await
                            .map(|(x,)| {
                                #output_as_dbus_arg::from_dbus(x, None, None, None).unwrap()
                            })
                    }
                }
            };


            // Assemble the method body. May have object conversions if there is a param that is
            // Assemble the method body. May have object conversions if there is a param that is
            // a proxy object (`Box<dyn>` type).
            // a proxy object (`Box<dyn>` type).
@@ -351,6 +402,11 @@ pub fn generate_dbus_interface_client(_attr: TokenStream, item: TokenStream) ->


                #body
                #body
            };
            };
            let rpc_body = quote! {
                #object_conversions

                #rpc_body
            };


            // The method definition is its signature and the body.
            // The method definition is its signature and the body.
            let generated_method = quote! {
            let generated_method = quote! {
@@ -358,6 +414,11 @@ pub fn generate_dbus_interface_client(_attr: TokenStream, item: TokenStream) ->
                    #body
                    #body
                }
                }
            };
            };
            let generated_rpc_method = quote! {
                #rpc_sig {
                    #rpc_body
                }
            };


            // Assemble all the method definitions.
            // Assemble all the method definitions.
            methods = quote! {
            methods = quote! {
@@ -365,13 +426,33 @@ pub fn generate_dbus_interface_client(_attr: TokenStream, item: TokenStream) ->


                #generated_method
                #generated_method
            };
            };
            rpc_methods = quote! {
                #rpc_methods

                #generated_rpc_method
            };
        }
    }

    // Generated code for the RPC wrapper struct.
    let rpc_gen = if rpc_struct_name.is_empty() {
        quote! {}
    } else {
        let rpc_struct = format_ident!("{}", rpc_struct_name);
        quote! {
            impl #rpc_struct {
                #rpc_methods
            }
            }
        }
        }
    };


    // The final generated code.
    let gen = quote! {
    let gen = quote! {
        impl #trait_path for #struct_path {
        impl #trait_path for #struct_path {
            #methods
            #methods
        }
        }

        #rpc_gen
    };
    };


    debug_output_to_file(
    debug_output_to_file(
+23 −0
Original line number Original line Diff line number Diff line
@@ -185,6 +185,29 @@ impl ClientDBusProxy {
        )
        )
    }
    }


    /// Asynchronously calls the method and returns the D-Bus result and lets the caller unwrap.
    pub async fn async_method<
        A: AppendAll,
        T: 'static + dbus::arg::Arg + for<'z> dbus::arg::Get<'z>,
    >(
        &self,
        member: &str,
        args: A,
    ) -> Result<(T,), dbus::Error> {
        let proxy = self.create_proxy();
        proxy.method_call(self.interface.clone(), member, args).await
    }

    /// Asynchronously calls the method and returns the D-Bus result with empty return data.
    pub async fn async_method_noreturn<A: AppendAll>(
        &self,
        member: &str,
        args: A,
    ) -> Result<(), dbus::Error> {
        let proxy = self.create_proxy();
        proxy.method_call(self.interface.clone(), member, args).await
    }

    /// Calls the method and returns the D-Bus result and lets the caller unwrap.
    /// Calls the method and returns the D-Bus result and lets the caller unwrap.
    pub fn method_withresult<
    pub fn method_withresult<
        A: AppendAll,
        A: AppendAll,