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

Commit 1ccf8ee8 authored by Sonny Sasaka's avatar Sonny Sasaka Committed by Gerrit Code Review
Browse files

Merge changes I7b8e58e8,I7a218feb

* changes:
  Add macro to dispatch btif callbacks
  Add some API implementations
parents 4415ddcb 564dde34
Loading
Loading
Loading
Loading
+35 −4
Original line number Diff line number Diff line
extern crate bt_shim;

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

use dbus::arg::RefArg;

use dbus::nonblock::SyncConnection;
use dbus::strings::{BusName, Path};

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

use dbus_projection::impl_dbus_arg_enum;
use dbus_projection::DisconnectWatcher;

use num_traits::cast::{FromPrimitive, ToPrimitive};

use std::error::Error;
use std::sync::Arc;
use std::sync::Mutex;

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

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

#[allow(dead_code)]
struct BluetoothCallbackDBus {}

#[dbus_proxy_obj(BluetoothCallback, "org.chromium.bluetooth.BluetoothCallback")]
impl IBluetoothCallback for BluetoothCallbackDBus {
    #[dbus_method("OnBluetoothStateChange")]
    #[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) {}
}

impl_dbus_arg_enum!(BluetoothTransport);

#[allow(dead_code)]
struct IBluetoothDBus {}

@@ -48,4 +64,19 @@ impl IBluetooth for IBluetoothDBus {
    fn get_address(&self) -> String {
        String::from("")
    }

    #[dbus_method("StartDiscovery")]
    fn start_discovery(&self) -> bool {
        true
    }

    #[dbus_method("CancelDiscovery")]
    fn cancel_discovery(&self) -> bool {
        true
    }

    #[dbus_method("CreateBond")]
    fn create_bond(&self, _device: BluetoothDevice, _transport: BluetoothTransport) -> bool {
        true
    }
}
+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),
                }
            }
        }
    };
+160 −7
Original line number Diff line number Diff line
//! Anything related to the adapter API (IBluetooth).

use bt_topshim::btif::{
    BaseCallbacksDispatcher, BluetoothInterface, BtProperty, BtPropertyType, BtState, BtStatus,
    BaseCallbacks, BaseCallbacksDispatcher, BluetoothInterface, BtBondState, BtDiscoveryState,
    BtProperty, BtPropertyType, BtSspVariant, BtState, BtStatus, BtTransport, RawAddress,
};
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;
@@ -31,6 +35,52 @@ pub trait IBluetooth {

    /// Returns the Bluetooth address of the local adapter.
    fn get_address(&self) -> String;

    /// Starts BREDR Inquiry.
    fn start_discovery(&self) -> bool;

    /// Cancels BREDR Inquiry.
    fn cancel_discovery(&self) -> bool;

    /// Initiates pairing to a remote device. Triggers connection if not already started.
    fn create_bond(&self, device: BluetoothDevice, transport: BluetoothTransport) -> bool;
}

#[derive(Debug, FromPrimitive, ToPrimitive)]
#[repr(i32)]
pub enum BluetoothTransport {
    Auto = 0,
    Bredr = 1,
    Le = 2,
}

#[derive(Debug, Default)]
pub struct BluetoothDevice {
    pub address: String,
    pub name: String,
}

impl BluetoothDevice {
    pub(crate) fn from_properties(properties: &Vec<BtProperty>) -> BluetoothDevice {
        let mut address = String::from("");
        let mut name = String::from("");

        for prop in properties {
            match prop.prop_type {
                BtPropertyType::BdAddr => {
                    if let Some(addr) = BDAddr::from_byte_vec(&prop.val) {
                        address = addr.to_string();
                    }
                }
                BtPropertyType::BdName => {
                    name = String::from_utf8(prop.val.clone()).unwrap();
                }
                _ => {}
            }
        }

        BluetoothDevice { address, name }
    }
}

/// The interface for adapter callbacks registered through `IBluetooth::register_callback`.
@@ -40,6 +90,12 @@ pub trait IBluetoothCallback: RPCProxy {

    /// When any of the adapter local address is changed.
    fn on_bluetooth_address_changed(&self, addr: String);

    /// When a device is found via discovery.
    fn on_device_found(&self, remote_device: BluetoothDevice);

    /// When the discovery state is changed.
    fn on_discovering_changed(&self, discovering: bool);
}

/// Implementation of the adapter API.
@@ -50,6 +106,7 @@ pub struct Bluetooth {
    callbacks_last_id: u32,
    tx: Sender<Message>,
    local_address: Option<BDAddr>,
    hh: Option<HidHost>,
}

impl Bluetooth {
@@ -62,14 +119,31 @@ impl Bluetooth {
            callbacks: vec![],
            callbacks_last_id: 0,
            local_address: None,
            hh: None,
        }
    }

    pub fn init_profiles(&mut self) {
        self.hh = Some(HidHost::new(&self.intf.lock().unwrap()));
        self.hh.as_mut().unwrap().initialize(HHCallbacksDispatcher {
            dispatch: Box::new(move |_cb| {
                // TODO("Implement the callbacks");
                println!("received HH callback");
            }),
        });
    }

    fn update_local_address(&mut self, raw: &Vec<u8>) {
        self.local_address = Some(BDAddr::from_byte_vec(raw));
        self.local_address = BDAddr::from_byte_vec(raw);

        self.for_all_callbacks(|callback| {
            callback.on_bluetooth_address_changed(self.local_address.unwrap().to_string());
        });
    }

    fn for_all_callbacks<F: Fn(&Box<dyn IBluetoothCallback + Send>)>(&self, f: F) {
        for callback in &self.callbacks {
            callback.1.on_bluetooth_address_changed(self.local_address.unwrap().to_string());
            f(&callback.1);
        }
    }

