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

Commit 74d85f37 authored by Abhishek Pandit-Subedi's avatar Abhishek Pandit-Subedi Committed by Abhishek Pandit-Subedi
Browse files

floss: Add support for BtConnectionCallbacks

Bug: 196887445
Tag: #floss
Test: btclient on ChromeOS
Change-Id: I49aaceaec69d3bb3ac54c4135e7a1a4672c74c43
parent e860b8ed
Loading
Loading
Loading
Loading
+34 −1
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@ use crate::ClientContext;
use crate::{console_yellow, print_info};
use bt_topshim::btif::BtSspVariant;
use bt_topshim::profiles::gatt::GattStatus;
use btstack::bluetooth::{BluetoothDevice, IBluetoothCallback};
use btstack::bluetooth::{BluetoothDevice, IBluetoothCallback, IBluetoothConnectionCallback};
use btstack::bluetooth_gatt::{BluetoothGattService, IBluetoothGattCallback, LePhy};
use btstack::RPCProxy;
use manager_service::iface_bluetooth_manager::IBluetoothManagerCallback;
@@ -124,6 +124,39 @@ impl RPCProxy for BtCallback {
    }
}

pub(crate) struct BtConnectionCallback {
    objpath: String,
    _context: Arc<Mutex<ClientContext>>,
}

impl BtConnectionCallback {
    pub(crate) fn new(objpath: String, _context: Arc<Mutex<ClientContext>>) -> Self {
        Self { objpath, _context }
    }
}

impl IBluetoothConnectionCallback for BtConnectionCallback {
    fn on_device_connected(&self, remote_device: BluetoothDevice) {
        print_info!("Connected: [{}]: {}", remote_device.address, remote_device.name);
    }

    fn on_device_disconnected(&self, remote_device: BluetoothDevice) {
        print_info!("Disconnected: [{}]: {}", remote_device.address, remote_device.name);
    }
}

impl RPCProxy for BtConnectionCallback {
    fn register_disconnect(&mut self, _id: u32, _f: Box<dyn Fn(u32) + Send>) {}

    fn get_object_id(&self) -> String {
        self.objpath.clone()
    }

    fn unregister(&mut self, _id: u32) -> bool {
        false
    }
}

pub(crate) struct BtGattCallback {
    objpath: String,
    context: Arc<Mutex<ClientContext>>,
+51 −1
Original line number Diff line number Diff line
@@ -3,7 +3,10 @@
use bt_topshim::btif::{BtSspVariant, Uuid128Bit};
use bt_topshim::profiles::gatt::GattStatus;

use btstack::bluetooth::{BluetoothDevice, BluetoothTransport, IBluetooth, IBluetoothCallback};
use btstack::bluetooth::{
    BluetoothDevice, BluetoothTransport, IBluetooth, IBluetoothCallback,
    IBluetoothConnectionCallback,
};
use btstack::bluetooth_gatt::{
    BluetoothGattCharacteristic, BluetoothGattDescriptor, BluetoothGattService,
    GattWriteRequestStatus, GattWriteType, IBluetoothGatt, IBluetoothGattCallback,
@@ -177,6 +180,32 @@ impl IBluetoothCallback for IBluetoothCallbackDBus {
    fn on_bond_state_changed(&self, status: u32, address: String, state: u32) {}
}

#[allow(dead_code)]
struct IBluetoothConnectionCallbackDBus {}

impl btstack::RPCProxy for IBluetoothConnectionCallbackDBus {
    // Dummy implementations just to satisfy impl RPCProxy requirements.
    fn register_disconnect(&mut self, _id: u32, _f: Box<dyn Fn(u32) + Send>) {}
    fn get_object_id(&self) -> String {
        String::from("")
    }
    fn unregister(&mut self, _id: u32) -> bool {
        false
    }
}

#[generate_dbus_exporter(
    export_bluetooth_connection_callback_dbus_obj,
    "org.chromium.bluetooth.BluetoothConnectionCallback"
)]
impl IBluetoothConnectionCallback for IBluetoothConnectionCallbackDBus {
    #[dbus_method("OnDeviceConnected")]
    fn on_device_connected(&self, remote_device: BluetoothDevice) {}

