• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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