blob: b7b88969e652984b726417c0b9e98bfbe43bd398 [file] [log] [blame]
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<String>) -> i32 {
for arg in args {
if arg.starts_with("--hci=") {
let num = (&arg[6..]).parse::<i32>();
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<dyn Error>> {
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::<Vec<String>>();
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);
}
}