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

Commit e4de4806 authored by Hsin-chen Chuang's avatar Hsin-chen Chuang Committed by Gerrit Code Review
Browse files

Merge changes from topic "qualification-hfp"

* changes:
  floss: telephony: Support for SCO manipulate
  floss: telephony: Support hold call operations
  floss: telephony: Support dialing operations from HF
  floss: telephony: Implemented basic phone operations
  floss: media: Store call status and handle CLCC in stack
  floss: media: Add set device status support
  topshim: hfp: Respond to AT+CNUM query
parents 19565cda f41438df
Loading
Loading
Loading
Loading
+223 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ use bt_topshim::profiles::hid_host::BthhReportType;
use bt_topshim::profiles::{gatt::LePhy, ProfileConnectionState};
use btstack::bluetooth::{BluetoothDevice, IBluetooth, IBluetoothQA};
use btstack::bluetooth_gatt::{GattWriteType, IBluetoothGatt, ScanSettings, ScanType};
use btstack::bluetooth_media::IBluetoothTelephony;
use btstack::socket_manager::{IBluetoothSocketManager, SocketResult};
use btstack::uuid::{Profile, UuidHelper, UuidWrapper};
use manager_service::iface_bluetooth_manager::IBluetoothManager;
@@ -271,6 +272,27 @@ fn build_commands() -> HashMap<String, CommandOption> {
            function_pointer: CommandHandler::cmd_list_devices,
        },
    );
    command_options.insert(
        String::from("telephony"),
        CommandOption {
            rules: vec![
                String::from("telephony set-network <on|off>"),
                String::from("telephony set-roaming <on|off>"),
                String::from("telephony set-signal <strength>"),
                String::from("telephony set-battery <level>"),
                String::from("telephony <enable|disable>"),
                String::from("telephony <incoming-call|dialing-call> <number>"),
                String::from("telephony <answer-call|hangup-call>"),
                String::from("telephony <set-memory-call|set-last-call> [<number>]"),
                String::from(
                    "telephony <release-held|release-active-accept-held|hold-active-accept-held>",
                ),
                String::from("telephony <audio-connect|audio-disconnect> <address>"),
            ],
            description: String::from("Set device telephony status."),
            function_pointer: CommandHandler::cmd_telephony,
        },
    );
    command_options.insert(
        String::from("quit"),
        CommandOption {
@@ -1498,6 +1520,207 @@ impl CommandHandler {

        Ok(())
    }

    fn cmd_telephony(&mut self, args: &Vec<String>) -> CommandResult {
        if !self.context.lock().unwrap().adapter_ready {
            return Err(self.adapter_not_ready());
        }

        match &get_arg(args, 0)?[..] {
            "set-network" => {
                self.context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .set_network_available(match &get_arg(args, 1)?[..] {
                        "on" => true,
                        "off" => false,
                        other => {
                            return Err(format!("Invalid argument '{}'", other).into());
                        }
                    });
            }
            "set-roaming" => {
                self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().set_roaming(
                    match &get_arg(args, 1)?[..] {
                        "on" => true,
                        "off" => false,
                        other => {
                            return Err(format!("Invalid argument '{}'", other).into());
                        }
                    },
                );
            }
            "set-signal" => {
                let strength = String::from(get_arg(args, 1)?)
                    .parse::<i32>()
                    .or(Err("Failed parsing signal strength"))?;
                if strength < 0 || strength > 5 {
                    return Err(
                        format!("Invalid signal strength, got {}, want 0 to 5", strength).into()
                    );
                }
                self.context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .set_signal_strength(strength);
            }
            "set-battery" => {
                let level = String::from(get_arg(args, 1)?)
                    .parse::<i32>()
                    .or(Err("Failed parsing battery level"))?;
                if level < 0 || level > 5 {
                    return Err(format!("Invalid battery level, got {}, want 0 to 5", level).into());
                }
                self.context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .set_battery_level(level);
            }
            "enable" | "disable" => {
                self.context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .set_phone_ops_enabled(get_arg(args, 0)? == "enable");
            }
            "incoming-call" => {
                let success = self
                    .context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .incoming_call(String::from(get_arg(args, 1)?));
                if !success {
                    return Err("IncomingCall failed".into());
                }
            }
            "dialing-call" => {
                let success = self
                    .context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .dialing_call(String::from(get_arg(args, 1)?));
                if !success {
                    return Err("DialingCall failed".into());
                }
            }
            "answer-call" => {
                let success =
                    self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().answer_call();
                if !success {
                    return Err("AnswerCall failed".into());
                }
            }
            "hangup-call" => {
                let success =
                    self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().hangup_call();
                if !success {
                    return Err("HangupCall failed".into());
                }
            }
            "set-memory-call" => {
                let success = self
                    .context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .set_memory_call(get_arg(args, 1).ok().map(String::from));
                if !success {
                    return Err("SetMemoryCall failed".into());
                }
            }
            "set-last-call" => {
                let success = self
                    .context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .set_last_call(get_arg(args, 1).ok().map(String::from));
                if !success {
                    return Err("SetLastCall failed".into());
                }
            }
            "release-held" => {
                let success =
                    self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().release_held();
                if !success {
                    return Err("ReleaseHeld failed".into());
                }
            }
            "release-active-accept-held" => {
                let success = self
                    .context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .release_active_accept_held();
                if !success {
                    return Err("ReleaseActiveAcceptHeld failed".into());
                }
            }
            "hold-active-accept-held" => {
                let success = self
                    .context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .hold_active_accept_held();
                if !success {
                    return Err("HoldActiveAcceptHeld failed".into());
                }
            }
            "audio-connect" => {
                let success = self
                    .context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .audio_connect(String::from(get_arg(args, 1)?));
                if !success {
                    return Err("ConnectAudio failed".into());
                }
            }
            "audio-disconnect" => {
                self.context
                    .lock()
                    .unwrap()
                    .telephony_dbus
                    .as_mut()
                    .unwrap()
                    .audio_disconnect(String::from(get_arg(args, 1)?));
            }
            other => {
                return Err(format!("Invalid argument '{}'", other).into());
            }
        }
        Ok(())
    }
}

#[cfg(test)]
+86 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ use btstack::bluetooth_gatt::{
    IBluetoothGattServerCallback, IScannerCallback, ScanFilter, ScanFilterCondition,
    ScanFilterPattern, ScanResult, ScanSettings, ScanType,
};
use btstack::bluetooth_media::IBluetoothTelephony;
use btstack::socket_manager::{
    BluetoothServerSocket, BluetoothSocket, CallbackId, IBluetoothSocketManager,
    IBluetoothSocketManagerCallbacks, SocketId, SocketResult,
@@ -1817,3 +1818,88 @@ impl ISuspendCallback for ISuspendCallbackDBus {
    #[dbus_method("OnResumed")]
    fn on_resumed(&self, suspend_id: i32) {}
}

pub(crate) struct BluetoothTelephonyDBus {
    client_proxy: ClientDBusProxy,
}

impl BluetoothTelephonyDBus {
    pub(crate) fn new(conn: Arc<SyncConnection>, index: i32) -> BluetoothTelephonyDBus {
        BluetoothTelephonyDBus {
            client_proxy: ClientDBusProxy::new(
                conn.clone(),
                String::from("org.chromium.bluetooth"),
                make_object_path(index, "telephony"),
                String::from("org.chromium.bluetooth.BluetoothTelephony"),
            ),
        }
    }
}

#[generate_dbus_interface_client]
impl IBluetoothTelephony for BluetoothTelephonyDBus {
    #[dbus_method("SetNetworkAvailable")]
    fn set_network_available(&mut self, network_available: bool) {
        dbus_generated!()
    }
    #[dbus_method("SetRoaming")]
    fn set_roaming(&mut self, roaming: bool) {
        dbus_generated!()
    }
    #[dbus_method("SetSignalStrength")]
    fn set_signal_strength(&mut self, signal_strength: i32) -> bool {
        dbus_generated!()
    }
    #[dbus_method("SetBatteryLevel")]
    fn set_battery_level(&mut self, battery_level: i32) -> bool {
        dbus_generated!()
    }
    #[dbus_method("SetPhoneOpsEnabled")]
    fn set_phone_ops_enabled(&mut self, enable: bool) {
        dbus_generated!()
    }
    #[dbus_method("IncomingCall")]
    fn incoming_call(&mut self, number: String) -> bool {
        dbus_generated!()
    }
    #[dbus_method("DialingCall")]
    fn dialing_call(&mut self, number: String) -> bool {
        dbus_generated!()
    }
    #[dbus_method("AnswerCall")]
    fn answer_call(&mut self) -> bool {
        dbus_generated!()
    }
    #[dbus_method("HangupCall")]
    fn hangup_call(&mut self) -> bool {
        dbus_generated!()
    }
    #[dbus_method("SetMemoryCall")]
    fn set_memory_call(&mut self, number: Option<String>) -> bool {
        dbus_generated!()
    }
    #[dbus_method("SetLastCall")]
    fn set_last_call(&mut self, number: Option<String>) -> bool {
        dbus_generated!()
    }
    #[dbus_method("ReleaseHeld")]
    fn release_held(&mut self) -> bool {
        dbus_generated!()
    }
    #[dbus_method("ReleaseActiveAcceptHeld")]
    fn release_active_accept_held(&mut self) -> bool {
        dbus_generated!()
    }
    #[dbus_method("HoldActiveAcceptHeld")]
    fn hold_active_accept_held(&mut self) -> bool {
        dbus_generated!()
    }
    #[dbus_method("AudioConnect")]
    fn audio_connect(&mut self, address: String) -> bool {
        dbus_generated!()
    }
    #[dbus_method("AudioDisconnect")]
    fn audio_disconnect(&mut self, address: String) {
        dbus_generated!()
    }
}
+7 −1
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ use crate::callbacks::{
use crate::command_handler::{CommandHandler, SocketSchedule};
use crate::dbus_iface::{
    BluetoothAdminDBus, BluetoothDBus, BluetoothGattDBus, BluetoothManagerDBus, BluetoothQADBus,
    BluetoothSocketManagerDBus, SuspendDBus,
    BluetoothSocketManagerDBus, BluetoothTelephonyDBus, SuspendDBus,
};
use crate::editor::AsyncEditor;
use bt_topshim::topstack;
@@ -89,6 +89,9 @@ pub(crate) struct ClientContext {
    /// Proxy for socket manager interface.
    pub(crate) socket_manager_dbus: Option<BluetoothSocketManagerDBus>,

    /// Proxy for Telephony interface.
    pub(crate) telephony_dbus: Option<BluetoothTelephonyDBus>,

    /// Channel to send actions to take in the foreground
    fg: mpsc::Sender<ForegroundActions>,

@@ -154,6 +157,7 @@ impl ClientContext {
            admin_dbus: None,
            suspend_dbus: None,
            socket_manager_dbus: None,
            telephony_dbus: None,
            fg: tx,
            dbus_connection,
            dbus_crossroads,
@@ -209,6 +213,8 @@ impl ClientContext {

        self.suspend_dbus = Some(SuspendDBus::new(conn.clone(), idx));

        self.telephony_dbus = Some(BluetoothTelephonyDBus::new(conn.clone(), idx));

        // Trigger callback registration in the foreground
        let fg = self.fg.clone();
        tokio::spawn(async move {
+81 −0
Original line number Diff line number Diff line
use btstack::bluetooth_media::IBluetoothTelephony;

use dbus_macros::{dbus_method, generate_dbus_exporter};

use dbus_projection::dbus_generated;

use crate::dbus_arg::DBusArg;

#[allow(dead_code)]
struct IBluetoothTelephonyDBus {}

#[generate_dbus_exporter(
    export_bluetooth_telephony_dbus_intf,
    "org.chromium.bluetooth.BluetoothTelephony"
)]
impl IBluetoothTelephony for IBluetoothTelephonyDBus {
    #[dbus_method("SetNetworkAvailable")]
    fn set_network_available(&mut self, network_available: bool) {
        dbus_generated!()
    }
    #[dbus_method("SetRoaming")]
    fn set_roaming(&mut self, roaming: bool) {
        dbus_generated!()
    }
    #[dbus_method("SetSignalStrength")]
    fn set_signal_strength(&mut self, signal_strength: i32) -> bool {
        dbus_generated!()
    }
    #[dbus_method("SetBatteryLevel")]
    fn set_battery_level(&mut self, battery_level: i32) -> bool {
        dbus_generated!()
    }
    #[dbus_method("SetPhoneOpsEnabled")]
    fn set_phone_ops_enabled(&mut self, enable: bool) {
        dbus_generated!()
    }
    #[dbus_method("IncomingCall")]
    fn incoming_call(&mut self, number: String) -> bool {
        dbus_generated!()
    }
    #[dbus_method("DialingCall")]
    fn dialing_call(&mut self, number: String) -> bool {
        dbus_generated!()
    }
    #[dbus_method("AnswerCall")]
    fn answer_call(&mut self) -> bool {
        dbus_generated!()
    }
    #[dbus_method("HangupCall")]
    fn hangup_call(&mut self) -> bool {
        dbus_generated!()
    }
    #[dbus_method("SetMemoryCall")]
    fn set_memory_call(&mut self, number: Option<String>) -> bool {
        dbus_generated!()
    }
    #[dbus_method("SetLastCall")]
    fn set_last_call(&mut self, number: Option<String>) -> bool {
        dbus_generated!()
    }
    #[dbus_method("ReleaseHeld")]
    fn release_held(&mut self) -> bool {
        dbus_generated!()
    }
    #[dbus_method("ReleaseActiveAcceptHeld")]
    fn release_active_accept_held(&mut self) -> bool {
        dbus_generated!()
    }
    #[dbus_method("HoldActiveAcceptHeld")]
    fn hold_active_accept_held(&mut self) -> bool {
        dbus_generated!()
    }
    #[dbus_method("AudioConnect")]
    fn audio_connect(&mut self, address: String) -> bool {
        dbus_generated!()
    }
    #[dbus_method("AudioDisconnect")]
    fn audio_disconnect(&mut self, address: String) {
        dbus_generated!()
    }
}
+15 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ mod iface_bluetooth;
mod iface_bluetooth_admin;
mod iface_bluetooth_gatt;
mod iface_bluetooth_media;
mod iface_bluetooth_telephony;
mod iface_logging;

const DBUS_SERVICE_NAME: &str = "org.chromium.bluetooth";
@@ -244,6 +245,12 @@ fn main() -> Result<(), Box<dyn Error>> {
            disconnect_watcher.clone(),
        );

        let telephony_iface = iface_bluetooth_telephony::export_bluetooth_telephony_dbus_intf(
            conn.clone(),
            &mut cr.lock().unwrap(),
            disconnect_watcher.clone(),
        );

        let battery_provider_manager_iface =
            iface_battery_provider_manager::export_battery_provider_manager_dbus_intf(
                conn.clone(),
@@ -288,11 +295,19 @@ fn main() -> Result<(), Box<dyn Error>> {
            &[media_iface],
            bluetooth_media.clone(),
        );

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "telephony"),
            &[telephony_iface],
            bluetooth_media.clone(),
        );

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "battery_provider_manager"),
            &[battery_provider_manager_iface],
            battery_provider_manager.clone(),
        );

        cr.lock().unwrap().insert(
            make_object_name(adapter_index, "battery_manager"),
            &[battery_manager_iface],
Loading