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

Commit 8b4b83cf authored by Ying Hsu's avatar Ying Hsu
Browse files

floss: Add advertise set-interval command

`advertise set-interval` command changes the advertising interval
of all advertising sets registered.
This patch also introduces struct AdvSet to keep information of an
advertising set, so that btclient has the capability to alter
current parameters and data.

Bug: 233128828
Tag: #floss
Test: build.py --target test
Test: btclient advertise set-interval 400

Change-Id: I2c1db17399215c1d06180a82e326743c47208c2d
parent 2b412dc8
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
use std::collections::HashMap;

use bt_topshim::btif::Uuid;
use bt_topshim::profiles::gatt::LePhy;
use btstack::bluetooth_adv::{AdvertiseData, AdvertiserId, AdvertisingSetParameters};

/// Avertisement parameter and data for a BLE advertising set.
#[derive(Debug, Clone)]
pub(crate) struct AdvSet {
    /// ID for the advertising set if it's being started successfully, None otherwise.
    pub(crate) adv_id: Option<AdvertiserId>,

    /// Advertising parameters.
    pub(crate) params: AdvertisingSetParameters,

    /// Advertising data.
    pub(crate) data: AdvertiseData,
}

impl AdvSet {
    pub(crate) fn new() -> Self {
        let params = AdvertisingSetParameters {
            connectable: false,
            scannable: false,
            is_legacy: true,
            is_anonymous: false,
            include_tx_power: true,
            primary_phy: LePhy::Phy1m,
            secondary_phy: LePhy::Phy1m,
            interval: 100,
            tx_power_level: 0x7f, // no preference
            own_address_type: 1,  // random
        };

        let data = AdvertiseData {
            service_uuids: vec![Uuid::from([
                0x00, 0x00, 0xfe, 0xf3, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b,
                0x34, 0xfb,
            ])],
            solicit_uuids: Vec::new(),
            transport_discovery_data: Vec::new(),
            manufacturer_data: HashMap::from([(0, vec![0, 1, 2])]),
            service_data: HashMap::from([(
                "0000fef3-0000-1000-8000-00805f9b34fb".to_string(),
                vec![0x0a, 0x0b],
            )]),
            include_tx_power_level: true,
            include_device_name: true,
        };

        AdvSet { adv_id: None, params, data }
    }
}
+8 −12
Original line number Diff line number Diff line
@@ -436,22 +436,18 @@ impl IAdvertisingSetCallback for AdvertisingSetCallback {
            tx_power,
            status
        );
        if status == GattStatus::Success {
            if let Some(Some(ex_adv_id)) =
                self.context.lock().unwrap().adv_sets.insert(reg_id, Some(advertiser_id))
            {
                print_error!(
                    "on_advertising_set_started: previous advertising set ({}) registered ({}) is omitted",
                    ex_adv_id,
                    reg_id,
                    );
            }
        } else {
        if status != GattStatus::Success {
            print_error!(
                "on_advertising_set_started: remove advertising set registered ({})",
                reg_id
            );
            self.context.lock().unwrap().adv_sets.remove(&reg_id);
            return;
        }
        if let Some(s) = self.context.lock().unwrap().adv_sets.get_mut(&reg_id) {
            s.adv_id = Some(advertiser_id);
        } else {
            print_error!("on_advertising_set_started: invalid callback for reg_id={}", reg_id);
        }
    }