    #[dbus_method("OnDeviceDisconencted")]
    fn on_device_disconnected(&self, remote_device: BluetoothDevice) {}
}

pub(crate) struct BluetoothDBus {
    client_proxy: ClientDBusProxy,
}
@@ -215,6 +244,27 @@ impl IBluetooth for BluetoothDBus {
        self.client_proxy.method_noreturn("RegisterCallback", (path,))
    }

    fn register_connection_callback(
        &mut self,
        callback: Box<dyn IBluetoothConnectionCallback + Send>,
    ) -> u32 {
        let path_string = callback.get_object_id();
        let path = dbus::Path::new(path_string.clone()).unwrap();
        export_bluetooth_connection_callback_dbus_obj(
            path_string,
            self.client_proxy.conn.clone(),
            &mut self.client_proxy.cr.lock().unwrap(),
            Arc::new(Mutex::new(callback)),
            Arc::new(Mutex::new(DisconnectWatcher::new())),
        );

        self.client_proxy.method("RegisterConnectionCallback", (path,))
    }

    fn unregister_connection_callback(&mut self, id: u32) -> bool {
        self.client_proxy.method("UnregisterConnectionCallback", (id,))
    }

    fn enable(&mut self) -> bool {
        // Not implemented by server
        true
+21 −6
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@ use dbus::nonblock::SyncConnection;
use dbus_crossroads::Crossroads;
use tokio::sync::mpsc;

use crate::callbacks::{BtCallback, BtManagerCallback};
use crate::callbacks::{BtCallback, BtConnectionCallback, BtManagerCallback};
use crate::command_handler::CommandHandler;
use crate::dbus_iface::{BluetoothDBus, BluetoothGattDBus, BluetoothManagerDBus};
use crate::editor::AsyncEditor;
@@ -134,8 +134,8 @@ impl ClientContext {
        // Trigger callback registration in the foreground
        let fg = self.fg.clone();
        tokio::spawn(async move {
            let objpath = String::from("/org/chromium/bluetooth/client/bluetooth_callback");
            let _ = fg.send(ForegroundActions::RegisterAdapterCallback(objpath)).await;
            let adapter = String::from(format!("adapter{}", idx));
            let _ = fg.send(ForegroundActions::RegisterAdapterCallback(adapter)).await;
        });
    }

@@ -151,7 +151,7 @@ impl ClientContext {
/// Actions to take on the foreground loop. This allows us to queue actions in
/// callbacks that get run in the foreground context.
enum ForegroundActions {
    RegisterAdapterCallback(String), // Register callbacks with this objpath
    RegisterAdapterCallback(String), // Register callbacks for this adapter
    Readline(rustyline::Result<String>), // Readline result from rustyline
}

@@ -260,14 +260,29 @@ async fn start_interactive_shell(

        match m.unwrap() {
            // Once adapter is ready, register callbacks, get the address and mark it as ready
            ForegroundActions::RegisterAdapterCallback(objpath) => {
            ForegroundActions::RegisterAdapterCallback(adapter) => {
                let cb_objpath: String =
                    format!("/org/chromium/bluetooth/client/{}/bluetooth_callback", adapter);
                let conn_cb_objpath: String =
                    format!("/org/chromium/bluetooth/client/{}/bluetooth_conn_callback", adapter);

                context
                    .lock()
                    .unwrap()
                    .adapter_dbus
                    .as_mut()
                    .unwrap()
                    .register_callback(Box::new(BtCallback::new(objpath, context.clone())));
                    .register_callback(Box::new(BtCallback::new(cb_objpath, context.clone())));
                context
                    .lock()
                    .unwrap()
                    .adapter_dbus
                    .as_mut()
                    .unwrap()
                    .register_connection_callback(Box::new(BtConnectionCallback::new(
                        conn_cb_objpath,
                        context.clone(),
                    )));
                context.lock().unwrap().adapter_ready = true;
                let adapter_address = context.lock().unwrap().update_adapter_address();
                print_info!("Adapter {} is ready", adapter_address);
+29 −1
Original line number Diff line number Diff line
@@ -2,7 +2,10 @@ extern crate bt_shim;

use bt_topshim::btif::{BtSspVariant, Uuid128Bit};

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

use dbus::arg::RefArg;
@@ -54,6 +57,18 @@ impl IBluetoothCallback for BluetoothCallbackDBus {
impl_dbus_arg_enum!(BluetoothTransport);
impl_dbus_arg_enum!(BtSspVariant);

#[allow(dead_code)]
struct BluetoothConnectionCallbackDBus {}

#[dbus_proxy_obj(BluetoothConnectionCallback, "org.chromium.bluetooth.BluetoothConnectionCallback")]
impl IBluetoothConnectionCallback for BluetoothConnectionCallbackDBus {
    #[dbus_method("OnDeviceConnected")]
    fn on_device_connected(&self, remote_device: BluetoothDevice) {}

    #[dbus_method("OnDeviceDisconencted")]
    fn on_device_disconnected(&self, remote_device: BluetoothDevice) {}
}

#[allow(dead_code)]
struct IBluetoothDBus {}

@@ -62,6 +77,19 @@ impl IBluetooth for IBluetoothDBus {
    #[dbus_method("RegisterCallback")]
    fn register_callback(&mut self, callback: Box<dyn IBluetoothCallback + Send>) {}

    #[dbus_method("RegisterConnectionCallback")]
    fn register_connection_callback(
        &mut self,
        callback: Box<dyn IBluetoothConnectionCallback + Send>,
    ) -> u32 {
        0
    }

    #[dbus_method("UnregisterConnectionCallback")]
    fn unregister_connection_callback(&mut self, id: u32) -> bool {
        false
    }

    // Not exposed over D-Bus. The stack is automatically enabled when the daemon starts.
    fn enable(&mut self) -> bool {
        false
+154 −11
Original line number Diff line number Diff line
//! Anything related to the adapter API (IBluetooth).

use bt_topshim::btif::{
    BaseCallbacks, BaseCallbacksDispatcher, BluetoothInterface, BluetoothProperty, BtBondState,
    BtDiscoveryState, BtPropertyType, BtSspVariant, BtState, BtStatus, BtTransport, RawAddress,
    Uuid, Uuid128Bit,
    BaseCallbacks, BaseCallbacksDispatcher, BluetoothInterface, BluetoothProperty, BtAclState,
    BtBondState, BtDiscoveryState, BtHciErrorCode, BtPropertyType, BtSspVariant, BtState, BtStatus,
    BtTransport, RawAddress, Uuid, Uuid128Bit,
};
use bt_topshim::{
    profiles::hid_host::{HHCallbacksDispatcher, HidHost},
@@ -22,13 +22,22 @@ use std::time::Instant;
use tokio::sync::mpsc::Sender;

use crate::bluetooth_media::{BluetoothMedia, IBluetoothMedia};
use crate::{Message, RPCProxy};
use crate::{BluetoothCallbackType, Message, RPCProxy};

/// Defines the adapter API.
pub trait IBluetooth {
    /// Adds a callback from a client who wishes to observe adapter events.
    fn register_callback(&mut self, callback: Box<dyn IBluetoothCallback + Send>);

    /// Adds a callback from a client who wishes to observe connection events.
    fn register_connection_callback(
        &mut self,
        callback: Box<dyn IBluetoothConnectionCallback + Send>,
    ) -> u32;

    /// Removes registered callback.
    fn unregister_connection_callback(&mut self, callback_id: u32) -> bool;

    /// Enables the adapter.
    ///
    /// Returns true if the request is accepted.
@@ -131,6 +140,7 @@ impl BluetoothDevice {
/// Internal data structure that keeps a map of cached properties for a remote device.
struct BluetoothDeviceContext {
    pub bond_state: BtBondState,
    pub acl_state: BtAclState,
    pub info: BluetoothDevice,
    pub properties: HashMap<BtPropertyType, BluetoothProperty>,
}
@@ -138,11 +148,12 @@ struct BluetoothDeviceContext {
impl BluetoothDeviceContext {
    pub(crate) fn new(
        bond_state: BtBondState,
        acl_state: BtAclState,
        info: BluetoothDevice,
        properties: Vec<BluetoothProperty>,
    ) -> BluetoothDeviceContext {
        let bond_state = BtBondState::NotBonded;
        let mut device = BluetoothDeviceContext { bond_state, info, properties: HashMap::new() };
        let mut device =
            BluetoothDeviceContext { bond_state, acl_state, info, properties: HashMap::new() };
        device.update_properties(properties);
        device
    }
@@ -188,6 +199,14 @@ pub trait IBluetoothCallback: RPCProxy {
    fn on_bond_state_changed(&self, status: u32, device_address: String, state: u32);
}

pub trait IBluetoothConnectionCallback: RPCProxy {
    /// Notification sent when a remote device completes HCI connection.
    fn on_device_connected(&self, remote_device: BluetoothDevice);

    /// Notification sent when a remote device completes HCI disconnection.
    fn on_device_disconnected(&self, remote_device: BluetoothDevice);
}

/// Implementation of the adapter API.
pub struct Bluetooth {
    intf: Arc<Mutex<BluetoothInterface>>,
@@ -196,6 +215,7 @@ pub struct Bluetooth {
    bluetooth_media: Arc<Mutex<Box<BluetoothMedia>>>,
    callbacks: Vec<(u32, Box<dyn IBluetoothCallback + Send>)>,
    callbacks_last_id: u32,
    connection_callbacks: Vec<(u32, Box<dyn IBluetoothConnectionCallback + Send>)>,
    discovering_started: Instant,
    hh: Option<HidHost>,
    is_discovering: bool,
@@ -218,6 +238,7 @@ impl Bluetooth {
            bonded_devices: HashMap::new(),
            callbacks: vec![],
            callbacks_last_id: 0,
            connection_callbacks: vec![],
            hh: None,
            bluetooth_media,
            discovering_started: Instant::now(),
@@ -270,13 +291,25 @@ impl Bluetooth {
        }
    }

    fn for_all_connection_callbacks<F: Fn(&Box<dyn IBluetoothConnectionCallback + Send>)>(
        &self,
        f: F,
    ) {
        for callback in &self.connection_callbacks {
            f(&callback.1);
        }
    }

    fn get_next_id(&mut self) -> u32 {
        self.callbacks_last_id += 1;
        self.callbacks_last_id
    }

    pub(crate) fn callback_disconnected(&mut self, id: u32) {
        self.callbacks.retain(|x| x.0 != id);
    pub(crate) fn callback_disconnected(&mut self, id: u32, cb_type: BluetoothCallbackType) {
        match cb_type {
            BluetoothCallbackType::Adapter => self.callbacks.retain(|x| x.0 != id),
            BluetoothCallbackType::Connection => self.connection_callbacks.retain(|x| x.0 != id),
        }
    }
}

@@ -326,6 +359,16 @@ pub(crate) trait BtifBluetoothCallbacks {
        num_properties: i32,
        properties: Vec<BluetoothProperty>,
    );

    #[btif_callback(AclState)]
    fn acl_state(
        &mut self,
        status: BtStatus,
        addr: RawAddress,
        state: BtAclState,
        link_type: BtTransport,
        hci_reason: BtHciErrorCode,
    );
}

#[btif_callbacks_dispatcher(Bluetooth, dispatch_sdp_callbacks, SdpCallbacks)]
@@ -402,6 +445,7 @@ impl BtifBluetoothCallbacks for Bluetooth {
                            .and_modify(|d| d.bond_state = BtBondState::Bonded)
                            .or_insert(BluetoothDeviceContext::new(
                                BtBondState::Bonded,
                                BtAclState::Disconnected,
                                BluetoothDevice::new(address.clone(), "".to_string()),
                                vec![],
                            ));
@@ -421,8 +465,12 @@ impl BtifBluetoothCallbacks for Bluetooth {
        if let Some(existing) = self.found_devices.get_mut(&address) {
            existing.update_properties(properties);
        } else {
            let device_with_props =
                BluetoothDeviceContext::new(BtBondState::NotBonded, device, properties);
            let device_with_props = BluetoothDeviceContext::new(
                BtBondState::NotBonded,
                BtAclState::Disconnected,
                device,
                properties,
            );
            self.found_devices.insert(address.clone(), device_with_props);
        }

@@ -504,6 +552,7 @@ impl BtifBluetoothCallbacks for Bluetooth {
                }
                None => BluetoothDeviceContext::new(
                    bond_state.clone(),
                    BtAclState::Disconnected,
                    BluetoothDevice::new(address.clone(), "".to_string()),
                    vec![],
                ),
@@ -545,6 +594,7 @@ impl BtifBluetoothCallbacks for Bluetooth {
                address.clone(),
                BluetoothDeviceContext::new(
                    BtBondState::NotBonded,
                    BtAclState::Disconnected,
                    BluetoothDevice::new(address.clone(), String::from("")),
                    vec![],
                ),
@@ -555,6 +605,60 @@ impl BtifBluetoothCallbacks for Bluetooth {

        device.unwrap().update_properties(properties);
    }

    fn acl_state(
        &mut self,
        status: BtStatus,
        addr: RawAddress,
        state: BtAclState,
        _link_type: BtTransport,
        _hci_reason: BtHciErrorCode,
    ) {
        if status != BtStatus::Success {
            warn!("Connection to [{}] failed. Status: {:?}", addr.to_string(), status);
            return;
        }

        let address = addr.to_string();

        let found = if self.bonded_devices.contains_key(&address) {
            self.bonded_devices.get_mut(&address)
        } else if self.found_devices.contains_key(&address) {
            self.found_devices.get_mut(&address)
        } else {
            self.found_devices.insert(
                address.clone(),
                BluetoothDeviceContext::new(
                    BtBondState::NotBonded,
                    BtAclState::Disconnected,
                    BluetoothDevice::new(address.clone(), String::from("")),
                    vec![],
                ),
            );

            self.found_devices.get_mut(&address)
        };

        // Only notify if there's been a change in state
        let prev_state = &found.as_ref().unwrap().acl_state;
        if prev_state != &state {
            let device = found.as_ref().unwrap().info.clone();
            found.unwrap().acl_state = state.clone();

            match state {
                BtAclState::Connected => {
                    self.for_all_connection_callbacks(|callback| {
                        callback.on_device_connected(device.clone());
                    });
                }
                BtAclState::Disconnected => {
                    self.for_all_connection_callbacks(|callback| {
                        callback.on_device_disconnected(device.clone());
                    });
                }
            };
        }
    }
}

// TODO: Add unit tests for this implementation
@@ -568,7 +672,12 @@ impl IBluetooth for Bluetooth {
            Box::new(move |cb_id| {
                let tx = tx.clone();
                tokio::spawn(async move {
                    let _result = tx.send(Message::BluetoothCallbackDisconnected(cb_id)).await;
                    let _result = tx
                        .send(Message::BluetoothCallbackDisconnected(
                            cb_id,
                            BluetoothCallbackType::Adapter,
                        ))
                        .await;
                });
            }),
        );
@@ -576,6 +685,40 @@ impl IBluetooth for Bluetooth {
        self.callbacks.push((id, callback))
    }

    fn register_connection_callback(
        &mut self,
        mut callback: Box<dyn IBluetoothConnectionCallback + Send>,
    ) -> u32 {
        let tx = self.tx.clone();
        let id = self.get_next_id();

        callback.register_disconnect(
            id,
            Box::new(move |cb_id| {
                let tx = tx.clone();
                tokio::spawn(async move {
                    let _ = tx
                        .send(Message::BluetoothCallbackDisconnected(
                            cb_id,
                            BluetoothCallbackType::Connection,
                        ))
                        .await;
                });
            }),
        );

        self.connection_callbacks.push((id, callback));

        id
    }

    fn unregister_connection_callback(&mut self, callback_id: u32) -> bool {
        match self.connection_callbacks.iter().position(|x| x.0 == callback_id) {
            Some(index) => self.connection_callbacks[index].1.unregister(callback_id),
            None => false,
        }
    }

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