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