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

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

Add some API implementations

This adds initial implementations of StartDiscovery and CancelDiscovery,
and CreateBond. At this point the implementations are focused on making
a HID use case work and still missing other case handling.

Bug: 188718204
Bug: 188718859
Tag: #floss
Test: manual - build floss on Linux

Change-Id: I7a218feb902d27397ac7c35a3fe5ad98cc0d2759
parent ced2a66a
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
    }
}
+181 −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 num_traits::cast::ToPrimitive;
@@ -31,6 +33,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 +88,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 +104,7 @@ pub struct Bluetooth {
    callbacks_last_id: u32,
    tx: Sender<Message>,
    local_address: Option<BDAddr>,
    hh: Option<HidHost>,
}

impl Bluetooth {
@@ -62,20 +117,67 @@ 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);
        }
    }

    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),
        }
    }
}

pub(crate) trait BtifBluetoothCallbacks {
@@ -87,6 +189,21 @@ pub(crate) trait BtifBluetoothCallbacks {
        num_properties: i32,
        properties: Vec<BtProperty>,
    );

    fn device_found(&mut self, properties: Vec<BtProperty>);

    fn discovery_state(&mut self, state: BtDiscoveryState);

    fn ssp_request(
        &mut self,
        remote_addr: RawAddress,
        remote_name: String,
        cod: u32,
        variant: BtSspVariant,
        passkey: u32,
    );

    fn bond_state(&mut self, status: BtStatus, addr: RawAddress, bond_state: BtBondState);
}

pub fn get_bt_dispatcher(tx: Sender<Message>) -> BaseCallbacksDispatcher {
@@ -102,11 +219,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 +247,40 @@ impl BtifBluetoothCallbacks for Bluetooth {
            }
        }
    }

    fn device_found(&mut self, 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 +316,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().handle_base_callback(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);
                }