@@ -78,15 +152,37 @@ impl Bluetooth {
    }
}

#[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,
        num_properties: i32,
        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,
        remote_name: String,
        cod: u32,
        variant: BtSspVariant,
        passkey: u32,
    );

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

pub fn get_bt_dispatcher(tx: Sender<Message>) -> BaseCallbacksDispatcher {
@@ -102,11 +198,10 @@ pub fn get_bt_dispatcher(tx: Sender<Message>) -> BaseCallbacksDispatcher {

impl BtifBluetoothCallbacks for Bluetooth {
    fn adapter_state_changed(&mut self, state: BtState) {
        for callback in &self.callbacks {
        self.for_all_callbacks(|callback| {
            callback
                .1
                .on_bluetooth_state_changed(self.state.to_u32().unwrap(), state.to_u32().unwrap());
        }
                .on_bluetooth_state_changed(self.state.to_u32().unwrap(), state.to_u32().unwrap())
        });

        self.state = state;
    }
@@ -131,6 +226,40 @@ impl BtifBluetoothCallbacks for Bluetooth {
            }
        }
    }

    fn device_found(&mut self, _n: i32, properties: Vec<BtProperty>) {
        self.for_all_callbacks(|callback| {
            callback.on_device_found(BluetoothDevice::from_properties(&properties));
        });
    }

    fn discovery_state(&mut self, state: BtDiscoveryState) {
        self.for_all_callbacks(|callback| {
            callback.on_discovering_changed(state == BtDiscoveryState::Started);
        });
    }

    fn ssp_request(
        &mut self,
        remote_addr: RawAddress,
        _remote_name: String,
        _cod: u32,
        variant: BtSspVariant,
        passkey: u32,
    ) {
        // Immediately accept the pairing.
        // TODO: Delegate the pairing confirmation to agent.
        // TODO: Implement other pairing confirmations (passkey, passcode, etc);
        self.intf.lock().unwrap().ssp_reply(&remote_addr, variant, 1, passkey);
    }

    fn bond_state(&mut self, _status: BtStatus, mut addr: RawAddress, bond_state: BtBondState) {
        if bond_state == BtBondState::Bonded {
            // We are assuming that peer is a HID device and automatically connect to that profile.
            // TODO: Only connect to enabled profiles on that device.
            self.hh.as_ref().unwrap().connect(&mut addr);
        }
    }
}

// TODO: Add unit tests for this implementation
@@ -166,4 +295,28 @@ impl IBluetooth for Bluetooth {
            Some(addr) => addr.to_string(),
        }
    }

    fn start_discovery(&self) -> bool {
        self.intf.lock().unwrap().start_discovery() == 0
    }

    fn cancel_discovery(&self) -> bool {
        self.intf.lock().unwrap().cancel_discovery() == 0
    }

    fn create_bond(&self, device: BluetoothDevice, transport: BluetoothTransport) -> bool {
        let addr = BDAddr::from_string(device.address.clone());

        if addr.is_none() {
            println!("address {} is not valid", device.address);
            return false;
        }

        let address = unsafe { RawAddress::new(&addr.unwrap().val) };
        self.intf
            .lock()
            .unwrap()
            .create_bond(&address, BtTransport::from(transport.to_i32().unwrap()))
            == 0
    }
}
+35 −18
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ use std::sync::{Arc, Mutex};
use tokio::sync::mpsc::channel;
use tokio::sync::mpsc::{Receiver, Sender};

use crate::bluetooth::{Bluetooth, BtifBluetoothCallbacks};
use crate::bluetooth::Bluetooth;

/// Represents a Bluetooth address.
// TODO: Add support for LE random addresses.
@@ -36,6 +36,12 @@ impl Debug for BDAddr {
    }
}

impl Default for BDAddr {
    fn default() -> Self {
        Self { val: [0; 6] }
    }
}

impl ToString for BDAddr {
    fn to_string(&self) -> String {
        String::from(format!(
@@ -47,8 +53,31 @@ impl ToString for BDAddr {

impl BDAddr {
    /// Constructs a BDAddr from a vector of 6 bytes.
    fn from_byte_vec(raw_addr: &Vec<u8>) -> BDAddr {
        BDAddr { val: raw_addr.clone().try_into().unwrap() }
    fn from_byte_vec(raw_addr: &Vec<u8>) -> Option<BDAddr> {
        if let Ok(val) = raw_addr.clone().try_into() {
            return Some(BDAddr { val });
        }
        None
    }

    fn from_string(addr_str: String) -> Option<BDAddr> {
        let s = addr_str.split(':').collect::<Vec<&str>>();

        if s.len() != 6 {
            return None;
        }

        let mut raw: [u8; 6] = [0; 6];
        for i in 0..s.len() {
            raw[i] = match u8::from_str_radix(s[i], 16) {
                Ok(res) => res,
                Err(_) => {
                    return None;
                }
            };
        }

        Some(BDAddr { val: raw })
    }
}

@@ -78,22 +107,10 @@ impl Stack {
            }

            match m.unwrap() {
                Message::Base(b) => match b {
                    BaseCallbacks::AdapterState(state) => {
                        bluetooth.lock().unwrap().adapter_state_changed(state);
                Message::Base(b) => {
                    bluetooth.lock().unwrap().dispatch_base_callbacks(b);
                }

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

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

                Message::BluetoothCallbackDisconnected(id) => {
                    bluetooth.lock().unwrap().callback_disconnected(id);
                }