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