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

Commit 2c52b912 authored by Hsin-chen Chuang's avatar Hsin-chen Chuang
Browse files

floss: telephony: Implemented basic phone operations

Implemented the following functions and wired to btclient:
- IncomingCall
- DialingCall
- AnswerCall
- HangupCall

Handled the following command sent from the HF:
- ATA (answer call)
- AT+CHUP (hangup call)

Since the media stack modifies the phone state as well, added
SetPhoneOpsEnabled for switching the current owner of the phone state
between the telephony and the media stack.

Bug: 214149380
Tag: #floss
Test: HFP/AG/ICA/BV-06-I, HFP/AG/CIT/BV-01-I
Change-Id: I5dfddf699a59d314e9fe40b2fcf7edf641e13288
parent f0a89674
Loading
Loading
Loading
Loading
+52 −0
Original line number Diff line number Diff line
@@ -280,6 +280,9 @@ fn build_commands() -> HashMap<String, CommandOption> {
                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>"),
            ],
            description: String::from("Set device telephony status."),
            function_pointer: CommandHandler::cmd_telephony,
@@ -1579,6 +1582,55 @@ impl CommandHandler {
                    .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());
                }
            }
            other => {
                return Err(format!("Invalid argument '{}'", other).into());
            }
+20 −0
Original line number Diff line number Diff line
@@ -1854,4 +1854,24 @@ impl IBluetoothTelephony for BluetoothTelephonyDBus {
    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!()
    }
}
+20 −0
Original line number Diff line number Diff line
@@ -30,4 +30,24 @@ impl IBluetoothTelephony for IBluetoothTelephonyDBus {
    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!()
    }
}
+157 −2
Original line number Diff line number Diff line
@@ -154,6 +154,17 @@ pub trait IBluetoothTelephony {
    fn set_signal_strength(&mut self, signal_strength: i32) -> bool;
    /// Sets the device battery level, 0 to 5.
    fn set_battery_level(&mut self, battery_level: i32) -> bool;
    /// Enables/disables phone operations.
    /// The call state is fully reset whenever this is called.
    fn set_phone_ops_enabled(&mut self, enable: bool);
    /// Acts like the AG received an incoming call.
    fn incoming_call(&mut self, number: String) -> bool;
    /// Acts like dialing a call from the AG.
    fn dialing_call(&mut self, number: String) -> bool;
    /// Acts like answering an incoming/dialing call from the AG.
    fn answer_call(&mut self) -> bool;
    /// Acts like hanging up an active/incoming/dialing call from the AG.
    fn hangup_call(&mut self) -> bool;
}

/// Serializable device used in.
@@ -218,6 +229,7 @@ pub struct BluetoothMedia {
    telephony_device_status: TelephonyDeviceStatus,
    phone_state: PhoneState,
    call_list: Vec<CallInfo>,
    phone_ops_enabled: bool,
}

impl BluetoothMedia {
@@ -260,6 +272,7 @@ impl BluetoothMedia {
            telephony_device_status: TelephonyDeviceStatus::new(),
            phone_state: PhoneState { num_active: 0, num_held: 0, state: CallState::Idle },
            call_list: vec![],
            phone_ops_enabled: false,
        }
    }

@@ -599,7 +612,9 @@ impl BluetoothMedia {

                        self.hfp_audio_state.insert(addr, state);

                        if self.phone_state.num_active != 1 {
                        // Change the phone state only when it's currently managed by media stack
                        // (I.e., phone operations are not enabled).
                        if !self.phone_ops_enabled && self.phone_state.num_active != 1 {
                            // This triggers a +CIEV command to set the call status for HFP devices.
                            // It is required for some devices to provide sound.
                            self.phone_state.num_active = 1;
@@ -624,7 +639,9 @@ impl BluetoothMedia {
                            });
                        }

                        if self.phone_state.num_active != 0 {
                        // Change the phone state only when it's currently managed by media stack
                        // (I.e., phone operations are not enabled).
                        if !self.phone_ops_enabled && self.phone_state.num_active != 0 {
                            self.phone_state.num_active = 0;
                            self.call_list = vec![];
                            self.phone_state_change("".into());
@@ -708,6 +725,20 @@ impl BluetoothMedia {
                    None => warn!("Uninitialized HFP to notify telephony status"),
                };
            }
            HfpCallbacks::AnswerCall(addr) => {
                if !self.answer_call_impl() {
                    warn!("[{}]: answer_call triggered by ATA failed", addr.to_string());
                    return;
                }
                self.phone_state_change("".into());
            }
            HfpCallbacks::HangupCall(addr) => {
                if !self.hangup_call_impl() {
                    warn!("[{}]: hangup_call triggered by AT+CHUP failed", addr.to_string());
                    return;
                }
                self.phone_state_change("".into());
            }
        }
    }

