• 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::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