blob: 4ca2440db53036fb4984b0c4c87bd0d002b947ef [file] [log] [blame]
extern crate clap;
use clap::{App, AppSettings, Arg};
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,
suspend::Suspend,
Stack,
};
use dbus_projection::DisconnectWatcher;
mod dbus_arg;
mod iface_bluetooth;
mod iface_bluetooth_gatt;
mod iface_bluetooth_media;
mod iface_suspend;
const DBUS_SERVICE_NAME: &str = "org.chromium.bluetooth";
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 matches = App::new("Bluetooth Adapter Daemon")
// Allows multiple INIT_ flags to be given at the end of the arguments.
.setting(AppSettings::TrailingVarArg)
.arg(
Arg::with_name("hci")
.long("hci")
.value_name("HCI")
.takes_value(true)
.help("The HCI index"),
)
.arg(Arg::with_name("debug").long("debug").short("d").help("Enables debug level logs"))
.arg(Arg::from_usage("[init-flags] 'Fluoride INIT_ flags'").multiple(true))
.get_matches();
let is_debug = matches.is_present("debug");
let adapter_index = match matches.value_of("hci") {
Some(idx) => idx.parse::<i32>().unwrap_or(0),
None => 0,
};
// The remaining flags are passed down to Fluoride as is.
let mut init_flags: Vec<String> = match matches.values_of("init-flags") {
Some(args) => args.map(|s| String::from(s)).collect(),
None => vec![],
};
// Forward --hci to Fluoride.
init_flags.push(format!("--hci={}", adapter_index));
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(if is_debug { LevelFilter::Debug } else { LevelFilter::Info })
});
let (tx, rx) = Stack::create_channel();
let intf = Arc::new(Mutex::new(get_btinterface().unwrap()));
let suspend = Arc::new(Mutex::new(Box::new(Suspend::new(intf.clone(), tx.clone()))));
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(),
))));
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);
}),
)));
// Announce the exported adapter objects so that clients can properly detect the readiness
// of the adapter APIs.
cr.set_object_manager_support(Some(conn.clone()));
cr.insert("/", &[cr.object_manager()], {});
// Run the stack main dispatch loop.
topstack::get_runtime().spawn(Stack::dispatch(
rx,
bluetooth.clone(),
bluetooth_gatt.clone(),
bluetooth_media.clone(),
suspend.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.clone(),
disconnect_watcher.clone(),
);
iface_bluetooth_media::export_bluetooth_media_dbus_obj(
make_object_name(adapter_index, "media"),
conn.clone(),
&mut cr,
bluetooth_media.clone(),
disconnect_watcher.clone(),
);
iface_suspend::export_suspend_dbus_obj(
make_object_name(adapter_index, "suspend"),
conn.clone(),
&mut cr,
suspend,
disconnect_watcher.clone(),
);
// Hold locks and initialize all interfaces. This must be done AFTER DBus is
// initialized so DBus can properly enforce user policies.
{
intf.lock().unwrap().initialize(get_bt_dispatcher(tx.clone()), init_flags);
bluetooth_media.lock().unwrap().set_adapter(bluetooth.clone());
let mut bluetooth = bluetooth.lock().unwrap();
bluetooth.init_profiles();
bluetooth.enable();
bluetooth_gatt.lock().unwrap().init_profiles(tx.clone());
}
// Start listening on DBus after exporting interfaces and initializing
// all bluetooth objects.
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!()
})
}