@@ -1076,6 +1107,52 @@ impl BluetoothMedia {
            None => warn!("Uninitialized HFP to notify telephony status"),
        }
    }

    // Returns the minimum unoccupied index starting from 1.
    fn new_call_index(&self) -> i32 {
        (1..)
            .find(|&index| self.call_list.iter().all(|x| x.index != index))
            .expect("There must be an unoccupied index")
    }

    fn answer_call_impl(&mut self) -> bool {
        if !self.phone_ops_enabled || self.phone_state.state == CallState::Idle {
            return false;
        }
        // There must be exactly one incoming/dialing call in the list.
        for c in self.call_list.iter_mut() {
            if c.state == CallState::Incoming || c.state == CallState::Dialing {
                c.state = CallState::Active;
                break;
            }
        }
        self.phone_state.state = CallState::Idle;
        self.phone_state.num_active += 1;
        true
    }

    fn hangup_call_impl(&mut self) -> bool {
        if !self.phone_ops_enabled {
            return false;
        }
        match self.phone_state.state {
            CallState::Idle if self.phone_state.num_active > 0 => {
                self.phone_state.num_active -= 1;
            }
            CallState::Incoming | CallState::Dialing => {
                self.phone_state.state = CallState::Idle;
            }
            _ => {
                return false;
            }
        }
        // At this point, there must be exactly one incoming/dialing/active call to be removed.
        self.call_list.retain(|x| match x.state {
            CallState::Active | CallState::Incoming | CallState::Dialing => false,
            _ => true,
        });
        true
    }
}

fn get_a2dp_dispatcher(tx: Sender<Message>) -> A2dpCallbacksDispatcher {
@@ -1714,6 +1791,84 @@ impl IBluetoothTelephony for BluetoothMedia {

        true
    }

    fn set_phone_ops_enabled(&mut self, enable: bool) {
        if self.phone_ops_enabled == enable {
            return;
        }

        self.call_list = vec![];
        self.phone_state.num_active = 0;
        self.phone_state.num_held = 0;
        self.phone_state.state = CallState::Idle;

        if !enable {
            if self.hfp_states.values().any(|x| x == &BthfConnectionState::SlcConnected) {
                self.call_list.push(CallInfo {
                    index: 1,
                    dir_incoming: false,
                    state: CallState::Active,
                    number: "".into(),
                });
                self.phone_state.num_active = 1;
            }
        }

        self.phone_ops_enabled = enable;
        self.phone_state_change("".into());
    }

    fn incoming_call(&mut self, number: String) -> bool {
        if !self.phone_ops_enabled
            || self.phone_state.state != CallState::Idle
            || self.phone_state.num_active > 0
        {
            return false;
        }
        self.call_list.push(CallInfo {
            index: self.new_call_index(),
            dir_incoming: true,
            state: CallState::Incoming,
            number: number.clone(),
        });
        self.phone_state.state = CallState::Incoming;
        self.phone_state_change(number);
        true
    }

    fn dialing_call(&mut self, number: String) -> bool {
        if !self.phone_ops_enabled
            || self.phone_state.state != CallState::Idle
            || self.phone_state.num_active > 0
        {
            return false;
        }
        self.call_list.push(CallInfo {
            index: self.new_call_index(),
            dir_incoming: false,
            state: CallState::Dialing,
            number: number.clone(),
        });
        self.phone_state.state = CallState::Dialing;
        self.phone_state_change("".into());
        true
    }

    fn answer_call(&mut self) -> bool {
        if !self.answer_call_impl() {
            return false;
        }
        self.phone_state_change("".into());
        true
    }

    fn hangup_call(&mut self) -> bool {
        if !self.hangup_call_impl() {
            return false;
        }
        self.phone_state_change("".into());
        true
    }
}

struct BatteryProviderCallback {}
+14 −2
Original line number Diff line number Diff line
@@ -54,6 +54,14 @@ static void current_calls_query_cb(RawAddress* addr) {
  rusty::hfp_current_calls_query_callback(*addr);
}

static void answer_call_cb(RawAddress* addr) {
  rusty::hfp_answer_call_callback(*addr);
}

static void hangup_call_cb(RawAddress* addr) {
  rusty::hfp_hangup_call_callback(*addr);
}

static headset::bthf_call_state_t from_rust_call_state(rusty::CallState state) {
  switch (state) {
    case rusty::CallState::Idle:
@@ -95,9 +103,13 @@ class DBusHeadsetCallbacks : public headset::Callbacks {
  void VoiceRecognitionCallback(
      [[maybe_unused]] headset::bthf_vr_state_t state, [[maybe_unused]] RawAddress* bd_addr) override {}

  void AnswerCallCallback([[maybe_unused]] RawAddress* bd_addr) override {}
  void AnswerCallCallback(RawAddress* bd_addr) override {
    topshim::rust::internal::answer_call_cb(bd_addr);
  }

  void HangupCallCallback([[maybe_unused]] RawAddress* bd_addr) override {}
  void HangupCallCallback(RawAddress* bd_addr) override {
    topshim::rust::internal::hangup_call_cb(bd_addr);
  }

  void VolumeControlCallback(headset::bthf_volume_type_t type, int volume, RawAddress* bd_addr) override {
    if (type != headset::bthf_volume_type_t::BTHF_VOLUME_TYPE_SPK || volume < 0) return;
Loading