1 use log::LevelFilter;
2 use serde_json::{Map, Value};
3
4 // Directory for Bluetooth hci devices
5 pub const HCI_DEVICES_DIR: &str = "/sys/class/bluetooth";
6
7 // File to store the Bluetooth daemon to use (bluez or floss)
8 const BLUETOOTH_DAEMON_CURRENT: &str = "/var/lib/bluetooth/bluetooth-daemon.current";
9
10 // File to store the config for BluetoothManager
11 const BTMANAGERD_CONF: &str = "/var/lib/bluetooth/btmanagerd.json";
12
is_floss_enabled() -> bool13 pub fn is_floss_enabled() -> bool {
14 match std::fs::read(BLUETOOTH_DAEMON_CURRENT) {
15 Ok(v) => {
16 let content = std::str::from_utf8(&v);
17 match content {
18 Ok(version) => version.contains("floss"),
19 Err(_) => false,
20 }
21 }
22 Err(_) => false,
23 }
24 }
25
write_floss_enabled(enabled: bool) -> bool26 pub fn write_floss_enabled(enabled: bool) -> bool {
27 std::fs::write(
28 BLUETOOTH_DAEMON_CURRENT,
29 match enabled {
30 true => "floss",
31 _ => "bluez",
32 },
33 )
34 .is_ok()
35 }
36
read_config() -> std::io::Result<String>37 pub fn read_config() -> std::io::Result<String> {
38 std::fs::read_to_string(BTMANAGERD_CONF)
39 }
40
get_log_level() -> Option<LevelFilter>41 pub fn get_log_level() -> Option<LevelFilter> {
42 get_log_level_internal(read_config().ok()?)
43 }
44
get_log_level_internal(config: String) -> Option<LevelFilter>45 fn get_log_level_internal(config: String) -> Option<LevelFilter> {
46 serde_json::from_str::<Value>(config.as_str())
47 .ok()?
48 .get("log_level")?
49 .as_str()?
50 .parse::<LevelFilter>()
51 .ok()
52 }
53
54 /// Returns whether hci N is enabled in config; defaults to true.
is_hci_n_enabled(n: i32) -> bool55 pub fn is_hci_n_enabled(n: i32) -> bool {
56 match read_config().ok().and_then(|config| is_hci_n_enabled_internal(config, n)) {
57 Some(v) => v,
58 _ => true,
59 }
60 }
61
is_hci_n_enabled_internal(config: String, n: i32) -> Option<bool>62 fn is_hci_n_enabled_internal(config: String, n: i32) -> Option<bool> {
63 serde_json::from_str::<Value>(config.as_str())
64 .ok()?
65 .get(format!("hci{}", n))?
66 .as_object()?
67 .get("enabled")?
68 .as_bool()
69 }
70
71 // When we initialize BluetoothManager, we need to make sure the file is a well-formatted json.
fix_config_file_format() -> bool72 pub fn fix_config_file_format() -> bool {
73 match read_config() {
74 Ok(s) => match serde_json::from_str::<Value>(s.as_str()) {
75 Ok(_) => true,
76 _ => std::fs::write(BTMANAGERD_CONF, "{}").is_ok(),
77 },
78 _ => std::fs::write(BTMANAGERD_CONF, "{}").is_ok(),
79 }
80 }
81
modify_hci_n_enabled(n: i32, enabled: bool) -> bool82 pub fn modify_hci_n_enabled(n: i32, enabled: bool) -> bool {
83 if !fix_config_file_format() {
84 false
85 } else {
86 match read_config()
87 .ok()
88 .and_then(|config| modify_hci_n_enabled_internal(config, n, enabled))
89 {
90 Some(s) => std::fs::write(BTMANAGERD_CONF, s).is_ok(),
91 _ => false,
92 }
93 }
94 }
95
modify_hci_n_enabled_internal(config: String, n: i32, enabled: bool) -> Option<String>96 fn modify_hci_n_enabled_internal(config: String, n: i32, enabled: bool) -> Option<String> {
97 let hci_interface = format!("hci{}", n);
98 let mut o = serde_json::from_str::<Value>(config.as_str()).ok()?;
99 match o.get_mut(hci_interface.clone()) {
100 Some(section) => {
101 section.as_object_mut()?.insert("enabled".to_string(), Value::Bool(enabled));
102 serde_json::ser::to_string_pretty(&o).ok()
103 }
104 _ => {
105 let mut entry_map = Map::new();
106 entry_map.insert("enabled".to_string(), Value::Bool(enabled));
107 o.as_object_mut()?.insert(hci_interface, Value::Object(entry_map));
108 serde_json::ser::to_string_pretty(&o).ok()
109 }
110 }
111 }
112
list_hci_devices() -> Vec<i32>113 pub fn list_hci_devices() -> Vec<i32> {
114 hci_devices_string_to_int(list_hci_devices_string())
115 }
116
list_hci_devices_string() -> Vec<String>117 fn list_hci_devices_string() -> Vec<String> {
118 match std::fs::read_dir(HCI_DEVICES_DIR) {
119 Ok(entries) => entries
120 .map(|e| e.unwrap().path().file_name().unwrap().to_str().unwrap().to_string())
121 .collect::<Vec<_>>(),
122 _ => Vec::new(),
123 }
124 }
125
hci_devices_string_to_int(devices: Vec<String>) -> Vec<i32>126 fn hci_devices_string_to_int(devices: Vec<String>) -> Vec<i32> {
127 devices
128 .into_iter()
129 .filter_map(|e| if e.starts_with("hci") { e[3..].parse::<i32>().ok() } else { None })
130 .collect()
131 }
132
list_pid_files(pid_dir: &str) -> Vec<String>133 pub fn list_pid_files(pid_dir: &str) -> Vec<String> {
134 match std::fs::read_dir(pid_dir) {
135 Ok(entries) => entries
136 .map(|e| e.unwrap().path().file_name().unwrap().to_str().unwrap().to_string())
137 .collect::<Vec<_>>(),
138 _ => Vec::new(),
139 }
140 }
141
142 #[cfg(test)]
143 mod tests {
144 use super::*;
145
is_hci_n_enabled_internal_wrapper(config: String, n: i32) -> bool146 fn is_hci_n_enabled_internal_wrapper(config: String, n: i32) -> bool {
147 is_hci_n_enabled_internal(config, n).or(Some(true)).unwrap()
148 }
149
150 #[test]
parse_log_level()151 fn parse_log_level() {
152 assert_eq!(
153 get_log_level_internal("{\"log_level\": \"error\"}".to_string()).unwrap(),
154 LevelFilter::Error
155 );
156 assert_eq!(
157 get_log_level_internal("{\"log_level\": \"warn\"}".to_string()).unwrap(),
158 LevelFilter::Warn
159 );
160 assert_eq!(
161 get_log_level_internal("{\"log_level\": \"info\"}".to_string()).unwrap(),
162 LevelFilter::Info
163 );
164 assert_eq!(
165 get_log_level_internal("{\"log_level\": \"debug\"}".to_string()).unwrap(),
166 LevelFilter::Debug
167 );
168 assert_eq!(
169 get_log_level_internal("{\"log_level\": \"trace\"}".to_string()).unwrap(),
170 LevelFilter::Trace
171 );
172 assert_eq!(
173 get_log_level_internal("{\"log_level\": \"random\"}".to_string()).is_none(),
174 true
175 );
176 }
177
178 #[test]
parse_hci0_enabled()179 fn parse_hci0_enabled() {
180 assert_eq!(
181 is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": true}}".to_string(), 0),
182 true
183 );
184 }
185
186 #[test]
modify_hci0_enabled()187 fn modify_hci0_enabled() {
188 let modified_string =
189 modify_hci_n_enabled_internal("{\"hci0\":\n{\"enabled\": false}}".to_string(), 0, true)
190 .unwrap();
191 assert_eq!(is_hci_n_enabled_internal_wrapper(modified_string, 0), true);
192 }
193
194 #[test]
modify_hci0_enabled_from_empty()195 fn modify_hci0_enabled_from_empty() {
196 let modified_string = modify_hci_n_enabled_internal("{}".to_string(), 0, true).unwrap();
197 assert_eq!(is_hci_n_enabled_internal_wrapper(modified_string, 0), true);
198 }
199
200 #[test]
parse_hci0_not_enabled()201 fn parse_hci0_not_enabled() {
202 assert_eq!(
203 is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": false}}".to_string(), 0),
204 false
205 );
206 }
207
208 #[test]
parse_hci1_not_present()209 fn parse_hci1_not_present() {
210 assert_eq!(
211 is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": true}}".to_string(), 1),
212 true
213 );
214 }
215
216 #[test]
test_hci_devices_string_to_int_none()217 fn test_hci_devices_string_to_int_none() {
218 assert_eq!(hci_devices_string_to_int(vec!["somethingelse".to_string()]), Vec::<i32>::new());
219 }
220
221 #[test]
test_hci_devices_string_to_int_one()222 fn test_hci_devices_string_to_int_one() {
223 assert_eq!(hci_devices_string_to_int(vec!["hci0".to_string()]), vec![0]);
224 }
225
226 #[test]
test_hci_devices_string_to_int_two()227 fn test_hci_devices_string_to_int_two() {
228 assert_eq!(
229 hci_devices_string_to_int(vec!["hci0".to_string(), "hci1".to_string()]),
230 vec![0, 1]
231 );
232 }
233 }
234