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

Commit b80a1f6e authored by Whale Chang's avatar Whale Chang Committed by Gerrit Code Review
Browse files

Merge "Floss: Refactor hfp phone state function" into main

parents 426fa0d8 8813e967
Loading
Loading
Loading
Loading
+212 −148
Original line number Diff line number Diff line
@@ -23,8 +23,8 @@ use bt_topshim::{metrics, topstack};
use bt_utils::at_command_parser::{calculate_battery_percent, parse_at_command_data};
use bt_utils::uhid_hfp::{
    OutputEvent, UHidHfp, BLUETOOTH_TELEPHONY_UHID_REPORT_ID, UHID_INPUT_HOOK_SWITCH,
    UHID_INPUT_PHONE_MUTE, UHID_OUTPUT_MUTE, UHID_OUTPUT_NONE, UHID_OUTPUT_OFF_HOOK,
    UHID_OUTPUT_RING,
    UHID_INPUT_NONE, UHID_INPUT_PHONE_MUTE, UHID_OUTPUT_MUTE, UHID_OUTPUT_NONE,
    UHID_OUTPUT_OFF_HOOK, UHID_OUTPUT_RING,
};
use bt_utils::uinput::UInput;

@@ -850,12 +850,12 @@ impl BluetoothMedia {
                if let Some(uhid) = self.uhid.get_mut(&addr) {
                    if volume == 0 && !uhid.muted {
                        uhid.muted = true;
                        self.uhid_send_input_report(&addr);
                        self.uhid_send_phone_mute_input_report(&addr, true);
                    } else if volume > 0 {
                        uhid.volume = volume;
                        if uhid.muted {
                            uhid.muted = false;
                            self.uhid_send_input_report(&addr);
                            self.uhid_send_phone_mute_input_report(&addr, false);
                        }
                    }
                }
@@ -1001,19 +1001,18 @@ impl BluetoothMedia {
                };
            }
            HfpCallbacks::AnswerCall(addr) => {
                if !self.answer_call_impl() {
                    warn!("[{}]: answer_call triggered by ATA failed", DisplayAddress(&addr));
                if !self.phone_ops_enabled && !self.mps_qualification_enabled {
                    warn!("Unexpected answer call. phone_ops_enabled and mps_qualification_enabled does not enabled.");
                    return;
                }
                self.phone_state_change("".into());

                if self.mps_qualification_enabled {
                    debug!("[{}]: Start SCO call due to ATA", DisplayAddress(&addr));
                    self.start_sco_call_impl(addr.to_string(), false, HfpCodecBitId::NONE);
                }
                self.uhid_send_input_report(&addr);
                self.answer_call_impl();
                self.uhid_send_hook_switch_input_report(&addr, true);
            }
            HfpCallbacks::HangupCall(addr) => {
                if !self.phone_ops_enabled && !self.mps_qualification_enabled {
                    warn!("Unexpected hangup call. phone_ops_enabled and mps_qualification_enabled does not enabled.");
                    return;
                }
                if self.should_insert_call_when_sco_start(addr) {
                    // The devices requiring a +CIEV event are not managed through the telephony commands.
                    // This allows to prevent to stop the SCO link as there is no command to set it up again.
@@ -1023,20 +1022,15 @@ impl BluetoothMedia {
                    );
                    return;
                }
                if !self.hangup_call_impl() {
                    warn!("[{}]: hangup_call triggered by AT+CHUP failed", DisplayAddress(&addr));
                    return;
                }
                self.phone_state_change("".into());
                self.uhid_send_input_report(&addr);

                // Try resume the A2DP stream (per MPS v1.0) on rejecting an incoming call or an
                // outgoing call is rejected.
                // It may fail if a SCO connection is still active (terminate call case), in that
                // case we will retry on SCO disconnected.
                self.try_a2dp_resume();
                self.hangup_call_impl();
                self.uhid_send_hook_switch_input_report(&addr, false);
            }
            HfpCallbacks::DialCall(number, addr) => {
                if !self.mps_qualification_enabled {
                    warn!("Unexpected dail call. mps_qualification_enabled does not enabled.");
                    self.simple_at_response(false, addr);
                    return;
                }
                let number = if number == "" {
                    self.last_dialing_number.clone()
                } else if number.starts_with(">") {
@@ -1045,42 +1039,29 @@ impl BluetoothMedia {
                    Some(number)
                };

                let success = number.map_or(false, |num| self.dialing_call_impl(num));

                // Respond OK/ERROR to the HF which sent the command.
                // This should be called before calling phone_state_change.
                self.simple_at_response(success, addr.clone());
                if !success {
                    warn!("[{}]: Unexpected dialing command from HF", DisplayAddress(&addr));
                    return;
                if let Some(number) = number {
                    self.dialing_call_impl(number, Some(addr));
                } else {
                    self.simple_at_response(false, addr);
                }
                // Inform libbluetooth that the state has changed to dialing.
                self.phone_state_change("".into());
                self.try_a2dp_suspend();
                // Change to alerting state and inform libbluetooth.
                self.dialing_to_alerting();
                self.phone_state_change("".into());
            }
            HfpCallbacks::CallHold(command, addr) => {
                if !self.mps_qualification_enabled {
                    warn!("Unexpected call hold. mps_qualification_enabled does not enabled.");
                    self.simple_at_response(false, addr);
                    return;
                }
                let success = match command {
                    CallHoldCommand::ReleaseHeld => self.release_held_impl(),
                    CallHoldCommand::ReleaseHeld => self.release_held_impl(Some(addr)),
                    CallHoldCommand::ReleaseActiveAcceptHeld => {
                        self.release_active_accept_held_impl()
                        self.release_active_accept_held_impl(Some(addr))
                    }
                    CallHoldCommand::HoldActiveAcceptHeld => {
                        self.hold_active_accept_held_impl(Some(addr))
                    }
                    CallHoldCommand::HoldActiveAcceptHeld => self.hold_active_accept_held_impl(),
                    _ => false, // We only support the 3 operations above.
                };
                // Respond OK/ERROR to the HF which sent the command.
                // This should be called before calling phone_state_change.
                self.simple_at_response(success, addr.clone());
                if success {
                    // Success means the call state has changed. Inform libbluetooth.
                    self.phone_state_change("".into());
                } else {
                if !success {
                    warn!(
                        "[{}]: Unexpected or unsupported CHLD command {:?} from HF",
                        DisplayAddress(&addr),
@@ -1214,25 +1195,63 @@ impl BluetoothMedia {
        }
    }

    fn uhid_send_input_report(&mut self, addr: &RawAddress) {
        // To change the value of phone_ops_enabled, you need to toggle the BluetoothFlossTelephony feature flag on chrome://flags.
    fn uhid_send_hook_switch_input_report(&mut self, addr: &RawAddress, hook: bool) {
        if !self.phone_ops_enabled {
            return;
        }
        if let Some(uhid) = self.uhid.get_mut(addr) {
            let mut data = 0;
            if self.phone_state.num_active > 0 {
            let mut data = UHID_INPUT_NONE;
            if hook {
                data |= UHID_INPUT_HOOK_SWITCH;
            }
            // Preserve the muted state when sending the hook switch event.
            if uhid.muted {
                data |= UHID_INPUT_PHONE_MUTE;
            }
            debug!("[{}]: UHID: Send input report: {}", DisplayAddress(&addr), data);
            info!(
                "[{}]: UHID: Send hook-switch({}) hid input report. phone_mute({})",
                DisplayAddress(&addr),
                hook,
                uhid.muted
            );
            match uhid.handle.send_input(data) {
                Err(e) => log::error!(
                    "[{}]: UHID: Fail to send hook-switch({}) hid input report. phone_mute({}) err:{}",
                    DisplayAddress(&addr),
                    hook,
                    uhid.muted,
                    e
                ),
                Ok(_) => (),
            };
        };
    }
    fn uhid_send_phone_mute_input_report(&mut self, addr: &RawAddress, muted: bool) {
        if !self.phone_ops_enabled {
            return;
        }
        if let Some(uhid) = self.uhid.get_mut(addr) {
            let mut data = UHID_INPUT_NONE;
            // Preserve the hook switch state when sending the microphone mute event.
            let call_active = self.phone_state.num_active > 0;
            if call_active {
                data |= UHID_INPUT_HOOK_SWITCH;
            }
            if muted {
                data |= UHID_INPUT_PHONE_MUTE;
            }
            info!(
                "[{}]: UHID: Send phone_mute({}) hid input report. hook-switch({})",
                DisplayAddress(&addr),
                muted,
                call_active
            );
            match uhid.handle.send_input(data) {
                Err(e) => log::error!(
                    "[{}]: UHID: Fail to send Input Report ({}) to uhid: {}",
                    "[{}]: UHID: Fail to send phone_mute({}) hid input report. hook-switch({}) err:{}",
                    DisplayAddress(&addr),
                    data,
                    muted,
                    call_active,
                    e
                ),
                Ok(_) => (),
@@ -1241,6 +1260,11 @@ impl BluetoothMedia {
    }

    pub fn dispatch_uhid_hfp_output_callback(&mut self, address: String, id: u8, data: u8) {
        if !self.phone_ops_enabled {
            warn!("Unexpected dispatch_uhid_hfp_output_callback uhid output. phone_ops_enabled does not enabled.");
            return;
        }

        let addr = match RawAddress::from_string(address.clone()) {
            None => {
                warn!("UHID: Invalid device address for dispatch_uhid_hfp_output_callback");
@@ -1277,13 +1301,13 @@ impl BluetoothMedia {

            let call_state = data & (UHID_OUTPUT_RING | UHID_OUTPUT_OFF_HOOK);
            if call_state == UHID_OUTPUT_NONE {
                self.hangup_call();
                self.hangup_call_impl();
            } else if call_state == UHID_OUTPUT_RING {
                self.incoming_call("".into());
                self.incoming_call_impl("".into());
            } else if call_state == UHID_OUTPUT_OFF_HOOK {
                self.dialing_call("".into());
                self.answer_call();
                self.uhid_send_input_report(&addr);
                self.dialing_call_impl("".into(), None);
                self.answer_call_impl();
                self.uhid_send_hook_switch_input_report(&addr, true);
            }
        }
    }
@@ -1303,9 +1327,7 @@ impl BluetoothMedia {
            // or pre-exists, and that an app which disconnects from WebHID may not have trigger
            // the UHID_OUTPUT_NONE, we need to remove all pending HID calls on telephony use
            // release to keep lower HF layer in sync and not prevent A2DP streaming
            if self.hangup_call_impl() {
                self.phone_state_change("".into());
            }
            self.hangup_call_impl();
        }
        self.telephony_callbacks.lock().unwrap().for_all_callbacks(|callback| {
            callback.on_telephony_use(address.to_string(), state);
@@ -1797,6 +1819,10 @@ impl BluetoothMedia {
    }

    fn try_a2dp_resume(&mut self) {
        // Try resume the A2DP stream (per MPS v1.0) on rejecting an incoming call or an
        // outgoing call is rejected.
        // It may fail if a SCO connection is still active (terminate call case), in that
        // case we will retry on SCO disconnected.
        if !self.mps_qualification_enabled {
            return;
        }
@@ -1810,6 +1836,7 @@ impl BluetoothMedia {
    }

    fn try_a2dp_suspend(&mut self) {
        // Try suspend the A2DP stream (per MPS v1.0) when receiving an incoming call
        if !self.mps_qualification_enabled {
            return;
        }
@@ -1934,11 +1961,28 @@ impl BluetoothMedia {
        }
    }

    fn answer_call_impl(&mut self) -> bool {
        if !self.phone_ops_enabled && !self.mps_qualification_enabled {
    fn incoming_call_impl(&mut self, number: String) -> bool {
        if self.phone_state.state != CallState::Idle {
            return false;
        }

        if 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);
        self.try_a2dp_suspend();
        true
    }

    fn answer_call_impl(&mut self) -> bool {
        if self.phone_state.state == CallState::Idle {
            return false;
        }
@@ -1955,6 +1999,22 @@ impl BluetoothMedia {
        self.phone_state.state = CallState::Idle;
        self.phone_state.num_active += 1;

        self.phone_state_change("".into());

        if self.mps_qualification_enabled {
            // Find a connected HFP and try to establish an SCO.
            if let Some(addr) = self.hfp_states.iter().find_map(|(addr, state)| {
                if *state == BthfConnectionState::SlcConnected {
                    Some(addr.clone())
                } else {
                    None
                }
            }) {
                info!("Start SCO call due to call answered");
                self.start_sco_call_impl(addr.to_string(), false, HfpCodecBitId::NONE);
            }
        }

        true
    }

@@ -1981,17 +2041,18 @@ impl BluetoothMedia {
            _ => true,
        });

        self.phone_state_change("".into());
        self.try_a2dp_resume();

        true
    }

    fn dialing_call_impl(&mut self, number: String) -> bool {
        if !(self.phone_ops_enabled || self.mps_qualification_enabled)
            || self.phone_state.state != CallState::Idle
        {
            return false;
    fn dialing_call_impl(&mut self, number: String, addr: Option<RawAddress>) -> bool {
        if self.phone_state.num_active > 0 || self.phone_state.state != CallState::Idle {
            if let Some(addr) = addr {
                self.simple_at_response(false, addr);
                warn!("[{}]: Unexpected dialing command from HF", DisplayAddress(&addr));
            }

        if self.phone_state.num_active > 0 {
            return false;
        }

@@ -2002,6 +2063,17 @@ impl BluetoothMedia {
            number: number.clone(),
        });
        self.phone_state.state = CallState::Dialing;

        if let Some(addr) = addr {
            self.simple_at_response(true, addr);
            warn!("[{}]: Unexpected dialing command from HF", DisplayAddress(&addr));
        }

        // Inform libbluetooth that the state has changed to dialing.
        self.phone_state_change("".into());
        self.try_a2dp_suspend();
        // Change to alerting state and inform libbluetooth.
        self.dialing_to_alerting();
        true
    }

@@ -2018,29 +2090,39 @@ impl BluetoothMedia {
            }
        }
        self.phone_state.state = CallState::Alerting;
        self.phone_state_change("".into());
        true
    }

    fn release_held_impl(&mut self) -> bool {
        if !self.mps_qualification_enabled {
            return false;
        }
    fn release_held_impl(&mut self, addr: Option<RawAddress>) -> bool {
        if self.phone_state.state != CallState::Idle {
            if let Some(addr) = addr {
                // Respond ERROR to the HF which sent the command.
                self.simple_at_response(false, addr);
            }
            return false;
        }
        self.call_list.retain(|x| x.state != CallState::Held);
        self.phone_state.num_held = 0;

        if let Some(addr) = addr {
            // This should be called before calling phone_state_change.
            self.simple_at_response(true, addr.clone());
        }
        // Success means the call state has changed. Inform libbluetooth.
        self.phone_state_change("".into());
        true
    }

    fn release_active_accept_held_impl(&mut self) -> bool {
        if !self.mps_qualification_enabled {
            return false;
        }
    fn release_active_accept_held_impl(&mut self, addr: Option<RawAddress>) -> bool {
        self.call_list.retain(|x| x.state != CallState::Active);
        self.phone_state.num_active = 0;
        // Activate the first held call
        if self.phone_state.state != CallState::Idle {
            if let Some(addr) = addr {
                // Respond ERROR to the HF which sent the command.
                self.simple_at_response(false, addr);
            }
            return false;
        }
        for c in self.call_list.iter_mut() {
@@ -2051,14 +2133,21 @@ impl BluetoothMedia {
                break;
            }
        }
        if let Some(addr) = addr {
            // This should be called before calling phone_state_change.
            self.simple_at_response(true, addr);
        }
        // Success means the call state has changed. Inform libbluetooth.
        self.phone_state_change("".into());
        true
    }

    fn hold_active_accept_held_impl(&mut self) -> bool {
        if !self.mps_qualification_enabled {
            return false;
        }
    fn hold_active_accept_held_impl(&mut self, addr: Option<RawAddress>) -> bool {
        if self.phone_state.state != CallState::Idle {
            if let Some(addr) = addr {
                // Respond ERROR to the HF which sent the command.
                self.simple_at_response(false, addr);
            }
            return false;
        }
        self.phone_state.num_held += self.phone_state.num_active;
@@ -2078,6 +2167,12 @@ impl BluetoothMedia {
                _ => {}
            }
        }
        if let Some(addr) = addr {
            // This should be called before calling phone_state_change.
            self.simple_at_response(true, addr);
        }
        // Success means the call state has changed. Inform libbluetooth.
        self.phone_state_change("".into());
        true
    }

@@ -2925,78 +3020,44 @@ impl IBluetoothTelephony for BluetoothMedia {
    }

    fn incoming_call(&mut self, number: String) -> bool {
        if !(self.phone_ops_enabled || self.mps_qualification_enabled)
            || self.phone_state.state != CallState::Idle
        {
            return false;
        }

        if self.phone_state.num_active > 0 {
        if !self.mps_qualification_enabled {
            warn!("Unexpected incoming_call dbus command. mps_qualification_enabled does not enabled.");
            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);
        self.try_a2dp_suspend();
        true
        return self.incoming_call_impl(number);
    }

    fn dialing_call(&mut self, number: String) -> bool {
        if !self.dialing_call_impl(number) {
        if !self.mps_qualification_enabled {
            warn!("Unexpected incoming_call dbus command. mps_qualification_enabled does not enabled.");
            return false;
        }
        self.phone_state_change("".into());
        self.try_a2dp_suspend();
        // Change to alerting state and inform libbluetooth.
        self.dialing_to_alerting();
        self.phone_state_change("".into());
        true
        return self.dialing_call_impl(number, None);
    }

    fn answer_call(&mut self) -> bool {
        if !self.answer_call_impl() {
        if !self.mps_qualification_enabled {
            warn!(
                "Unexpected answer_call dbus command. mps_qualification_enabled does not enabled."
            );
            return false;
        }
        self.phone_state_change("".into());

        if self.mps_qualification_enabled {
            // Find a connected HFP and try to establish an SCO.
            if let Some(addr) = self.hfp_states.iter().find_map(|(addr, state)| {
                if *state == BthfConnectionState::SlcConnected {
                    Some(addr.clone())
                } else {
                    None
                }
            }) {
                info!("Start SCO call due to call answered");
                self.start_sco_call_impl(addr.to_string(), false, HfpCodecBitId::NONE);
            }
        }

        true
        return self.answer_call_impl();
    }

    fn hangup_call(&mut self) -> bool {
        if !self.hangup_call_impl() {
        if !self.mps_qualification_enabled {
            warn!(
                "Unexpected hangup_call dbus command. mps_qualification_enabled does not enabled."
            );
            return false;
        }
        self.phone_state_change("".into());
        // Try resume the A2DP stream (per MPS v1.0) on rejecting an incoming call or an
        // outgoing call is rejected.
        // It may fail if a SCO connection is still active (terminate call case), in that
        // case we will retry on SCO disconnected.
        self.try_a2dp_resume();
        true
        return self.hangup_call_impl();
    }

    fn set_memory_call(&mut self, number: Option<String>) -> bool {
        if !(self.phone_ops_enabled || self.mps_qualification_enabled) {
        if !self.mps_qualification_enabled {
            warn!("Unexpected set_memory_call dbus command. mps_qualification_enabled does not enabled.");
            return false;
        }
        self.memory_dialing_number = number;
@@ -3004,7 +3065,8 @@ impl IBluetoothTelephony for BluetoothMedia {
    }

    fn set_last_call(&mut self, number: Option<String>) -> bool {
        if !(self.phone_ops_enabled || self.mps_qualification_enabled) {
        if !self.mps_qualification_enabled {
            warn!("Unexpected set_last_call dbus command. mps_qualification_enabled does not enabled.");
            return false;
        }
        self.last_dialing_number = number;
@@ -3012,27 +3074,29 @@ impl IBluetoothTelephony for BluetoothMedia {
    }

    fn release_held(&mut self) -> bool {
        if !self.release_held_impl() {
        if !self.mps_qualification_enabled {
            warn!(
                "Unexpected release_held dbus command. mps_qualification_enabled does not enabled."
            );
            return false;
        }
        self.phone_state_change("".into());
        true
        return self.release_held_impl(None);
    }

    fn release_active_accept_held(&mut self) -> bool {
        if !self.release_active_accept_held_impl() {
        if !self.mps_qualification_enabled {
            warn!("Unexpected release_active_accept_held dbus command. mps_qualification_enabled does not enabled.");
            return false;
        }
        self.phone_state_change("".into());
        true
        return self.release_active_accept_held_impl(None);
    }

    fn hold_active_accept_held(&mut self) -> bool {
        if !self.hold_active_accept_held_impl() {
        if !self.mps_qualification_enabled {
            warn!("Unexpected hold_active_accept_held dbus command. mps_qualification_enabled does not enabled.");
            return false;
        }
        self.phone_state_change("".into());
        true
        return self.hold_active_accept_held_impl(None);
    }

    fn audio_connect(&mut self, address: String) -> bool {
+1 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ pub use uhid_virt::OutputEvent;
use uhid_virt::{Bus, CreateParams, InputEvent, StreamError, UHID_EVENT_SIZE};

pub const BLUETOOTH_TELEPHONY_UHID_REPORT_ID: u8 = 1;
pub const UHID_INPUT_NONE: u8 = 0;
pub const UHID_INPUT_HOOK_SWITCH: u8 = 1 << 0;
pub const UHID_INPUT_PHONE_MUTE: u8 = 1 << 1;
pub const UHID_OUTPUT_NONE: u8 = 0;