@@ -466,7 +462,7 @@ impl IAdvertisingSetCallback for AdvertisingSetCallback {

    fn on_advertising_set_stopped(&self, advertiser_id: i32) {
        print_info!("on_advertising_set_stopped: advertiser_id = {}", advertiser_id);
        self.context.lock().unwrap().adv_sets.retain(|_, val| *val != Some(advertiser_id));
        self.context.lock().unwrap().adv_sets.retain(|_, s| s.adv_id != Some(advertiser_id));
    }

    fn on_advertising_enabled(&self, advertiser_id: i32, enable: bool, status: GattStatus) {
+66 −46
Original line number Diff line number Diff line
@@ -2,13 +2,14 @@ use std::collections::HashMap;
use std::fmt::{Display, Formatter, Result};
use std::sync::{Arc, Mutex};

use crate::bt_adv::AdvSet;
use crate::callbacks::BtGattCallback;
use crate::ClientContext;
use crate::{console_red, console_yellow, print_error, print_info};
use bt_topshim::btif::{BtConnectionState, BtStatus, BtTransport, Uuid};
use bt_topshim::btif::{BtConnectionState, BtStatus, BtTransport};
use bt_topshim::profiles::gatt::LePhy;
use btstack::bluetooth::{BluetoothDevice, IBluetooth, IBluetoothQA};
use btstack::bluetooth_adv::{AdvertiseData, AdvertisingSetParameters};
use btstack::bluetooth_adv::{AdvertiserId, RegId};
use btstack::bluetooth_gatt::{
    IBluetoothGatt, ScanFilter, ScanFilterCondition, ScanSettings, ScanType,
};
@@ -164,7 +165,7 @@ fn build_commands() -> HashMap<String, CommandOption> {
    command_options.insert(
        String::from("advertise"),
        CommandOption {
            rules: vec![String::from("advertise <on|off>")],
            rules: vec![String::from("advertise <on|off|set-interval>")],
            description: String::from("Advertising utilities."),
            function_pointer: CommandHandler::cmd_advertise,
        },
@@ -939,67 +940,86 @@ impl CommandHandler {
        }
        let callback_id = self.context.lock().unwrap().advertiser_callback_id.clone().unwrap();

        enforce_arg_len(args, 1, "advertise <commands>", || match &args[0][0..] {
        enforce_arg_len(args, 1, "advertise <on|off|set-interval>", || match &args[0][0..] {
            "on" => {
                if self.context.lock().unwrap().adv_sets.keys().len() > 0 {
                    print_error!("Already started advertising");
                    return;
                }

                let params = AdvertisingSetParameters {
                    connectable: false,
                    scannable: false,
                    is_legacy: true,
                    is_anonymous: false,
                    include_tx_power: true,
                    primary_phy: LePhy::Phy1m,
                    secondary_phy: LePhy::Phy1m,
                    interval: 160,
                    tx_power_level: -21,
                    own_address_type: 0, // random
                };

                let data = AdvertiseData {
                    service_uuids: vec![Uuid::from([
                        0x00, 0x00, 0xfe, 0xf3, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80,
                        0x5f, 0x9b, 0x34, 0xfb,
                    ])],
                    solicit_uuids: Vec::new(),
                    transport_discovery_data: Vec::new(),
                    manufacturer_data: HashMap::from([(0, vec![0, 1, 2])]),
                    service_data: HashMap::from([(
                        "0000fef3-0000-1000-8000-00805f9b34fb".to_string(),
                        vec![0x0a, 0x0b],
                    )]),
                    include_tx_power_level: true,
                    include_device_name: true,
                };

                let reg_id = self
                let s = AdvSet::new();
                let reg_id =
                    self.context.lock().unwrap().gatt_dbus.as_mut().unwrap().start_advertising_set(
                        s.params.clone(),
                        s.data.clone(),
                        None,
                        None,
                        None,
                        0,
                        0,
                        callback_id,
                    );
                print_info!("Starting advertising set for reg_id = {}", reg_id);
                self.context.lock().unwrap().adv_sets.insert(reg_id, s);
            }
            "off" => {
                let adv_ids: Vec<AdvertiserId> = self
                    .context
                    .lock()
                    .unwrap()
                    .adv_sets
                    .iter()
                    .filter_map(|(_, s)| s.adv_id)
                    .collect();
                for adv_id in adv_ids {
                    print_info!("Stopping advertising set {}", adv_id);
                    self.context
                        .lock()
                        .unwrap()
                        .gatt_dbus
                        .as_mut()
                        .unwrap()
                    .start_advertising_set(params, data, None, None, None, 0, 0, callback_id);
                print_info!("Starting advertising set for reg_id = {}", reg_id);
                        .stop_advertising_set(adv_id);
                }
            "off" => {
                let adv_sets = self.context.lock().unwrap().adv_sets.clone();
                for (_, val) in adv_sets.iter() {
                    if let Some(&adv_id) = val.as_ref() {
                        print_info!("Stopping advertising set {}", adv_id);
                self.context.lock().unwrap().adv_sets.clear();
            }
            "set-interval" => {
                if args.len() < 2 {
                    println!("usage: advertise set-interval <ms>");
                    return;
                }
                let ms = String::from(&args[1]).parse::<i32>();
                if !ms.is_ok() {
                    print_error!("Failed parsing interval");
                    return;
                }
                let interval = ms.unwrap() * 8 / 5; // in 0.625 ms.

                let reg_ids: Vec<RegId> =
                    self.context.lock().unwrap().adv_sets.keys().copied().collect();
                for reg_id in reg_ids {
                    self.context
                        .lock()
                        .unwrap()
                        .adv_sets
                        .get_mut(&reg_id)
                        .unwrap()
                        .params
                        .interval = interval;

                    let adv_id = self.context.lock().unwrap().adv_sets[&reg_id].adv_id.clone();
                    if let Some(adv_id) = adv_id {
                        let params = self.context.lock().unwrap().adv_sets[&reg_id].params.clone();
                        print_info!("Setting advertising parameters for {}", adv_id);
                        self.context
                            .lock()
                            .unwrap()
                            .gatt_dbus
                            .as_mut()
                            .unwrap()
                            .stop_advertising_set(adv_id);
                            .set_advertising_parameters(adv_id, params);
                    }
                }
                self.context.lock().unwrap().adv_sets.clear();
            }
            _ => {
                println!("Invalid argument '{}'", args[0]);
+4 −2
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ use dbus::nonblock::SyncConnection;
use dbus_crossroads::Crossroads;
use tokio::sync::mpsc;

use crate::bt_adv::AdvSet;
use crate::callbacks::{
    AdminCallback, AdvertisingSetCallback, BtCallback, BtConnectionCallback, BtManagerCallback,
    BtSocketManagerCallback, ScannerCallback, SuspendCallback,
@@ -22,6 +23,7 @@ use btstack::bluetooth::{BluetoothDevice, IBluetooth};
use btstack::suspend::ISuspend;
use manager_service::iface_bluetooth_manager::IBluetoothManager;

mod bt_adv;
mod callbacks;
mod command_handler;
mod console;
@@ -107,8 +109,8 @@ pub(crate) struct ClientContext {
    /// Keeps track of active LE scanners.
    active_scanner_ids: HashSet<u8>,

    /// Advertising sets started/registered. Map from reg_id to advertiser_id.
    adv_sets: HashMap<i32, Option<i32>>,
    /// Keeps track of advertising sets registered. Map from reg_id to AdvSet.
    adv_sets: HashMap<i32, AdvSet>,

    /// Identifies the callback to receive IBluetoothSocketManagerCallback method calls.
    socket_manager_callback_id: Option<u32>,
+2 −2
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ pub type RegId = i32;
pub type ManfId = u16;

/// Advertising parameters for each BLE advertising set.
#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct AdvertisingSetParameters {
    /// Whether the advertisement will be connectable.
    pub connectable: bool,
@@ -49,7 +49,7 @@ pub struct AdvertisingSetParameters {
}

/// Represents the data to be advertised and the scan response data for active scans.
#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct AdvertiseData {
    /// A list of service UUIDs within the advertisement that are used to identify
    /// the Bluetooth GATT services.
Loading