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