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

Commit 564dde34 authored by Sonny Sasaka's avatar Sonny Sasaka
Browse files

Add macro to dispatch btif callbacks

Add a macro to reduce boilerplate code of dispatching the messages to
the matching function.

Bug: 186492781
Tag: #floss
Test: manual - build floss on Linux

Change-Id: I7b8e58e856b512944c79ae6503dd1f9f3aaba765
parent 50188324
Loading
Loading
Loading
Loading
+28 −38
Original line number Diff line number Diff line
@@ -19,40 +19,44 @@ fn debug_output_to_file(gen: &proc_macro2::TokenStream, filename: String) {
    file.write_all(gen.to_string().as_bytes()).unwrap();
}

/// Specifies the `Stack::Message` associated with a topshim callback.
/// Associates a function with a btif callback message.
#[proc_macro_attribute]
pub fn stack_message(_attr: TokenStream, item: TokenStream) -> TokenStream {
pub fn btif_callback(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let ori_item: proc_macro2::TokenStream = item.clone().into();
    let gen = quote! {
        #[allow(unused_variables)]
        #ori_item
    };
    gen.into()
}

/// Generates a topshim callback object that contains closures.
///
/// The closures are generated to be calls to the corresponding `Stack::Message`.
/// Generates a dispatcher from a message to a function.
#[proc_macro_attribute]
pub fn btif_callbacks_generator(attr: TokenStream, item: TokenStream) -> TokenStream {
pub fn btif_callbacks_dispatcher(attr: TokenStream, item: TokenStream) -> TokenStream {
    let args = Punctuated::<Expr, Comma>::parse_separated_nonempty.parse(attr.clone()).unwrap();

    let fn_ident = if let Expr::Path(p) = &args[0] {
    let struct_ident = if let Expr::Path(p) = &args[0] {
        p.path.get_ident().unwrap()
    } else {
        panic!("struct name must be specified");
    };

    let fn_ident = if let Expr::Path(p) = &args[1] {
        p.path.get_ident().unwrap()
    } else {
        panic!("function name must be specified");
    };

    let callbacks_struct_ident = if let Expr::Path(p) = &args[1] {
    let callbacks_struct_ident = if let Expr::Path(p) = &args[2] {
        p.path.get_ident().unwrap()
    } else {
        panic!("callbacks struct ident must be specified");
    };

    let mut dispatch_arms = quote! {};

    let ast: ItemTrait = syn::parse(item.clone()).unwrap();

    let mut fn_names = quote! {};
    let mut closure_defs = quote! {};
    for attr in ast.items {
        if let TraitItem::Method(m) = attr {
            if m.attrs.len() != 1 {
@@ -60,18 +64,18 @@ pub fn btif_callbacks_generator(attr: TokenStream, item: TokenStream) -> TokenSt
            }

            let attr = &m.attrs[0];
            if !attr.path.get_ident().unwrap().to_string().eq("stack_message") {
            if !attr.path.get_ident().unwrap().to_string().eq("btif_callback") {
                continue;
            }

            let attr_args = attr.parse_meta().unwrap();
            let stack_message = if let Meta::List(meta_list) = attr_args {
            let btif_callback = if let Meta::List(meta_list) = attr_args {
                Some(meta_list.nested[0].clone())
            } else {
                None
            };

            if stack_message.is_none() {
            if btif_callback.is_none() {
                continue;
            }

@@ -91,18 +95,11 @@ pub fn btif_callbacks_generator(attr: TokenStream, item: TokenStream) -> TokenSt
                #method_ident,
            };

            closure_defs = quote! {
                #closure_defs
                let tx_clone = tx.clone();
                let #method_ident = Box::new(move |#arg_names| {
                    let tx = tx_clone.clone();
                    topstack::get_runtime().spawn(async move {
                        let result = tx.send(Message::#stack_message(#arg_names)).await;
                        if let Err(e) = result {
                            eprintln!("Error in sending message: {}", e);
            dispatch_arms = quote! {
                #dispatch_arms
                #callbacks_struct_ident::#btif_callback(#arg_names) => {
                    self.#method_ident(#arg_names);
                }
                    });
                });
            };
        }
    }
@@ -111,20 +108,13 @@ pub fn btif_callbacks_generator(attr: TokenStream, item: TokenStream) -> TokenSt

    let gen = quote! {
        #ori_item
        impl #struct_ident {
            pub(crate) fn #fn_ident(&mut self, cb: #callbacks_struct_ident) {
                match cb {
                    #dispatch_arms

        /// Returns a callback object to be passed to topshim.
        pub fn #fn_ident(tx: tokio::sync::mpsc::Sender<Message>) -> #callbacks_struct_ident {
            #closure_defs
            #callbacks_struct_ident {
                #fn_names
                // TODO: Handle these in main loop.
                acl_state_changed: Box::new(|_, _, _, _| {}),
                bond_state_changed: Box::new(|_, _, _| {}),
                device_found: Box::new(|_, _| {}),
                discovery_state_changed: Box::new(|_| {}),
                pin_request: Box::new(|_, _, _, _| {}),
                remote_device_properties_changed: Box::new(|_, _, _, _| {}),
                ssp_request: Box::new(|_, _, _, _, _| {}),
                    _ => println!("Unhandled callback arm {:?}", cb),
                }
            }
        }
    };
+11 −32
Original line number Diff line number Diff line
@@ -7,6 +7,8 @@ use bt_topshim::btif::{
use bt_topshim::profiles::hid_host::{HHCallbacksDispatcher, HidHost};
use bt_topshim::topstack;

use btif_macros::{btif_callback, btif_callbacks_dispatcher};

use num_traits::cast::ToPrimitive;

use std::sync::Arc;
@@ -148,41 +150,14 @@ impl Bluetooth {
    pub(crate) fn callback_disconnected(&mut self, id: u32) {
        self.callbacks.retain(|x| x.0 != id);
    }

    pub(crate) fn handle_base_callback(&mut self, cb: BaseCallbacks) {
        match cb {
            BaseCallbacks::AdapterState(state) => {
                self.adapter_state_changed(state);
            }

            BaseCallbacks::AdapterProperties(status, num_properties, properties) => {
                self.adapter_properties_changed(status, num_properties, properties);
            }

            BaseCallbacks::DeviceFound(_n, properties) => {
                self.device_found(properties);
            }

            BaseCallbacks::DiscoveryState(state) => {
                self.discovery_state(state);
            }

            BaseCallbacks::SspRequest(remote_addr, remote_name, cod, variant, passkey) => {
                self.ssp_request(remote_addr, remote_name, cod, variant, passkey);
            }

            BaseCallbacks::BondState(status, addr, bond_state) => {
                self.bond_state(status, addr, bond_state);
            }

            _ => println!("Unhandled callback arm {:?}", cb),
        }
    }
}

#[btif_callbacks_dispatcher(Bluetooth, dispatch_base_callbacks, BaseCallbacks)]
pub(crate) trait BtifBluetoothCallbacks {
    #[btif_callback(AdapterState)]
    fn adapter_state_changed(&mut self, state: BtState);

    #[btif_callback(AdapterProperties)]
    fn adapter_properties_changed(
        &mut self,
        status: BtStatus,
@@ -190,10 +165,13 @@ pub(crate) trait BtifBluetoothCallbacks {
        properties: Vec<BtProperty>,
    );

    fn device_found(&mut self, properties: Vec<BtProperty>);
    #[btif_callback(DeviceFound)]
    fn device_found(&mut self, n: i32, properties: Vec<BtProperty>);

    #[btif_callback(DiscoveryState)]
    fn discovery_state(&mut self, state: BtDiscoveryState);

    #[btif_callback(SspRequest)]
    fn ssp_request(
        &mut self,
        remote_addr: RawAddress,
@@ -203,6 +181,7 @@ pub(crate) trait BtifBluetoothCallbacks {
        passkey: u32,
    );

    #[btif_callback(BondState)]
    fn bond_state(&mut self, status: BtStatus, addr: RawAddress, bond_state: BtBondState);
}

@@ -248,7 +227,7 @@ impl BtifBluetoothCallbacks for Bluetooth {
        }
    }

    fn device_found(&mut self, properties: Vec<BtProperty>) {
    fn device_found(&mut self, _n: i32, properties: Vec<BtProperty>) {
        self.for_all_callbacks(|callback| {
            callback.on_device_found(BluetoothDevice::from_properties(&properties));
        });
+1 −1
Original line number Diff line number Diff line
@@ -108,7 +108,7 @@ impl Stack {

            match m.unwrap() {
                Message::Base(b) => {
                    bluetooth.lock().unwrap().handle_base_callback(b);
                    bluetooth.lock().unwrap().dispatch_base_callbacks(b);
                }

                Message::BluetoothCallbackDisconnected(id) => {