bluetooth_traits/
blocklist.rs1use std::cell::RefCell;
6use std::collections::HashMap;
7use std::string::String;
8
9use embedder_traits::resources::{self, Resource};
10use regex::Regex;
11
12const EXCLUDE_READS: &str = "exclude-reads";
13const EXCLUDE_WRITES: &str = "exclude-writes";
14const VALID_UUID_REGEX: &str = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
15
16thread_local!(pub static BLUETOOTH_BLOCKLIST: RefCell<BluetoothBlocklist> =
17 RefCell::new(BluetoothBlocklist(parse_blocklist())));
18
19pub fn uuid_is_blocklisted(uuid: &str, exclude_type: Blocklist) -> bool {
20 BLUETOOTH_BLOCKLIST.with(|blist| match exclude_type {
21 Blocklist::All => blist.borrow().is_blocklisted(uuid),
22 Blocklist::Reads => blist.borrow().is_blocklisted_for_reads(uuid),
23 Blocklist::Writes => blist.borrow().is_blocklisted_for_writes(uuid),
24 })
25}
26
27pub struct BluetoothBlocklist(Option<HashMap<String, Blocklist>>);
28
29#[derive(Eq, PartialEq)]
30pub enum Blocklist {
31 All, Reads,
33 Writes,
34}
35
36impl BluetoothBlocklist {
37 pub fn is_blocklisted(&self, uuid: &str) -> bool {
39 match self.0 {
40 Some(ref map) => map.get(uuid).is_some_and(|et| et.eq(&Blocklist::All)),
41 None => false,
42 }
43 }
44
45 pub fn is_blocklisted_for_reads(&self, uuid: &str) -> bool {
47 match self.0 {
48 Some(ref map) => map
49 .get(uuid)
50 .is_some_and(|et| et.eq(&Blocklist::All) || et.eq(&Blocklist::Reads)),
51 None => false,
52 }
53 }
54
55 pub fn is_blocklisted_for_writes(&self, uuid: &str) -> bool {
57 match self.0 {
58 Some(ref map) => map
59 .get(uuid)
60 .is_some_and(|et| et.eq(&Blocklist::All) || et.eq(&Blocklist::Writes)),
61 None => false,
62 }
63 }
64}
65
66fn parse_blocklist() -> Option<HashMap<String, Blocklist>> {
68 let valid_uuid_regex = Regex::new(VALID_UUID_REGEX).unwrap();
70 let content = resources::read_string(Resource::BluetoothBlocklist);
71 let mut result = HashMap::new();
73 for line in content.lines() {
75 if line.is_empty() || line.starts_with('#') {
77 continue;
78 }
79 let mut exclude_type = Blocklist::All;
80 let mut words = line.split_whitespace();
81 let uuid = match words.next() {
82 Some(uuid) => uuid,
83 None => continue,
84 };
85 if !valid_uuid_regex.is_match(uuid) {
86 return None;
87 }
88 match words.next() {
89 None => {},
91 Some(EXCLUDE_READS) => {
93 exclude_type = Blocklist::Reads;
94 },
95 Some(EXCLUDE_WRITES) => {
96 exclude_type = Blocklist::Writes;
97 },
98 _ => {
100 return None;
101 },
102 }
103 if result.contains_key(uuid) {
105 return None;
106 }
107 result.insert(uuid.to_string(), exclude_type);
109 }
110 Some(result)
112}