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, Uuid128Bit}; 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: Uuid128Bit) -> bool21 fn is_service_allowed(&self, service: Uuid128Bit) -> bool; 22 /// Overwrite the current settings and store it to a file. set_allowed_services(&mut self, services: Vec<Uuid128Bit>) -> bool23 fn set_allowed_services(&mut self, services: Vec<Uuid128Bit>) -> bool; 24 /// Get the allowlist in UUIDs get_allowed_services(&self) -> Vec<Uuid128Bit>25 fn get_allowed_services(&self) -> Vec<Uuid128Bit>; 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<Uuid128Bit>, 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<Uuid128Bit>)48 fn on_service_allowlist_changed(&mut self, allowlist: Vec<Uuid128Bit>); 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<Uuid128Bit>, 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<Uuid128Bit>) -> Vec<Uuid128Bit>92 fn get_blocked_services(&self, remote_uuids: &Vec<Uuid128Bit>) -> Vec<Uuid128Bit> { 93 remote_uuids 94 .iter() 95 .filter(|&s| !self.is_service_allowed(s.clone())) 96 .cloned() 97 .collect::<Vec<Uuid128Bit>>() 98 } 99 get_affected_status(&self, blocked_services: &Vec<Uuid128Bit>) -> bool100 fn get_affected_status(&self, blocked_services: &Vec<Uuid128Bit>) -> bool { 101 // return true if a supported profile is in blocked services. 102 blocked_services 103 .iter() 104 .find(|&uuid| { 105 UuidHelper::is_known_profile(uuid) 106 .map_or(false, |p| UuidHelper::is_profile_supported(&p)) 107 }) 108 .is_some() 109 } 110 load_config(&mut self) -> Result<()>111 fn load_config(&mut self) -> Result<()> { 112 let mut file = File::open(&self.path)?; 113 let mut contents = String::new(); 114 file.read_to_string(&mut contents)?; 115 let json = serde_json::from_str::<Value>(contents.as_str()).unwrap(); 116 if let Some(_res) = self.load_config_from_json(&json) { 117 info!("Load settings from {} successfully", &self.path); 118 } 119 Ok(()) 120 } 121 load_config_from_json(&mut self, json: &Value) -> Option<bool>122 fn load_config_from_json(&mut self, json: &Value) -> Option<bool> { 123 let allowed_services: Vec<Uuid128Bit> = json 124 .get("allowed_services")? 125 .as_array()? 126 .iter() 127 .filter_map(|v| UuidHelper::from_string(v.as_str()?)) 128 .collect(); 129 self.set_allowed_services(allowed_services); 130 Some(true) 131 } 132 write_config(&self) -> Result<()>133 fn write_config(&self) -> Result<()> { 134 let mut f = File::create(&self.path)?; 135 f.write_all(self.get_config_string().as_bytes()).and_then(|_| { 136 info!("Write settings into {} successfully", &self.path); 137 Ok(()) 138 }) 139 } 140 get_config_string(&self) -> String141 fn get_config_string(&self) -> String { 142 serde_json::to_string_pretty(&json!({ 143 "allowed_services": 144 self.get_allowed_services() 145 .iter() 146 .map(UuidHelper::to_string) 147 .collect::<Vec<String>>() 148 })) 149 .ok() 150 .unwrap() 151 } 152 new_device_policy_effect(&self, uuids: Option<Vec<Uuid128Bit>>) -> Option<PolicyEffect>153 fn new_device_policy_effect(&self, uuids: Option<Vec<Uuid128Bit>>) -> Option<PolicyEffect> { 154 uuids.map(|uuids| { 155 let service_blocked = self.get_blocked_services(&uuids); 156 let affected = self.get_affected_status(&service_blocked); 157 PolicyEffect { service_blocked, affected } 158 }) 159 } 160 on_device_found(&mut self, remote_device: &BluetoothDevice)161 pub fn on_device_found(&mut self, remote_device: &BluetoothDevice) { 162 self.device_policy_affect_cache.insert(remote_device.clone(), None).or_else(|| { 163 self.callbacks.for_all_callbacks(|cb| { 164 cb.on_device_policy_effect_changed(remote_device.clone(), None); 165 }); 166 None 167 }); 168 } 169 on_device_cleared(&mut self, remote_device: &BluetoothDevice)170 pub fn on_device_cleared(&mut self, remote_device: &BluetoothDevice) { 171 self.device_policy_affect_cache.remove(remote_device); 172 } 173 on_remote_device_properties_changed( &mut self, remote_device: &BluetoothDevice, properties: &Vec<BluetoothProperty>, )174 pub fn on_remote_device_properties_changed( 175 &mut self, 176 remote_device: &BluetoothDevice, 177 properties: &Vec<BluetoothProperty>, 178 ) { 179 let new_uuids = properties.iter().find_map(|p| match p { 180 BluetoothProperty::Uuids(uuids) => { 181 Some(uuids.iter().map(|&x| x.uu.clone()).collect::<Vec<Uuid128Bit>>()) 182 } 183 _ => None, 184 }); 185 186 // No need to update policy effect if remote UUID is not changed. 187 if new_uuids.is_none() { 188 return; 189 } 190 191 let new_effect = self.new_device_policy_effect(new_uuids); 192 let cur_effect = self.device_policy_affect_cache.get(remote_device); 193 194 if cur_effect.is_none() || *cur_effect.unwrap() != new_effect.clone() { 195 self.callbacks.for_all_callbacks(|cb| { 196 cb.on_device_policy_effect_changed(remote_device.clone(), new_effect.clone()) 197 }); 198 self.device_policy_affect_cache.insert(remote_device.clone(), new_effect.clone()); 199 } 200 } 201 } 202 203 impl IBluetoothAdmin for BluetoothAdmin { is_service_allowed(&self, service: Uuid128Bit) -> bool204 fn is_service_allowed(&self, service: Uuid128Bit) -> bool { 205 self.allowed_services.is_empty() || self.allowed_services.contains(&service) 206 } 207 set_allowed_services(&mut self, services: Vec<Uuid128Bit>) -> bool208 fn set_allowed_services(&mut self, services: Vec<Uuid128Bit>) -> bool { 209 if self.get_allowed_services() == services { 210 // Allowlist is not changed. 211 return true; 212 } 213 214 self.allowed_services.clear(); 215 216 for service in services.iter() { 217 self.allowed_services.insert(service.clone()); 218 } 219 220 if let Some(adapter) = &self.adapter { 221 let allowed_services = self.get_allowed_services(); 222 adapter.lock().unwrap().toggle_enabled_profiles(&allowed_services); 223 if self.write_config().is_err() { 224 warn!("Failed to write config"); 225 } 226 227 let allowed_services = self.get_allowed_services(); 228 self.callbacks.for_all_callbacks(|cb| { 229 cb.on_service_allowlist_changed(allowed_services.clone()); 230 }); 231 232 let txl = self.tx.clone(); 233 tokio::spawn(async move { 234 let _ = txl.send(Message::AdminPolicyChanged).await; 235 }); 236 237 for (device, effect) in self.device_policy_affect_cache.clone().iter() { 238 let uuids = adapter.lock().unwrap().get_remote_uuids(device.clone()); 239 let new_effect = self.new_device_policy_effect(Some(uuids)); 240 241 if new_effect.clone() != *effect { 242 self.callbacks.for_all_callbacks(|cb| { 243 cb.on_device_policy_effect_changed(device.clone(), new_effect.clone()) 244 }); 245 self.device_policy_affect_cache.insert(device.clone(), new_effect.clone()); 246 } 247 } 248 return true; 249 } 250 251 false 252 } 253 get_allowed_services(&self) -> Vec<Uuid128Bit>254 fn get_allowed_services(&self) -> Vec<Uuid128Bit> { 255 self.allowed_services.iter().cloned().collect() 256 } 257 get_device_policy_effect(&self, device: BluetoothDevice) -> Option<PolicyEffect>258 fn get_device_policy_effect(&self, device: BluetoothDevice) -> Option<PolicyEffect> { 259 if let Some(effect) = self.device_policy_affect_cache.get(&device) { 260 effect.clone() 261 } else { 262 warn!("Device not found in cache"); 263 None 264 } 265 } 266 register_admin_policy_callback( &mut self, callback: Box<dyn IBluetoothAdminPolicyCallback + Send>, ) -> u32267 fn register_admin_policy_callback( 268 &mut self, 269 callback: Box<dyn IBluetoothAdminPolicyCallback + Send>, 270 ) -> u32 { 271 self.callbacks.add_callback(callback) 272 } 273 unregister_admin_policy_callback(&mut self, callback_id: u32) -> bool274 fn unregister_admin_policy_callback(&mut self, callback_id: u32) -> bool { 275 self.callbacks.remove_callback(callback_id) 276 } 277 } 278 279 #[cfg(test)] 280 mod tests { 281 use crate::bluetooth_admin::{BluetoothAdmin, IBluetoothAdmin}; 282 use crate::uuid::UuidHelper; 283 use crate::Stack; 284 use bt_topshim::btif::Uuid128Bit; 285 286 // A workaround needed for linking. For more details, check the comment in 287 // system/gd/rust/topshim/facade/src/main.rs 288 #[allow(unused)] 289 use bt_shim::*; 290 use serde_json::{json, Value}; 291 292 #[test] test_set_service_allowed()293 fn test_set_service_allowed() { 294 let (tx, _) = Stack::create_channel(); 295 let mut admin = BluetoothAdmin::new(String::from(""), tx.clone()); 296 let uuid1: Uuid128Bit = [1; 16]; 297 let uuid2: Uuid128Bit = [2; 16]; 298 let uuid3: Uuid128Bit = [3; 16]; 299 let uuids = vec![uuid1.clone(), uuid2.clone(), uuid3.clone()]; 300 301 // Default admin allows everything 302 assert!(admin.is_service_allowed(uuid1)); 303 assert!(admin.is_service_allowed(uuid2)); 304 assert!(admin.is_service_allowed(uuid3)); 305 assert_eq!(admin.get_blocked_services(&uuids), Vec::<Uuid128Bit>::new()); 306 307 admin.set_allowed_services(vec![uuid1.clone(), uuid3.clone()]); 308 309 // Admin disallows uuid2 now 310 assert!(admin.is_service_allowed(uuid1)); 311 assert!(!admin.is_service_allowed(uuid2)); 312 assert!(admin.is_service_allowed(uuid3)); 313 assert_eq!(admin.get_blocked_services(&uuids), vec![uuid2.clone()]); 314 315 admin.set_allowed_services(vec![uuid2.clone()]); 316 317 // Allowed services were overwritten. 318 assert!(!admin.is_service_allowed(uuid1)); 319 assert!(admin.is_service_allowed(uuid2)); 320 assert!(!admin.is_service_allowed(uuid3)); 321 assert_eq!(admin.get_blocked_services(&uuids), vec![uuid1.clone(), uuid3.clone()]); 322 } 323 get_sorted_allowed_services_from_config(admin: &BluetoothAdmin) -> Vec<String>324 fn get_sorted_allowed_services_from_config(admin: &BluetoothAdmin) -> Vec<String> { 325 let mut v = serde_json::from_str::<Value>(admin.get_config_string().as_str()) 326 .unwrap() 327 .get("allowed_services") 328 .unwrap() 329 .as_array() 330 .unwrap() 331 .iter() 332 .map(|v| String::from(v.as_str().unwrap())) 333 .collect::<Vec<String>>(); 334 v.sort(); 335 v 336 } 337 get_sorted_allowed_services(admin: &BluetoothAdmin) -> Vec<Uuid128Bit>338 fn get_sorted_allowed_services(admin: &BluetoothAdmin) -> Vec<Uuid128Bit> { 339 let mut v = admin.get_allowed_services(); 340 v.sort(); 341 v 342 } 343 344 #[test] test_config()345 fn test_config() { 346 let (tx, _) = Stack::create_channel(); 347 let mut admin = BluetoothAdmin::new(String::from(""), tx.clone()); 348 let a2dp_sink = "0000110b-0000-1000-8000-00805f9b34fb"; 349 let a2dp_source = "0000110a-0000-1000-8000-00805f9b34fb"; 350 351 let a2dp_sink_uuid128 = UuidHelper::from_string(a2dp_sink).unwrap(); 352 let a2dp_source_uuid128 = UuidHelper::from_string(a2dp_source).unwrap(); 353 354 let mut allowed_services = vec![a2dp_sink, a2dp_source]; 355 356 let mut allowed_services_128 = vec![a2dp_sink_uuid128, a2dp_source_uuid128]; 357 358 allowed_services.sort(); 359 allowed_services_128.sort(); 360 361 // valid configuration 362 assert_eq!( 363 admin.load_config_from_json(&json!({ 364 "allowed_services": allowed_services.clone() 365 })), 366 Some(true) 367 ); 368 assert_eq!(get_sorted_allowed_services(&admin), allowed_services_128); 369 assert_eq!(get_sorted_allowed_services_from_config(&admin), allowed_services); 370 371 // invalid configuration 372 assert_eq!(admin.load_config_from_json(&json!({ "allowed_services": a2dp_sink })), None); 373 // config should remain unchanged 374 assert_eq!(get_sorted_allowed_services(&admin), allowed_services_128); 375 assert_eq!(get_sorted_allowed_services_from_config(&admin), allowed_services); 376 } 377 } 378