1 use crate::battery_manager::{Battery, BatterySet}; 2 use crate::battery_provider_manager::{ 3 BatteryProviderManager, IBatteryProviderCallback, IBatteryProviderManager, 4 }; 5 use crate::bluetooth_gatt::{ 6 BluetoothGatt, BluetoothGattService, IBluetoothGatt, IBluetoothGattCallback, 7 }; 8 use crate::callbacks::Callbacks; 9 use crate::Message; 10 use crate::RPCProxy; 11 use crate::{uuid, APIMessage, BluetoothAPI}; 12 use bt_topshim::btif::{BtTransport, DisplayAddress, RawAddress, Uuid}; 13 use bt_topshim::profiles::gatt::{GattStatus, LePhy}; 14 use log::{debug, info}; 15 use std::collections::HashMap; 16 use std::convert::TryInto; 17 use std::iter; 18 use std::sync::{Arc, Mutex}; 19 use tokio::sync::mpsc::Sender; 20 21 /// The UUID corresponding to the BatteryLevel characteristic defined by the BatteryService 22 /// specification. 23 pub const CHARACTERISTIC_BATTERY_LEVEL: &str = "00002A1-9000-0100-0800-000805F9B34FB"; 24 25 /// The app UUID BAS provides when connecting as a GATT client. Chosen at random. 26 /// TODO(b/233101174): make dynamic or decide on a static UUID 27 pub const BATTERY_SERVICE_GATT_CLIENT_APP_ID: &str = "e4d2acffcfaa42198f494606b7412117"; 28 29 /// Represents the Floss BatteryService implementation. 30 pub struct BatteryService { 31 gatt: Arc<Mutex<Box<BluetoothGatt>>>, 32 battery_provider_manager: Arc<Mutex<Box<BatteryProviderManager>>>, 33 battery_provider_id: u32, 34 /// Sender for callback communication with the main thread. 35 tx: Sender<Message>, 36 /// Sender for callback communication with the api message thread. 37 api_tx: Sender<APIMessage>, 38 callbacks: Callbacks<dyn IBatteryServiceCallback + Send>, 39 /// The GATT client ID needed for GATT calls. 40 client_id: Option<i32>, 41 /// Cached battery info keyed by remote device. 42 battery_sets: HashMap<RawAddress, BatterySet>, 43 /// Found handles for battery levels. Required for faster 44 /// refreshes than initiating another search. 45 handles: HashMap<RawAddress, i32>, 46 } 47 48 /// Enum for GATT callbacks to relay messages to the main processing thread. Newly supported 49 /// callbacks should add a corresponding entry here. 50 pub enum BatteryServiceActions { 51 /// Params: status, client_id 52 OnClientRegistered(GattStatus, i32), 53 /// Params: status, client_id, connected, addr 54 OnClientConnectionState(GattStatus, i32, bool, RawAddress), 55 /// Params: addr, services, status 56 OnSearchComplete(RawAddress, Vec<BluetoothGattService>, GattStatus), 57 /// Params: addr, status, handle, value 58 OnCharacteristicRead(RawAddress, GattStatus, i32, Vec<u8>), 59 /// Params: addr, handle, value 60 OnNotify(RawAddress, i32, Vec<u8>), 61 } 62 63 /// API for Floss implementation of the Bluetooth Battery Service (BAS). BAS is built on GATT and 64 /// this implementation wraps all of the GATT calls and handles tracking battery information for the 65 /// client. 66 pub trait IBatteryService { 67 /// Registers a callback for interacting with BatteryService. register_callback(&mut self, callback: Box<dyn IBatteryServiceCallback + Send>) -> u3268 fn register_callback(&mut self, callback: Box<dyn IBatteryServiceCallback + Send>) -> u32; 69 70 /// Unregisters a callback. unregister_callback(&mut self, callback_id: u32)71 fn unregister_callback(&mut self, callback_id: u32); 72 73 /// Returns the battery info of the remote device if available in BatteryService's cache. get_battery_info(&self, remote_address: RawAddress) -> Option<BatterySet>74 fn get_battery_info(&self, remote_address: RawAddress) -> Option<BatterySet>; 75 76 /// Forces an explicit read of the device's battery level, including initiating battery level 77 /// tracking if not yet performed. refresh_battery_info(&self, remote_address: RawAddress) -> bool78 fn refresh_battery_info(&self, remote_address: RawAddress) -> bool; 79 } 80 81 /// Callback for interacting with BAS. 82 pub trait IBatteryServiceCallback: RPCProxy { 83 /// Called when the status of BatteryService has changed. Trying to read from devices that do 84 /// not support BAS will result in this method being called with BatteryServiceNotSupported. on_battery_service_status_updated( &mut self, remote_address: RawAddress, status: BatteryServiceStatus, )85 fn on_battery_service_status_updated( 86 &mut self, 87 remote_address: RawAddress, 88 status: BatteryServiceStatus, 89 ); 90 91 /// Invoked when battery level for a device has been changed due to notification. on_battery_info_updated(&mut self, remote_address: RawAddress, battery_info: BatterySet)92 fn on_battery_info_updated(&mut self, remote_address: RawAddress, battery_info: BatterySet); 93 } 94 95 impl BatteryService { 96 /// Construct a new BatteryService with callbacks relaying messages through tx. new( gatt: Arc<Mutex<Box<BluetoothGatt>>>, battery_provider_manager: Arc<Mutex<Box<BatteryProviderManager>>>, tx: Sender<Message>, api_tx: Sender<APIMessage>, ) -> BatteryService97 pub fn new( 98 gatt: Arc<Mutex<Box<BluetoothGatt>>>, 99 battery_provider_manager: Arc<Mutex<Box<BatteryProviderManager>>>, 100 tx: Sender<Message>, 101 api_tx: Sender<APIMessage>, 102 ) -> BatteryService { 103 let tx = tx.clone(); 104 let callbacks = Callbacks::new(tx.clone(), Message::BatteryServiceCallbackDisconnected); 105 let client_id = None; 106 let battery_sets = HashMap::new(); 107 let handles = HashMap::new(); 108 let battery_provider_id = battery_provider_manager 109 .lock() 110 .unwrap() 111 .register_battery_provider(Box::new(BatteryProviderCallback::new(tx.clone()))); 112 Self { 113 gatt, 114 battery_provider_manager, 115 battery_provider_id, 116 tx, 117 api_tx, 118 callbacks, 119 client_id, 120 battery_sets, 121 handles, 122 } 123 } 124 125 /// Must be called after BluetoothGatt's init_profiles method has completed. init(&self)126 pub fn init(&self) { 127 debug!("Registering GATT client for BatteryService"); 128 self.gatt.lock().unwrap().register_client( 129 String::from(BATTERY_SERVICE_GATT_CLIENT_APP_ID), 130 Box::new(GattCallback::new(self.tx.clone(), self.api_tx.clone())), 131 false, 132 ); 133 } 134 135 /// Handles all callback messages in a central location to avoid deadlocks. handle_action(&mut self, action: BatteryServiceActions)136 pub fn handle_action(&mut self, action: BatteryServiceActions) { 137 match action { 138 BatteryServiceActions::OnClientRegistered(_status, client_id) => { 139 debug!("GATT client registered for BAS with id {}", client_id); 140 self.client_id = Some(client_id); 141 } 142 143 BatteryServiceActions::OnClientConnectionState(status, _client_id, connected, addr) => { 144 if !connected || status != GattStatus::Success { 145 info!( 146 "BAS: Dropping {} due to GATT state changed: connected={}, status={:?}", 147 DisplayAddress(&addr), 148 connected, 149 status 150 ); 151 self.drop_device(addr); 152 return; 153 } 154 let client_id = match self.client_id { 155 Some(id) => id, 156 None => { 157 return; 158 } 159 }; 160 self.gatt.lock().unwrap().discover_services(client_id, addr); 161 } 162 163 BatteryServiceActions::OnSearchComplete(addr, services, status) => { 164 if status != GattStatus::Success { 165 info!( 166 "BAS: Service discovery for {} failed: status={:?}", 167 DisplayAddress(&addr), 168 status 169 ); 170 self.drop_device(addr); 171 return; 172 } 173 let handle = match self.get_battery_level_handle(addr, services) { 174 Ok(battery_level_handle) => battery_level_handle, 175 Err(status) => { 176 if let Some(BatteryServiceStatus::BatteryServiceNotSupported) = status { 177 self.callbacks.for_all_callbacks(|callback| { 178 callback.on_battery_service_status_updated( 179 addr, 180 BatteryServiceStatus::BatteryServiceNotSupported, 181 ) 182 }); 183 } 184 info!( 185 "BAS: Failed to get handle from {}: status={:?}", 186 DisplayAddress(&addr), 187 status 188 ); 189 self.drop_device(addr); 190 return; 191 } 192 }; 193 info!("BAS: Found handle from {}", DisplayAddress(&addr)); 194 let client_id = match self.client_id { 195 Some(id) => id, 196 None => { 197 self.drop_device(addr); 198 return; 199 } 200 }; 201 self.handles.insert(addr, handle); 202 self.gatt.lock().unwrap().register_for_notification(client_id, addr, handle, true); 203 if self.battery_sets.get(&addr).is_none() { 204 self.gatt.lock().unwrap().read_characteristic(client_id, addr, handle, 0); 205 } 206 } 207 208 BatteryServiceActions::OnCharacteristicRead(addr, status, handle, value) => { 209 if status != GattStatus::Success { 210 return; 211 } 212 match self.handles.get(&addr) { 213 Some(stored_handle) => { 214 if *stored_handle != handle { 215 return; 216 } 217 } 218 None => { 219 self.drop_device(addr); 220 return; 221 } 222 } 223 let battery_info = self.set_battery_info(&addr, &value); 224 self.callbacks.for_all_callbacks(|callback| { 225 callback.on_battery_info_updated(addr, battery_info.clone()); 226 }); 227 } 228 229 BatteryServiceActions::OnNotify(addr, _handle, value) => { 230 let battery_info = self.set_battery_info(&addr, &value); 231 self.callbacks.for_all_callbacks(|callback| { 232 callback.on_battery_info_updated(addr, battery_info.clone()); 233 }); 234 } 235 } 236 } 237 set_battery_info(&mut self, remote_address: &RawAddress, value: &Vec<u8>) -> BatterySet238 fn set_battery_info(&mut self, remote_address: &RawAddress, value: &Vec<u8>) -> BatterySet { 239 let level: Vec<_> = value.iter().cloned().chain(iter::repeat(0_u8)).take(4).collect(); 240 let level = u32::from_le_bytes(level.try_into().unwrap()); 241 debug!("BAS received battery level for {}: {}", DisplayAddress(remote_address), level); 242 let battery_set = self.battery_sets.entry(*remote_address).or_insert_with(|| { 243 BatterySet::new( 244 *remote_address, 245 uuid::BAS.to_string(), 246 "BAS".to_string(), 247 vec![Battery { percentage: level, variant: "".to_string() }], 248 ) 249 }); 250 self.battery_provider_manager 251 .lock() 252 .unwrap() 253 .set_battery_info(self.battery_provider_id, battery_set.clone()); 254 battery_set.clone() 255 } 256 init_device(&self, remote_address: RawAddress)257 pub(crate) fn init_device(&self, remote_address: RawAddress) { 258 let client_id = match self.client_id { 259 Some(id) => id, 260 None => return, 261 }; 262 self.gatt.lock().unwrap().client_connect( 263 client_id, 264 remote_address, 265 false, 266 BtTransport::Le, 267 false, 268 LePhy::Phy1m, 269 ); 270 } 271 drop_device(&mut self, remote_address: RawAddress)272 pub(crate) fn drop_device(&mut self, remote_address: RawAddress) { 273 if self.handles.contains_key(&remote_address) { 274 // Let BatteryProviderManager know that BAS no longer has a battery for this device. 275 self.battery_provider_manager.lock().unwrap().remove_battery_info( 276 self.battery_provider_id, 277 remote_address, 278 uuid::BAS.to_string(), 279 ); 280 } 281 self.battery_sets.remove(&remote_address); 282 self.handles.remove(&remote_address); 283 match self.client_id { 284 Some(client_id) => { 285 self.gatt.lock().unwrap().client_disconnect(client_id, remote_address); 286 } 287 None => (), 288 } 289 } 290 get_battery_level_handle( &mut self, remote_address: RawAddress, services: Vec<BluetoothGattService>, ) -> Result<i32, Option<BatteryServiceStatus>>291 fn get_battery_level_handle( 292 &mut self, 293 remote_address: RawAddress, 294 services: Vec<BluetoothGattService>, 295 ) -> Result<i32, Option<BatteryServiceStatus>> { 296 let (bas_uuid, battery_level_uuid) = 297 match (Uuid::from_string(uuid::BAS), Uuid::from_string(CHARACTERISTIC_BATTERY_LEVEL)) { 298 (Some(bas_uuid), Some(battery_level_uuid)) => (bas_uuid, battery_level_uuid), 299 _ => { 300 return Err(None); 301 } 302 }; 303 // TODO(b/233101174): handle multiple instances of BAS 304 let bas = match services.iter().find(|service| service.uuid == bas_uuid) { 305 Some(bas) => bas, 306 None => return Err(Some(BatteryServiceStatus::BatteryServiceNotSupported)), 307 }; 308 let battery_level = match bas 309 .characteristics 310 .iter() 311 .find(|characteristic| characteristic.uuid == battery_level_uuid) 312 { 313 Some(battery_level) => battery_level, 314 None => { 315 debug!( 316 "Device {} has no BatteryLevel characteristic", 317 DisplayAddress(&remote_address) 318 ); 319 return Err(None); 320 } 321 }; 322 Ok(battery_level.instance_id) 323 } 324 325 /// Perform an explicit read on all devices BAS knows about. refresh_all_devices(&self)326 pub fn refresh_all_devices(&self) { 327 self.handles.keys().for_each(|&addr| { 328 self.refresh_device(addr); 329 }); 330 } 331 refresh_device(&self, remote_address: RawAddress) -> bool332 fn refresh_device(&self, remote_address: RawAddress) -> bool { 333 let client_id = match self.client_id { 334 Some(id) => id, 335 None => return false, 336 }; 337 let handle = match self.handles.get(&remote_address) { 338 Some(id) => *id, 339 None => return false, 340 }; 341 self.gatt.lock().unwrap().read_characteristic(client_id, remote_address, handle, 0); 342 true 343 } 344 345 /// Remove a callback due to disconnection or unregistration. remove_callback(&mut self, callback_id: u32)346 pub fn remove_callback(&mut self, callback_id: u32) { 347 self.callbacks.remove_callback(callback_id); 348 } 349 } 350 351 /// Status enum for relaying the state of BAS or a particular device. 352 #[derive(Debug)] 353 pub enum BatteryServiceStatus { 354 /// Device does not report support for BAS. 355 BatteryServiceNotSupported, 356 } 357 358 impl IBatteryService for BatteryService { register_callback(&mut self, callback: Box<dyn IBatteryServiceCallback + Send>) -> u32359 fn register_callback(&mut self, callback: Box<dyn IBatteryServiceCallback + Send>) -> u32 { 360 self.callbacks.add_callback(callback) 361 } 362 unregister_callback(&mut self, callback_id: u32)363 fn unregister_callback(&mut self, callback_id: u32) { 364 self.remove_callback(callback_id); 365 } 366 get_battery_info(&self, remote_address: RawAddress) -> Option<BatterySet>367 fn get_battery_info(&self, remote_address: RawAddress) -> Option<BatterySet> { 368 self.battery_sets.get(&remote_address).cloned() 369 } 370 refresh_battery_info(&self, remote_address: RawAddress) -> bool371 fn refresh_battery_info(&self, remote_address: RawAddress) -> bool { 372 self.refresh_device(remote_address) 373 } 374 } 375 376 struct BatteryProviderCallback { 377 tx: Sender<Message>, 378 } 379 380 impl BatteryProviderCallback { new(tx: Sender<Message>) -> Self381 fn new(tx: Sender<Message>) -> Self { 382 Self { tx } 383 } 384 } 385 386 impl IBatteryProviderCallback for BatteryProviderCallback { refresh_battery_info(&mut self)387 fn refresh_battery_info(&mut self) { 388 let tx = self.tx.clone(); 389 tokio::spawn(async move { 390 let _ = tx.send(Message::BatteryServiceRefresh).await; 391 }); 392 } 393 } 394 395 impl RPCProxy for BatteryProviderCallback { get_object_id(&self) -> String396 fn get_object_id(&self) -> String { 397 "BAS BatteryProvider Callback".to_string() 398 } 399 } 400 401 struct GattCallback { 402 tx: Sender<Message>, 403 api_tx: Sender<APIMessage>, 404 } 405 406 impl GattCallback { new(tx: Sender<Message>, api_tx: Sender<APIMessage>) -> Self407 fn new(tx: Sender<Message>, api_tx: Sender<APIMessage>) -> Self { 408 Self { tx, api_tx } 409 } 410 } 411 412 impl IBluetoothGattCallback for GattCallback { 413 // All callback methods relay messages through the stack receiver to allow BAS to operate on 414 // requests serially. This reduces overall complexity including removing the need to share state 415 // data with callbacks. 416 on_client_registered(&mut self, status: GattStatus, client_id: i32)417 fn on_client_registered(&mut self, status: GattStatus, client_id: i32) { 418 let tx = self.tx.clone(); 419 let api_tx = self.api_tx.clone(); 420 tokio::spawn(async move { 421 let _ = tx 422 .send(Message::BatteryService(BatteryServiceActions::OnClientRegistered( 423 status, client_id, 424 ))) 425 .await; 426 let _ = api_tx.send(APIMessage::IsReady(BluetoothAPI::Battery)).await; 427 }); 428 } 429 on_client_connection_state( &mut self, status: GattStatus, client_id: i32, connected: bool, addr: RawAddress, )430 fn on_client_connection_state( 431 &mut self, 432 status: GattStatus, 433 client_id: i32, 434 connected: bool, 435 addr: RawAddress, 436 ) { 437 let tx = self.tx.clone(); 438 tokio::spawn(async move { 439 let _ = tx 440 .send(Message::BatteryService(BatteryServiceActions::OnClientConnectionState( 441 status, client_id, connected, addr, 442 ))) 443 .await; 444 }); 445 } 446 on_search_complete( &mut self, addr: RawAddress, services: Vec<BluetoothGattService>, status: GattStatus, )447 fn on_search_complete( 448 &mut self, 449 addr: RawAddress, 450 services: Vec<BluetoothGattService>, 451 status: GattStatus, 452 ) { 453 let tx = self.tx.clone(); 454 tokio::spawn(async move { 455 let _ = tx 456 .send(Message::BatteryService(BatteryServiceActions::OnSearchComplete( 457 addr, services, status, 458 ))) 459 .await; 460 }); 461 } 462 on_characteristic_read( &mut self, addr: RawAddress, status: GattStatus, handle: i32, value: Vec<u8>, )463 fn on_characteristic_read( 464 &mut self, 465 addr: RawAddress, 466 status: GattStatus, 467 handle: i32, 468 value: Vec<u8>, 469 ) { 470 let tx = self.tx.clone(); 471 tokio::spawn(async move { 472 let _ = tx 473 .send(Message::BatteryService(BatteryServiceActions::OnCharacteristicRead( 474 addr, status, handle, value, 475 ))) 476 .await; 477 }); 478 } 479 on_notify(&mut self, addr: RawAddress, handle: i32, value: Vec<u8>)480 fn on_notify(&mut self, addr: RawAddress, handle: i32, value: Vec<u8>) { 481 let tx = self.tx.clone(); 482 tokio::spawn(async move { 483 let _ = tx 484 .send(Message::BatteryService(BatteryServiceActions::OnNotify(addr, handle, value))) 485 .await; 486 }); 487 } 488 on_phy_update( &mut self, _addr: RawAddress, _tx_phy: LePhy, _rx_phy: LePhy, _status: GattStatus, )489 fn on_phy_update( 490 &mut self, 491 _addr: RawAddress, 492 _tx_phy: LePhy, 493 _rx_phy: LePhy, 494 _status: GattStatus, 495 ) { 496 } 497 on_phy_read( &mut self, _addr: RawAddress, _tx_phy: LePhy, _rx_phy: LePhy, _status: GattStatus, )498 fn on_phy_read( 499 &mut self, 500 _addr: RawAddress, 501 _tx_phy: LePhy, 502 _rx_phy: LePhy, 503 _status: GattStatus, 504 ) { 505 } 506 on_characteristic_write(&mut self, _addr: RawAddress, _status: GattStatus, _handle: i32)507 fn on_characteristic_write(&mut self, _addr: RawAddress, _status: GattStatus, _handle: i32) {} 508 on_execute_write(&mut self, _addr: RawAddress, _status: GattStatus)509 fn on_execute_write(&mut self, _addr: RawAddress, _status: GattStatus) {} 510 on_descriptor_read( &mut self, _addr: RawAddress, _status: GattStatus, _handle: i32, _value: Vec<u8>, )511 fn on_descriptor_read( 512 &mut self, 513 _addr: RawAddress, 514 _status: GattStatus, 515 _handle: i32, 516 _value: Vec<u8>, 517 ) { 518 } 519 on_descriptor_write(&mut self, _addr: RawAddress, _status: GattStatus, _handle: i32)520 fn on_descriptor_write(&mut self, _addr: RawAddress, _status: GattStatus, _handle: i32) {} 521 on_read_remote_rssi(&mut self, _addr: RawAddress, _rssi: i32, _status: GattStatus)522 fn on_read_remote_rssi(&mut self, _addr: RawAddress, _rssi: i32, _status: GattStatus) {} 523 on_configure_mtu(&mut self, _addr: RawAddress, _mtu: i32, _status: GattStatus)524 fn on_configure_mtu(&mut self, _addr: RawAddress, _mtu: i32, _status: GattStatus) {} 525 on_connection_updated( &mut self, _addr: RawAddress, _interval: i32, _latency: i32, _timeout: i32, _status: GattStatus, )526 fn on_connection_updated( 527 &mut self, 528 _addr: RawAddress, 529 _interval: i32, 530 _latency: i32, 531 _timeout: i32, 532 _status: GattStatus, 533 ) { 534 } 535 on_service_changed(&mut self, _addr: RawAddress)536 fn on_service_changed(&mut self, _addr: RawAddress) {} 537 } 538 539 impl RPCProxy for GattCallback { get_object_id(&self) -> String540 fn get_object_id(&self) -> String { 541 "BAS Gatt Callback".to_string() 542 } 543 } 544