diff --git a/system/gd/rust/linux/stack/src/bluetooth_adv.rs b/system/gd/rust/linux/stack/src/bluetooth_adv.rs index 427e3620c2a103b29e3666ca07f621a21257dc5c..0c331889ff6c160f5f7793d086410c2805222ddf 100644 --- a/system/gd/rust/linux/stack/src/bluetooth_adv.rs +++ b/system/gd/rust/linux/stack/src/bluetooth_adv.rs @@ -6,11 +6,14 @@ use bt_topshim::btif::{RawAddress, Uuid}; use bt_topshim::profiles::gatt::{AdvertisingStatus, Gatt, GattAdvCallbacks, LePhy}; use itertools::Itertools; -use log::{debug, error, warn}; +use log::{debug, error, info, warn}; use num_traits::clamp; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; use tokio::sync::mpsc::Sender; +use tokio::task::JoinHandle; +use tokio::time; use crate::bluetooth::{Bluetooth, IBluetooth}; use crate::callbacks::Callbacks; @@ -354,16 +357,9 @@ impl AdvertiseData { } /// Checks if the advertisement can be upgraded to extended. - pub fn can_upgrade( - parameters: &mut AdvertisingSetParameters, - adv_bytes: &Vec<u8>, - is_le_extended_advertising_supported: bool, - ) -> bool { - if parameters.is_legacy - && is_le_extended_advertising_supported - && !AdvertiseData::validate_raw_data(true, adv_bytes) - { - log::info!("Auto upgrading advertisement to extended"); + pub fn can_upgrade(parameters: &mut AdvertisingSetParameters, adv_bytes: &Vec<u8>) -> bool { + if parameters.is_legacy && !AdvertiseData::validate_raw_data(true, adv_bytes) { + info!("Auto upgrading advertisement to extended"); parameters.is_legacy = false; return true; } @@ -546,10 +542,15 @@ impl AdvertiseManager { is_le_ext_adv_supported: bool, ) { self.adv_manager_impl = if is_le_ext_adv_supported { + info!("AdvertiseManager: Selected extended advertising stack"); Some(Box::new(AdvertiseManagerImpl::new(self.tx.clone(), gatt, adapter))) } else { - // TODO: Implement software rotation for legacy controller - Some(Box::new(AdvertiseManagerImpl::new(self.tx.clone(), gatt, adapter))) + info!("AdvertiseManager: Selected software rotation stack"); + Some(Box::new(SoftwareRotationAdvertiseManagerImpl::new( + self.tx.clone(), + gatt, + adapter, + ))) } } @@ -699,6 +700,12 @@ impl AdvertiseManagerImpl { } } +pub enum AdvertiserActions { + /// Triggers the rotation of the advertising set. + /// Should only be used in the software rotation stack. + RunRotate, +} + /// Defines all required ops for an AdvertiseManager to communicate with the upper/lower layers. pub(crate) trait AdvertiseManagerOps: IBluetoothAdvertiseManager + BtifGattAdvCallbacks @@ -708,6 +715,9 @@ pub(crate) trait AdvertiseManagerOps: /// Undoes previous suspend preparation fn exit_suspend(&mut self); + + /// Handles advertise manager actions + fn handle_action(&mut self, action: AdvertiserActions); } impl AdvertiseManagerOps for AdvertiseManagerImpl { @@ -748,6 +758,14 @@ impl AdvertiseManagerOps for AdvertiseManagerImpl { self.set_suspend_mode(SuspendMode::Normal); } + + fn handle_action(&mut self, action: AdvertiserActions) { + match action { + AdvertiserActions::RunRotate => { + error!("Unexpected RunRotate call in hardware offloaded stack"); + } + } + } } pub trait IBluetoothAdvertiseManager { @@ -876,15 +894,9 @@ impl IBluetoothAdvertiseManager for AdvertiseManagerImpl { let device_name = self.get_adapter_name(); let adv_bytes = advertise_data.make_with(&device_name); - let is_le_extended_advertising_supported = - self.adapter.lock().unwrap().is_le_extended_advertising_supported(); // TODO(b/311417973): Remove this once we have more robust /device/bluetooth APIs to control extended advertising - let is_legacy = parameters.is_legacy - && !AdvertiseData::can_upgrade( - &mut parameters, - &adv_bytes, - is_le_extended_advertising_supported, - ); + let is_legacy = + parameters.is_legacy && !AdvertiseData::can_upgrade(&mut parameters, &adv_bytes); let params = parameters.into(); if !AdvertiseData::validate_raw_data(is_legacy, &adv_bytes) { warn!("Failed to start advertising set with invalid advertise data"); @@ -1369,6 +1381,691 @@ impl BtifGattAdvCallbacks for AdvertiseManagerImpl { } } +/// The underlying legacy advertising rotates every SOFTWARE_ROTATION_INTERVAL seconds. +const SOFTWARE_ROTATION_INTERVAL: Duration = Duration::from_secs(2); + +/// The ID of a software rotation advertising. +/// +/// From DBus API's perspective this is used as both Advertiser ID and Register ID. +/// Unlike the extended advertising stack we can't propagate the LibBluetooth Advertiser ID to +/// DBus clients because there can be at most 1 advertiser in LibBluetooth layer at the same time. +pub type SoftwareRotationAdvertierId = i32; + +struct SoftwareRotationAdvertiseInfo { + id: SoftwareRotationAdvertierId, + callback_id: u32, + + advertising_params: AdvertisingSetParameters, + advertising_data: Vec<u8>, + scan_response_data: Vec<u8>, + + /// Filled in on the first time the advertiser started. + tx_power: Option<i32>, + + /// True if it's advertising (from DBus client's perspective), false otherwise. + enabled: bool, + duration: i32, + /// None means no timeout + expire_time: Option<Instant>, +} + +enum SoftwareRotationAdvertiseState { + /// No advertiser is running in LibBluetooth. + Stopped, + /// A StartAdvertisingSet call to LibBluetooth is pending. + Pending(SoftwareRotationAdvertierId), + /// An advertiser is running in LibBluetooth, i.e., an OnAdvertisingSetStarted is received. + /// Parameters: ID, LibBluetooth BLE Advertiser ID, rotation timer handle + Advertising(SoftwareRotationAdvertierId, u8, JoinHandle<()>), +} + +struct SoftwareRotationAdvertiseManagerImpl { + callbacks: Callbacks<dyn IAdvertisingSetCallback + Send>, + suspend_mode: SuspendMode, + gatt: Arc<Mutex<Gatt>>, + adapter: Arc<Mutex<Box<Bluetooth>>>, + tx: Sender<Message>, + + state: SoftwareRotationAdvertiseState, + adv_info: HashMap<SoftwareRotationAdvertierId, SoftwareRotationAdvertiseInfo>, + /// The enabled advertising sets to be rotate. + /// When they are removed from the queue, OnAdvertisingEnabled needs to be sent. + /// Note that the current advertiser running in LibBluetooth must *NOT* be in the queue. + adv_queue: VecDeque<SoftwareRotationAdvertierId>, +} + +impl SoftwareRotationAdvertiseManagerImpl { + fn new( + tx: Sender<Message>, + gatt: Arc<Mutex<Gatt>>, + adapter: Arc<Mutex<Box<Bluetooth>>>, + ) -> Self { + Self { + callbacks: Callbacks::new(tx.clone(), Message::AdvertiserCallbackDisconnected), + suspend_mode: SuspendMode::Normal, + gatt, + adapter, + tx, + state: SoftwareRotationAdvertiseState::Stopped, + adv_info: HashMap::new(), + adv_queue: VecDeque::new(), + } + } +} + +impl SoftwareRotationAdvertiseManagerImpl { + /// Updates suspend mode. + fn set_suspend_mode(&mut self, suspend_mode: SuspendMode) { + if suspend_mode != self.suspend_mode { + self.suspend_mode = suspend_mode.clone(); + self.callbacks.for_all_callbacks(|cb| { + cb.on_suspend_mode_change(suspend_mode.clone()); + }); + } + } + + fn get_adapter_name(&self) -> String { + self.adapter.lock().unwrap().get_name() + } + + /// Returns the ID of the advertiser running in LibBluetooth. + fn current_id(&self) -> Option<SoftwareRotationAdvertierId> { + match &self.state { + SoftwareRotationAdvertiseState::Pending(id) => Some(*id), + SoftwareRotationAdvertiseState::Advertising(id, _, _) => Some(*id), + SoftwareRotationAdvertiseState::Stopped => None, + } + } + + /// Returns the minimum unoccupied ID from 0. + fn new_id(&mut self) -> SoftwareRotationAdvertierId { + // The advertiser running in LibBluetooth may have been removed in this layer. + // Avoid conflicting with it. + let current_id = self.current_id(); + (0..) + .find(|id| !self.adv_info.contains_key(id) && Some(*id) != current_id) + .expect("There must be an unoccupied register ID") + } + + fn is_pending(&self) -> bool { + matches!(&self.state, SoftwareRotationAdvertiseState::Pending(_)) + } + + fn is_stopped(&self) -> bool { + matches!(&self.state, SoftwareRotationAdvertiseState::Stopped) + } + + /// Clears the removed or disabled advertisers from the queue and invokes callback. + fn refresh_queue(&mut self) { + let now = Instant::now(); + let adv_info = &mut self.adv_info; + let callbacks = &mut self.callbacks; + self.adv_queue.retain(|id| { + let Some(info) = adv_info.get_mut(id) else { + // This advertiser has been removed. + return false; + }; + if info.expire_time.map_or(false, |t| t < now) { + // This advertiser has expired. + info.enabled = false; + callbacks.get_by_id_mut(info.callback_id).map(|cb| { + cb.on_advertising_enabled(info.id, false, AdvertisingStatus::Success); + }); + } + info.enabled + }); + } + + fn stop_current_advertising(&mut self) { + match &self.state { + SoftwareRotationAdvertiseState::Advertising(id, adv_id, handle) => { + handle.abort(); + self.gatt.lock().unwrap().advertiser.unregister(*adv_id); + self.adv_queue.push_back(*id); + self.state = SoftwareRotationAdvertiseState::Stopped; + } + SoftwareRotationAdvertiseState::Pending(_) => { + error!("stop_current_advertising: Unexpected Pending state"); + } + SoftwareRotationAdvertiseState::Stopped => {} + }; + } + + fn start_next_advertising(&mut self) { + match &self.state { + SoftwareRotationAdvertiseState::Stopped => { + self.state = loop { + let Some(id) = self.adv_queue.pop_front() else { + break SoftwareRotationAdvertiseState::Stopped; + }; + let Some(info) = self.adv_info.get(&id) else { + error!("start_next_advertising: Unknown ID, which means queue is not refreshed!"); + continue; + }; + self.gatt.lock().unwrap().advertiser.start_advertising_set( + id, + info.advertising_params.clone().into(), + info.advertising_data.clone(), + info.scan_response_data.clone(), + Default::default(), // Unsupported periodic_parameters + vec![], // Unsupported periodic_data + 0, // Set no timeout. Timeout is controlled in this layer. + 0, // Unsupported max_ext_adv_events + ); + break SoftwareRotationAdvertiseState::Pending(id); + } + } + SoftwareRotationAdvertiseState::Pending(_) => { + error!("start_next_advertising: Unexpected Pending state"); + } + SoftwareRotationAdvertiseState::Advertising(_, _, _) => { + error!("start_next_advertising: Unexpected Advertising state"); + } + }; + } + + fn run_rotate(&mut self) { + if self.is_pending() { + return; + } + let Some(current_id) = self.current_id() else { + // State is Stopped. Try to start next one. + self.refresh_queue(); + self.start_next_advertising(); + return; + }; + // We are Advertising. Checks if the current advertiser is still allowed + // to advertise, or if we should schedule the next one in the queue. + let current_is_enabled = { + let now = Instant::now(); + if let Some(info) = self.adv_info.get(¤t_id) { + if info.enabled { + info.expire_time.map_or(true, |t| t >= now) + } else { + false + } + } else { + false + } + }; + if !current_is_enabled { + // If current advertiser is not allowed to advertise, + // stop it and then let |refresh_queue| handle the callback. + self.stop_current_advertising(); + self.refresh_queue(); + self.start_next_advertising(); + } else { + // Current advertiser is still enabled, refresh the other advertisers in the queue. + self.refresh_queue(); + if self.adv_queue.is_empty() { + // No need to rotate. + } else { + self.stop_current_advertising(); + self.start_next_advertising(); + } + } + } +} + +impl AdvertiseManagerOps for SoftwareRotationAdvertiseManagerImpl { + fn enter_suspend(&mut self) { + if self.suspend_mode != SuspendMode::Normal { + return; + } + + self.set_suspend_mode(SuspendMode::Suspending); + if self.is_pending() { + // We will unregister it on_advertising_set_started and then set mode to suspended. + return; + } + self.stop_current_advertising(); + self.set_suspend_mode(SuspendMode::Suspended); + } + + fn exit_suspend(&mut self) { + if self.suspend_mode != SuspendMode::Suspended { + return; + } + self.refresh_queue(); + self.start_next_advertising(); + self.set_suspend_mode(SuspendMode::Normal); + } + + fn handle_action(&mut self, action: AdvertiserActions) { + match action { + AdvertiserActions::RunRotate => { + if self.suspend_mode == SuspendMode::Normal { + self.run_rotate(); + } + } + } + } +} + +/// Generates expire time from now per the definition in IBluetoothAdvertiseManager +/// +/// None means never timeout. +fn gen_expire_time_from_now(duration: i32) -> Option<Instant> { + let duration = clamp(duration, 0, 0xffff) as u64; + if duration != 0 { + Some(Instant::now() + Duration::from_millis(duration * 10)) + } else { + None + } +} + +impl IBluetoothAdvertiseManager for SoftwareRotationAdvertiseManagerImpl { + fn register_callback(&mut self, callback: Box<dyn IAdvertisingSetCallback + Send>) -> u32 { + self.callbacks.add_callback(callback) + } + + fn unregister_callback(&mut self, callback_id: u32) -> bool { + self.adv_info.retain(|_, info| info.callback_id != callback_id); + let ret = self.callbacks.remove_callback(callback_id); + if let Some(current_id) = self.current_id() { + if !self.adv_info.contains_key(¤t_id) { + self.run_rotate(); + } + } + ret + } + + fn start_advertising_set( + &mut self, + advertising_params: AdvertisingSetParameters, + advertising_data: AdvertiseData, + scan_response_data: Option<AdvertiseData>, + periodic_parameters: Option<PeriodicAdvertisingParameters>, + periodic_data: Option<AdvertiseData>, + duration: i32, + max_ext_adv_events: i32, + callback_id: u32, + ) -> i32 { + if self.suspend_mode != SuspendMode::Normal { + return INVALID_REG_ID; + } + + let is_legacy = advertising_params.is_legacy; + let device_name = self.get_adapter_name(); + + let advertising_data = advertising_data.make_with(&device_name); + if !AdvertiseData::validate_raw_data(is_legacy, &advertising_data) { + warn!("Failed to start advertising set with invalid advertising data"); + return INVALID_REG_ID; + } + + let scan_response_data = + scan_response_data.map_or(vec![], |data| data.make_with(&device_name)); + if !AdvertiseData::validate_raw_data(is_legacy, &scan_response_data) { + warn!("Failed to start advertising set with invalid scan response data"); + return INVALID_REG_ID; + } + + if periodic_parameters.is_some() { + warn!("Periodic parameters is not supported in software rotation stack, ignored"); + } + if periodic_data.is_some() { + warn!("Periodic data is not supported in software rotation stack, ignored"); + } + if max_ext_adv_events != 0 { + warn!("max_ext_adv_events is not supported in software rotation stack, ignored"); + } + + let id = self.new_id(); + + // expire_time will be determined on this advertiser is started at the first time. + self.adv_info.insert( + id, + SoftwareRotationAdvertiseInfo { + id, + callback_id, + advertising_params, + advertising_data, + scan_response_data, + tx_power: None, + enabled: true, + duration, + expire_time: None, + }, + ); + // Schedule it as the next one and rotate. + self.adv_queue.push_front(id); + self.run_rotate(); + + id + } + + fn stop_advertising_set(&mut self, adv_id: i32) { + let current_id = self.current_id(); + let Some(info) = self.adv_info.remove(&adv_id) else { + warn!("stop_advertising_set: Unknown adv_id {}", adv_id); + return; + }; + self.callbacks.get_by_id_mut(info.callback_id).map(|cb| { + cb.on_advertising_set_stopped(info.id); + }); + if current_id == Some(info.id) { + self.run_rotate(); + } + } + + fn get_own_address(&mut self, _adv_id: i32) { + error!("get_own_address is not supported in software rotation stack"); + } + + fn enable_advertising_set( + &mut self, + adv_id: i32, + enable: bool, + duration: i32, + max_ext_adv_events: i32, + ) { + if self.suspend_mode != SuspendMode::Normal { + return; + } + + let current_id = self.current_id(); + let Some(info) = self.adv_info.get_mut(&adv_id) else { + warn!("enable_advertising_set: Unknown adv_id {}", adv_id); + return; + }; + + if max_ext_adv_events != 0 { + warn!("max_ext_adv_events is not supported in software rotation stack, ignored"); + } + + info.enabled = enable; + // We won't really call enable() to LibBluetooth so calculate the expire time right now. + info.expire_time = gen_expire_time_from_now(duration); + // This actually won't be used as the expire_time is already determined. + // Still fill it in to keep the data updated. + info.duration = duration; + + if enable && !self.adv_queue.contains(&info.id) && current_id != Some(info.id) { + // The adv was not enabled and not in the queue. Invoke callback and queue it. + self.callbacks.get_by_id_mut(info.callback_id).map(|cb| { + cb.on_advertising_enabled(info.id, false, AdvertisingStatus::Success); + }); + self.adv_queue.push_back(info.id); + if self.is_stopped() { + self.start_next_advertising(); + } + } else if !enable && current_id == Some(info.id) { + self.run_rotate(); + } + } + + fn set_advertising_data(&mut self, adv_id: i32, data: AdvertiseData) { + if self.suspend_mode != SuspendMode::Normal { + return; + } + + let current_id = self.current_id(); + let device_name = self.get_adapter_name(); + let Some(info) = self.adv_info.get_mut(&adv_id) else { + warn!("set_advertising_data: Unknown adv_id {}", adv_id); + return; + }; + let data = data.make_with(&device_name); + if !AdvertiseData::validate_raw_data(info.advertising_params.is_legacy, &data) { + warn!("set_advertising_data {}: invalid advertise data to update", adv_id); + return; + } + info.advertising_data = data; + self.callbacks.get_by_id_mut(info.callback_id).map(|cb| { + cb.on_advertising_data_set(info.id, AdvertisingStatus::Success); + }); + + if current_id == Some(info.id) { + self.run_rotate(); + } + } + + fn set_raw_adv_data(&mut self, adv_id: i32, data: Vec<u8>) { + if self.suspend_mode != SuspendMode::Normal { + return; + } + + let current_id = self.current_id(); + let Some(info) = self.adv_info.get_mut(&adv_id) else { + warn!("set_raw_adv_data: Unknown adv_id {}", adv_id); + return; + }; + if !AdvertiseData::validate_raw_data(info.advertising_params.is_legacy, &data) { + warn!("set_raw_adv_data {}: invalid raw advertise data to update", adv_id); + return; + } + info.advertising_data = data; + self.callbacks.get_by_id_mut(info.callback_id).map(|cb| { + cb.on_advertising_data_set(info.id, AdvertisingStatus::Success); + }); + + if current_id == Some(info.id) { + self.run_rotate(); + } + } + + fn set_scan_response_data(&mut self, adv_id: i32, data: AdvertiseData) { + if self.suspend_mode != SuspendMode::Normal { + return; + } + + let current_id = self.current_id(); + let device_name = self.get_adapter_name(); + let Some(info) = self.adv_info.get_mut(&adv_id) else { + warn!("set_scan_response_data: Unknown adv_id {}", adv_id); + return; + }; + let data = data.make_with(&device_name); + if !AdvertiseData::validate_raw_data(info.advertising_params.is_legacy, &data) { + warn!("set_scan_response_data {}: invalid scan response to update", adv_id); + return; + } + info.scan_response_data = data; + self.callbacks.get_by_id_mut(info.callback_id).map(|cb| { + cb.on_scan_response_data_set(info.id, AdvertisingStatus::Success); + }); + + if current_id == Some(info.id) { + self.run_rotate(); + } + } + + fn set_advertising_parameters(&mut self, adv_id: i32, params: AdvertisingSetParameters) { + if self.suspend_mode != SuspendMode::Normal { + return; + } + + let current_id = self.current_id(); + let Some(info) = self.adv_info.get_mut(&adv_id) else { + warn!("set_advertising_parameters: Unknown adv_id {}", adv_id); + return; + }; + info.advertising_params = params; + let Some(tx_power) = info.tx_power else { + error!("set_advertising_parameters: tx_power is None! Is this called before adv has started?"); + return; + }; + self.callbacks.get_by_id_mut(info.callback_id).map(|cb| { + cb.on_advertising_parameters_updated(info.id, tx_power, AdvertisingStatus::Success); + }); + + if current_id == Some(info.id) { + self.run_rotate(); + } + } + + fn set_periodic_advertising_parameters( + &mut self, + _adv_id: i32, + _parameters: PeriodicAdvertisingParameters, + ) { + error!("set_periodic_advertising_parameters is not supported in software rotation stack"); + } + + fn set_periodic_advertising_data(&mut self, _adv_id: i32, _data: AdvertiseData) { + error!("set_periodic_advertising_data is not supported in software rotation stack"); + } + + fn set_periodic_advertising_enable(&mut self, _adv_id: i32, _enable: bool, _include_adi: bool) { + error!("set_periodic_advertising_enable is not supported in software rotation stack"); + } +} + +impl BtifGattAdvCallbacks for SoftwareRotationAdvertiseManagerImpl { + fn on_advertising_set_started( + &mut self, + reg_id: i32, + adv_id: u8, + tx_power: i8, + status: AdvertisingStatus, + ) { + debug!( + "on_advertising_set_started(): reg_id = {}, advertiser_id = {}, tx_power = {}, status = {:?}", + reg_id, adv_id, tx_power, status + ); + + // Unregister if it's unexpected. + match &self.state { + SoftwareRotationAdvertiseState::Pending(pending_id) if pending_id == ®_id => {} + _ => { + error!( + "Unexpected on_advertising_set_started reg_id = {}, adv_id = {}, status = {:?}", + reg_id, adv_id, status + ); + if status == AdvertisingStatus::Success { + self.gatt.lock().unwrap().advertiser.unregister(adv_id); + } + return; + } + } + // Switch out from the pending state. + self.state = if status != AdvertisingStatus::Success { + warn!("on_advertising_set_started failed: reg_id = {}, status = {:?}", reg_id, status); + SoftwareRotationAdvertiseState::Stopped + } else { + let txl = self.tx.clone(); + SoftwareRotationAdvertiseState::Advertising( + reg_id, + adv_id, + tokio::spawn(async move { + loop { + time::sleep(SOFTWARE_ROTATION_INTERVAL).await; + let _ = txl + .send(Message::AdvertiserActions(AdvertiserActions::RunRotate)) + .await; + } + }), + ) + }; + + // 1. Handle on_advertising_set_started callback if it's the first time it started + // 2. Stop advertising if it's removed or disabled + // 3. Disable or remove the advertiser if it failed + if let Some(info) = self.adv_info.get_mut(®_id) { + if info.tx_power.is_none() { + // tx_power is none means it's the first time this advertiser started. + if status != AdvertisingStatus::Success { + self.callbacks.get_by_id_mut(info.callback_id).map(|cb| { + cb.on_advertising_set_started(info.id, INVALID_ADV_ID, 0, status); + }); + self.adv_info.remove(®_id); + } else { + info.tx_power = Some(tx_power.into()); + info.expire_time = gen_expire_time_from_now(info.duration); + self.callbacks.get_by_id_mut(info.callback_id).map(|cb| { + cb.on_advertising_set_started(info.id, info.id, tx_power.into(), status); + }); + } + } else { + // Not the first time. This means we are not able to report the failure through + // on_advertising_set_started if it failed. Disable it instead in that case. + if status != AdvertisingStatus::Success { + info.enabled = false; + // Push to the queue and let refresh_queue handle the disabled callback. + self.adv_queue.push_back(reg_id); + } else { + if !info.enabled { + self.stop_current_advertising(); + } + } + } + } else { + self.stop_current_advertising(); + } + + // Rotate again if the next advertiser is new. We need to consume all + // "first time" advertiser before suspended to make sure callbacks are sent. + if let Some(id) = self.adv_queue.front() { + if let Some(info) = self.adv_info.get(id) { + if info.tx_power.is_none() { + self.run_rotate(); + return; + } + } + } + + // We're fine to suspend since there is no advertiser pending callback. + if self.suspend_mode != SuspendMode::Normal { + self.stop_current_advertising(); + self.set_suspend_mode(SuspendMode::Suspended); + return; + } + + // If the current advertiser is stopped for some reason, schedule the next one. + if self.is_stopped() { + self.refresh_queue(); + self.start_next_advertising(); + } + } + + fn on_advertising_enabled(&mut self, _adv_id: u8, _enabled: bool, _status: AdvertisingStatus) { + error!("Unexpected on_advertising_enabled in software rotation stack"); + } + + fn on_advertising_data_set(&mut self, _adv_id: u8, _status: AdvertisingStatus) { + error!("Unexpected on_advertising_data_set in software rotation stack"); + } + + fn on_scan_response_data_set(&mut self, _adv_id: u8, _status: AdvertisingStatus) { + error!("Unexpected on_scan_response_data_set in software rotation stack"); + } + + fn on_advertising_parameters_updated( + &mut self, + _adv_id: u8, + _tx_power: i8, + _status: AdvertisingStatus, + ) { + error!("Unexpected on_advertising_parameters_updated in software rotation stack"); + } + + fn on_periodic_advertising_parameters_updated( + &mut self, + _adv_id: u8, + _status: AdvertisingStatus, + ) { + error!("Unexpected on_periodic_advertising_parameters_updated in software rotation stack"); + } + + fn on_periodic_advertising_data_set(&mut self, _adv_id: u8, _status: AdvertisingStatus) { + error!("Unexpected on_periodic_advertising_data_set in software rotation stack"); + } + + fn on_periodic_advertising_enabled( + &mut self, + _adv_id: u8, + _enabled: bool, + _status: AdvertisingStatus, + ) { + error!("Unexpected on_periodic_advertising_enabled in software rotation stack"); + } + + fn on_own_address_read(&mut self, _adv_id: u8, _addr_type: u8, _address: RawAddress) { + error!("Unexpected on_own_address_read in software rotation stack"); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/system/gd/rust/linux/stack/src/bluetooth_gatt.rs b/system/gd/rust/linux/stack/src/bluetooth_gatt.rs index d054af6b68c95683ae8a71fca11d6c3783b17052..4e02cfb52d83249d9ebf7cdf547db3c9d376c43a 100644 --- a/system/gd/rust/linux/stack/src/bluetooth_gatt.rs +++ b/system/gd/rust/linux/stack/src/bluetooth_gatt.rs @@ -20,8 +20,8 @@ use bt_utils::array_utils; use crate::async_helper::{AsyncHelper, CallbackSender}; use crate::bluetooth::{Bluetooth, BluetoothDevice}; use crate::bluetooth_adv::{ - AdvertiseData, AdvertiseManager, AdvertisingSetParameters, BtifGattAdvCallbacks, - IAdvertisingSetCallback, PeriodicAdvertisingParameters, + AdvertiseData, AdvertiseManager, AdvertiserActions, AdvertisingSetParameters, + BtifGattAdvCallbacks, IAdvertisingSetCallback, PeriodicAdvertisingParameters, }; use crate::callbacks::Callbacks; use crate::uuid::UuidHelper; @@ -1881,6 +1881,10 @@ impl BluetoothGatt { } } } + + pub fn handle_adv_action(&mut self, action: AdvertiserActions) { + self.adv_manager.get_impl().handle_action(action); + } } #[derive(Debug, FromPrimitive, ToPrimitive)] diff --git a/system/gd/rust/linux/stack/src/lib.rs b/system/gd/rust/linux/stack/src/lib.rs index e3fe6502130a65c6706c2e72ec14d98e8db63c8f..6616b2d9df6a025525f4d46d1c821e75ccfbdb54 100644 --- a/system/gd/rust/linux/stack/src/lib.rs +++ b/system/gd/rust/linux/stack/src/lib.rs @@ -35,7 +35,7 @@ use crate::bluetooth::{ BluetoothDevice, DelayedActions, IBluetooth, }; use crate::bluetooth_admin::{BluetoothAdmin, IBluetoothAdmin}; -use crate::bluetooth_adv::dispatch_le_adv_callbacks; +use crate::bluetooth_adv::{dispatch_le_adv_callbacks, AdvertiserActions}; use crate::bluetooth_gatt::{ dispatch_gatt_client_callbacks, dispatch_gatt_server_callbacks, dispatch_le_scanner_callbacks, dispatch_le_scanner_inband_callbacks, BluetoothGatt, GattActions, @@ -116,6 +116,7 @@ pub enum Message { // Advertising related AdvertiserCallbackDisconnected(u32), + AdvertiserActions(AdvertiserActions), SocketManagerActions(SocketActions), SocketManagerCallbackDisconnected(u32), @@ -367,6 +368,10 @@ impl Stack { bluetooth_gatt.lock().unwrap().remove_adv_callback(id); } + Message::AdvertiserActions(action) => { + bluetooth_gatt.lock().unwrap().handle_adv_action(action); + } + Message::SocketManagerActions(action) => { bluetooth_socketmgr.lock().unwrap().handle_actions(action); }