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

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

floss: Improve bonding procedure

Improve bonding in both the stack and client by doing a few things:
* Don't automatically accept pairing and let the client handle bonding.
* Client handles consent based bonding by accepting locally initiated
  bonding.
* Keep track of client initiated bonding
* Add stack apis: set_pin, set_passkey, and set_pairing_confirmation

Bug: 196887009
Tag: #floss
Test: btclient bond with consent and passkey notification
Change-Id: I6f6845d1af92e5cd10349c3bdb0b18183430b47f
parent b8b6cb24
Loading
Loading
Loading
Loading
+57 −12
Original line number Diff line number Diff line
@@ -2,7 +2,9 @@ use crate::ClientContext;
use crate::{console_yellow, print_info};
use bt_topshim::btif::{BtBondState, BtSspVariant};
use bt_topshim::profiles::gatt::GattStatus;
use btstack::bluetooth::{BluetoothDevice, IBluetoothCallback, IBluetoothConnectionCallback};
use btstack::bluetooth::{
    BluetoothDevice, IBluetooth, IBluetoothCallback, IBluetoothConnectionCallback,
};
use btstack::bluetooth_gatt::{BluetoothGattService, IBluetoothGattCallback, LePhy};
use btstack::RPCProxy;
use manager_service::iface_bluetooth_manager::IBluetoothManagerCallback;
@@ -93,23 +95,66 @@ impl IBluetoothCallback for BtCallback {
        variant: BtSspVariant,
        passkey: u32,
    ) {
        if variant == BtSspVariant::PasskeyNotification {
        match variant {
            BtSspVariant::PasskeyNotification => {
                print_info!(
                "device {}{} would like to pair, enter passkey on remote device: {:06}",
                remote_device.address.to_string(),
                if remote_device.name.len() > 0 {
                    format!(" ({})", remote_device.name)
                } else {
                    String::from("")
                },
                    "Device [{}: {}] would like to pair, enter passkey on remote device: {:06}",
                    &remote_device.address,
                    &remote_device.name,
                    passkey
                );
            }
            BtSspVariant::Consent => {
                let rd = remote_device.clone();
                self.context.lock().unwrap().run_callback(Box::new(move |context| {
                    // Auto-confirm bonding attempts that were locally initiated.
                    // Ignore all other bonding attempts.
                    let bonding_device = context.lock().unwrap().bonding_attempt.as_ref().cloned();
                    match bonding_device {
                        Some(bd) => {
                            if bd.address == rd.address {
                                context
                                    .lock()
                                    .unwrap()
                                    .adapter_dbus
                                    .as_ref()
                                    .unwrap()
                                    .set_pairing_confirmation(rd.clone(), true);
                            }
                        }
                        None => (),
                    }
                }));
            }
            BtSspVariant::PasskeyEntry => {
                println!("Got PasskeyEntry but it is not supported...");
            }
            BtSspVariant::PasskeyConfirmation => {
                println!("Got PasskeyConfirmation but there's nothing to do...");
            }
        }
    }

    fn on_bond_state_changed(&self, status: u32, address: String, state: u32) {
        print_info!("Bonding state changed: [{}] state: {}, Status = {}", address, state, status);

        // Clear bonding attempt if bonding fails or succeeds
        match BtBondState::from(state) {
            BtBondState::NotBonded | BtBondState::Bonded => {
                let bonding_attempt =
                    self.context.lock().unwrap().bonding_attempt.as_ref().cloned();
                match bonding_attempt {
                    Some(bd) => {
                        if &address == &bd.address {
                            self.context.lock().unwrap().bonding_attempt = None;
                        }
                    }
                    None => (),
                }
            }
            BtBondState::Bonding => (),
        }

        // If bonded, we should also automatically connect all enabled profiles
        if BtBondState::Bonded == state.into() {
            self.context.lock().unwrap().connect_all_enabled_profiles(BluetoothDevice {
+20 −5
Original line number Diff line number Diff line
@@ -2,12 +2,11 @@ use std::collections::HashMap;
use std::fmt::{Display, Formatter, Result};
use std::sync::{Arc, Mutex};

use num_traits::cast::FromPrimitive;

use crate::callbacks::BtGattCallback;
use crate::ClientContext;
use crate::{console_red, console_yellow, print_error, print_info};
use btstack::bluetooth::{BluetoothDevice, BluetoothTransport, IBluetooth};
use bt_topshim::btif::BtTransport;
use btstack::bluetooth::{BluetoothDevice, IBluetooth};
use btstack::bluetooth_gatt::IBluetoothGatt;
use btstack::uuid::UuidHelper;
use manager_service::iface_bluetooth_manager::IBluetoothManager;
@@ -318,13 +317,29 @@ impl CommandHandler {
                    name: String::from("Classic Device"),
                };

                self.context
                let bonding_attempt =
                    &self.context.lock().unwrap().bonding_attempt.as_ref().cloned();

                if bonding_attempt.is_some() {
                    print_info!(
                        "Already bonding [{}]. Cancel bonding first.",
                        bonding_attempt.as_ref().unwrap().address,
                    );
                    return;
                }

                let success = self
                    .context
                    .lock()
                    .unwrap()
                    .adapter_dbus
                    .as_ref()
                    .unwrap()
                    .create_bond(device, BluetoothTransport::from_i32(0).unwrap());
                    .create_bond(device.clone(), BtTransport::Auto);

                if success {
                    self.context.lock().unwrap().bonding_attempt = Some(device);
                }
            }
            "remove" => {
                let device = BluetoothDevice {
+26 −9
Original line number Diff line number Diff line
//! D-Bus proxy implementations of the APIs.

use bt_topshim::btif::{BtSspVariant, Uuid128Bit};
use bt_topshim::btif::{BtSspVariant, BtTransport, Uuid128Bit};
use bt_topshim::profiles::gatt::GattStatus;

use btstack::bluetooth::{
    BluetoothDevice, BluetoothTransport, IBluetooth, IBluetoothCallback,
    IBluetoothConnectionCallback,
    BluetoothDevice, IBluetooth, IBluetoothCallback, IBluetoothConnectionCallback,
};
use btstack::bluetooth_gatt::{
    BluetoothGattCharacteristic, BluetoothGattDescriptor, BluetoothGattService,
@@ -37,7 +36,7 @@ fn make_object_path(idx: i32, name: &str) -> dbus::Path {
    dbus::Path::new(format!("/org/chromium/bluetooth/hci{}/{}", idx, name)).unwrap()
}

impl_dbus_arg_enum!(BluetoothTransport);
impl_dbus_arg_enum!(BtTransport);
impl_dbus_arg_enum!(BtSspVariant);
impl_dbus_arg_enum!(GattStatus);
impl_dbus_arg_enum!(GattWriteType);
@@ -308,13 +307,10 @@ impl IBluetooth for BluetoothDBus {
        self.client_proxy.method("GetDiscoveryEndMillis", ())
    }

    fn create_bond(&self, device: BluetoothDevice, transport: BluetoothTransport) -> bool {
    fn create_bond(&self, device: BluetoothDevice, transport: BtTransport) -> bool {
        self.client_proxy.method(
            "CreateBond",
            (
                BluetoothDevice::to_dbus(device).unwrap(),
                BluetoothTransport::to_dbus(transport).unwrap(),
            ),
            (BluetoothDevice::to_dbus(device).unwrap(), BtTransport::to_dbus(transport).unwrap()),
        )
    }

@@ -335,6 +331,27 @@ impl IBluetooth for BluetoothDBus {
        self.client_proxy.method("GetBondState", (BluetoothDevice::to_dbus(device).unwrap(),))
    }

    fn set_pin(&self, device: BluetoothDevice, accept: bool, len: u32, pin_code: Vec<u8>) -> bool {
        self.client_proxy
            .method("SetPin", (BluetoothDevice::to_dbus(device).unwrap(), accept, len, pin_code))
    }

    fn set_passkey(
        &self,
        device: BluetoothDevice,
        accept: bool,
        len: u32,
        passkey: Vec<u8>,
    ) -> bool {
        self.client_proxy
            .method("SetPasskey", (BluetoothDevice::to_dbus(device).unwrap(), accept, len, passkey))
    }

    fn set_pairing_confirmation(&self, device: BluetoothDevice, accept: bool) -> bool {
        self.client_proxy
            .method("SetPairingConfirmation", (BluetoothDevice::to_dbus(device).unwrap(), accept))
    }

    fn get_connection_state(&self, device: BluetoothDevice) -> u32 {
        self.client_proxy.method("GetConnectionState", (BluetoothDevice::to_dbus(device).unwrap(),))
    }
+16 −0
Original line number Diff line number Diff line
@@ -41,6 +41,10 @@ pub(crate) struct ClientContext {
    /// Current adapter address if known.
    pub(crate) adapter_address: Option<String>,

    /// Currently active bonding attempt. If it is not none, we are currently attempting to bond
    /// this device.
    pub(crate) bonding_attempt: Option<BluetoothDevice>,

    /// Is adapter discovering?
    pub(crate) discovering_state: bool,

@@ -87,6 +91,7 @@ impl ClientContext {
            enabled: false,
            adapter_ready: false,
            adapter_address: None,
            bonding_attempt: None,
            discovering_state: false,
            found_devices: HashMap::new(),
            gatt_client_id: None,
@@ -153,12 +158,20 @@ impl ClientContext {
            let _ = fg.send(ForegroundActions::ConnectAllEnabledProfiles(device)).await;
        });
    }

    fn run_callback(&mut self, callback: Box<dyn Fn(Arc<Mutex<ClientContext>>) + Send>) {
        let fg = self.fg.clone();
        tokio::spawn(async move {
            let _ = fg.send(ForegroundActions::RunCallback(callback)).await;
        });
    }
}

/// Actions to take on the foreground loop. This allows us to queue actions in
/// callbacks that get run in the foreground context.
enum ForegroundActions {
    ConnectAllEnabledProfiles(BluetoothDevice), // Connect all enabled profiles for this device
    RunCallback(Box<dyn Fn(Arc<Mutex<ClientContext>>) + Send>), // Run callback in foreground
    RegisterAdapterCallback(String),            // Register callbacks for this adapter
    Readline(rustyline::Result<String>),        // Readline result from rustyline
}
@@ -280,6 +293,9 @@ async fn start_interactive_shell(
                    println!("Adapter isn't ready to connect profiles.");
                }
            }
            ForegroundActions::RunCallback(callback) => {
                callback(context.clone());
            }
            // Once adapter is ready, register callbacks, get the address and mark it as ready
            ForegroundActions::RegisterAdapterCallback(adapter) => {
                let cb_objpath: String =
+31 −5
Original line number Diff line number Diff line
extern crate bt_shim;

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

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

@@ -54,7 +53,7 @@ impl IBluetoothCallback for BluetoothCallbackDBus {
    fn on_bond_state_changed(&self, status: u32, address: String, state: u32) {}
}

impl_dbus_arg_enum!(BluetoothTransport);
impl_dbus_arg_enum!(BtTransport);
impl_dbus_arg_enum!(BtSspVariant);

#[allow(dead_code)]
@@ -142,7 +141,7 @@ impl IBluetooth for IBluetoothDBus {
    }

    #[dbus_method("CreateBond")]
    fn create_bond(&self, _device: BluetoothDevice, _transport: BluetoothTransport) -> bool {
    fn create_bond(&self, _device: BluetoothDevice, _transport: BtTransport) -> bool {
        true
    }

@@ -166,6 +165,33 @@ impl IBluetooth for IBluetoothDBus {
        0
    }

    #[dbus_method("SetPin")]
    fn set_pin(
        &self,
        _device: BluetoothDevice,
        _accept: bool,
        _len: u32,
        _pin_code: Vec<u8>,
    ) -> bool {
        false
    }

    #[dbus_method("SetPasskey")]
    fn set_passkey(
        &self,
        _device: BluetoothDevice,
        _accept: bool,
        _len: u32,
        _passkey: Vec<u8>,
    ) -> bool {
        false
    }

    #[dbus_method("SetPairingConfirmation")]
    fn set_pairing_confirmation(&self, _device: BluetoothDevice, _accept: bool) -> bool {
        false
    }

    #[dbus_method("GetConnectionState")]
    fn get_connection_state(&self, _device: BluetoothDevice) -> u32 {
        0
Loading