Loading system/gd/rust/linux/client/src/command_handler.rs +5 −5 Original line number Diff line number Diff line Loading @@ -707,7 +707,7 @@ impl CommandHandler { ); } "client-connect" => { if args.len() < 3 { if args.len() < 2 { println!("usage: gatt client-connect <addr>"); return; } Loading @@ -729,7 +729,7 @@ impl CommandHandler { ); } "client-disconnect" => { if args.len() < 3 { if args.len() < 2 { println!("usage: gatt client-disconnect <addr>"); return; } Loading @@ -750,7 +750,7 @@ impl CommandHandler { .client_disconnect(client_id.unwrap(), addr); } "client-read-phy" => { if args.len() < 3 { if args.len() < 2 { println!("usage: gatt client-read-phy <addr>"); return; } Loading @@ -771,7 +771,7 @@ impl CommandHandler { .client_read_phy(client_id.unwrap(), addr); } "client-discover-services" => { if args.len() < 3 { if args.len() < 2 { println!("usage: gatt client-discover-services <addr>"); return; } Loading @@ -792,7 +792,7 @@ impl CommandHandler { .discover_services(client_id.unwrap(), addr); } "configure-mtu" => { if args.len() < 4 { if args.len() < 3 { println!("usage: gatt configure-mtu <addr> <mtu>"); return; } Loading system/gd/rust/linux/service/src/iface_battery_manager.rs +13 −9 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ use crate::dbus_arg::{DBusArg, DBusArgError, RefArgToRust}; #[dbus_propmap(Battery)] pub struct BatteryDBus { percentage: i32, percentage: u32, source_info: String, variant: String, } Loading @@ -28,22 +28,26 @@ struct IBatteryManagerDBus {} #[generate_dbus_exporter(export_battery_manager_dbus_intf, "org.chromium.bluetooth.BatteryManager")] impl IBatteryManager for IBatteryManagerDBus { #[dbus_method("GetBatteryInformation")] fn get_battery_information(&self, remote_address: String) -> Battery { dbus_generated!() } #[dbus_method("RegisterBatteryCallback")] fn register_battery_callback( &mut self, remote_address: String, battery_manager_callback: Box<dyn IBatteryManagerCallback + Send>, ) -> i32 { ) -> u32 { dbus_generated!() } #[dbus_method("UnregisterBatteryCallback")] fn unregister_battery_callback(&mut self, callback_id: i32) { fn unregister_battery_callback(&mut self, callback_id: u32) { dbus_generated!() } #[dbus_method("EnableNotifications")] fn enable_notifications(&mut self, callback_id: u32, enable: bool) { dbus_generated!() } #[dbus_method("GetBatteryInformation")] fn get_battery_information(&self, remote_address: String) -> Option<Battery> { dbus_generated!() } } system/gd/rust/linux/service/src/main.rs +18 −2 Original line number Diff line number Diff line Loading @@ -11,12 +11,15 @@ use log::LevelFilter; use nix::sys::signal; use std::error::Error; use std::sync::{Arc, Condvar, Mutex}; use std::time::Duration; use syslog::{BasicLogger, Facility, Formatter3164}; use tokio::time; use bt_topshim::{btif::get_btinterface, topstack}; use btstack::{ battery_manager::BatteryManager, battery_provider_manager::BatteryProviderManager, battery_service::BatteryService, bluetooth::{get_bt_dispatcher, Bluetooth, IBluetooth}, bluetooth_gatt::BluetoothGatt, bluetooth_media::BluetoothMedia, Loading Loading @@ -112,7 +115,10 @@ fn main() -> Result<(), Box<dyn Error>> { let bluetooth_media = Arc::new(Mutex::new(Box::new(BluetoothMedia::new(tx.clone(), intf.clone())))); let battery_provider_manager = Arc::new(Mutex::new(Box::new(BatteryProviderManager::new()))); let battery_manager = Arc::new(Mutex::new(Box::new(BatteryManager::new()))); let battery_service = Arc::new(Mutex::new(Box::new(BatteryService::new(bluetooth_gatt.clone(), tx.clone())))); let battery_manager = Arc::new(Mutex::new(Box::new(BatteryManager::new(battery_service.clone(), tx.clone())))); let bluetooth = Arc::new(Mutex::new(Box::new(Bluetooth::new( tx.clone(), intf.clone(), Loading Loading @@ -162,6 +168,8 @@ fn main() -> Result<(), Box<dyn Error>> { rx, bluetooth.clone(), bluetooth_gatt.clone(), battery_service.clone(), battery_manager.clone(), bluetooth_media.clone(), suspend.clone(), bt_sock_mgr.clone(), Loading Loading @@ -261,7 +269,7 @@ fn main() -> Result<(), Box<dyn Error>> { battery_provider_manager.clone(), ); cr.lock().unwrap().insert( make_object_name(adapter_index, "battery__manager"), make_object_name(adapter_index, "battery_manager"), &[battery_manager_iface], battery_manager.clone(), ); Loading @@ -279,6 +287,14 @@ fn main() -> Result<(), Box<dyn Error>> { bluetooth.enable(); bluetooth_gatt.lock().unwrap().init_profiles(tx.clone(), adapter.clone()); // TODO(b/247093293): Gatt topshim api is only usable some // time after init. Investigate why this delay is needed // and make it a blocking part of init before removing // this. tokio::spawn(async move { time::sleep(Duration::from_millis(500)).await; battery_service.lock().unwrap().init(); }); bt_sock_mgr.lock().unwrap().initialize(intf.clone()); // Install SIGTERM handler so that we can properly shutdown Loading system/gd/rust/linux/stack/src/battery_manager.rs +102 −21 Original line number Diff line number Diff line use crate::battery_service::{ BatteryService, BatteryServiceStatus, IBatteryService, IBatteryServiceCallback, }; use crate::callbacks::Callbacks; use crate::Message; use crate::RPCProxy; use std::collections::HashSet; 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 Battery { pub percentage: i32, pub percentage: u32, pub source_info: String, pub variant: String, } pub struct BatteryManager {} impl BatteryManager { pub fn new() -> BatteryManager { BatteryManager {} } } /// Callback for interacting with the BatteryManager. pub trait IBatteryManagerCallback { pub trait IBatteryManagerCallback: RPCProxy { /// Invoked whenever battery information associated with the given remote changes. fn on_battery_info_updated(&self, remote_address: String, battery: Battery); } Loading @@ -25,31 +29,108 @@ pub trait IBatteryManager { /// callback_id for future calls. fn register_battery_callback( &mut self, remote_address: String, battery_manager_callback: Box<dyn IBatteryManagerCallback + Send>, ) -> i32; ) -> u32; /// Unregister a callback. fn unregister_battery_callback(&mut self, callback_id: i32); fn unregister_battery_callback(&mut self, callback_id: u32); /// Enables notifications for a given callback. fn enable_notifications(&mut self, callback_id: u32, enable: bool); /// Returns battery information for the remote, sourced from the highest priority origin. fn get_battery_information(&self, remote_address: String) -> Battery; fn get_battery_information(&self, remote_address: String) -> Option<Battery>; } impl IBatteryManager for BatteryManager { fn register_battery_callback( &mut self, /// Repesentation of the BatteryManager. pub struct BatteryManager { bas: Arc<Mutex<Box<BatteryService>>>, callbacks: Callbacks<dyn IBatteryManagerCallback + Send>, /// List of callback IDs that have enabled notifications. notifications_enabled: HashSet<u32>, } impl BatteryManager { /// Construct a new BatteryManager with callbacks communicating on tx. pub fn new(bas: Arc<Mutex<Box<BatteryService>>>, tx: Sender<Message>) -> BatteryManager { let callbacks = Callbacks::new(tx.clone(), Message::BatteryManagerCallbackDisconnected); let notifications_enabled = HashSet::new(); Self { bas, callbacks, notifications_enabled } } /// Invoked after BAS has been initialized. pub fn init(&self) { self.bas.lock().unwrap().register_callback(Box::new(BasCallback::new())); } /// Remove a callback due to disconnection or unregistration. pub fn remove_callback(&mut self, callback_id: u32) { self.callbacks.remove_callback(callback_id); } } struct BasCallback {} impl BasCallback { pub fn new() -> BasCallback { Self {} } } impl IBatteryServiceCallback for BasCallback { fn on_battery_service_status_updated( &self, _remote_address: String, _battery_manager_callback: Box<dyn IBatteryManagerCallback + Send>, ) -> i32 { _status: BatteryServiceStatus, ) { todo!() } fn unregister_battery_callback(&mut self, _callback_id: i32) { fn on_battery_level_updated(&self, _remote_address: String, _battery_level: u32) { todo!() } fn get_battery_information(&self, _remote_address: String) -> Battery { fn on_battery_level_read(&self, _remote_address: String, _battery_level: u32) { todo!() } } impl RPCProxy for BasCallback { fn get_object_id(&self) -> String { "BAS Callback".to_string() } } impl IBatteryManager for BatteryManager { fn register_battery_callback( &mut self, battery_manager_callback: Box<dyn IBatteryManagerCallback + Send>, ) -> u32 { self.callbacks.add_callback(battery_manager_callback) } fn unregister_battery_callback(&mut self, callback_id: u32) { self.remove_callback(callback_id); } fn enable_notifications(&mut self, callback_id: u32, enable: bool) { if self.callbacks.get_by_id(callback_id).is_none() { return; } self.notifications_enabled.remove(&callback_id); if enable { self.notifications_enabled.insert(callback_id); } } // TODO(b/233101174): update to use all available sources once // BatteryProviderManager is implemented. fn get_battery_information(&self, remote_address: String) -> Option<Battery> { let battery_level = self.bas.lock().unwrap().get_battery_level(remote_address)?; Some(Battery { percentage: battery_level, source_info: "BAS".to_string(), variant: "".to_string(), }) } } system/gd/rust/linux/stack/src/battery_service.rs 0 → 100644 +397 −0 Original line number Diff line number Diff line use crate::bluetooth_gatt::{ BluetoothGatt, BluetoothGattService, IBluetoothGatt, IBluetoothGattCallback, }; use crate::callbacks::Callbacks; use crate::uuid; use crate::uuid::parse_uuid_string; use crate::Message; use crate::RPCProxy; use bt_topshim::btif::BtTransport; use bt_topshim::profiles::gatt::{GattStatus, LePhy}; use log::debug; use std::collections::{HashMap, HashSet}; use std::convert::TryInto; use std::iter; use std::sync::{Arc, Mutex}; use tokio::sync::mpsc::Sender; /// The UUID corresponding to the BatteryLevel characteristic defined /// by the BatteryService specification. pub const CHARACTERISTIC_BATTERY_LEVEL: &str = "00002A1900001000800000805F9B34FB"; /// Represents the Floss BatteryService implementation. pub struct BatteryService { gatt: Arc<Mutex<Box<BluetoothGatt>>>, /// Sender for callback communication with the main thread. tx: Sender<Message>, callbacks: Callbacks<dyn IBatteryServiceCallback + Send>, /// The GATT client ID needed for GATT calls. client_id: Option<i32>, /// Cached battery levels keyed by remote device. battery_levels: HashMap<String, u32>, /// Callback IDs that have enabled notifications. notifications_enabled: HashSet<u32>, /// Found handles for battery levels. Required for faster /// refreshes than initiating another search. handles: HashMap<String, i32>, } /// Enum for GATT callbacks to relay messages to the main processing /// thread. Newly supported callbacks should add a corresponding entry /// here. pub enum GattBatteryCallbacks { /// Params: status, client_id OnClientRegistered(GattStatus, i32), /// Params: status, client_id, connected, addr OnClientConnectionState(GattStatus, i32, bool, String), /// Params: addr, services, status OnSearchComplete(String, Vec<BluetoothGattService>, GattStatus), /// Params: addr, status, handle, value OnCharacteristicRead(String, GattStatus, i32, Vec<u8>), /// Params: addr, handle, value OnNotify(String, i32, Vec<u8>), } /// API for Floss implementation of the Bluetooth Battery Service /// (BAS). BAS is built on GATT and this implementation wraps all of /// the GATT calls and handles tracking battery information for the /// client. pub trait IBatteryService { /// Registers a callback for interacting with BatteryService. fn register_callback(&mut self, callback: Box<dyn IBatteryServiceCallback + Send>) -> u32; /// Unregisters a callback. fn unregister_callback(&mut self, callback_id: u32); /// Enables notifications for a given callback. fn enable_notifications(&mut self, callback_id: u32, enable: bool); /// Returns the battery level of the remove device if available in /// BatteryService's cache. Call refresh_battery_level at least /// once to ensure that BatteryService is tracking the device's /// battery information. fn get_battery_level(&self, remote_address: String) -> Option<u32>; /// Forces an explicit read of the device's battery level, /// including initiating battery level tracking if not yet /// performed. fn refresh_battery_level(&self, remote_address: String) -> bool; } /// Callback for interacting with BAS. pub trait IBatteryServiceCallback: RPCProxy { /// Called when the status of BatteryService has changed. Trying /// to read from devices that do not support BAS will result in /// this method being called with BatteryServiceNotSupported. fn on_battery_service_status_updated( &self, remote_address: String, status: BatteryServiceStatus, ); /// Invoked when battery level for a device has been changed due to notification. fn on_battery_level_updated(&self, remote_address: String, battery_level: u32); /// Invoked whenever an explicit read of a devices battery level completes. fn on_battery_level_read(&self, remote_address: String, battery_level: u32); } impl BatteryService { /// Construct a new BatteryService with callbacks relaying messages through tx. pub fn new(gatt: Arc<Mutex<Box<BluetoothGatt>>>, tx: Sender<Message>) -> BatteryService { let tx = tx.clone(); let callbacks = Callbacks::new(tx.clone(), Message::BatteryServiceCallbackDisconnected); let client_id = None; let battery_levels = HashMap::new(); let notifications_enabled = HashSet::new(); let handles = HashMap::new(); Self { gatt, tx, callbacks, client_id, battery_levels, notifications_enabled, handles } } /// Must be called after BluetoothGatt's init_profiles method has completed. pub fn init(&self) { self.gatt.lock().unwrap().register_client( // TODO(b/233101174): make dynamic or decide on a static UUID String::from("e4d2acffcfaa42198f494606b7412117"), Box::new(GattCallback::new(self.tx.clone())), false, ); } /// Handles all callback messages in a central location to avoid deadlocks. pub fn handle_callback(&mut self, callback: GattBatteryCallbacks) { match callback { GattBatteryCallbacks::OnClientRegistered(_status, client_id) => { self.client_id = Some(client_id); } GattBatteryCallbacks::OnClientConnectionState(_status, _client_id, connected, addr) => { if !connected { return; } let client_id = match self.client_id { Some(id) => id, None => { return; } }; self.gatt.lock().unwrap().discover_services(client_id, addr); } GattBatteryCallbacks::OnSearchComplete(addr, services, status) => { if status != GattStatus::Success { debug!("GATT service discovery for {} failed with status {:?}", addr, status); return; } let (bas_uuid, battery_level_uuid) = match ( parse_uuid_string(uuid::BAS), parse_uuid_string(CHARACTERISTIC_BATTERY_LEVEL), ) { (Some(bas_uuid), Some(battery_level_uuid)) => (bas_uuid, battery_level_uuid), _ => return, }; // TODO(b/233101174): handle multiple instances of BAS let bas = match services.iter().find(|service| service.uuid == bas_uuid.uu) { Some(bas) => bas, None => { self.callbacks.for_all_callbacks(|callback| { callback.on_battery_service_status_updated( addr.clone(), BatteryServiceStatus::BatteryServiceNotSupported, ) }); return; } }; let battery_level = match bas .characteristics .iter() .find(|characteristic| characteristic.uuid == battery_level_uuid.uu) { Some(battery_level) => battery_level, None => { debug!("Device {} has no BatteryLevel characteristic", addr); return; } }; let client_id = match self.client_id { Some(id) => id, None => return, }; let handle = battery_level.instance_id; self.handles.insert(addr.clone(), handle.clone()); self.gatt.lock().unwrap().register_for_notification( client_id, addr.clone(), handle, true, ); if let None = self.battery_levels.get(&addr) { self.gatt.lock().unwrap().read_characteristic( client_id, addr, battery_level.instance_id, 0, ); } } GattBatteryCallbacks::OnCharacteristicRead(addr, status, _handle, value) => { if status != GattStatus::Success { return; } let level = self.set_battery_level(addr.clone(), value.clone()); self.callbacks.for_all_callbacks(|callback| { callback.on_battery_level_read(addr.clone(), level); }); } GattBatteryCallbacks::OnNotify(addr, _handle, value) => { let level = self.set_battery_level(addr.clone(), value); // TODO(b/247551256): expand Callbacks to allow direct // filtering/exposing the underlying iter let to_notify = self.notifications_enabled.clone(); to_notify.iter().for_each(|id| match self.callbacks.get_by_id(*id) { Some(callback) => callback.on_battery_level_updated(addr.clone(), level), None => (), }); } } } fn set_battery_level(&mut self, remote_address: String, value: Vec<u8>) -> u32 { let level: Vec<_> = value.iter().cloned().chain(iter::repeat(0 as u8)).take(4).collect(); let level = u32::from_le_bytes(level.try_into().unwrap()); self.battery_levels.insert(remote_address, level); level } fn init_device(&self, remote_address: String) { let client_id = match self.client_id { Some(id) => id, None => return, }; self.gatt.lock().unwrap().client_connect( client_id, remote_address, false, BtTransport::Le, false, LePhy::Phy1m, ); } /// Remove a callback due to disconnection or unregistration. pub fn remove_callback(&mut self, callback_id: u32) { self.callbacks.remove_callback(callback_id); } } /// Status enum for relaying the state of BAS or a particular device. pub enum BatteryServiceStatus { /// Device does not report support for BAS. BatteryServiceNotSupported, } impl IBatteryService for BatteryService { fn register_callback(&mut self, callback: Box<dyn IBatteryServiceCallback + Send>) -> u32 { self.callbacks.add_callback(callback) } fn unregister_callback(&mut self, callback_id: u32) { self.remove_callback(callback_id); } fn enable_notifications(&mut self, callback_id: u32, enable: bool) { if self.callbacks.get_by_id(callback_id).is_none() { return; } self.notifications_enabled.remove(&callback_id); if enable { self.notifications_enabled.insert(callback_id); } } fn get_battery_level(&self, remote_address: String) -> Option<u32> { self.battery_levels.get(&remote_address).cloned() } fn refresh_battery_level(&self, remote_address: String) -> bool { let client_id = match self.client_id { Some(id) => id, None => return false, }; let handle = match self.handles.get(&remote_address) { Some(id) => *id, None => { self.init_device(remote_address); return true; } }; self.gatt.lock().unwrap().read_characteristic(client_id, remote_address.clone(), handle, 0); self.gatt.lock().unwrap().register_for_notification( client_id, remote_address, handle, true, ); true } } struct GattCallback { tx: Sender<Message>, } impl GattCallback { fn new(tx: Sender<Message>) -> Self { Self { tx } } } impl IBluetoothGattCallback for GattCallback { // All callback methods relay messages through the stack receiver // to allow BAS to operate on requests serially. This reduces // overall complexity including removing the need to share state // data with callbacks. fn on_client_registered(&self, status: GattStatus, client_id: i32) { let tx = self.tx.clone(); tokio::spawn(async move { let _ = tx .send(Message::BatteryServiceCallbacks(GattBatteryCallbacks::OnClientRegistered( status, client_id, ))) .await; }); } fn on_client_connection_state( &self, status: GattStatus, client_id: i32, connected: bool, addr: String, ) { let tx = self.tx.clone(); tokio::spawn(async move { let _ = tx .send(Message::BatteryServiceCallbacks( GattBatteryCallbacks::OnClientConnectionState( status, client_id, connected, addr, ), )) .await; }); } fn on_search_complete( &self, addr: String, services: Vec<BluetoothGattService>, status: GattStatus, ) { let tx = self.tx.clone(); tokio::spawn(async move { let _ = tx .send(Message::BatteryServiceCallbacks(GattBatteryCallbacks::OnSearchComplete( addr, services, status, ))) .await; }); } fn on_characteristic_read( &self, addr: String, status: GattStatus, handle: i32, value: Vec<u8>, ) { let tx = self.tx.clone(); tokio::spawn(async move { let _ = tx .send(Message::BatteryServiceCallbacks(GattBatteryCallbacks::OnCharacteristicRead( addr, status, handle, value, ))) .await; }); } fn on_notify(&self, addr: String, handle: i32, value: Vec<u8>) { let tx = self.tx.clone(); tokio::spawn(async move { let _ = tx .send(Message::BatteryServiceCallbacks(GattBatteryCallbacks::OnNotify( addr, handle, value, ))) .await; }); } } impl RPCProxy for GattCallback { fn get_object_id(&self) -> String { "BAS Gatt Callback".to_string() } } Loading
system/gd/rust/linux/client/src/command_handler.rs +5 −5 Original line number Diff line number Diff line Loading @@ -707,7 +707,7 @@ impl CommandHandler { ); } "client-connect" => { if args.len() < 3 { if args.len() < 2 { println!("usage: gatt client-connect <addr>"); return; } Loading @@ -729,7 +729,7 @@ impl CommandHandler { ); } "client-disconnect" => { if args.len() < 3 { if args.len() < 2 { println!("usage: gatt client-disconnect <addr>"); return; } Loading @@ -750,7 +750,7 @@ impl CommandHandler { .client_disconnect(client_id.unwrap(), addr); } "client-read-phy" => { if args.len() < 3 { if args.len() < 2 { println!("usage: gatt client-read-phy <addr>"); return; } Loading @@ -771,7 +771,7 @@ impl CommandHandler { .client_read_phy(client_id.unwrap(), addr); } "client-discover-services" => { if args.len() < 3 { if args.len() < 2 { println!("usage: gatt client-discover-services <addr>"); return; } Loading @@ -792,7 +792,7 @@ impl CommandHandler { .discover_services(client_id.unwrap(), addr); } "configure-mtu" => { if args.len() < 4 { if args.len() < 3 { println!("usage: gatt configure-mtu <addr> <mtu>"); return; } Loading
system/gd/rust/linux/service/src/iface_battery_manager.rs +13 −9 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ use crate::dbus_arg::{DBusArg, DBusArgError, RefArgToRust}; #[dbus_propmap(Battery)] pub struct BatteryDBus { percentage: i32, percentage: u32, source_info: String, variant: String, } Loading @@ -28,22 +28,26 @@ struct IBatteryManagerDBus {} #[generate_dbus_exporter(export_battery_manager_dbus_intf, "org.chromium.bluetooth.BatteryManager")] impl IBatteryManager for IBatteryManagerDBus { #[dbus_method("GetBatteryInformation")] fn get_battery_information(&self, remote_address: String) -> Battery { dbus_generated!() } #[dbus_method("RegisterBatteryCallback")] fn register_battery_callback( &mut self, remote_address: String, battery_manager_callback: Box<dyn IBatteryManagerCallback + Send>, ) -> i32 { ) -> u32 { dbus_generated!() } #[dbus_method("UnregisterBatteryCallback")] fn unregister_battery_callback(&mut self, callback_id: i32) { fn unregister_battery_callback(&mut self, callback_id: u32) { dbus_generated!() } #[dbus_method("EnableNotifications")] fn enable_notifications(&mut self, callback_id: u32, enable: bool) { dbus_generated!() } #[dbus_method("GetBatteryInformation")] fn get_battery_information(&self, remote_address: String) -> Option<Battery> { dbus_generated!() } }
system/gd/rust/linux/service/src/main.rs +18 −2 Original line number Diff line number Diff line Loading @@ -11,12 +11,15 @@ use log::LevelFilter; use nix::sys::signal; use std::error::Error; use std::sync::{Arc, Condvar, Mutex}; use std::time::Duration; use syslog::{BasicLogger, Facility, Formatter3164}; use tokio::time; use bt_topshim::{btif::get_btinterface, topstack}; use btstack::{ battery_manager::BatteryManager, battery_provider_manager::BatteryProviderManager, battery_service::BatteryService, bluetooth::{get_bt_dispatcher, Bluetooth, IBluetooth}, bluetooth_gatt::BluetoothGatt, bluetooth_media::BluetoothMedia, Loading Loading @@ -112,7 +115,10 @@ fn main() -> Result<(), Box<dyn Error>> { let bluetooth_media = Arc::new(Mutex::new(Box::new(BluetoothMedia::new(tx.clone(), intf.clone())))); let battery_provider_manager = Arc::new(Mutex::new(Box::new(BatteryProviderManager::new()))); let battery_manager = Arc::new(Mutex::new(Box::new(BatteryManager::new()))); let battery_service = Arc::new(Mutex::new(Box::new(BatteryService::new(bluetooth_gatt.clone(), tx.clone())))); let battery_manager = Arc::new(Mutex::new(Box::new(BatteryManager::new(battery_service.clone(), tx.clone())))); let bluetooth = Arc::new(Mutex::new(Box::new(Bluetooth::new( tx.clone(), intf.clone(), Loading Loading @@ -162,6 +168,8 @@ fn main() -> Result<(), Box<dyn Error>> { rx, bluetooth.clone(), bluetooth_gatt.clone(), battery_service.clone(), battery_manager.clone(), bluetooth_media.clone(), suspend.clone(), bt_sock_mgr.clone(), Loading Loading @@ -261,7 +269,7 @@ fn main() -> Result<(), Box<dyn Error>> { battery_provider_manager.clone(), ); cr.lock().unwrap().insert( make_object_name(adapter_index, "battery__manager"), make_object_name(adapter_index, "battery_manager"), &[battery_manager_iface], battery_manager.clone(), ); Loading @@ -279,6 +287,14 @@ fn main() -> Result<(), Box<dyn Error>> { bluetooth.enable(); bluetooth_gatt.lock().unwrap().init_profiles(tx.clone(), adapter.clone()); // TODO(b/247093293): Gatt topshim api is only usable some // time after init. Investigate why this delay is needed // and make it a blocking part of init before removing // this. tokio::spawn(async move { time::sleep(Duration::from_millis(500)).await; battery_service.lock().unwrap().init(); }); bt_sock_mgr.lock().unwrap().initialize(intf.clone()); // Install SIGTERM handler so that we can properly shutdown Loading
system/gd/rust/linux/stack/src/battery_manager.rs +102 −21 Original line number Diff line number Diff line use crate::battery_service::{ BatteryService, BatteryServiceStatus, IBatteryService, IBatteryServiceCallback, }; use crate::callbacks::Callbacks; use crate::Message; use crate::RPCProxy; use std::collections::HashSet; 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 Battery { pub percentage: i32, pub percentage: u32, pub source_info: String, pub variant: String, } pub struct BatteryManager {} impl BatteryManager { pub fn new() -> BatteryManager { BatteryManager {} } } /// Callback for interacting with the BatteryManager. pub trait IBatteryManagerCallback { pub trait IBatteryManagerCallback: RPCProxy { /// Invoked whenever battery information associated with the given remote changes. fn on_battery_info_updated(&self, remote_address: String, battery: Battery); } Loading @@ -25,31 +29,108 @@ pub trait IBatteryManager { /// callback_id for future calls. fn register_battery_callback( &mut self, remote_address: String, battery_manager_callback: Box<dyn IBatteryManagerCallback + Send>, ) -> i32; ) -> u32; /// Unregister a callback. fn unregister_battery_callback(&mut self, callback_id: i32); fn unregister_battery_callback(&mut self, callback_id: u32); /// Enables notifications for a given callback. fn enable_notifications(&mut self, callback_id: u32, enable: bool); /// Returns battery information for the remote, sourced from the highest priority origin. fn get_battery_information(&self, remote_address: String) -> Battery; fn get_battery_information(&self, remote_address: String) -> Option<Battery>; } impl IBatteryManager for BatteryManager { fn register_battery_callback( &mut self, /// Repesentation of the BatteryManager. pub struct BatteryManager { bas: Arc<Mutex<Box<BatteryService>>>, callbacks: Callbacks<dyn IBatteryManagerCallback + Send>, /// List of callback IDs that have enabled notifications. notifications_enabled: HashSet<u32>, } impl BatteryManager { /// Construct a new BatteryManager with callbacks communicating on tx. pub fn new(bas: Arc<Mutex<Box<BatteryService>>>, tx: Sender<Message>) -> BatteryManager { let callbacks = Callbacks::new(tx.clone(), Message::BatteryManagerCallbackDisconnected); let notifications_enabled = HashSet::new(); Self { bas, callbacks, notifications_enabled } } /// Invoked after BAS has been initialized. pub fn init(&self) { self.bas.lock().unwrap().register_callback(Box::new(BasCallback::new())); } /// Remove a callback due to disconnection or unregistration. pub fn remove_callback(&mut self, callback_id: u32) { self.callbacks.remove_callback(callback_id); } } struct BasCallback {} impl BasCallback { pub fn new() -> BasCallback { Self {} } } impl IBatteryServiceCallback for BasCallback { fn on_battery_service_status_updated( &self, _remote_address: String, _battery_manager_callback: Box<dyn IBatteryManagerCallback + Send>, ) -> i32 { _status: BatteryServiceStatus, ) { todo!() } fn unregister_battery_callback(&mut self, _callback_id: i32) { fn on_battery_level_updated(&self, _remote_address: String, _battery_level: u32) { todo!() } fn get_battery_information(&self, _remote_address: String) -> Battery { fn on_battery_level_read(&self, _remote_address: String, _battery_level: u32) { todo!() } } impl RPCProxy for BasCallback { fn get_object_id(&self) -> String { "BAS Callback".to_string() } } impl IBatteryManager for BatteryManager { fn register_battery_callback( &mut self, battery_manager_callback: Box<dyn IBatteryManagerCallback + Send>, ) -> u32 { self.callbacks.add_callback(battery_manager_callback) } fn unregister_battery_callback(&mut self, callback_id: u32) { self.remove_callback(callback_id); } fn enable_notifications(&mut self, callback_id: u32, enable: bool) { if self.callbacks.get_by_id(callback_id).is_none() { return; } self.notifications_enabled.remove(&callback_id); if enable { self.notifications_enabled.insert(callback_id); } } // TODO(b/233101174): update to use all available sources once // BatteryProviderManager is implemented. fn get_battery_information(&self, remote_address: String) -> Option<Battery> { let battery_level = self.bas.lock().unwrap().get_battery_level(remote_address)?; Some(Battery { percentage: battery_level, source_info: "BAS".to_string(), variant: "".to_string(), }) } }
system/gd/rust/linux/stack/src/battery_service.rs 0 → 100644 +397 −0 Original line number Diff line number Diff line use crate::bluetooth_gatt::{ BluetoothGatt, BluetoothGattService, IBluetoothGatt, IBluetoothGattCallback, }; use crate::callbacks::Callbacks; use crate::uuid; use crate::uuid::parse_uuid_string; use crate::Message; use crate::RPCProxy; use bt_topshim::btif::BtTransport; use bt_topshim::profiles::gatt::{GattStatus, LePhy}; use log::debug; use std::collections::{HashMap, HashSet}; use std::convert::TryInto; use std::iter; use std::sync::{Arc, Mutex}; use tokio::sync::mpsc::Sender; /// The UUID corresponding to the BatteryLevel characteristic defined /// by the BatteryService specification. pub const CHARACTERISTIC_BATTERY_LEVEL: &str = "00002A1900001000800000805F9B34FB"; /// Represents the Floss BatteryService implementation. pub struct BatteryService { gatt: Arc<Mutex<Box<BluetoothGatt>>>, /// Sender for callback communication with the main thread. tx: Sender<Message>, callbacks: Callbacks<dyn IBatteryServiceCallback + Send>, /// The GATT client ID needed for GATT calls. client_id: Option<i32>, /// Cached battery levels keyed by remote device. battery_levels: HashMap<String, u32>, /// Callback IDs that have enabled notifications. notifications_enabled: HashSet<u32>, /// Found handles for battery levels. Required for faster /// refreshes than initiating another search. handles: HashMap<String, i32>, } /// Enum for GATT callbacks to relay messages to the main processing /// thread. Newly supported callbacks should add a corresponding entry /// here. pub enum GattBatteryCallbacks { /// Params: status, client_id OnClientRegistered(GattStatus, i32), /// Params: status, client_id, connected, addr OnClientConnectionState(GattStatus, i32, bool, String), /// Params: addr, services, status OnSearchComplete(String, Vec<BluetoothGattService>, GattStatus), /// Params: addr, status, handle, value OnCharacteristicRead(String, GattStatus, i32, Vec<u8>), /// Params: addr, handle, value OnNotify(String, i32, Vec<u8>), } /// API for Floss implementation of the Bluetooth Battery Service /// (BAS). BAS is built on GATT and this implementation wraps all of /// the GATT calls and handles tracking battery information for the /// client. pub trait IBatteryService { /// Registers a callback for interacting with BatteryService. fn register_callback(&mut self, callback: Box<dyn IBatteryServiceCallback + Send>) -> u32; /// Unregisters a callback. fn unregister_callback(&mut self, callback_id: u32); /// Enables notifications for a given callback. fn enable_notifications(&mut self, callback_id: u32, enable: bool); /// Returns the battery level of the remove device if available in /// BatteryService's cache. Call refresh_battery_level at least /// once to ensure that BatteryService is tracking the device's /// battery information. fn get_battery_level(&self, remote_address: String) -> Option<u32>; /// Forces an explicit read of the device's battery level, /// including initiating battery level tracking if not yet /// performed. fn refresh_battery_level(&self, remote_address: String) -> bool; } /// Callback for interacting with BAS. pub trait IBatteryServiceCallback: RPCProxy { /// Called when the status of BatteryService has changed. Trying /// to read from devices that do not support BAS will result in /// this method being called with BatteryServiceNotSupported. fn on_battery_service_status_updated( &self, remote_address: String, status: BatteryServiceStatus, ); /// Invoked when battery level for a device has been changed due to notification. fn on_battery_level_updated(&self, remote_address: String, battery_level: u32); /// Invoked whenever an explicit read of a devices battery level completes. fn on_battery_level_read(&self, remote_address: String, battery_level: u32); } impl BatteryService { /// Construct a new BatteryService with callbacks relaying messages through tx. pub fn new(gatt: Arc<Mutex<Box<BluetoothGatt>>>, tx: Sender<Message>) -> BatteryService { let tx = tx.clone(); let callbacks = Callbacks::new(tx.clone(), Message::BatteryServiceCallbackDisconnected); let client_id = None; let battery_levels = HashMap::new(); let notifications_enabled = HashSet::new(); let handles = HashMap::new(); Self { gatt, tx, callbacks, client_id, battery_levels, notifications_enabled, handles } } /// Must be called after BluetoothGatt's init_profiles method has completed. pub fn init(&self) { self.gatt.lock().unwrap().register_client( // TODO(b/233101174): make dynamic or decide on a static UUID String::from("e4d2acffcfaa42198f494606b7412117"), Box::new(GattCallback::new(self.tx.clone())), false, ); } /// Handles all callback messages in a central location to avoid deadlocks. pub fn handle_callback(&mut self, callback: GattBatteryCallbacks) { match callback { GattBatteryCallbacks::OnClientRegistered(_status, client_id) => { self.client_id = Some(client_id); } GattBatteryCallbacks::OnClientConnectionState(_status, _client_id, connected, addr) => { if !connected { return; } let client_id = match self.client_id { Some(id) => id, None => { return; } }; self.gatt.lock().unwrap().discover_services(client_id, addr); } GattBatteryCallbacks::OnSearchComplete(addr, services, status) => { if status != GattStatus::Success { debug!("GATT service discovery for {} failed with status {:?}", addr, status); return; } let (bas_uuid, battery_level_uuid) = match ( parse_uuid_string(uuid::BAS), parse_uuid_string(CHARACTERISTIC_BATTERY_LEVEL), ) { (Some(bas_uuid), Some(battery_level_uuid)) => (bas_uuid, battery_level_uuid), _ => return, }; // TODO(b/233101174): handle multiple instances of BAS let bas = match services.iter().find(|service| service.uuid == bas_uuid.uu) { Some(bas) => bas, None => { self.callbacks.for_all_callbacks(|callback| { callback.on_battery_service_status_updated( addr.clone(), BatteryServiceStatus::BatteryServiceNotSupported, ) }); return; } }; let battery_level = match bas .characteristics .iter() .find(|characteristic| characteristic.uuid == battery_level_uuid.uu) { Some(battery_level) => battery_level, None => { debug!("Device {} has no BatteryLevel characteristic", addr); return; } }; let client_id = match self.client_id { Some(id) => id, None => return, }; let handle = battery_level.instance_id; self.handles.insert(addr.clone(), handle.clone()); self.gatt.lock().unwrap().register_for_notification( client_id, addr.clone(), handle, true, ); if let None = self.battery_levels.get(&addr) { self.gatt.lock().unwrap().read_characteristic( client_id, addr, battery_level.instance_id, 0, ); } } GattBatteryCallbacks::OnCharacteristicRead(addr, status, _handle, value) => { if status != GattStatus::Success { return; } let level = self.set_battery_level(addr.clone(), value.clone()); self.callbacks.for_all_callbacks(|callback| { callback.on_battery_level_read(addr.clone(), level); }); } GattBatteryCallbacks::OnNotify(addr, _handle, value) => { let level = self.set_battery_level(addr.clone(), value); // TODO(b/247551256): expand Callbacks to allow direct // filtering/exposing the underlying iter let to_notify = self.notifications_enabled.clone(); to_notify.iter().for_each(|id| match self.callbacks.get_by_id(*id) { Some(callback) => callback.on_battery_level_updated(addr.clone(), level), None => (), }); } } } fn set_battery_level(&mut self, remote_address: String, value: Vec<u8>) -> u32 { let level: Vec<_> = value.iter().cloned().chain(iter::repeat(0 as u8)).take(4).collect(); let level = u32::from_le_bytes(level.try_into().unwrap()); self.battery_levels.insert(remote_address, level); level } fn init_device(&self, remote_address: String) { let client_id = match self.client_id { Some(id) => id, None => return, }; self.gatt.lock().unwrap().client_connect( client_id, remote_address, false, BtTransport::Le, false, LePhy::Phy1m, ); } /// Remove a callback due to disconnection or unregistration. pub fn remove_callback(&mut self, callback_id: u32) { self.callbacks.remove_callback(callback_id); } } /// Status enum for relaying the state of BAS or a particular device. pub enum BatteryServiceStatus { /// Device does not report support for BAS. BatteryServiceNotSupported, } impl IBatteryService for BatteryService { fn register_callback(&mut self, callback: Box<dyn IBatteryServiceCallback + Send>) -> u32 { self.callbacks.add_callback(callback) } fn unregister_callback(&mut self, callback_id: u32) { self.remove_callback(callback_id); } fn enable_notifications(&mut self, callback_id: u32, enable: bool) { if self.callbacks.get_by_id(callback_id).is_none() { return; } self.notifications_enabled.remove(&callback_id); if enable { self.notifications_enabled.insert(callback_id); } } fn get_battery_level(&self, remote_address: String) -> Option<u32> { self.battery_levels.get(&remote_address).cloned() } fn refresh_battery_level(&self, remote_address: String) -> bool { let client_id = match self.client_id { Some(id) => id, None => return false, }; let handle = match self.handles.get(&remote_address) { Some(id) => *id, None => { self.init_device(remote_address); return true; } }; self.gatt.lock().unwrap().read_characteristic(client_id, remote_address.clone(), handle, 0); self.gatt.lock().unwrap().register_for_notification( client_id, remote_address, handle, true, ); true } } struct GattCallback { tx: Sender<Message>, } impl GattCallback { fn new(tx: Sender<Message>) -> Self { Self { tx } } } impl IBluetoothGattCallback for GattCallback { // All callback methods relay messages through the stack receiver // to allow BAS to operate on requests serially. This reduces // overall complexity including removing the need to share state // data with callbacks. fn on_client_registered(&self, status: GattStatus, client_id: i32) { let tx = self.tx.clone(); tokio::spawn(async move { let _ = tx .send(Message::BatteryServiceCallbacks(GattBatteryCallbacks::OnClientRegistered( status, client_id, ))) .await; }); } fn on_client_connection_state( &self, status: GattStatus, client_id: i32, connected: bool, addr: String, ) { let tx = self.tx.clone(); tokio::spawn(async move { let _ = tx .send(Message::BatteryServiceCallbacks( GattBatteryCallbacks::OnClientConnectionState( status, client_id, connected, addr, ), )) .await; }); } fn on_search_complete( &self, addr: String, services: Vec<BluetoothGattService>, status: GattStatus, ) { let tx = self.tx.clone(); tokio::spawn(async move { let _ = tx .send(Message::BatteryServiceCallbacks(GattBatteryCallbacks::OnSearchComplete( addr, services, status, ))) .await; }); } fn on_characteristic_read( &self, addr: String, status: GattStatus, handle: i32, value: Vec<u8>, ) { let tx = self.tx.clone(); tokio::spawn(async move { let _ = tx .send(Message::BatteryServiceCallbacks(GattBatteryCallbacks::OnCharacteristicRead( addr, status, handle, value, ))) .await; }); } fn on_notify(&self, addr: String, handle: i32, value: Vec<u8>) { let tx = self.tx.clone(); tokio::spawn(async move { let _ = tx .send(Message::BatteryServiceCallbacks(GattBatteryCallbacks::OnNotify( addr, handle, value, ))) .await; }); } } impl RPCProxy for GattCallback { fn get_object_id(&self) -> String { "BAS Gatt Callback".to_string() } }