1 use crate::state_machine::{RealHciIndex, VirtualHciIndex};
2
3 use log::LevelFilter;
4 use serde_json::{Map, Value};
5 use std::convert::TryInto;
6 use std::path::Path;
7
8 // Directory for Bluetooth hci devices
9 pub const HCI_DEVICES_DIR: &str = "/sys/class/bluetooth";
10
11 // File to store the Bluetooth daemon to use (bluez or floss)
12 const BLUETOOTH_DAEMON_CURRENT: &str = "/var/lib/bluetooth/bluetooth-daemon.current";
13
14 // File to store the config for BluetoothManager
15 const BTMANAGERD_CONF: &str = "/var/lib/bluetooth/btmanagerd.json";
16
17 /// Folder to keep files which override floss configuration
18 const FLOSS_SYSPROPS_OVERRIDE_DIR: &str = "/var/lib/bluetooth/sysprops.conf.d";
19
20 /// File to store bluetooth devcoredump configuration. This file is used by the
21 /// udev rule to copy the devcoredump state to .../device/coredump_enabled sysfs
22 /// entry. It is also used by the user-space crash reporter to enable/disable
23 /// parsing of the devcoredump.
24 const FLOSS_COREDUMP_CONF_PATH: &str = "/var/run/bluetooth/coredump_disabled";
25
26 /// Key used for default adapter entry.
27 const DEFAULT_ADAPTER_KEY: &str = "default_adapter";
28
29 /// In the absence of other values, default to hci0.
30 const DEFAULT_ADAPTER: VirtualHciIndex = VirtualHciIndex(0);
31
is_floss_enabled() -> bool32 pub fn is_floss_enabled() -> bool {
33 match std::fs::read(BLUETOOTH_DAEMON_CURRENT) {
34 Ok(v) => {
35 let content = std::str::from_utf8(&v);
36 match content {
37 Ok(version) => version.contains("floss"),
38 Err(_) => false,
39 }
40 }
41 Err(_) => false,
42 }
43 }
44
write_floss_enabled(enabled: bool) -> bool45 pub fn write_floss_enabled(enabled: bool) -> bool {
46 std::fs::write(
47 BLUETOOTH_DAEMON_CURRENT,
48 match enabled {
49 true => "floss",
50 _ => "bluez",
51 },
52 )
53 .is_ok()
54 }
55
read_config() -> std::io::Result<String>56 pub fn read_config() -> std::io::Result<String> {
57 std::fs::read_to_string(BTMANAGERD_CONF)
58 }
59
get_log_level() -> Option<LevelFilter>60 pub fn get_log_level() -> Option<LevelFilter> {
61 get_log_level_internal(read_config().ok()?)
62 }
63
get_log_level_internal(config: String) -> Option<LevelFilter>64 fn get_log_level_internal(config: String) -> Option<LevelFilter> {
65 serde_json::from_str::<Value>(config.as_str())
66 .ok()?
67 .get("log_level")?
68 .as_str()?
69 .parse::<LevelFilter>()
70 .ok()
71 }
72
73 /// Returns whether hci N is enabled in config; defaults to true.
is_hci_n_enabled(hci: VirtualHciIndex) -> bool74 pub fn is_hci_n_enabled(hci: VirtualHciIndex) -> bool {
75 read_config().ok().and_then(|config| is_hci_n_enabled_internal(config, hci)).unwrap_or(true)
76 }
77
is_hci_n_enabled_internal(config: String, hci: VirtualHciIndex) -> Option<bool>78 fn is_hci_n_enabled_internal(config: String, hci: VirtualHciIndex) -> Option<bool> {
79 serde_json::from_str::<Value>(config.as_str())
80 .ok()?
81 .get(format!("hci{}", hci.to_i32()))?
82 .as_object()?
83 .get("enabled")?
84 .as_bool()
85 }
86
87 // When we initialize BluetoothManager, we need to make sure the file is a well-formatted json.
fix_config_file_format() -> bool88 pub fn fix_config_file_format() -> bool {
89 match read_config() {
90 Ok(s) => match serde_json::from_str::<Value>(s.as_str()) {
91 Ok(_) => true,
92 _ => std::fs::write(BTMANAGERD_CONF, "{}").is_ok(),
93 },
94 _ => std::fs::write(BTMANAGERD_CONF, "{}").is_ok(),
95 }
96 }
97
modify_hci_n_enabled(hci: VirtualHciIndex, enabled: bool) -> bool98 pub fn modify_hci_n_enabled(hci: VirtualHciIndex, enabled: bool) -> bool {
99 if !fix_config_file_format() {
100 return false;
101 }
102 match read_config().ok().and_then(|config| modify_hci_n_enabled_internal(config, hci, enabled))
103 {
104 Some(s) => std::fs::write(BTMANAGERD_CONF, s).is_ok(),
105 _ => false,
106 }
107 }
108
modify_hci_n_enabled_internal( config: String, hci: VirtualHciIndex, enabled: bool, ) -> Option<String>109 fn modify_hci_n_enabled_internal(
110 config: String,
111 hci: VirtualHciIndex,
112 enabled: bool,
113 ) -> Option<String> {
114 let hci_str = format!("hci{}", hci.to_i32());
115 let mut o = serde_json::from_str::<Value>(config.as_str()).ok()?;
116 match o.get_mut(hci_str.clone()) {
117 Some(section) => {
118 section.as_object_mut()?.insert("enabled".to_string(), Value::Bool(enabled));
119 serde_json::ser::to_string_pretty(&o).ok()
120 }
121 _ => {
122 let mut entry_map = Map::new();
123 entry_map.insert("enabled".to_string(), Value::Bool(enabled));
124 o.as_object_mut()?.insert(hci_str, Value::Object(entry_map));
125 serde_json::ser::to_string_pretty(&o).ok()
126 }
127 }
128 }
129
get_default_adapter() -> VirtualHciIndex130 pub fn get_default_adapter() -> VirtualHciIndex {
131 read_config()
132 .ok()
133 .and_then(|config| {
134 serde_json::from_str::<Value>(config.as_str())
135 .ok()?
136 .get(DEFAULT_ADAPTER_KEY)?
137 .as_i64()?
138 .try_into()
139 .ok()
140 })
141 .map(|i| VirtualHciIndex(i))
142 .unwrap_or(DEFAULT_ADAPTER)
143 }
144
set_default_adapter(hci: VirtualHciIndex) -> bool145 pub fn set_default_adapter(hci: VirtualHciIndex) -> bool {
146 match read_config().ok().and_then(|config| {
147 let mut cfg = serde_json::from_str::<Value>(config.as_str()).ok()?;
148 cfg[DEFAULT_ADAPTER_KEY] = serde_json::to_value(hci.to_i32()).ok().unwrap();
149 serde_json::ser::to_string_pretty(&cfg).ok()
150 }) {
151 Some(s) => std::fs::write(BTMANAGERD_CONF, s).is_ok(),
152 None => false,
153 }
154 }
155
list_hci_devices_string() -> Vec<String>156 fn list_hci_devices_string() -> Vec<String> {
157 match std::fs::read_dir(HCI_DEVICES_DIR) {
158 Ok(entries) => entries
159 .map(|e| e.unwrap().path().file_name().unwrap().to_str().unwrap().to_string())
160 .collect::<Vec<_>>(),
161 _ => Vec::new(),
162 }
163 }
164
165 /// Check whether a certain hci device exists in sysfs.
check_hci_device_exists(hci: RealHciIndex) -> bool166 pub fn check_hci_device_exists(hci: RealHciIndex) -> bool {
167 Path::new(format!("{}/hci{}", HCI_DEVICES_DIR, hci.to_i32()).as_str()).exists()
168 }
169
170 /// Get the devpath for a given hci index. This gives a stable path that can be
171 /// used to identify a device even as the hci index fluctuates.
get_devpath_for_hci(hci: RealHciIndex) -> Option<String>172 pub fn get_devpath_for_hci(hci: RealHciIndex) -> Option<String> {
173 match std::fs::canonicalize(format!("{}/hci{}/device", HCI_DEVICES_DIR, hci.to_i32()).as_str())
174 {
175 Ok(p) => Some(p.into_os_string().into_string().ok()?),
176 Err(e) => {
177 log::debug!("Failed to get devpath for {} with error: {}", hci, e);
178 None
179 }
180 }
181 }
182
list_pid_files(pid_dir: &str) -> Vec<String>183 pub fn list_pid_files(pid_dir: &str) -> Vec<String> {
184 match std::fs::read_dir(pid_dir) {
185 Ok(entries) => entries
186 .map(|e| e.unwrap().path().file_name().unwrap().to_str().unwrap().to_string())
187 .collect::<Vec<_>>(),
188 _ => Vec::new(),
189 }
190 }
191
192 /// Calls the reset sysfs entry for an hci device. Returns True if the write succeeds.
reset_hci_device(hci: RealHciIndex) -> bool193 pub fn reset_hci_device(hci: RealHciIndex) -> bool {
194 let path = format!("/sys/class/bluetooth/hci{}/reset", hci.to_i32());
195 std::fs::write(path, "1").is_ok()
196 }
197
read_floss_ll_privacy_enabled() -> std::io::Result<bool>198 pub fn read_floss_ll_privacy_enabled() -> std::io::Result<bool> {
199 let parent = Path::new(FLOSS_SYSPROPS_OVERRIDE_DIR);
200 if !parent.is_dir() {
201 return Ok(false);
202 }
203
204 let data = std::fs::read_to_string(format!(
205 "{}/{}",
206 FLOSS_SYSPROPS_OVERRIDE_DIR, "privacy_override.conf"
207 ))?;
208
209 Ok(data == "[Sysprops]\nbluetooth.core.gap.le.privacy.enabled=true\n")
210 }
211
write_floss_ll_privacy_enabled(enabled: bool) -> std::io::Result<()>212 pub fn write_floss_ll_privacy_enabled(enabled: bool) -> std::io::Result<()> {
213 let parent = Path::new(FLOSS_SYSPROPS_OVERRIDE_DIR);
214
215 std::fs::create_dir_all(parent)?;
216
217 let data = format!(
218 "[Sysprops]\nbluetooth.core.gap.le.privacy.enabled={}",
219 if enabled { "true\n" } else { "false\n" }
220 );
221
222 std::fs::write(format!("{}/{}", FLOSS_SYSPROPS_OVERRIDE_DIR, "privacy_override.conf"), data)
223 }
224
read_floss_address_privacy_enabled() -> std::io::Result<bool>225 pub fn read_floss_address_privacy_enabled() -> std::io::Result<bool> {
226 let parent = Path::new(FLOSS_SYSPROPS_OVERRIDE_DIR);
227 if !parent.is_dir() {
228 return Ok(false);
229 }
230
231 let data = std::fs::read_to_string(format!(
232 "{}/{}",
233 FLOSS_SYSPROPS_OVERRIDE_DIR, "privacy_address_override.conf"
234 ))?;
235
236 Ok(data == "[Sysprops]\nbluetooth.core.gap.le.privacy.own_address_type.enabled=true\n")
237 }
238
write_floss_address_privacy_enabled(enabled: bool) -> std::io::Result<()>239 pub fn write_floss_address_privacy_enabled(enabled: bool) -> std::io::Result<()> {
240 let parent = Path::new(FLOSS_SYSPROPS_OVERRIDE_DIR);
241
242 std::fs::create_dir_all(parent)?;
243
244 let data = format!(
245 "[Sysprops]\nbluetooth.core.gap.le.privacy.own_address_type.enabled={}",
246 if enabled { "true\n" } else { "false\n" }
247 );
248
249 std::fs::write(
250 format!("{}/{}", FLOSS_SYSPROPS_OVERRIDE_DIR, "privacy_address_override.conf"),
251 data,
252 )
253 }
254
set_adapter_coredump_state(enabled: bool) -> std::io::Result<()>255 pub fn set_adapter_coredump_state(enabled: bool) -> std::io::Result<()> {
256 let data = format!("{}\n", !enabled as i32);
257
258 for hci in list_hci_devices_string() {
259 let path = format!("{}/{}/device/coredump_disabled", HCI_DEVICES_DIR, hci);
260
261 std::fs::write(path, &data)?;
262 }
263
264 Ok(())
265 }
266
write_coredump_state_to_file(enabled: bool) -> bool267 pub fn write_coredump_state_to_file(enabled: bool) -> bool {
268 let data = format!("{}\n", !enabled as i32);
269
270 if let Err(e) = std::fs::write(FLOSS_COREDUMP_CONF_PATH, data) {
271 log::error!("Failed to write devcoredump state (error: {})", e);
272 return false;
273 }
274
275 if let Err(e) = set_adapter_coredump_state(enabled) {
276 log::error!("Failed to set devcoredump state (error: {})", e);
277 return false;
278 }
279
280 true
281 }
282
283 #[cfg(test)]
284 mod tests {
285 use super::*;
286
is_hci_n_enabled_internal_wrapper(config: String, n: i32) -> bool287 fn is_hci_n_enabled_internal_wrapper(config: String, n: i32) -> bool {
288 is_hci_n_enabled_internal(config, VirtualHciIndex(n)).unwrap_or(true)
289 }
290
291 #[test]
parse_log_level()292 fn parse_log_level() {
293 assert_eq!(
294 get_log_level_internal("{\"log_level\": \"error\"}".to_string()).unwrap(),
295 LevelFilter::Error
296 );
297 assert_eq!(
298 get_log_level_internal("{\"log_level\": \"warn\"}".to_string()).unwrap(),
299 LevelFilter::Warn
300 );
301 assert_eq!(
302 get_log_level_internal("{\"log_level\": \"info\"}".to_string()).unwrap(),
303 LevelFilter::Info
304 );
305 assert_eq!(
306 get_log_level_internal("{\"log_level\": \"debug\"}".to_string()).unwrap(),
307 LevelFilter::Debug
308 );
309 assert_eq!(
310 get_log_level_internal("{\"log_level\": \"trace\"}".to_string()).unwrap(),
311 LevelFilter::Trace
312 );
313 assert_eq!(
314 get_log_level_internal("{\"log_level\": \"random\"}".to_string()).is_none(),
315 true
316 );
317 }
318
319 #[test]
parse_hci0_enabled()320 fn parse_hci0_enabled() {
321 assert_eq!(
322 is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": true}}".to_string(), 0),
323 true
324 );
325 }
326
327 #[test]
modify_hci0_enabled()328 fn modify_hci0_enabled() {
329 let modified_string = modify_hci_n_enabled_internal(
330 "{\"hci0\":\n{\"enabled\": false}}".to_string(),
331 VirtualHciIndex(0),
332 true,
333 )
334 .unwrap();
335 assert_eq!(is_hci_n_enabled_internal_wrapper(modified_string, 0), true);
336 }
337
338 #[test]
modify_hci0_enabled_from_empty()339 fn modify_hci0_enabled_from_empty() {
340 let modified_string =
341 modify_hci_n_enabled_internal("{}".to_string(), VirtualHciIndex(0), true).unwrap();
342 assert_eq!(is_hci_n_enabled_internal_wrapper(modified_string, 0), true);
343 }
344
345 #[test]
parse_hci0_not_enabled()346 fn parse_hci0_not_enabled() {
347 assert_eq!(
348 is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": false}}".to_string(), 0),
349 false
350 );
351 }
352
353 #[test]
parse_hci1_not_present()354 fn parse_hci1_not_present() {
355 assert_eq!(
356 is_hci_n_enabled_internal_wrapper("{\"hci0\":\n{\"enabled\": true}}".to_string(), 1),
357 true
358 );
359 }
360 }
361