use dbus::{channel::MatchingReceiver, message::MatchRule}; use dbus_crossroads::Crossroads; use dbus_tokio::connection; use futures::future; use log::LevelFilter; use std::error::Error; use std::sync::{Arc, Mutex}; use syslog::{BasicLogger, Facility, Formatter3164}; use bt_topshim::{btif::get_btinterface, topstack}; use btstack::{ bluetooth::{get_bt_dispatcher, Bluetooth, IBluetooth}, bluetooth_gatt::BluetoothGatt, bluetooth_media::BluetoothMedia, Stack, }; use dbus_projection::DisconnectWatcher; mod dbus_arg; mod iface_bluetooth; mod iface_bluetooth_gatt; mod iface_bluetooth_media; const DBUS_SERVICE_NAME: &str = "org.chromium.bluetooth"; /// Check command line arguments for target hci adapter (--hci=N). If no adapter /// is set, default to 0. fn get_adapter_index(args: &Vec) -> i32 { for arg in args { if arg.starts_with("--hci=") { let num = (&arg[6..]).parse::(); if num.is_ok() { return num.unwrap(); } } } 0 } fn make_object_name(idx: i32, name: &str) -> String { String::from(format!("/org/chromium/bluetooth/hci{}/{}", idx, name)) } /// Runs the Bluetooth daemon serving D-Bus IPC. fn main() -> Result<(), Box> { let formatter = Formatter3164 { facility: Facility::LOG_USER, hostname: None, process: "btadapterd".into(), pid: 0, }; let logger = syslog::unix(formatter).expect("could not connect to syslog"); let _ = log::set_boxed_logger(Box::new(BasicLogger::new(logger))) .map(|()| log::set_max_level(LevelFilter::Info)); let (tx, rx) = Stack::create_channel(); let intf = Arc::new(Mutex::new(get_btinterface().unwrap())); let bluetooth_gatt = Arc::new(Mutex::new(Box::new(BluetoothGatt::new(intf.clone())))); let bluetooth_media = Arc::new(Mutex::new(Box::new(BluetoothMedia::new(tx.clone(), intf.clone())))); let bluetooth = Arc::new(Mutex::new(Box::new(Bluetooth::new( tx.clone(), intf.clone(), bluetooth_media.clone(), )))); // Args don't include arg[0] which is the binary name let all_args = std::env::args().collect::>(); let args = all_args[1..].to_vec(); let adapter_index = get_adapter_index(&args); // Hold locks and initialize all interfaces. { intf.lock().unwrap().initialize(get_bt_dispatcher(tx.clone()), args); let mut bluetooth = bluetooth.lock().unwrap(); bluetooth.init_profiles(); bluetooth.enable(); bluetooth_gatt.lock().unwrap().init_profiles(tx.clone()); } topstack::get_runtime().block_on(async { // Connect to D-Bus system bus. let (resource, conn) = connection::new_system_sync()?; // The `resource` is a task that should be spawned onto a tokio compatible // reactor ASAP. If the resource ever finishes, we lost connection to D-Bus. tokio::spawn(async { let err = resource.await; panic!("Lost connection to D-Bus: {}", err); }); // Request a service name and quit if not able to. conn.request_name(DBUS_SERVICE_NAME, false, true, false).await?; // Prepare D-Bus interfaces. let mut cr = Crossroads::new(); cr.set_async_support(Some(( conn.clone(), Box::new(|x| { tokio::spawn(x); }), ))); // Run the stack main dispatch loop. topstack::get_runtime().spawn(Stack::dispatch( rx, bluetooth.clone(), bluetooth_gatt.clone(), bluetooth_media.clone(), )); // Set up the disconnect watcher to monitor client disconnects. let disconnect_watcher = Arc::new(Mutex::new(DisconnectWatcher::new())); disconnect_watcher.lock().unwrap().setup_watch(conn.clone()).await; // Register D-Bus method handlers of IBluetooth. iface_bluetooth::export_bluetooth_dbus_obj( make_object_name(adapter_index, "adapter"), conn.clone(), &mut cr, bluetooth.clone(), disconnect_watcher.clone(), ); // Register D-Bus method handlers of IBluetoothGatt. iface_bluetooth_gatt::export_bluetooth_gatt_dbus_obj( make_object_name(adapter_index, "gatt"), conn.clone(), &mut cr, bluetooth_gatt, disconnect_watcher.clone(), ); iface_bluetooth_media::export_bluetooth_media_dbus_obj( make_object_name(adapter_index, "media"), conn.clone(), &mut cr, bluetooth_media, disconnect_watcher.clone(), ); conn.start_receive( MatchRule::new_method_call(), Box::new(move |msg, conn| { cr.handle_message(msg, conn).unwrap(); true }), ); // Serve clients forever. future::pending::<()>().await; unreachable!() }) } #[cfg(test)] mod tests { use crate::get_adapter_index; #[test] fn device_index_parsed() { // A few failing cases assert_eq!(get_adapter_index(&vec! {}), 0); assert_eq!(get_adapter_index(&vec! {"--bar".to_string(), "--hci".to_string()}), 0); assert_eq!(get_adapter_index(&vec! {"--hci=foo".to_string()}), 0); assert_eq!(get_adapter_index(&vec! {"--hci=12t".to_string()}), 0); // Some passing cases assert_eq!(get_adapter_index(&vec! {"--hci=12".to_string()}), 12); assert_eq!(get_adapter_index(&vec! {"--hci=1".to_string(), "--hci=2".to_string()}), 1); } }