1 use crate::battery_manager::{Battery, BatterySet}; 2 use crate::battery_provider_manager::{ 3 BatteryProviderManager, IBatteryProviderCallback, IBatteryProviderManager, 4 }; 5 use crate::bluetooth::BluetoothDevice; 6 use crate::bluetooth_gatt::{ 7 BluetoothGatt, BluetoothGattService, IBluetoothGatt, IBluetoothGattCallback, 8 }; 9 use crate::callbacks::Callbacks; 10 use crate::uuid; 11 use crate::uuid::UuidHelper; 12 use crate::Message; 13 use crate::RPCProxy; 14 use bt_topshim::btif::BtTransport; 15 use bt_topshim::profiles::gatt::{GattStatus, LePhy}; 16 use log::debug; 17 use std::collections::HashMap; 18 use std::convert::TryInto; 19 use std::iter; 20 use std::sync::{Arc, Mutex}; 21 use tokio::sync::mpsc::Sender; 22 23 /// The UUID corresponding to the BatteryLevel characteristic defined by the BatteryService 24 /// specification. 25 pub const CHARACTERISTIC_BATTERY_LEVEL: &str = "00002A1-9000-0100-0800-000805F9B34FB"; 26 27 /// Represents the Floss BatteryService implementation. 28 pub struct BatteryService { 29 gatt: Arc<Mutex<Box<BluetoothGatt>>>, 30 battery_provider_manager: Arc<Mutex<Box<BatteryProviderManager>>>, 31 battery_provider_id: u32, 32 /// Sender for callback communication with the main thread. 33 tx: Sender<Message>, 34 callbacks: Callbacks<dyn IBatteryServiceCallback + Send>, 35 /// The GATT client ID needed for GATT calls. 36 client_id: Option<i32>, 37 /// Cached battery info keyed by remote device. 38 battery_sets: HashMap<String, BatterySet>, 39 /// Found handles for battery levels. Required for faster 40 /// refreshes than initiating another search. 41 handles: HashMap<String, i32>, 42 } 43 44 /// Enum for GATT callbacks to relay messages to the main processing thread. Newly supported 45 /// callbacks should add a corresponding entry here. 46 pub enum BatteryServiceActions { 47 /// Params: status, client_id 48 OnClientRegistered(GattStatus, i32), 49 /// Params: status, client_id, connected, addr 50 OnClientConnectionState(GattStatus, i32, bool, String), 51 /// Params: addr, services, status 52 OnSearchComplete(String, Vec<BluetoothGattService>, GattStatus), 53 /// Params: addr, status, handle, value 54 OnCharacteristicRead(String, GattStatus, i32, Vec<u8>), 55 /// Params: addr, handle, value 56 OnNotify(String, i32, Vec<u8>), 57 /// Params: remote_device, transport 58 Connect(BluetoothDevice, BtTransport), 59 /// Params: remote_device 60 Disconnect(BluetoothDevice), 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: String) -> Option<BatterySet>74 fn get_battery_info(&self, remote_address: String) -> 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: String) -> bool78 fn refresh_battery_info(&self, remote_address: String) -> 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: String, status: BatteryServiceStatus, )85 fn on_battery_service_status_updated( 86 &mut self, 87 remote_address: String, 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: String, battery_info: BatterySet)92 fn on_battery_info_updated(&mut self, remote_address: String, 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>, ) -> BatteryService97 pub fn new( 98 gatt: Arc<Mutex<Box<BluetoothGatt>>>, 99 battery_provider_manager: Arc<Mutex<Box<BatteryProviderManager>>>, 100 tx: Sender<Message>, 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 callbacks, 117 client_id, 118 battery_sets, 119 handles, 120 } 121 } 122 123 /// Must be called after BluetoothGatt's init_profiles method has completed. init(&self)124 pub fn init(&self) { 125 debug!("Registering GATT client for BatteryService"); 126 self.gatt.lock().unwrap().register_client( 127 // TODO(b/233101174): make dynamic or decide on a static UUID 128 String::from("e4d2acffcfaa42198f494606b7412117"), 129 Box::new(GattCallback::new(self.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 return; 145 } 146 let client_id = match self.client_id { 147 Some(id) => id, 148 None => { 149 return; 150 } 151 }; 152 self.gatt.lock().unwrap().discover_services(client_id, addr); 153 } 154 155 BatteryServiceActions::OnSearchComplete(addr, services, status) => { 156 if status != GattStatus::Success { 157 debug!("GATT service discovery for {} failed with status {:?}", addr, status); 158 return; 159 } 160 let (bas_uuid, battery_level_uuid) = match ( 161 UuidHelper::parse_string(uuid::BAS), 162 UuidHelper::parse_string(CHARACTERISTIC_BATTERY_LEVEL), 163 ) { 164 (Some(bas_uuid), Some(battery_level_uuid)) => (bas_uuid, battery_level_uuid), 165 _ => return, 166 }; 167 // TODO(b/233101174): handle multiple instances of BAS 168 let bas = match services.iter().find(|service| service.uuid == bas_uuid.uu) { 169 Some(bas) => bas, 170 None => { 171 self.callbacks.for_all_callbacks(|callback| { 172 callback.on_battery_service_status_updated( 173 addr.clone(), 174 BatteryServiceStatus::BatteryServiceNotSupported, 175 ) 176 }); 177 return; 178 } 179 }; 180 let battery_level = match bas 181 .characteristics 182 .iter() 183 .find(|characteristic| characteristic.uuid == battery_level_uuid.uu) 184 { 185 Some(battery_level) => battery_level, 186 None => { 187 debug!("Device {} has no BatteryLevel characteristic", addr); 188 return; 189 } 190 }; 191 let client_id = match self.client_id { 192 Some(id) => id, 193 None => return, 194 }; 195 let handle = battery_level.instance_id; 196 self.handles.insert(addr.clone(), handle.clone()); 197 self.gatt.lock().unwrap().register_for_notification( 198 client_id, 199 addr.clone(), 200 handle, 201 true, 202 ); 203 if let None = self.battery_sets.get(&addr) { 204 self.gatt.lock().unwrap().read_characteristic( 205 client_id, 206 addr, 207 battery_level.instance_id, 208 0, 209 ); 210 } 211 } 212 213 BatteryServiceActions::OnCharacteristicRead(addr, status, _handle, value) => { 214 if status != GattStatus::Success { 215 return; 216 } 217 let battery_info = self.set_battery_info(&addr, &value); 218 self.callbacks.for_all_callbacks(|callback| { 219 callback.on_battery_info_updated(addr.clone(), battery_info.clone()); 220 }); 221 } 222 223 BatteryServiceActions::OnNotify(addr, _handle, value) => { 224 let battery_info = self.set_battery_info(&addr, &value); 225 self.callbacks.for_all_callbacks(|callback| { 226 callback.on_battery_info_updated(addr.clone(), battery_info.clone()); 227 }); 228 } 229 230 BatteryServiceActions::Connect(device, transport) => { 231 if transport != BtTransport::Le { 232 return; 233 } 234 self.init_device(device.address, transport); 235 } 236 237 BatteryServiceActions::Disconnect(device) => { 238 self.drop_device(device.address); 239 } 240 } 241 } 242 set_battery_info(&mut self, remote_address: &String, value: &Vec<u8>) -> BatterySet243 fn set_battery_info(&mut self, remote_address: &String, value: &Vec<u8>) -> BatterySet { 244 let level: Vec<_> = value.iter().cloned().chain(iter::repeat(0 as u8)).take(4).collect(); 245 let level = u32::from_le_bytes(level.try_into().unwrap()); 246 debug!("Received battery level for {}: {}", remote_address.clone(), level); 247 let battery_set = self.battery_sets.entry(remote_address.clone()).or_insert_with(|| { 248 BatterySet::new( 249 remote_address.clone(), 250 uuid::BAS.to_string(), 251 "BAS".to_string(), 252 vec![Battery { percentage: level, variant: "".to_string() }], 253 ) 254 }); 255 self.battery_provider_manager 256 .lock() 257 .unwrap() 258 .set_battery_info(self.battery_provider_id, battery_set.clone()); 259 battery_set.clone() 260 } 261 init_device(&self, remote_address: String, transport: BtTransport)262 fn init_device(&self, remote_address: String, transport: BtTransport) { 263 let client_id = match self.client_id { 264 Some(id) => id, 265 None => return, 266 }; 267 debug!("Attempting GATT connection to {}", remote_address.clone()); 268 self.gatt.lock().unwrap().client_connect( 269 client_id, 270 remote_address, 271 false, 272 transport, 273 false, 274 LePhy::Phy1m, 275 ); 276 } 277 drop_device(&mut self, remote_address: String)278 fn drop_device(&mut self, remote_address: String) { 279 self.handles.remove(&remote_address); 280 match self.client_id { 281 Some(client_id) => { 282 self.gatt.lock().unwrap().client_disconnect(client_id, remote_address.clone()) 283 } 284 None => return, 285 } 286 // Let BatteryProviderManager know that BAS no longer has a battery for this device. 287 self.battery_provider_manager.lock().unwrap().set_battery_info( 288 self.battery_provider_id, 289 BatterySet::new( 290 remote_address.clone(), 291 uuid::BAS.to_string(), 292 "BAS".to_string(), 293 vec![], 294 ), 295 ); 296 self.battery_sets.remove(&remote_address); 297 } 298 299 /// Perform an explicit read on all devices BAS knows about. refresh_all_devices(&self)300 pub fn refresh_all_devices(&self) { 301 self.handles.keys().for_each(|device| { 302 self.refresh_device(device.to_string()); 303 }); 304 } 305 refresh_device(&self, remote_address: String) -> bool306 fn refresh_device(&self, remote_address: String) -> bool { 307 let client_id = match self.client_id { 308 Some(id) => id, 309 None => return false, 310 }; 311 let handle = match self.handles.get(&remote_address) { 312 Some(id) => *id, 313 None => return false, 314 }; 315 self.gatt.lock().unwrap().read_characteristic(client_id, remote_address.clone(), handle, 0); 316 true 317 } 318 319 /// Remove a callback due to disconnection or unregistration. remove_callback(&mut self, callback_id: u32)320 pub fn remove_callback(&mut self, callback_id: u32) { 321 self.callbacks.remove_callback(callback_id); 322 } 323 } 324 325 /// Status enum for relaying the state of BAS or a particular device. 326 pub enum BatteryServiceStatus { 327 /// Device does not report support for BAS. 328 BatteryServiceNotSupported, 329 } 330 331 impl IBatteryService for BatteryService { register_callback(&mut self, callback: Box<dyn IBatteryServiceCallback + Send>) -> u32332 fn register_callback(&mut self, callback: Box<dyn IBatteryServiceCallback + Send>) -> u32 { 333 self.callbacks.add_callback(callback) 334 } 335 unregister_callback(&mut self, callback_id: u32)336 fn unregister_callback(&mut self, callback_id: u32) { 337 self.remove_callback(callback_id); 338 } 339 get_battery_info(&self, remote_address: String) -> Option<BatterySet>340 fn get_battery_info(&self, remote_address: String) -> Option<BatterySet> { 341 self.battery_sets.get(&remote_address).cloned() 342 } 343 refresh_battery_info(&self, remote_address: String) -> bool344 fn refresh_battery_info(&self, remote_address: String) -> bool { 345 self.refresh_device(remote_address) 346 } 347 } 348 349 struct BatteryProviderCallback { 350 tx: Sender<Message>, 351 } 352 353 impl BatteryProviderCallback { new(tx: Sender<Message>) -> Self354 fn new(tx: Sender<Message>) -> Self { 355 Self { tx } 356 } 357 } 358 359 impl IBatteryProviderCallback for BatteryProviderCallback { refresh_battery_info(&mut self)360 fn refresh_battery_info(&mut self) { 361 let tx = self.tx.clone(); 362 tokio::spawn(async move { 363 let _ = tx.send(Message::BatteryServiceRefresh).await; 364 }); 365 } 366 } 367 368 impl RPCProxy for BatteryProviderCallback { get_object_id(&self) -> String369 fn get_object_id(&self) -> String { 370 "BAS BatteryProvider Callback".to_string() 371 } 372 } 373 374 struct GattCallback { 375 tx: Sender<Message>, 376 } 377 378 impl GattCallback { new(tx: Sender<Message>) -> Self379 fn new(tx: Sender<Message>) -> Self { 380 Self { tx } 381 } 382 } 383 384 impl IBluetoothGattCallback for GattCallback { 385 // All callback methods relay messages through the stack receiver to allow BAS to operate on 386 // requests serially. This reduces overall complexity including removing the need to share state 387 // data with callbacks. 388 on_client_registered(&mut self, status: GattStatus, client_id: i32)389 fn on_client_registered(&mut self, status: GattStatus, client_id: i32) { 390 let tx = self.tx.clone(); 391 tokio::spawn(async move { 392 let _ = tx 393 .send(Message::BatteryService(BatteryServiceActions::OnClientRegistered( 394 status, client_id, 395 ))) 396 .await; 397 }); 398 } 399 on_client_connection_state( &mut self, status: GattStatus, client_id: i32, connected: bool, addr: String, )400 fn on_client_connection_state( 401 &mut self, 402 status: GattStatus, 403 client_id: i32, 404 connected: bool, 405 addr: String, 406 ) { 407 let tx = self.tx.clone(); 408 tokio::spawn(async move { 409 let _ = tx 410 .send(Message::BatteryService(BatteryServiceActions::OnClientConnectionState( 411 status, client_id, connected, addr, 412 ))) 413 .await; 414 }); 415 } 416 on_search_complete( &mut self, addr: String, services: Vec<BluetoothGattService>, status: GattStatus, )417 fn on_search_complete( 418 &mut self, 419 addr: String, 420 services: Vec<BluetoothGattService>, 421 status: GattStatus, 422 ) { 423 let tx = self.tx.clone(); 424 tokio::spawn(async move { 425 let _ = tx 426 .send(Message::BatteryService(BatteryServiceActions::OnSearchComplete( 427 addr, services, status, 428 ))) 429 .await; 430 }); 431 } 432 on_characteristic_read( &mut self, addr: String, status: GattStatus, handle: i32, value: Vec<u8>, )433 fn on_characteristic_read( 434 &mut self, 435 addr: String, 436 status: GattStatus, 437 handle: i32, 438 value: Vec<u8>, 439 ) { 440 let tx = self.tx.clone(); 441 tokio::spawn(async move { 442 let _ = tx 443 .send(Message::BatteryService(BatteryServiceActions::OnCharacteristicRead( 444 addr, status, handle, value, 445 ))) 446 .await; 447 }); 448 } 449 on_notify(&mut self, addr: String, handle: i32, value: Vec<u8>)450 fn on_notify(&mut self, addr: String, handle: i32, value: Vec<u8>) { 451 let tx = self.tx.clone(); 452 tokio::spawn(async move { 453 let _ = tx 454 .send(Message::BatteryService(BatteryServiceActions::OnNotify(addr, handle, value))) 455 .await; 456 }); 457 } 458 on_phy_update( &mut self, _addr: String, _tx_phy: LePhy, _rx_phy: LePhy, _status: GattStatus, )459 fn on_phy_update( 460 &mut self, 461 _addr: String, 462 _tx_phy: LePhy, 463 _rx_phy: LePhy, 464 _status: GattStatus, 465 ) { 466 } 467 on_phy_read(&mut self, _addr: String, _tx_phy: LePhy, _rx_phy: LePhy, _status: GattStatus)468 fn on_phy_read(&mut self, _addr: String, _tx_phy: LePhy, _rx_phy: LePhy, _status: GattStatus) {} 469 on_characteristic_write(&mut self, _addr: String, _status: GattStatus, _handle: i32)470 fn on_characteristic_write(&mut self, _addr: String, _status: GattStatus, _handle: i32) {} 471 on_execute_write(&mut self, _addr: String, _status: GattStatus)472 fn on_execute_write(&mut self, _addr: String, _status: GattStatus) {} 473 on_descriptor_read( &mut self, _addr: String, _status: GattStatus, _handle: i32, _value: Vec<u8>, )474 fn on_descriptor_read( 475 &mut self, 476 _addr: String, 477 _status: GattStatus, 478 _handle: i32, 479 _value: Vec<u8>, 480 ) { 481 } 482 on_descriptor_write(&mut self, _addr: String, _status: GattStatus, _handle: i32)483 fn on_descriptor_write(&mut self, _addr: String, _status: GattStatus, _handle: i32) {} 484 on_read_remote_rssi(&mut self, _addr: String, _rssi: i32, _status: GattStatus)485 fn on_read_remote_rssi(&mut self, _addr: String, _rssi: i32, _status: GattStatus) {} 486 on_configure_mtu(&mut self, _addr: String, _mtu: i32, _status: GattStatus)487 fn on_configure_mtu(&mut self, _addr: String, _mtu: i32, _status: GattStatus) {} 488 on_connection_updated( &mut self, _addr: String, _interval: i32, _latency: i32, _timeout: i32, _status: GattStatus, )489 fn on_connection_updated( 490 &mut self, 491 _addr: String, 492 _interval: i32, 493 _latency: i32, 494 _timeout: i32, 495 _status: GattStatus, 496 ) { 497 } 498 on_service_changed(&mut self, _addr: String)499 fn on_service_changed(&mut self, _addr: String) {} 500 } 501 502 impl RPCProxy for GattCallback { get_object_id(&self) -> String503 fn get_object_id(&self) -> String { 504 "BAS Gatt Callback".to_string() 505 } 506 } 507