Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75de1f74 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Floss: Implement StartScan and StopScan with filter"

parents 076e69e1 167133d8
Loading
Loading
Loading
Loading
+11 −2
Original line number Diff line number Diff line
@@ -973,9 +973,18 @@ impl CommandHandler {
                if let Ok(id) = scanner_id {
                    self.context.lock().unwrap().gatt_dbus.as_mut().unwrap().start_scan(
                        id,
                        // TODO(b/217274432): Construct real settings and filters.
                        // TODO(b/254870159): Construct real settings and filters depending on
                        // command line options.
                        ScanSettings { interval: 0, window: 0, scan_type: ScanType::Active },
                        None,
                        Some(btstack::bluetooth_gatt::ScanFilter {
                            rssi_high_threshold: 0,
                            rssi_low_threshold: 0,
                            rssi_low_timeout: 0,
                            rssi_sampling_period: 0,
                            condition: btstack::bluetooth_gatt::ScanFilterCondition::Patterns(
                                vec![],
                            ),
                        }),
                    );
                    self.context.lock().unwrap().active_scanner_ids.insert(id);
                } else {
+85 −0
Original line number Diff line number Diff line
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tokio::sync::oneshot;
use tokio::time::Duration;

/// Helper for managing an async topshim function. It takes care of calling the function, preparing
/// the channel, waiting for the callback, and returning it in a Result.
///
/// `R` is the type of the return.
pub(crate) struct AsyncHelper<R> {
    // Name of the method that this struct helps. Useful for logging.
    method_name: String,

    // Keeps track of call_id. Increment each time and wrap to 0 when u32 max is reached.
    last_call_id: u32,

    // Keep pending calls' ids and senders.
    senders: Arc<Mutex<HashMap<u32, oneshot::Sender<R>>>>,
}

pub(crate) type CallbackSender<R> = Arc<Mutex<Box<(dyn Fn(u32, R) + Send)>>>;

impl<R: 'static + Send> AsyncHelper<R> {
    pub(crate) fn new(method_name: &str) -> Self {
        Self {
            method_name: String::from(method_name),
            last_call_id: 0,
            senders: Arc::new(Mutex::new(HashMap::new())),
        }
    }

    /// Calls a topshim method that expects the async return to be delivered via a callback.
    pub(crate) async fn call_method<F>(&mut self, f: F, timeout_ms: Option<u64>) -> Result<R, ()>
    where
        F: Fn(u32),
    {
        // Create a oneshot channel to be used by the callback to notify us that the return is
        // available.
        let (tx, rx) = oneshot::channel();

        // Use a unique method call ID so that we know which callback is corresponding to which
        // method call. The actual value of the ID does not matter as long as it's always unique,
        // so a simple increment (and wraps back to 0) is good enough.
        self.last_call_id = self.last_call_id.wrapping_add(1);

        // Keep track of the sender belonging to this call id.
        self.senders.lock().unwrap().insert(self.last_call_id, tx);

        // Call the method. `f` is freely defined by the user of this utility. This must be an
        // operation that expects a callback that will trigger sending of the return via the
        // oneshot channel.
        f(self.last_call_id);

        if let Some(timeout_ms) = timeout_ms {
            let senders = self.senders.clone();
            let call_id = self.last_call_id;
            tokio::spawn(async move {
                tokio::time::sleep(Duration::from_millis(timeout_ms)).await;

                // If the timer expires first before a callback is triggered, we remove the sender
                // which will invalidate the channel which in turn will notify the receiver of
                // an error.
                // If the callback gets triggered first, this does nothing since the entry has been
                // removed when sending the response.
                senders.lock().unwrap().remove(&call_id);
            });
        }

        // Wait for the callback and return when available.
        rx.await.map_err(|_| ())
    }

    /// Returns a function to be invoked when callback is triggered.
    pub(crate) fn get_callback_sender(&self) -> CallbackSender<R> {
        let senders = self.senders.clone();
        let method_name = self.method_name.clone();
        return Arc::new(Mutex::new(Box::new(move |call_id, ret| {
            if let Some(sender) = senders.lock().unwrap().remove(&call_id) {
                sender.send(ret).ok();
            } else {
                log::warn!("AsyncHelper {}: Sender no longer exists.", method_name);
            }
        })));
    }
}
+234 −23
Original line number Diff line number Diff line
@@ -10,10 +10,12 @@ use bt_topshim::profiles::gatt::{
    GattClientCallbacks, GattClientCallbacksDispatcher, GattScannerCallbacks,
    GattScannerCallbacksDispatcher, GattScannerInbandCallbacks,
    GattScannerInbandCallbacksDispatcher, GattServerCallbacksDispatcher, GattStatus, LePhy,
    MsftAdvMonitor, MsftAdvMonitorPattern,
};
use bt_topshim::topstack;
use bt_utils::adv_parser;

use crate::async_helper::{AsyncHelper, CallbackSender};
use crate::bluetooth::{Bluetooth, IBluetooth};
use crate::bluetooth_adv::{
    AdvertiseData, Advertisers, AdvertisingSetInfo, AdvertisingSetParameters,
@@ -28,6 +30,7 @@ use num_traits::clamp;
use rand::rngs::SmallRng;
use rand::{RngCore, SeedableRng};
use std::collections::{HashMap, HashSet};
use std::convert::TryInto;
use std::sync::{Arc, Mutex, MutexGuard};
use tokio::sync::mpsc::Sender;

@@ -665,7 +668,7 @@ pub struct ScanResult {
    pub adv_data: Vec<u8>,
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ScanFilterPattern {
    /// Specifies the starting byte position of the pattern immediately following AD Type.
    pub start_position: u8,
@@ -681,7 +684,7 @@ pub struct ScanFilterPattern {
/// Represents the condition for matching advertisements.
///
/// Only pattern-based matching is implemented.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum ScanFilterCondition {
    /// All advertisements are matched.
    All,
@@ -704,7 +707,7 @@ pub enum ScanFilterCondition {
/// This filter is intentionally modelled close to the MSFT hardware offload filter.
/// Reference:
/// https://learn.microsoft.com/en-us/windows-hardware/drivers/bluetooth/microsoft-defined-bluetooth-hci-commands-and-events
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ScanFilter {
    /// Advertisements with RSSI above or equal this value is considered "found".
    pub rssi_high_threshold: u8,
@@ -726,6 +729,76 @@ pub struct ScanFilter {

type ScannersMap = HashMap<Uuid, ScannerInfo>;

const DEFAULT_ASYNC_TIMEOUT_MS: u64 = 5000;

/// Abstraction for async GATT operations. Contains async methods for coordinating async operations
/// more conveniently.
struct GattAsyncIntf {
    scanners: Arc<Mutex<ScannersMap>>,
    gatt: Option<Arc<Mutex<Gatt>>>,

    async_helper_msft_adv_monitor_add: AsyncHelper<(u8, u8)>,
    async_helper_msft_adv_monitor_remove: AsyncHelper<u8>,
    async_helper_msft_adv_monitor_enable: AsyncHelper<u8>,
}

impl GattAsyncIntf {
    /// Adds an advertisement monitor. Returns monitor handle and status.
    async fn msft_adv_monitor_add(&mut self, monitor: MsftAdvMonitor) -> Result<(u8, u8), ()> {
        let gatt = self.gatt.as_ref().unwrap().clone();

        self.async_helper_msft_adv_monitor_add
            .call_method(
                move |call_id| {
                    gatt.lock().unwrap().scanner.msft_adv_monitor_add(call_id, &monitor);
                },
                Some(DEFAULT_ASYNC_TIMEOUT_MS),
            )
            .await
    }

    /// Removes an advertisement monitor. Returns status.
    async fn msft_adv_monitor_remove(&mut self, monitor_handle: u8) -> Result<u8, ()> {
        let gatt = self.gatt.as_ref().unwrap().clone();

        self.async_helper_msft_adv_monitor_remove
            .call_method(
                move |call_id| {
                    gatt.lock().unwrap().scanner.msft_adv_monitor_remove(call_id, monitor_handle);
                },
                Some(DEFAULT_ASYNC_TIMEOUT_MS),
            )
            .await
    }

    /// Enables/disables an advertisement monitor. Returns status.
    async fn msft_adv_monitor_enable(&mut self, enable: bool) -> Result<u8, ()> {
        let gatt = self.gatt.as_ref().unwrap().clone();

        self.async_helper_msft_adv_monitor_enable
            .call_method(
                move |call_id| {
                    gatt.lock().unwrap().scanner.msft_adv_monitor_enable(call_id, enable);
                },
                Some(DEFAULT_ASYNC_TIMEOUT_MS),
            )
            .await
    }

    /// Updates the topshim's scan state depending on the states of registered scanners. Scan is
    /// enabled if there is at least 1 active registered scanner.
    ///
    /// Note: this does not need to be async, but declared as async for consistency in this struct.
    /// May be converted into real async in the future if btif supports it.
    async fn update_scan(&mut self) {
        if self.scanners.lock().unwrap().values().find(|scanner| scanner.is_active).is_some() {
            self.gatt.as_ref().unwrap().lock().unwrap().scanner.start_scan();
        } else {
            self.gatt.as_ref().unwrap().lock().unwrap().scanner.stop_scan();
        }
    }
}

/// Implementation of the GATT API (IBluetoothGatt).
pub struct BluetoothGatt {
    intf: Arc<Mutex<BluetoothInterface>>,
@@ -741,14 +814,25 @@ pub struct BluetoothGatt {
    scanners: Arc<Mutex<ScannersMap>>,
    advertisers: Advertisers,

    adv_mon_add_cb_sender: CallbackSender<(u8, u8)>,
    adv_mon_remove_cb_sender: CallbackSender<u8>,
    adv_mon_enable_cb_sender: CallbackSender<u8>,

    // Used for generating random UUIDs. SmallRng is chosen because it is fast, don't use this for
    // cryptography.
    small_rng: SmallRng,

    gatt_async: Arc<tokio::sync::Mutex<GattAsyncIntf>>,
}

impl BluetoothGatt {
    /// Constructs a new IBluetoothGatt implementation.
    pub fn new(intf: Arc<Mutex<BluetoothInterface>>, tx: Sender<Message>) -> BluetoothGatt {
        let scanners = Arc::new(Mutex::new(HashMap::new()));

        let async_helper_msft_adv_monitor_add = AsyncHelper::new("MsftAdvMonitorAdd");
        let async_helper_msft_adv_monitor_remove = AsyncHelper::new("MsftAdvMonitorRemove");
        let async_helper_msft_adv_monitor_enable = AsyncHelper::new("MsftAdvMonitorEnable");
        BluetoothGatt {
            intf,
            gatt: None,
@@ -756,9 +840,19 @@ impl BluetoothGatt {
            context_map: ContextMap::new(tx.clone()),
            reliable_queue: HashSet::new(),
            scanner_callbacks: Callbacks::new(tx.clone(), Message::ScannerCallbackDisconnected),
            scanners: Arc::new(Mutex::new(HashMap::new())),
            scanners: scanners.clone(),
            small_rng: SmallRng::from_entropy(),
            advertisers: Advertisers::new(tx.clone()),
            adv_mon_add_cb_sender: async_helper_msft_adv_monitor_add.get_callback_sender(),
            adv_mon_remove_cb_sender: async_helper_msft_adv_monitor_remove.get_callback_sender(),
            adv_mon_enable_cb_sender: async_helper_msft_adv_monitor_enable.get_callback_sender(),
            gatt_async: Arc::new(tokio::sync::Mutex::new(GattAsyncIntf {
                scanners,
                gatt: None,
                async_helper_msft_adv_monitor_add,
                async_helper_msft_adv_monitor_remove,
                async_helper_msft_adv_monitor_enable,
            })),
        }
    }

@@ -831,6 +925,12 @@ impl BluetoothGatt {
            gatt_adv_inband_callbacks_dispatcher,
            gatt_adv_callbacks_dispatcher,
        );

        let gatt = self.gatt.clone();
        let gatt_async = self.gatt_async.clone();
        tokio::spawn(async move {
            gatt_async.lock().await.gatt = gatt;
        });
    }

    /// Remove a scanner callback and unregisters all scanners associated with that callback.
@@ -878,16 +978,6 @@ impl BluetoothGatt {
        todo!()
    }

    // Update the topshim's scan state depending on the states of registered scanners. Scan is
    // enabled if there is at least 1 active registered scanner.
    fn update_scan(&mut self) {
        if self.scanners.lock().unwrap().values().find(|scanner| scanner.is_active).is_some() {
            self.gatt.as_ref().unwrap().lock().unwrap().scanner.start_scan();
        } else {
            self.gatt.as_ref().unwrap().lock().unwrap().scanner.stop_scan();
        }
    }

    fn find_scanner_by_id<'a>(
        scanners: &'a mut MutexGuard<ScannersMap>,
        scanner_id: u8,
@@ -978,6 +1068,49 @@ struct ScannerInfo {
    scanner_id: Option<u8>,
    // If one of scanners is active, we scan.
    is_active: bool,
    // Scan filter.
    filter: Option<ScanFilter>,
    // Adv monitor handle, if exists.
    monitor_handle: Option<u8>,
}

impl ScannerInfo {
    fn new(callback_id: u32) -> Self {
        Self { callback_id, scanner_id: None, is_active: false, filter: None, monitor_handle: None }
    }
}

impl Into<MsftAdvMonitorPattern> for &ScanFilterPattern {
    fn into(self) -> MsftAdvMonitorPattern {
        MsftAdvMonitorPattern {
            ad_type: self.ad_type,
            start_byte: self.start_position,
            pattern: self.content.clone(),
        }
    }
}

impl Into<Vec<MsftAdvMonitorPattern>> for &ScanFilterCondition {
    fn into(self) -> Vec<MsftAdvMonitorPattern> {
        match self {
            ScanFilterCondition::Patterns(patterns) => {
                patterns.iter().map(|pattern| pattern.into()).collect()
            }
            _ => vec![],
        }
    }
}

impl Into<MsftAdvMonitor> for &ScanFilter {
    fn into(self) -> MsftAdvMonitor {
        MsftAdvMonitor {
            rssi_high_threshold: self.rssi_high_threshold.try_into().unwrap(),
            rssi_low_threshold: self.rssi_low_threshold.try_into().unwrap(),
            rssi_low_timeout: self.rssi_low_timeout.try_into().unwrap(),
            rssi_sampling_period: self.rssi_sampling_period.try_into().unwrap(),
            patterns: (&self.condition).into(),
        }
    }
}

impl IBluetoothGatt for BluetoothGatt {
@@ -999,10 +1132,7 @@ impl IBluetoothGatt for BluetoothGatt {
        self.small_rng.fill_bytes(&mut bytes);
        let uuid = Uuid::from(bytes);

        self.scanners
            .lock()
            .unwrap()
            .insert(uuid, ScannerInfo { callback_id, scanner_id: None, is_active: false });
        self.scanners.lock().unwrap().insert(uuid, ScannerInfo::new(callback_id));

        // libbluetooth's register_scanner takes a UUID of the scanning application. This UUID does
        // not correspond to higher level concept of "application" so we use random UUID that
@@ -1030,7 +1160,7 @@ impl IBluetoothGatt for BluetoothGatt {
        &mut self,
        scanner_id: u8,
        _settings: ScanSettings,
        _filter: Option<ScanFilter>,
        filter: Option<ScanFilter>,
    ) -> BtStatus {
        // Multiplexing scanners happens at this layer. The implementations of start_scan
        // and stop_scan maintains the state of all registered scanners and based on the states
@@ -1038,31 +1168,81 @@ impl IBluetoothGatt for BluetoothGatt {
        // TODO(b/217274432): Honor settings and filters.
        {
            let mut scanners_lock = self.scanners.lock().unwrap();

            if let Some(scanner) = Self::find_scanner_by_id(&mut scanners_lock, scanner_id) {
                scanner.is_active = true;
                scanner.filter = filter.clone();
            } else {
                log::warn!("Scanner {} not found", scanner_id);
                return BtStatus::Fail;
            }
        }

        self.update_scan();
        let gatt_async = self.gatt_async.clone();
        tokio::spawn(async move {
            // The three operations below (monitor add, monitor enable, update scan) happen one
            // after another, and cannot be interleaved with other GATT async operations.
            // So acquire the GATT async lock in the beginning of this block and will be released
            // at the end of this block.
            // TODO(b/217274432): Consider not using async model but instead add actions when
            // handling callbacks.
            let mut gatt_async = gatt_async.lock().await;

            if let Some(filter) = filter {
                let monitor_handle = match gatt_async.msft_adv_monitor_add((&filter).into()).await {
                    Ok((handle, 0)) => handle,
                    _ => {
                        log::error!("Error adding advertisement monitor");
                        return;
                    }
                };

                log::debug!("Added adv monitor handle = {}", monitor_handle);

                if !gatt_async
                    .msft_adv_monitor_enable(true)
                    .await
                    .map_or(false, |status| status == 0)
                {
                    log::error!("Error enabling Advertisement Monitor");
                }
            }

            gatt_async.update_scan().await;
        });

        BtStatus::Success
    }

    fn stop_scan(&mut self, scanner_id: u8) -> BtStatus {
        {
        let monitor_handle = {
            let mut scanners_lock = self.scanners.lock().unwrap();

            if let Some(scanner) = Self::find_scanner_by_id(&mut scanners_lock, scanner_id) {
                scanner.is_active = false;
                scanner.monitor_handle
            } else {
                log::warn!("Scanner {} not found", scanner_id);
                // Clients can assume success of the removal since the scanner does not exist.
                return BtStatus::Success;
            }
        };

        let gatt_async = self.gatt_async.clone();
        tokio::spawn(async move {
            // The two operations below (monitor remove, update scan) happen one after another, and
            // cannot be interleaved with other GATT async operations.
            // So acquire the GATT async lock in the beginning of this block and will be released
            // at the end of this block.
            let mut gatt_async = gatt_async.lock().await;

            if let Some(handle) = monitor_handle {
                let _res = gatt_async.msft_adv_monitor_remove(handle).await;
            }

        self.update_scan();
            gatt_async.update_scan().await;
        });

        BtStatus::Success
    }

@@ -2297,6 +2477,20 @@ pub(crate) trait BtifGattScannerInbandCallbacks {
        btm_status: u8,
    );

    #[btif_callback(MsftAdvMonitorAddCallback)]
    fn inband_msft_adv_monitor_add_callback(
        &mut self,
        call_id: u32,
        monitor_handle: u8,
        status: u8,
    );

    #[btif_callback(MsftAdvMonitorRemoveCallback)]
    fn inband_msft_adv_monitor_remove_callback(&mut self, call_id: u32, status: u8);

    #[btif_callback(MsftAdvMonitorEnableCallback)]
    fn inband_msft_adv_monitor_enable_callback(&mut self, call_id: u32, status: u8);

    #[btif_callback(StartSyncCallback)]
    fn inband_start_sync_callback(
        &mut self,
@@ -2386,6 +2580,23 @@ impl BtifGattScannerInbandCallbacks for BluetoothGatt {
        );
    }

    fn inband_msft_adv_monitor_add_callback(
        &mut self,
        call_id: u32,
        monitor_handle: u8,
        status: u8,
    ) {
        (self.adv_mon_add_cb_sender.lock().unwrap())(call_id, (monitor_handle, status));
    }

    fn inband_msft_adv_monitor_remove_callback(&mut self, call_id: u32, status: u8) {
        (self.adv_mon_remove_cb_sender.lock().unwrap())(call_id, status);
    }

    fn inband_msft_adv_monitor_enable_callback(&mut self, call_id: u32, status: u8) {
        (self.adv_mon_enable_cb_sender.lock().unwrap())(call_id, status);
    }

    fn inband_start_sync_callback(
        &mut self,
        status: u8,
+1 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ extern crate num_derive;
#[macro_use]
extern crate lazy_static;

pub mod async_helper;
pub mod battery_manager;
pub mod battery_provider_manager;
pub mod battery_service;
+12 −12
Original line number Diff line number Diff line
@@ -245,20 +245,20 @@ void BleScannerIntf::ScanFilterEnable(bool enable) {
  scanner_intf_->ScanFilterEnable(enable, base::Bind(&BleScannerIntf::OnEnableCallback, base::Unretained(this)));
}

void BleScannerIntf::MsftAdvMonitorAdd(RustMsftAdvMonitor monitor) {
void BleScannerIntf::MsftAdvMonitorAdd(uint32_t call_id, const RustMsftAdvMonitor& monitor) {
  scanner_intf_->MsftAdvMonitorAdd(
      internal::ConvertAdvMonitor(monitor),
      base::Bind(&BleScannerIntf::OnMsftAdvMonitorAddCallback, base::Unretained(this)));
      base::Bind(&BleScannerIntf::OnMsftAdvMonitorAddCallback, base::Unretained(this), call_id));
}

void BleScannerIntf::MsftAdvMonitorRemove(uint8_t monitor_handle) {
void BleScannerIntf::MsftAdvMonitorRemove(uint32_t call_id, uint8_t monitor_handle) {
  scanner_intf_->MsftAdvMonitorRemove(
      monitor_handle, base::Bind(&BleScannerIntf::OnMsftAdvMonitorRemoveCallback, base::Unretained(this)));
      monitor_handle, base::Bind(&BleScannerIntf::OnMsftAdvMonitorRemoveCallback, base::Unretained(this), call_id));
}

void BleScannerIntf::MsftAdvMonitorEnable(bool enable) {
void BleScannerIntf::MsftAdvMonitorEnable(uint32_t call_id, bool enable) {
  scanner_intf_->MsftAdvMonitorEnable(
      enable, base::Bind(&BleScannerIntf::OnMsftAdvMonitorEnableCallback, base::Unretained(this)));
      enable, base::Bind(&BleScannerIntf::OnMsftAdvMonitorEnableCallback, base::Unretained(this), call_id));
}

void BleScannerIntf::SetScanParameters(uint8_t scanner_id, uint16_t scan_interval, uint16_t scan_window) {
@@ -347,16 +347,16 @@ void BleScannerIntf::OnFilterConfigCallback(
  rusty::gdscan_filter_config_callback(filter_index, filt_type, avbl_space, action, btm_status);
}

void BleScannerIntf::OnMsftAdvMonitorAddCallback(uint8_t monitor_handle, uint8_t status) {
  rusty::gdscan_msft_adv_monitor_add_callback(monitor_handle, status);
void BleScannerIntf::OnMsftAdvMonitorAddCallback(uint32_t call_id, uint8_t monitor_handle, uint8_t status) {
  rusty::gdscan_msft_adv_monitor_add_callback(call_id, monitor_handle, status);
}

void BleScannerIntf::OnMsftAdvMonitorRemoveCallback(uint8_t status) {
  rusty::gdscan_msft_adv_monitor_remove_callback(status);
void BleScannerIntf::OnMsftAdvMonitorRemoveCallback(uint32_t call_id, uint8_t status) {
  rusty::gdscan_msft_adv_monitor_remove_callback(call_id, status);
}

void BleScannerIntf::OnMsftAdvMonitorEnableCallback(uint8_t status) {
  rusty::gdscan_msft_adv_monitor_enable_callback(status);
void BleScannerIntf::OnMsftAdvMonitorEnableCallback(uint32_t call_id, uint8_t status) {
  rusty::gdscan_msft_adv_monitor_enable_callback(call_id, status);
}

void BleScannerIntf::OnPeriodicSyncStarted(
Loading