1 //! Anything related to the Admin API (IBluetoothAdmin).
2 
3 use std::collections::{HashMap, HashSet};
4 use std::fs::File;
5 use std::io::{Read, Result, Write};
6 use std::sync::{Arc, Mutex};
7 
8 use crate::bluetooth::{Bluetooth, BluetoothDevice, IBluetooth};
9 use crate::callbacks::Callbacks;
10 use crate::uuid::UuidHelper;
11 use crate::{Message, RPCProxy};
12 
13 use bt_topshim::btif::{BluetoothProperty, Uuid};
14 use log::{info, warn};
15 use serde_json::{json, Value};
16 use tokio::sync::mpsc::Sender;
17 
18 /// Defines the Admin API
19 pub trait IBluetoothAdmin {
20     /// Check if the given UUID is in the allowlist
is_service_allowed(&self, service: Uuid) -> bool21     fn is_service_allowed(&self, service: Uuid) -> bool;
22     /// Overwrite the current settings and store it to a file.
set_allowed_services(&mut self, services: Vec<Uuid>) -> bool23     fn set_allowed_services(&mut self, services: Vec<Uuid>) -> bool;
24     /// Get the allowlist in UUIDs
get_allowed_services(&self) -> Vec<Uuid>25     fn get_allowed_services(&self) -> Vec<Uuid>;
26     /// Get the PolicyEffect struct of a device
get_device_policy_effect(&self, device: BluetoothDevice) -> Option<PolicyEffect>27     fn get_device_policy_effect(&self, device: BluetoothDevice) -> Option<PolicyEffect>;
28     /// Register client callback
register_admin_policy_callback( &mut self, callback: Box<dyn IBluetoothAdminPolicyCallback + Send>, ) -> u3229     fn register_admin_policy_callback(
30         &mut self,
31         callback: Box<dyn IBluetoothAdminPolicyCallback + Send>,
32     ) -> u32;
33     /// Unregister client callback via callback ID
unregister_admin_policy_callback(&mut self, callback_id: u32) -> bool34     fn unregister_admin_policy_callback(&mut self, callback_id: u32) -> bool;
35 }
36 
37 /// Information of the effects to a remote device by the admin policies
38 #[derive(PartialEq, Clone, Debug)]
39 pub struct PolicyEffect {
40     /// Array of services that are blocked by policy
41     pub service_blocked: Vec<Uuid>,
42     /// Indicate if the device has an adapter-supported profile that is blocked by the policy
43     pub affected: bool,
44 }
45 
46 pub trait IBluetoothAdminPolicyCallback: RPCProxy {
47     /// This gets called when service allowlist changed.
on_service_allowlist_changed(&mut self, allowlist: Vec<Uuid>)48     fn on_service_allowlist_changed(&mut self, allowlist: Vec<Uuid>);
49     /// This gets called when
50     /// 1. a new device is found by adapter
51     /// 2. the policy effect to a device is changed due to
52     ///    the remote services changed or
53     ///    the service allowlist changed.
on_device_policy_effect_changed( &mut self, device: BluetoothDevice, new_policy_effect: Option<PolicyEffect>, )54     fn on_device_policy_effect_changed(
55         &mut self,
56         device: BluetoothDevice,
57         new_policy_effect: Option<PolicyEffect>,
58     );
59 }
60 
61 pub struct BluetoothAdmin {
62     path: String,
63     adapter: Option<Arc<Mutex<Box<Bluetooth>>>>,
64     allowed_services: HashSet<Uuid>,
65     callbacks: Callbacks<dyn IBluetoothAdminPolicyCallback + Send>,
66     device_policy_affect_cache: HashMap<BluetoothDevice, Option<PolicyEffect>>,
67     tx: Sender<Message>,
68 }
69 
70 impl BluetoothAdmin {
new(path: String, tx: Sender<Message>) -> BluetoothAdmin71     pub fn new(path: String, tx: Sender<Message>) -> BluetoothAdmin {
72         // default admin settings
73         let mut admin = BluetoothAdmin {
74             path,
75             adapter: None,
76             allowed_services: HashSet::new(), //empty means allowed all services
77             callbacks: Callbacks::new(tx.clone(), Message::AdminCallbackDisconnected),
78             device_policy_affect_cache: HashMap::new(),
79             tx: tx.clone(),
80         };
81 
82         if admin.load_config().is_err() {
83             warn!("Failed to load config file");
84         }
85         admin
86     }
87 
set_adapter(&mut self, adapter: Arc<Mutex<Box<Bluetooth>>>)88     pub fn set_adapter(&mut self, adapter: Arc<Mutex<Box<Bluetooth>>>) {
89         self.adapter = Some(adapter.clone());
90     }
91 
get_blocked_services(&self, remote_uuids: &Vec<Uuid>) -> Vec<Uuid>92     fn get_blocked_services(&self, remote_uuids: &Vec<Uuid>) -> Vec<Uuid> {
93         remote_uuids.iter().filter(|&&uu| !self.is_service_allowed(uu)).cloned().collect()
94     }
95 
get_affected_status(&self, blocked_services: &Vec<Uuid>) -> bool96     fn get_affected_status(&self, blocked_services: &Vec<Uuid>) -> bool {
97         // return true if a supported profile is in blocked services.
98         blocked_services
99             .iter()
100             .find(|&uuid| {
101                 UuidHelper::is_known_profile(uuid)
102                     .map_or(false, |p| UuidHelper::is_profile_supported(&p))
103             })
104             .is_some()
105     }
106 
load_config(&mut self) -> Result<()>107     fn load_config(&mut self) -> Result<()> {
108         let mut file = File::open(&self.path)?;
109         let mut contents = String::new();
110         file.read_to_string(&mut contents)?;
111         let json = serde_json::from_str::<Value>(contents.as_str())?;
112         if let Some(_res) = self.load_config_from_json(&json) {
113             info!("Load settings from {} successfully", &self.path);
114         }
115         Ok(())
116     }
117 
load_config_from_json(&mut self, json: &Value) -> Option<bool>118     fn load_config_from_json(&mut self, json: &Value) -> Option<bool> {
119         let allowed_services: Vec<Uuid> = json
120             .get("allowed_services")?
121             .as_array()?
122             .iter()
123             .filter_map(|v| Uuid::from_string(v.as_str()?))
124             .collect();
125         self.set_allowed_services(allowed_services);
126         Some(true)
127     }
128 
write_config(&self) -> Result<()>129     fn write_config(&self) -> Result<()> {
130         let mut f = File::create(&self.path)?;
131         f.write_all(self.get_config_string().as_bytes()).and_then(|_| {
132             info!("Write settings into {} successfully", &self.path);
133             Ok(())
134         })
135     }
136 
get_config_string(&self) -> String137     fn get_config_string(&self) -> String {
138         serde_json::to_string_pretty(&json!({
139             "allowed_services":
140                 self.get_allowed_services()
141                     .iter()
142                     .map(|uu| uu.to_string())
143                     .collect::<Vec<String>>()
144         }))
145         .ok()
146         .unwrap()
147     }
148 
new_device_policy_effect(&self, uuids: Option<Vec<Uuid>>) -> Option<PolicyEffect>149     fn new_device_policy_effect(&self, uuids: Option<Vec<Uuid>>) -> Option<PolicyEffect> {
150         uuids.map(|uuids| {
151             let service_blocked = self.get_blocked_services(&uuids);
152             let affected = self.get_affected_status(&service_blocked);
153             PolicyEffect { service_blocked, affected }
154         })
155     }
156 
on_device_found(&mut self, remote_device: &BluetoothDevice)157     pub fn on_device_found(&mut self, remote_device: &BluetoothDevice) {
158         self.device_policy_affect_cache.insert(remote_device.clone(), None).or_else(|| {
159             self.callbacks.for_all_callbacks(|cb| {
160                 cb.on_device_policy_effect_changed(remote_device.clone(), None);
161             });
162             None
163         });
164     }
165 
on_device_cleared(&mut self, remote_device: &BluetoothDevice)166     pub fn on_device_cleared(&mut self, remote_device: &BluetoothDevice) {
167         self.device_policy_affect_cache.remove(remote_device);
168     }
169 
on_remote_device_properties_changed( &mut self, remote_device: &BluetoothDevice, properties: &Vec<BluetoothProperty>, )170     pub fn on_remote_device_properties_changed(
171         &mut self,
172         remote_device: &BluetoothDevice,
173         properties: &Vec<BluetoothProperty>,
174     ) {
175         let new_uuids = properties.iter().find_map(|p| match p {
176             BluetoothProperty::Uuids(uuids) => Some(uuids.clone()),
177             _ => None,
178         });
179 
180         // No need to update policy effect if remote UUID is not changed.
181         if new_uuids.is_none() {
182             return;
183         }
184 
185         let new_effect = self.new_device_policy_effect(new_uuids);
186         let cur_effect = self.device_policy_affect_cache.get(remote_device);
187 
188         if cur_effect.is_none() || *cur_effect.unwrap() != new_effect.clone() {
189             self.callbacks.for_all_callbacks(|cb| {
190                 cb.on_device_policy_effect_changed(remote_device.clone(), new_effect.clone())
191             });
192             self.device_policy_affect_cache.insert(remote_device.clone(), new_effect.clone());
193         }
194     }
195 }
196 
197 impl IBluetoothAdmin for BluetoothAdmin {
is_service_allowed(&self, service: Uuid) -> bool198     fn is_service_allowed(&self, service: Uuid) -> bool {
199         self.allowed_services.is_empty() || self.allowed_services.contains(&service)
200     }
201 
set_allowed_services(&mut self, services: Vec<Uuid>) -> bool202     fn set_allowed_services(&mut self, services: Vec<Uuid>) -> bool {
203         if self.get_allowed_services() == services {
204             // Allowlist is not changed.
205             return true;
206         }
207 
208         self.allowed_services.clear();
209 
210         for service in services.iter() {
211             self.allowed_services.insert(*service);
212         }
213 
214         if let Some(adapter) = &self.adapter {
215             let allowed_services = self.get_allowed_services();
216             adapter.lock().unwrap().toggle_enabled_profiles(&allowed_services);
217             if self.write_config().is_err() {
218                 warn!("Failed to write config");
219             }
220 
221             let allowed_services = self.get_allowed_services();
222             self.callbacks.for_all_callbacks(|cb| {
223                 cb.on_service_allowlist_changed(allowed_services.clone());
224             });
225 
226             let txl = self.tx.clone();
227             tokio::spawn(async move {
228                 let _ = txl.send(Message::AdminPolicyChanged).await;
229             });
230 
231             for (device, effect) in self.device_policy_affect_cache.clone().iter() {
232                 let uuids = adapter.lock().unwrap().get_remote_uuids(device.clone());
233                 let new_effect = self.new_device_policy_effect(Some(uuids));
234 
235                 if new_effect.clone() != *effect {
236                     self.callbacks.for_all_callbacks(|cb| {
237                         cb.on_device_policy_effect_changed(device.clone(), new_effect.clone())
238                     });
239                     self.device_policy_affect_cache.insert(device.clone(), new_effect.clone());
240                 }
241             }
242             return true;
243         }
244 
245         false
246     }
247 
get_allowed_services(&self) -> Vec<Uuid>248     fn get_allowed_services(&self) -> Vec<Uuid> {
249         self.allowed_services.iter().cloned().collect()
250     }
251 
get_device_policy_effect(&self, device: BluetoothDevice) -> Option<PolicyEffect>252     fn get_device_policy_effect(&self, device: BluetoothDevice) -> Option<PolicyEffect> {
253         if let Some(effect) = self.device_policy_affect_cache.get(&device) {
254             effect.clone()
255         } else {
256             warn!("Device not found in cache");
257             None
258         }
259     }
260 
register_admin_policy_callback( &mut self, callback: Box<dyn IBluetoothAdminPolicyCallback + Send>, ) -> u32261     fn register_admin_policy_callback(
262         &mut self,
263         callback: Box<dyn IBluetoothAdminPolicyCallback + Send>,
264     ) -> u32 {
265         self.callbacks.add_callback(callback)
266     }
267 
unregister_admin_policy_callback(&mut self, callback_id: u32) -> bool268     fn unregister_admin_policy_callback(&mut self, callback_id: u32) -> bool {
269         self.callbacks.remove_callback(callback_id)
270     }
271 }
272 
273 #[cfg(test)]
274 mod tests {
275     use crate::bluetooth_admin::{BluetoothAdmin, IBluetoothAdmin};
276     use crate::Stack;
277     use bt_topshim::btif::Uuid;
278 
279     // A workaround needed for linking. For more details, check the comment in
280     // system/gd/rust/topshim/facade/src/main.rs
281     #[allow(unused)]
282     use bt_shim::*;
283     use serde_json::{json, Value};
284 
285     #[test]
test_set_service_allowed()286     fn test_set_service_allowed() {
287         let (tx, _) = Stack::create_channel();
288         let mut admin = BluetoothAdmin::new(String::from(""), tx.clone());
289         let uuid1: Uuid = [1; 16].into();
290         let uuid2: Uuid = [2; 16].into();
291         let uuid3: Uuid = [3; 16].into();
292         let uuids = vec![uuid1, uuid2, uuid3];
293 
294         // Default admin allows everything
295         assert!(admin.is_service_allowed(uuid1));
296         assert!(admin.is_service_allowed(uuid2));
297         assert!(admin.is_service_allowed(uuid3));
298         assert_eq!(admin.get_blocked_services(&uuids), Vec::<Uuid>::new());
299 
300         admin.set_allowed_services(vec![uuid1, uuid3]);
301 
302         // Admin disallows uuid2 now
303         assert!(admin.is_service_allowed(uuid1));
304         assert!(!admin.is_service_allowed(uuid2));
305         assert!(admin.is_service_allowed(uuid3));
306         assert_eq!(admin.get_blocked_services(&uuids), vec![uuid2]);
307 
308         admin.set_allowed_services(vec![uuid2]);
309 
310         // Allowed services were overwritten.
311         assert!(!admin.is_service_allowed(uuid1));
312         assert!(admin.is_service_allowed(uuid2));
313         assert!(!admin.is_service_allowed(uuid3));
314         assert_eq!(admin.get_blocked_services(&uuids), vec![uuid1, uuid3]);
315     }
316 
get_sorted_allowed_services_from_config(admin: &BluetoothAdmin) -> Vec<String>317     fn get_sorted_allowed_services_from_config(admin: &BluetoothAdmin) -> Vec<String> {
318         let mut v = serde_json::from_str::<Value>(admin.get_config_string().as_str())
319             .unwrap()
320             .get("allowed_services")
321             .unwrap()
322             .as_array()
323             .unwrap()
324             .iter()
325             .map(|v| String::from(v.as_str().unwrap()))
326             .collect::<Vec<String>>();
327         v.sort();
328         v
329     }
330 
get_sorted_allowed_services(admin: &BluetoothAdmin) -> Vec<Uuid>331     fn get_sorted_allowed_services(admin: &BluetoothAdmin) -> Vec<Uuid> {
332         let mut v = admin.get_allowed_services();
333         v.sort_by(|lhs, rhs| lhs.uu.cmp(&rhs.uu));
334         v
335     }
336 
337     #[test]
test_config()338     fn test_config() {
339         let (tx, _) = Stack::create_channel();
340         let mut admin = BluetoothAdmin::new(String::from(""), tx.clone());
341         let a2dp_sink_str = "0000110b-0000-1000-8000-00805f9b34fb";
342         let a2dp_source_str = "0000110a-0000-1000-8000-00805f9b34fb";
343 
344         let a2dp_sink_uuid = Uuid::from_string(a2dp_sink_str).unwrap();
345         let a2dp_source_uuid = Uuid::from_string(a2dp_source_str).unwrap();
346 
347         let mut allowed_services_str = vec![a2dp_sink_str, a2dp_source_str];
348 
349         let mut allowed_services_uuid = vec![a2dp_sink_uuid, a2dp_source_uuid];
350 
351         allowed_services_str.sort();
352         allowed_services_uuid.sort_by(|lhs, rhs| lhs.uu.cmp(&rhs.uu));
353 
354         // valid configuration
355         assert_eq!(
356             admin.load_config_from_json(&json!({
357                 "allowed_services": allowed_services_str.clone()
358             })),
359             Some(true)
360         );
361         assert_eq!(get_sorted_allowed_services(&admin), allowed_services_uuid);
362         assert_eq!(get_sorted_allowed_services_from_config(&admin), allowed_services_str);
363 
364         // invalid configuration
365         assert_eq!(
366             admin.load_config_from_json(&json!({ "allowed_services": a2dp_sink_str })),
367             None
368         );
369         // config should remain unchanged
370         assert_eq!(get_sorted_allowed_services(&admin), allowed_services_uuid);
371         assert_eq!(get_sorted_allowed_services_from_config(&admin), allowed_services_str);
372     }
373 }
374