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