Loading system/gd/rust/linux/client/src/callbacks.rs +122 −16 Original line number Diff line number Diff line use crate::ClientContext; use crate::{console_yellow, print_info}; use bt_topshim::btif::BtSspVariant; use bt_topshim::btif::{BtBondState, BtSspVariant}; use bt_topshim::profiles::gatt::GattStatus; use btstack::bluetooth::{BluetoothDevice, IBluetoothCallback}; use btstack::bluetooth::{ BluetoothDevice, IBluetooth, IBluetoothCallback, IBluetoothConnectionCallback, }; use btstack::bluetooth_gatt::{BluetoothGattService, IBluetoothGattCallback, LePhy}; use btstack::RPCProxy; use manager_service::iface_bluetooth_manager::IBluetoothManagerCallback; Loading Loading @@ -37,11 +39,17 @@ impl IBluetoothManagerCallback for BtManagerCallback { } impl manager_service::RPCProxy for BtManagerCallback { fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { self.objpath.clone() } fn unregister(&mut self, _id: u32) -> bool { false } } /// Callback container for adapter interface callbacks. Loading Loading @@ -89,31 +97,123 @@ 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 { address, name: String::from("Classic device"), }); } } } impl RPCProxy for BtCallback { fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { self.objpath.clone() } fn unregister(&mut self, _id: u32) -> bool { false } } 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, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { self.objpath.clone() } fn unregister(&mut self, _id: u32) -> bool { false } } pub(crate) struct BtGattCallback { Loading Loading @@ -256,9 +356,15 @@ impl IBluetoothGattCallback for BtGattCallback { } impl RPCProxy for BtGattCallback { fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { self.objpath.clone() } fn unregister(&mut self, _id: u32) -> bool { false } } system/gd/rust/linux/client/src/command_handler.rs +160 −55 Original line number Diff line number Diff line Loading @@ -2,14 +2,13 @@ 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 bt_topshim::btif::Uuid128Bit; 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; const INDENT_CHAR: &str = " "; Loading Loading @@ -49,20 +48,6 @@ impl<T: Display> Display for DisplayList<T> { } } struct DisplayUuid128Bit(Uuid128Bit); // UUID128Bit should have a standard output display format impl Display for DisplayUuid128Bit { fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}", self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7], self.0[8], self.0[9], self.0[10], self.0[11], self.0[12], self.0[13], self.0[14], self.0[15]) } } fn enforce_arg_len<F>(args: &Vec<String>, min_len: usize, msg: &str, mut action: F) where F: FnMut(), Loading Loading @@ -126,6 +111,13 @@ fn build_commands() -> HashMap<String, CommandOption> { function_pointer: CommandHandler::cmd_discovery, }, ); command_options.insert( String::from("floss"), CommandOption { description: String::from("Enable or disable Floss for dogfood."), function_pointer: CommandHandler::cmd_floss, }, ); command_options.insert( String::from("gatt"), CommandOption { Loading Loading @@ -255,6 +247,11 @@ impl CommandHandler { self.context.lock().unwrap().manager_dbus.stop(default_adapter); } "show" => { if !self.context.lock().unwrap().manager_dbus.get_floss_enabled() { println!("Floss is not enabled. First run, `floss enable`"); return; } let enabled = self.context.lock().unwrap().enabled; let address = match self.context.lock().unwrap().adapter_address.as_ref() { Some(x) => x.clone(), Loading @@ -268,10 +265,7 @@ impl CommandHandler { print_info!( "Uuids: {}", DisplayList( uuids .iter() .map(|&x| DisplayUuid128Bit(x)) .collect::<Vec<DisplayUuid128Bit>>() uuids.iter().map(|&x| UuidHelper::to_string(&x)).collect::<Vec<String>>() ) ); } Loading Loading @@ -316,9 +310,48 @@ impl CommandHandler { return; } enforce_arg_len(args, 1, "bond <address>", || { enforce_arg_len(args, 2, "bond <add|remove|cancel> <address>", || match &args[0][0..] { "add" => { let device = BluetoothDevice { address: String::from(&args[0]), address: String::from(&args[1]), name: String::from("Classic Device"), }; 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.clone(), BtTransport::Auto); if success { self.context.lock().unwrap().bonding_attempt = Some(device); } } "remove" => { let device = BluetoothDevice { address: String::from(&args[1]), name: String::from("Classic Device"), }; self.context.lock().unwrap().adapter_dbus.as_ref().unwrap().remove_bond(device); } "cancel" => { let device = BluetoothDevice { address: String::from(&args[1]), name: String::from("Classic Device"), }; Loading @@ -328,7 +361,11 @@ impl CommandHandler { .adapter_dbus .as_ref() .unwrap() .create_bond(device, BluetoothTransport::from_i32(0).unwrap()); .cancel_bond_process(device); } _ => { println!("Invalid argument '{}'", args[0]); } }); } Loading @@ -338,36 +375,104 @@ impl CommandHandler { return; } enforce_arg_len(args, 2, "device <info> <address>", || match &args[0][0..] { "info" => { enforce_arg_len(args, 2, "device <connect|disconnect|info> <address>", || { match &args[0][0..] { "connect" => { let device = BluetoothDevice { address: String::from(&args[1]), name: String::from("Classic Device"), }; let uuids = self let success = self .context .lock() .unwrap() .adapter_dbus .as_ref() .unwrap() .get_remote_uuids(device.clone()); .connect_all_enabled_profiles(device.clone()); if success { println!("Connecting to {}", &device.address); } else { println!("Can't connect to {}", &device.address); } } "disconnect" => { let device = BluetoothDevice { address: String::from(&args[1]), name: String::from("Classic Device"), }; let success = self .context .lock() .unwrap() .adapter_dbus .as_ref() .unwrap() .disconnect_all_enabled_profiles(device.clone()); if success { println!("Disconnecting from {}", &device.address); } else { println!("Can't disconnect from {}", &device.address); } } "info" => { let device = BluetoothDevice { address: String::from(&args[1]), name: String::from("Classic Device"), }; let (bonded, connected, uuids) = { let ctx = self.context.lock().unwrap(); let adapter = ctx.adapter_dbus.as_ref().unwrap(); let bonded = adapter.get_bond_state(device.clone()); let connected = adapter.get_connection_state(device.clone()); let uuids = adapter.get_remote_uuids(device.clone()); (bonded, connected, uuids) }; print_info!("Address: {}", &device.address); print_info!("Bonded: {}", bonded); print_info!("Connected: {}", connected); print_info!( "Uuids: {}", DisplayList( uuids .iter() .map(|&x| DisplayUuid128Bit(x)) .collect::<Vec<DisplayUuid128Bit>>() .map(|&x| UuidHelper::to_string(&x)) .collect::<Vec<String>>() ) ); } _ => { println!("Invalid argument '{}'", args[0]); } } }); } fn cmd_floss(&mut self, args: &Vec<String>) { enforce_arg_len(args, 1, "floss <enable|disable>", || match &args[0][0..] { "enable" => { self.context.lock().unwrap().manager_dbus.set_floss_enabled(true); } "disable" => { self.context.lock().unwrap().manager_dbus.set_floss_enabled(false); } "show" => { print_info!( "Floss enabled: {}", self.context.lock().unwrap().manager_dbus.get_floss_enabled() ); } _ => { println!("Invalid argument '{}'", args[0]); } }); } Loading system/gd/rust/linux/client/src/dbus_iface.rs +131 −22 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}; use btstack::bluetooth::{ BluetoothDevice, IBluetooth, IBluetoothCallback, IBluetoothConnectionCallback, }; use btstack::bluetooth_gatt::{ BluetoothGattCharacteristic, BluetoothGattDescriptor, BluetoothGattService, GattWriteRequestStatus, GattWriteType, IBluetoothGatt, IBluetoothGattCallback, Loading Loading @@ -34,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); Loading Loading @@ -110,28 +112,34 @@ impl ClientDBusProxy { ) } fn method<A: AppendAll, T: 'static + dbus::arg::Arg + for<'z> dbus::arg::Get<'z>>( /// Calls a method and returns the dbus result. fn method_withresult<A: AppendAll, T: 'static + dbus::arg::Arg + for<'z> dbus::arg::Get<'z>>( &self, member: &str, args: A, ) -> T { ) -> Result<(T,), dbus::Error> { let proxy = self.create_proxy(); // We know that all APIs return immediately, so we can block on it for simplicity. let (ret,): (T,) = futures::executor::block_on(async { return futures::executor::block_on(async { proxy.method_call(self.interface.clone(), member, args).await }) .unwrap(); }); } fn method<A: AppendAll, T: 'static + dbus::arg::Arg + for<'z> dbus::arg::Get<'z>>( &self, member: &str, args: A, ) -> T { let (ret,): (T,) = self.method_withresult(member, args).unwrap(); return ret; } fn method_noreturn<A: AppendAll>(&self, member: &str, args: A) { let proxy = self.create_proxy(); // We know that all APIs return immediately, so we can block on it for simplicity. let _: () = futures::executor::block_on(async { proxy.method_call(self.interface.clone(), member, args).await }) .unwrap(); // The real type should be Result<((),), _> since there is no return value. However, to // meet trait constraints, we just use bool and never unwrap the result. This calls the // method, waits for the response but doesn't actually attempt to parse the result (on // unwrap). let _: Result<(bool,), _> = self.method_withresult(member, args); } } Loading @@ -140,10 +148,15 @@ struct IBluetoothCallbackDBus {} impl btstack::RPCProxy for IBluetoothCallbackDBus { // Dummy implementations just to satisfy impl RPCProxy requirements. fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { String::from("") } fn unregister(&mut self, _id: u32) -> bool { false } } #[generate_dbus_exporter( Loading Loading @@ -174,6 +187,34 @@ 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, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } 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, } Loading Loading @@ -212,6 +253,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 Loading Loading @@ -255,13 +317,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()), ) } Loading @@ -282,6 +341,31 @@ 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(),)) } fn get_remote_uuids(&self, device: BluetoothDevice) -> Vec<Uuid128Bit> { let result: Vec<Vec<u8>> = self .client_proxy Loading @@ -299,6 +383,16 @@ impl IBluetooth for BluetoothDBus { (BluetoothDevice::to_dbus(device).unwrap(), Uuid128Bit::to_dbus(uuid).unwrap()), ) } fn connect_all_enabled_profiles(&self, device: BluetoothDevice) -> bool { self.client_proxy .method("ConnectAllEnabledProfiles", (BluetoothDevice::to_dbus(device).unwrap(),)) } fn disconnect_all_enabled_profiles(&self, device: BluetoothDevice) -> bool { self.client_proxy .method("DisconnectAllEnabledProfiles", (BluetoothDevice::to_dbus(device).unwrap(),)) } } #[dbus_propmap(AdapterWithEnabled)] Loading Loading @@ -326,6 +420,11 @@ impl BluetoothManagerDBus { }, } } pub(crate) fn is_valid(&self) -> bool { let result: Result<(bool,), _> = self.client_proxy.method_withresult("GetFlossEnabled", ()); return result.is_ok(); } } // TODO: These are boilerplate codes, consider creating a macro to generate. Loading Loading @@ -375,10 +474,15 @@ struct IBluetoothManagerCallbackDBus {} impl manager_service::RPCProxy for IBluetoothManagerCallbackDBus { // Placeholder implementations just to satisfy impl RPCProxy requirements. fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { String::from("") } fn unregister(&mut self, _id: u32) -> bool { false } } #[generate_dbus_exporter( Loading Loading @@ -600,10 +704,15 @@ struct IBluetoothGattCallbackDBus {} impl btstack::RPCProxy for IBluetoothGattCallbackDBus { // Placeholder implementations just to satisfy impl RPCProxy requirements. fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { String::from("") } fn unregister(&mut self, _id: u32) -> bool { false } } #[generate_dbus_exporter( Loading Loading
system/gd/rust/linux/client/src/callbacks.rs +122 −16 Original line number Diff line number Diff line use crate::ClientContext; use crate::{console_yellow, print_info}; use bt_topshim::btif::BtSspVariant; use bt_topshim::btif::{BtBondState, BtSspVariant}; use bt_topshim::profiles::gatt::GattStatus; use btstack::bluetooth::{BluetoothDevice, IBluetoothCallback}; use btstack::bluetooth::{ BluetoothDevice, IBluetooth, IBluetoothCallback, IBluetoothConnectionCallback, }; use btstack::bluetooth_gatt::{BluetoothGattService, IBluetoothGattCallback, LePhy}; use btstack::RPCProxy; use manager_service::iface_bluetooth_manager::IBluetoothManagerCallback; Loading Loading @@ -37,11 +39,17 @@ impl IBluetoothManagerCallback for BtManagerCallback { } impl manager_service::RPCProxy for BtManagerCallback { fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { self.objpath.clone() } fn unregister(&mut self, _id: u32) -> bool { false } } /// Callback container for adapter interface callbacks. Loading Loading @@ -89,31 +97,123 @@ 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 { address, name: String::from("Classic device"), }); } } } impl RPCProxy for BtCallback { fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { self.objpath.clone() } fn unregister(&mut self, _id: u32) -> bool { false } } 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, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { self.objpath.clone() } fn unregister(&mut self, _id: u32) -> bool { false } } pub(crate) struct BtGattCallback { Loading Loading @@ -256,9 +356,15 @@ impl IBluetoothGattCallback for BtGattCallback { } impl RPCProxy for BtGattCallback { fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { self.objpath.clone() } fn unregister(&mut self, _id: u32) -> bool { false } }
system/gd/rust/linux/client/src/command_handler.rs +160 −55 Original line number Diff line number Diff line Loading @@ -2,14 +2,13 @@ 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 bt_topshim::btif::Uuid128Bit; 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; const INDENT_CHAR: &str = " "; Loading Loading @@ -49,20 +48,6 @@ impl<T: Display> Display for DisplayList<T> { } } struct DisplayUuid128Bit(Uuid128Bit); // UUID128Bit should have a standard output display format impl Display for DisplayUuid128Bit { fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}", self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7], self.0[8], self.0[9], self.0[10], self.0[11], self.0[12], self.0[13], self.0[14], self.0[15]) } } fn enforce_arg_len<F>(args: &Vec<String>, min_len: usize, msg: &str, mut action: F) where F: FnMut(), Loading Loading @@ -126,6 +111,13 @@ fn build_commands() -> HashMap<String, CommandOption> { function_pointer: CommandHandler::cmd_discovery, }, ); command_options.insert( String::from("floss"), CommandOption { description: String::from("Enable or disable Floss for dogfood."), function_pointer: CommandHandler::cmd_floss, }, ); command_options.insert( String::from("gatt"), CommandOption { Loading Loading @@ -255,6 +247,11 @@ impl CommandHandler { self.context.lock().unwrap().manager_dbus.stop(default_adapter); } "show" => { if !self.context.lock().unwrap().manager_dbus.get_floss_enabled() { println!("Floss is not enabled. First run, `floss enable`"); return; } let enabled = self.context.lock().unwrap().enabled; let address = match self.context.lock().unwrap().adapter_address.as_ref() { Some(x) => x.clone(), Loading @@ -268,10 +265,7 @@ impl CommandHandler { print_info!( "Uuids: {}", DisplayList( uuids .iter() .map(|&x| DisplayUuid128Bit(x)) .collect::<Vec<DisplayUuid128Bit>>() uuids.iter().map(|&x| UuidHelper::to_string(&x)).collect::<Vec<String>>() ) ); } Loading Loading @@ -316,9 +310,48 @@ impl CommandHandler { return; } enforce_arg_len(args, 1, "bond <address>", || { enforce_arg_len(args, 2, "bond <add|remove|cancel> <address>", || match &args[0][0..] { "add" => { let device = BluetoothDevice { address: String::from(&args[0]), address: String::from(&args[1]), name: String::from("Classic Device"), }; 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.clone(), BtTransport::Auto); if success { self.context.lock().unwrap().bonding_attempt = Some(device); } } "remove" => { let device = BluetoothDevice { address: String::from(&args[1]), name: String::from("Classic Device"), }; self.context.lock().unwrap().adapter_dbus.as_ref().unwrap().remove_bond(device); } "cancel" => { let device = BluetoothDevice { address: String::from(&args[1]), name: String::from("Classic Device"), }; Loading @@ -328,7 +361,11 @@ impl CommandHandler { .adapter_dbus .as_ref() .unwrap() .create_bond(device, BluetoothTransport::from_i32(0).unwrap()); .cancel_bond_process(device); } _ => { println!("Invalid argument '{}'", args[0]); } }); } Loading @@ -338,36 +375,104 @@ impl CommandHandler { return; } enforce_arg_len(args, 2, "device <info> <address>", || match &args[0][0..] { "info" => { enforce_arg_len(args, 2, "device <connect|disconnect|info> <address>", || { match &args[0][0..] { "connect" => { let device = BluetoothDevice { address: String::from(&args[1]), name: String::from("Classic Device"), }; let uuids = self let success = self .context .lock() .unwrap() .adapter_dbus .as_ref() .unwrap() .get_remote_uuids(device.clone()); .connect_all_enabled_profiles(device.clone()); if success { println!("Connecting to {}", &device.address); } else { println!("Can't connect to {}", &device.address); } } "disconnect" => { let device = BluetoothDevice { address: String::from(&args[1]), name: String::from("Classic Device"), }; let success = self .context .lock() .unwrap() .adapter_dbus .as_ref() .unwrap() .disconnect_all_enabled_profiles(device.clone()); if success { println!("Disconnecting from {}", &device.address); } else { println!("Can't disconnect from {}", &device.address); } } "info" => { let device = BluetoothDevice { address: String::from(&args[1]), name: String::from("Classic Device"), }; let (bonded, connected, uuids) = { let ctx = self.context.lock().unwrap(); let adapter = ctx.adapter_dbus.as_ref().unwrap(); let bonded = adapter.get_bond_state(device.clone()); let connected = adapter.get_connection_state(device.clone()); let uuids = adapter.get_remote_uuids(device.clone()); (bonded, connected, uuids) }; print_info!("Address: {}", &device.address); print_info!("Bonded: {}", bonded); print_info!("Connected: {}", connected); print_info!( "Uuids: {}", DisplayList( uuids .iter() .map(|&x| DisplayUuid128Bit(x)) .collect::<Vec<DisplayUuid128Bit>>() .map(|&x| UuidHelper::to_string(&x)) .collect::<Vec<String>>() ) ); } _ => { println!("Invalid argument '{}'", args[0]); } } }); } fn cmd_floss(&mut self, args: &Vec<String>) { enforce_arg_len(args, 1, "floss <enable|disable>", || match &args[0][0..] { "enable" => { self.context.lock().unwrap().manager_dbus.set_floss_enabled(true); } "disable" => { self.context.lock().unwrap().manager_dbus.set_floss_enabled(false); } "show" => { print_info!( "Floss enabled: {}", self.context.lock().unwrap().manager_dbus.get_floss_enabled() ); } _ => { println!("Invalid argument '{}'", args[0]); } }); } Loading
system/gd/rust/linux/client/src/dbus_iface.rs +131 −22 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}; use btstack::bluetooth::{ BluetoothDevice, IBluetooth, IBluetoothCallback, IBluetoothConnectionCallback, }; use btstack::bluetooth_gatt::{ BluetoothGattCharacteristic, BluetoothGattDescriptor, BluetoothGattService, GattWriteRequestStatus, GattWriteType, IBluetoothGatt, IBluetoothGattCallback, Loading Loading @@ -34,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); Loading Loading @@ -110,28 +112,34 @@ impl ClientDBusProxy { ) } fn method<A: AppendAll, T: 'static + dbus::arg::Arg + for<'z> dbus::arg::Get<'z>>( /// Calls a method and returns the dbus result. fn method_withresult<A: AppendAll, T: 'static + dbus::arg::Arg + for<'z> dbus::arg::Get<'z>>( &self, member: &str, args: A, ) -> T { ) -> Result<(T,), dbus::Error> { let proxy = self.create_proxy(); // We know that all APIs return immediately, so we can block on it for simplicity. let (ret,): (T,) = futures::executor::block_on(async { return futures::executor::block_on(async { proxy.method_call(self.interface.clone(), member, args).await }) .unwrap(); }); } fn method<A: AppendAll, T: 'static + dbus::arg::Arg + for<'z> dbus::arg::Get<'z>>( &self, member: &str, args: A, ) -> T { let (ret,): (T,) = self.method_withresult(member, args).unwrap(); return ret; } fn method_noreturn<A: AppendAll>(&self, member: &str, args: A) { let proxy = self.create_proxy(); // We know that all APIs return immediately, so we can block on it for simplicity. let _: () = futures::executor::block_on(async { proxy.method_call(self.interface.clone(), member, args).await }) .unwrap(); // The real type should be Result<((),), _> since there is no return value. However, to // meet trait constraints, we just use bool and never unwrap the result. This calls the // method, waits for the response but doesn't actually attempt to parse the result (on // unwrap). let _: Result<(bool,), _> = self.method_withresult(member, args); } } Loading @@ -140,10 +148,15 @@ struct IBluetoothCallbackDBus {} impl btstack::RPCProxy for IBluetoothCallbackDBus { // Dummy implementations just to satisfy impl RPCProxy requirements. fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { String::from("") } fn unregister(&mut self, _id: u32) -> bool { false } } #[generate_dbus_exporter( Loading Loading @@ -174,6 +187,34 @@ 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, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } 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, } Loading Loading @@ -212,6 +253,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 Loading Loading @@ -255,13 +317,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()), ) } Loading @@ -282,6 +341,31 @@ 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(),)) } fn get_remote_uuids(&self, device: BluetoothDevice) -> Vec<Uuid128Bit> { let result: Vec<Vec<u8>> = self .client_proxy Loading @@ -299,6 +383,16 @@ impl IBluetooth for BluetoothDBus { (BluetoothDevice::to_dbus(device).unwrap(), Uuid128Bit::to_dbus(uuid).unwrap()), ) } fn connect_all_enabled_profiles(&self, device: BluetoothDevice) -> bool { self.client_proxy .method("ConnectAllEnabledProfiles", (BluetoothDevice::to_dbus(device).unwrap(),)) } fn disconnect_all_enabled_profiles(&self, device: BluetoothDevice) -> bool { self.client_proxy .method("DisconnectAllEnabledProfiles", (BluetoothDevice::to_dbus(device).unwrap(),)) } } #[dbus_propmap(AdapterWithEnabled)] Loading Loading @@ -326,6 +420,11 @@ impl BluetoothManagerDBus { }, } } pub(crate) fn is_valid(&self) -> bool { let result: Result<(bool,), _> = self.client_proxy.method_withresult("GetFlossEnabled", ()); return result.is_ok(); } } // TODO: These are boilerplate codes, consider creating a macro to generate. Loading Loading @@ -375,10 +474,15 @@ struct IBluetoothManagerCallbackDBus {} impl manager_service::RPCProxy for IBluetoothManagerCallbackDBus { // Placeholder implementations just to satisfy impl RPCProxy requirements. fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { String::from("") } fn unregister(&mut self, _id: u32) -> bool { false } } #[generate_dbus_exporter( Loading Loading @@ -600,10 +704,15 @@ struct IBluetoothGattCallbackDBus {} impl btstack::RPCProxy for IBluetoothGattCallbackDBus { // Placeholder implementations just to satisfy impl RPCProxy requirements. fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {} fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 { 0 } fn get_object_id(&self) -> String { String::from("") } fn unregister(&mut self, _id: u32) -> bool { false } } #[generate_dbus_exporter( Loading