use crate::battery_provider_manager::BatteryProviderManager; use crate::callbacks::Callbacks; use crate::uuid; use crate::Message; use crate::RPCProxy; use bt_topshim::btif::RawAddress; use itertools::Itertools; use std::sync::{Arc, Mutex}; use tokio::sync::mpsc::Sender; /// The primary representation of battery information for internal passing and external calls. #[derive(Debug, Clone)] pub struct BatterySet { /// Address of the remote device. pub address: RawAddress, /// UUID of where the battery info is decoded from as found in BT Spec. pub source_uuid: String, /// Information about the battery source, e.g. "BAS" or "HFP 1.8". pub source_info: String, /// Collection of batteries from this source. pub batteries: Vec, } /// Describes an individual battery measurement, possibly one of many for a given device. #[derive(Debug, Clone)] pub struct Battery { /// Battery charge percentage between 0 and 100. For protocols that use 0-5 this will be that /// number multiplied by 20. pub percentage: u32, /// Description of this battery, such as Left, Right, or Case. Only present if the source has /// this level of detail. pub variant: String, } /// Helper representation of a collection of BatterySet to simplify passing around data internally. pub struct Batteries(Vec); /// Callback for interacting with the BatteryManager. pub trait IBatteryManagerCallback: RPCProxy { /// Invoked whenever battery information associated with the given remote changes. fn on_battery_info_updated(&mut self, remote_address: RawAddress, battery_set: BatterySet); } /// Central point for getting battery information that might be sourced from numerous systems. pub trait IBatteryManager { /// Registers a callback for interfacing with the BatteryManager and returns a unique /// callback_id for future calls. fn register_battery_callback( &mut self, battery_manager_callback: Box, ) -> u32; /// Unregister a callback. fn unregister_battery_callback(&mut self, callback_id: u32) -> bool; /// Returns battery information for the remote, sourced from the highest priority origin. fn get_battery_information(&self, remote_address: RawAddress) -> Option; } /// Repesentation of the BatteryManager. pub struct BatteryManager { battery_provider_manager: Arc>>, callbacks: Callbacks, } impl BatteryManager { /// Construct a new BatteryManager with callbacks communicating on tx. pub fn new( battery_provider_manager: Arc>>, tx: Sender, ) -> BatteryManager { let callbacks = Callbacks::new(tx.clone(), Message::BatteryManagerCallbackDisconnected); Self { battery_provider_manager, callbacks } } /// Remove a callback due to disconnection or unregistration. pub fn remove_callback(&mut self, callback_id: u32) -> bool { self.callbacks.remove_callback(callback_id) } /// Handles a BatterySet update. pub fn handle_battery_updated(&mut self, remote_address: RawAddress, battery_set: BatterySet) { self.callbacks.for_all_callbacks(|callback| { callback.on_battery_info_updated(remote_address, battery_set.clone()) }); } } impl IBatteryManager for BatteryManager { fn register_battery_callback( &mut self, battery_manager_callback: Box, ) -> u32 { self.callbacks.add_callback(battery_manager_callback) } fn unregister_battery_callback(&mut self, callback_id: u32) -> bool { self.remove_callback(callback_id) } fn get_battery_information(&self, remote_address: RawAddress) -> Option { self.battery_provider_manager.lock().unwrap().get_battery_info(remote_address) } } impl BatterySet { pub fn new( address: RawAddress, source_uuid: String, source_info: String, batteries: Vec, ) -> Self { Self { address, source_uuid, source_info, batteries } } pub fn add_or_update_battery(&mut self, new_battery: Battery) { match self.batteries.iter_mut().find(|battery| battery.variant == new_battery.variant) { Some(battery) => *battery = new_battery, None => self.batteries.push(new_battery), } } } impl Batteries { pub fn new() -> Self { Self(vec![]) } /// Updates a battery matching all non-battery-level fields if found, otherwise adds new_battery /// verbatim. pub fn add_or_update_battery_set(&mut self, new_battery_set: BatterySet) { match self .0 .iter_mut() .find(|battery_set| battery_set.source_uuid == new_battery_set.source_uuid) { Some(battery_set) => *battery_set = new_battery_set, None => self.0.push(new_battery_set), } } pub fn remove_battery_set(&mut self, uuid: &String) { self.0.retain(|battery_set| &battery_set.source_uuid != uuid); } pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Returns the best BatterySet from among reported battery data. pub fn pick_best(&self) -> Option { self.0 .iter() .filter(|battery_set| !battery_set.batteries.is_empty()) // Now we prefer BAS, but we might need to prioritize other sources first // TODO (b/295577710): Make a preference list .find_or_first(|battery_set| battery_set.source_uuid == uuid::BAS) .or_else(|| self.0.first()) .cloned() } }