blob: 26c997b64ae5f2ed03268c389d245cbd7ecf7cbc [file] [log] [blame]
use log::LevelFilter;
use serde_json::{Map, Value};
// Directory for Bluetooth hci devices
pub const HCI_DEVICES_DIR: &str = "/sys/class/bluetooth";
// File to store the Bluetooth daemon to use (bluez or floss)
const BLUETOOTH_DAEMON_CURRENT: &str = "/var/lib/bluetooth/bluetooth-daemon.current";
// File to store the config for BluetoothManager
const BTMANAGERD_CONF: &str = "/var/lib/bluetooth/btmanagerd.json";
pub fn is_floss_enabled() -> bool {
match std::fs::read(BLUETOOTH_DAEMON_CURRENT) {
Ok(v) => {
let content = std::str::from_utf8(&v);
match content {
Ok(version) => version.contains("floss"),
Err(_) => false,
}
}
Err(_) => false,
}
}
pub fn write_floss_enabled(enabled: bool) -> bool {
std::fs::write(
BLUETOOTH_DAEMON_CURRENT,
match enabled {
true => "floss",
_ => "bluez",
},
)
.is_ok()
}
pub fn read_config() -> std::io::Result<String> {
std::fs::read_to_string(BTMANAGERD_CONF)
}
pub fn get_log_level() -> Option<LevelFilter> {
get_log_level_internal(read_config().ok()?)
}
fn get_log_level_internal(config: String) -> Option<LevelFilter> {
serde_json::from_str::<Value>(config.as_str())
.ok()?
.get("log_level")?
.as_str()?
.parse::<LevelFilter>()
.ok()
}
/// Returns whether hci N is enabled in config; defaults to true.
pub fn is_hci_n_enabled(n: i32) -> bool {
match read_config().ok().and_then(|config| is_hci_n_enabled_internal(config, n)) {
Some(v) => v,
_ => true,
}
}
fn is_hci_n_enabled_internal(config: String, n: i32) -> Option<bool> {
serde_json::from_str::<Value>(config.as_str())
.ok()?
.get(format!("hci{}", n))?
.as_object()?
.get("enabled")?
.as_bool()
}
// When we initialize BluetoothManager, we need to make sure the file is a well-formatted json.
pub fn fix_config_file_format() -> bool {
match read_config() {
Ok(s) => match serde_json::from_str::<Value>(s.as_str()) {
Ok(_) => true,
_ => std::fs::write(BTMANAGERD_CONF, "{}").is_ok(),
},
_ => std::fs::write(BTMANAGERD_CONF, "{}").is_ok(),
}
}
pub fn modify_hci_n_enabled(n: i32, enabled: bool) -> bool {
if !fix_config_file_format() {
false
} else {
match read_config()
.ok()
.and_then(|config| modify_hci_n_enabled_internal(config, n, enabled))
{
Some(s) => std::fs::write(BTMANAGERD_CONF, s).is_ok(),
_ => false,
}
}
}
fn modify_hci_n_enabled_internal(config: String, n: i32, enabled: bool) -> Option<String> {
let hci_interface = format!("hci{}", n);
let mut o = serde_json::from_str::<Value>(config.as_str()).ok()?;
match o.get_mut(hci_interface.clone()) {
Some(section) => {
section.as_object_mut()?.insert("enabled".to_string(), Value::Bool(enabled));
serde_json::ser::to_string_pretty(&o).ok()
}
_ => {
let mut entry_map = Map::new();
entry_map.insert("enabled".to_string(), Value::Bool(enabled));
o.as_object_mut()?.insert(hci_interface, Value::Object(entry_map));
serde_json::ser::to_string_pretty(&o).ok()
}
}
}
pub fn list_hci_devices() -> Vec<i32> {
hci_devices_string_to_int(list_hci_devices_string())
}
fn list_hci_devices_string() -> Vec<String> {
match std::fs::read_dir(HCI_DEVICES_DIR) {
Ok(entries) => entries
.map(|e| e.unwrap().path().file_name().unwrap().to_str().unwrap().to_string())
.collect::<Vec<_>>(),
_ => Vec::new(),
}
}
fn hci_devices_string_to_int(devices: Vec<String>) -> Vec<i32> {
devices
.into_iter()
.filter_map(|e| if e.starts_with("hci") { e[3..].parse::<i32>().ok() } else { None })
.collect()
}
pub fn list_pid_files(pid_dir: &str) -> Vec<String> {
match std::fs::read_dir(pid_dir) {
Ok(entries) => entries
.map(|e| e.unwrap().path().file_name().unwrap().to_str().unwrap().to_string())
.collect::<Vec<_>>(),
_ => Vec::new(),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn is_hci_n_enabled_internal_wrapper(config: String, n: i32) -> bool {
is_hci_n_enabled_internal(config, n).or(Some(true)).unwrap()
}
#[test]
fn parse_log_level() {
assert_eq!(
get_log_level_internal("{\"log_level\": \"error\"}".to_string()).unwrap(),
LevelFilter::Error
);
assert_eq!(
get_log_level_internal("{\"log_level\": \"warn\"}".to_string()).unwrap(),
LevelFilter::Warn
);
assert_eq!(
get_log_level_internal("{\"log_level\": \"info\"}".to_string()).unwrap(),
LevelFilter::Info
);
assert_eq!(
get_log_level_internal("{\"log_level\": \"debug\"}".to_string()).unwrap(),
LevelFilter::Debug
);
assert_eq!(
get_log_level_internal("{\"log_level\": \"trace\"}".to_string()).unwrap(),
LevelFilter::Trace
);
assert_eq!(
get_log_level_internal("{\"log_level\": \"random\"}".to_string()).is_none(),
true
);
}
#[test]
fn parse_hci0_enabled() {
assert_eq!(
is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": true}}".to_string(), 0),
true
);
}
#[test]
fn modify_hci0_enabled() {
let modified_string =
modify_hci_n_enabled_internal("{\"hci0\":\n{\"enabled\": false}}".to_string(), 0, true)
.unwrap();
assert_eq!(is_hci_n_enabled_internal_wrapper(modified_string, 0), true);
}
#[test]
fn modify_hci0_enabled_from_empty() {
let modified_string = modify_hci_n_enabled_internal("{}".to_string(), 0, true).unwrap();
assert_eq!(is_hci_n_enabled_internal_wrapper(modified_string, 0), true);
}
#[test]
fn parse_hci0_not_enabled() {
assert_eq!(
is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": false}}".to_string(), 0),
false
);
}
#[test]
fn parse_hci1_not_present() {
assert_eq!(
is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": true}}".to_string(), 1),
true
);
}
#[test]
fn test_hci_devices_string_to_int_none() {
assert_eq!(hci_devices_string_to_int(vec!["somethingelse".to_string()]), Vec::<i32>::new());
}
#[test]
fn test_hci_devices_string_to_int_one() {
assert_eq!(hci_devices_string_to_int(vec!["hci0".to_string()]), vec![0]);
}
#[test]
fn test_hci_devices_string_to_int_two() {
assert_eq!(
hci_devices_string_to_int(vec!["hci0".to_string(), "hci1".to_string()]),
vec![0, 1]
);
}
}