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

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

floss: media: Store call status and handle CLCC in stack

The call status was stored in topshim, which is a place that is better
to keep stateless. This CL wired the related callbacks/interfaces and
moved the call status to the Floss stack, which is helpful to the
upcoming telephony commands implementation.

Bug: 214149380
Tag: #floss
Test: ./build.py --target test
Change-Id: I947bc460cd8d9e4c8681b03eaed527a8670b5222
parent 1bb21539
Loading
Loading
Loading
Loading
+80 −6
Original line number Diff line number Diff line
@@ -12,8 +12,8 @@ use bt_topshim::profiles::avrcp::{
    Avrcp, AvrcpCallbacks, AvrcpCallbacksDispatcher, PlayerMetadata,
};
use bt_topshim::profiles::hfp::{
    BthfAudioState, BthfConnectionState, Hfp, HfpCallbacks, HfpCallbacksDispatcher,
    HfpCodecCapability, TelephonyDeviceStatus,
    BthfAudioState, BthfConnectionState, CallInfo, CallState, Hfp, HfpCallbacks,
    HfpCallbacksDispatcher, HfpCodecCapability, PhoneState, TelephonyDeviceStatus,
};
use bt_topshim::profiles::ProfileConnectionState;
use bt_topshim::{metrics, topstack};
@@ -216,6 +216,8 @@ pub struct BluetoothMedia {
    connected_profiles: HashMap<RawAddress, HashSet<uuid::Profile>>,
    device_states: Arc<Mutex<HashMap<RawAddress, DeviceConnectionStates>>>,
    telephony_device_status: TelephonyDeviceStatus,
    phone_state: PhoneState,
    call_list: Vec<CallInfo>,
}

impl BluetoothMedia {
@@ -256,6 +258,8 @@ impl BluetoothMedia {
            connected_profiles: HashMap::new(),
            device_states: Arc::new(Mutex::new(HashMap::new())),
            telephony_device_status: TelephonyDeviceStatus::new(),
            phone_state: PhoneState { num_active: 0, num_held: 0, state: CallState::Idle },
            call_list: vec![],
        }
    }

@@ -594,6 +598,19 @@ impl BluetoothMedia {
                        info!("[{}]: hfp audio connected.", addr.to_string());

                        self.hfp_audio_state.insert(addr, state);

                        if 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;
                            self.call_list = vec![CallInfo {
                                index: 1,
                                dir_incoming: false,
                                state: CallState::Active,
                                number: "".into(),
                            }];
                            self.phone_state_change("".into());
                        }
                    }
                    BthfAudioState::Disconnected => {
                        info!("[{}]: hfp audio disconnected.", addr.to_string());
@@ -606,6 +623,12 @@ impl BluetoothMedia {
                                callback.on_hfp_audio_disconnected(addr.to_string());
                            });
                        }

                        if self.phone_state.num_active != 0 {
                            self.phone_state.num_active = 0;
                            self.call_list = vec![];
                            self.phone_state_change("".into());
                        }
                    }
                    BthfAudioState::Connecting => {
                        info!("[{}]: hfp audio connecting.", addr.to_string());
@@ -644,12 +667,16 @@ impl BluetoothMedia {
                match self.hfp.as_mut() {
                    Some(hfp) => {
                        debug!(
                            "[{}]: Responding CIND query with {:?}",
                            "[{}]: Responding CIND query with device={:?} phone={:?}",
                            addr.to_string(),
                            self.telephony_device_status
                            self.telephony_device_status,
                            self.phone_state,
                        );
                        let status = hfp.indicator_query_response(
                            self.telephony_device_status,
                            self.phone_state,
                            addr,
                        );
                        let status =
                            hfp.indicator_query_response(self.telephony_device_status, addr);
                        if status != BtStatus::Success {
                            warn!(
                                "[{}]: CIND response failed, status={:?}",
@@ -661,6 +688,26 @@ impl BluetoothMedia {
                    None => warn!("Uninitialized HFP to notify telephony status"),
                };
            }
            HfpCallbacks::CurrentCallsQuery(addr) => {
                match self.hfp.as_mut() {
                    Some(hfp) => {
                        debug!(
                            "[{}]: Responding CLCC query with call_list={:?}",
                            addr.to_string(),
                            self.call_list,
                        );
                        let status = hfp.current_calls_query_response(&self.call_list, addr);
                        if status != BtStatus::Success {
                            warn!(
                                "[{}]: CLCC response failed, status={:?}",
                                addr.to_string(),
                                status
                            );
                        }
                    }
                    None => warn!("Uninitialized HFP to notify telephony status"),
                };
            }
        }
    }

@@ -1002,6 +1049,33 @@ impl BluetoothMedia {
            None => warn!("Uninitialized HFP to notify telephony status"),
        }
    }

    fn phone_state_change(&mut self, number: String) {
        match self.hfp.as_mut() {
            Some(hfp) => {
                for (addr, state) in self.hfp_states.iter() {
                    if *state != BthfConnectionState::SlcConnected {
                        continue;
                    }
                    debug!(
                        "[{}]: Phone state change state={:?} number={}",
                        addr.to_string(),
                        self.phone_state,
                        number
                    );
                    let status = hfp.phone_state_change(self.phone_state, &number, addr.clone());
                    if status != BtStatus::Success {
                        warn!(
                            "[{}]: Device status notification failed, status={:?}",
                            addr.to_string(),
                            status
                        );
                    }
                }
            }
            None => warn!("Uninitialized HFP to notify telephony status"),
        }
    }
}

fn get_a2dp_dispatcher(tx: Sender<Message>) -> A2dpCallbacksDispatcher {
+69 −63
Original line number Diff line number Diff line
@@ -49,6 +49,27 @@ static void battery_level_update_cb(uint8_t battery_level, RawAddress* addr) {
static void indicator_query_cb(RawAddress* addr) {
  rusty::hfp_indicator_query_callback(*addr);
}

static void current_calls_query_cb(RawAddress* addr) {
  rusty::hfp_current_calls_query_callback(*addr);
}

static headset::bthf_call_state_t from_rust_call_state(rusty::CallState state) {
  switch (state) {
    case rusty::CallState::Idle:
      return headset::BTHF_CALL_STATE_IDLE;
    case rusty::CallState::Incoming:
      return headset::BTHF_CALL_STATE_INCOMING;
    case rusty::CallState::Dialing:
      return headset::BTHF_CALL_STATE_DIALING;
    case rusty::CallState::Active:
      return headset::BTHF_CALL_STATE_ACTIVE;
    case rusty::CallState::Held:
      return headset::BTHF_CALL_STATE_HELD;
    default:
      ASSERT_LOG(false, "Unhandled enum value from Rust");
  }
}
}  // namespace internal

class DBusHeadsetCallbacks : public headset::Callbacks {
@@ -58,9 +79,7 @@ class DBusHeadsetCallbacks : public headset::Callbacks {
    return instance;
  }

  DBusHeadsetCallbacks(headset::Interface* headset) : headset_(headset) {
    call_status = 0;
  };
  DBusHeadsetCallbacks(headset::Interface* headset) : headset_(headset){};

  // headset::Callbacks
  void ConnectionStateCallback(headset::bthf_connection_state_t state, RawAddress* bd_addr) override {
@@ -71,17 +90,6 @@ class DBusHeadsetCallbacks : public headset::Callbacks {
  void AudioStateCallback(headset::bthf_audio_state_t state, RawAddress* bd_addr) override {
    LOG_INFO("AudioStateCallback %u from %s", state, ADDRESS_TO_LOGGABLE_CSTR(*bd_addr));
    topshim::rust::internal::audio_state_cb(state, bd_addr);

    switch (state) {
      case headset::bthf_audio_state_t::BTHF_AUDIO_STATE_CONNECTED:
        SetCallStatus(1, bd_addr);
        return;
      case headset::bthf_audio_state_t::BTHF_AUDIO_STATE_DISCONNECTED:
        SetCallStatus(0, bd_addr);
        return;
      default:
        return;
    }
  }

  void VoiceRecognitionCallback(
@@ -129,21 +137,7 @@ class DBusHeadsetCallbacks : public headset::Callbacks {
  }

  void AtClccCallback(RawAddress* bd_addr) override {
    // Reply +CLCC:<idx>,<dir>,<status>,<mode>,<mprty>[,<number>,<type>] if
    // there is an active audio connection. Simply rely OK otherwise.
    // This is required for some headsets to start to send actual data to AG.
    if (call_status)
      headset_->ClccResponse(
          /*index=*/1,
          /*dir=*/headset::BTHF_CALL_DIRECTION_OUTGOING,
          /*state=*/headset::BTHF_CALL_STATE_ACTIVE,
          /*mode=*/headset::BTHF_CALL_TYPE_VOICE,
          /*multi_party=*/headset::BTHF_CALL_MPTY_TYPE_SINGLE,
          /*number=*/"",
          /*type=*/headset::BTHF_CALL_ADDRTYPE_UNKNOWN,
          bd_addr);

    headset_->AtResponse(headset::BTHF_AT_RESPONSE_OK, 0, bd_addr);
    topshim::rust::internal::current_calls_query_cb(bd_addr);
  }

  void UnknownAtCallback(char* at_string, RawAddress* bd_addr) override {
@@ -187,36 +181,6 @@ class DBusHeadsetCallbacks : public headset::Callbacks {

 private:
  headset::Interface* headset_;
  int call_status;

  void SetCallStatus(int call, RawAddress* bd_addr) {
    if (call == call_status) return;

    if (call) {
      // This triggers a +CIEV command to set the call status for HFP
      // devices. It is required along with the SCO establishment for some
      // devices to provide sound.
      headset_->PhoneStateChange(
          /*num_active=*/1,
          /*num_held=*/0,
          /*call_setup_state=*/headset::bthf_call_state_t::BTHF_CALL_STATE_IDLE,
          /*number=*/"",
          /*type=*/(headset::bthf_call_addrtype_t)0,
          /*name=*/"",
          /*bd_addr=*/bd_addr);
    } else {
      headset_->PhoneStateChange(
          /*num_active=*/0,
          /*num_held=*/0,
          /*call_setup_state=*/headset::bthf_call_state_t::BTHF_CALL_STATE_IDLE,
          /*number=*/"",
          /*type=*/(headset::bthf_call_addrtype_t)0,
          /*name=*/"",
          /*bd_addr=*/bd_addr);
    }

    call_status = call;
  }
};

int HfpIntf::init() {
@@ -258,18 +222,60 @@ uint32_t HfpIntf::device_status_notification(TelephonyDeviceStatus status, RawAd
      &addr);
}

uint32_t HfpIntf::indicator_query_response(TelephonyDeviceStatus device_status, RawAddress addr) {
uint32_t HfpIntf::indicator_query_response(
    TelephonyDeviceStatus device_status, PhoneState phone_state, RawAddress addr) {
  return intf_->CindResponse(
      device_status.network_available ? 1 : 0,
      /*num_active=*/0,
      /*num_held=*/0,
      headset::BTHF_CALL_STATE_IDLE,
      phone_state.num_active,
      phone_state.num_held,
      topshim::rust::internal::from_rust_call_state(phone_state.state),
      device_status.signal_strength,
      device_status.roaming ? 1 : 0,
      device_status.battery_level,
      &addr);
}

uint32_t HfpIntf::current_calls_query_response(
    const ::rust::Vec<CallInfo>& call_list, RawAddress addr) {
  for (const auto& c : call_list) {
    std::string number{c.number};
    intf_->ClccResponse(
        c.index,
        c.dir_incoming ? headset::BTHF_CALL_DIRECTION_INCOMING
                       : headset::BTHF_CALL_DIRECTION_OUTGOING,
        topshim::rust::internal::from_rust_call_state(c.state),
        /*mode=*/headset::BTHF_CALL_TYPE_VOICE,
        /*multi_party=*/headset::BTHF_CALL_MPTY_TYPE_SINGLE,
        number.c_str(),
        /*type=*/headset::BTHF_CALL_ADDRTYPE_UNKNOWN,
        &addr);
  }

  // NULL termination (Completes response)
  return intf_->ClccResponse(
      /*index=*/0,
      /*dir=*/(headset::bthf_call_direction_t)0,
      /*state=*/(headset::bthf_call_state_t)0,
      /*mode=*/(headset::bthf_call_mode_t)0,
      /*multi_party=*/(headset::bthf_call_mpty_type_t)0,
      /*number=*/"",
      /*type=*/(headset::bthf_call_addrtype_t)0,
      &addr);
}

uint32_t HfpIntf::phone_state_change(
    PhoneState phone_state, const ::rust::String& number_rs, RawAddress addr) {
  std::string number{number_rs};
  return intf_->PhoneStateChange(
      phone_state.num_active,
      phone_state.num_held,
      topshim::rust::internal::from_rust_call_state(phone_state.state),
      number.c_str(),
      /*type=*/(headset::bthf_call_addrtype_t)0,
      /*name=*/"",
      &addr);
}

void HfpIntf::cleanup() {}

std::unique_ptr<HfpIntf> GetHfpProfile(const unsigned char* btif) {
+8 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@

#include "btif/include/btif_hf.h"
#include "include/hardware/bluetooth_headset_callbacks.h"
#include "rust/cxx.h"
#include "types/raw_address.h"

namespace bluetooth {
@@ -27,6 +28,8 @@ namespace topshim {
namespace rust {

struct TelephonyDeviceStatus;
struct CallInfo;
struct PhoneState;

class HfpIntf {
 public:
@@ -40,7 +43,11 @@ class HfpIntf {
  uint32_t disconnect(RawAddress addr);
  int disconnect_audio(RawAddress addr);
  uint32_t device_status_notification(TelephonyDeviceStatus status, RawAddress addr);
  uint32_t indicator_query_response(TelephonyDeviceStatus device_status, RawAddress addr);
  uint32_t indicator_query_response(
      TelephonyDeviceStatus device_status, PhoneState phone_state, RawAddress addr);
  uint32_t current_calls_query_response(const ::rust::Vec<CallInfo>& call_list, RawAddress addr);
  uint32_t phone_state_change(
      PhoneState phone_state, const ::rust::String& number, RawAddress addr);
  void cleanup();

 private:
+72 −1
Original line number Diff line number Diff line
@@ -79,6 +79,30 @@ pub mod ffi {
        battery_level: i32,
    }

    #[derive(Debug, Copy, Clone)]
    pub enum CallState {
        Idle,
        Incoming,
        Dialing,
        Active, // Only used by CLCC response
        Held,   // Only used by CLCC response
    }

    #[derive(Debug, Clone)]
    pub struct CallInfo {
        index: i32,
        dir_incoming: bool,
        state: CallState,
        number: String,
    }

    #[derive(Debug, Copy, Clone)]
    pub struct PhoneState {
        num_active: i32,
        num_held: i32,
        state: CallState,
    }

    unsafe extern "C++" {
        include!("hfp/hfp_shim.h");

@@ -106,6 +130,18 @@ pub mod ffi {
        fn indicator_query_response(
            self: Pin<&mut HfpIntf>,
            device_status: TelephonyDeviceStatus,
            phone_state: PhoneState,
            addr: RawAddress,
        ) -> u32;
        fn current_calls_query_response(
            self: Pin<&mut HfpIntf>,
            call_list: &Vec<CallInfo>,
            addr: RawAddress,
        ) -> u32;
        fn phone_state_change(
            self: Pin<&mut HfpIntf>,
            phone_state: PhoneState,
            number: &String,
            addr: RawAddress,
        ) -> u32;
        fn cleanup(self: Pin<&mut HfpIntf>);
@@ -118,6 +154,7 @@ pub mod ffi {
        fn hfp_battery_level_update_callback(battery_level: u8, addr: RawAddress);
        fn hfp_caps_update_callback(wbs_supported: bool, addr: RawAddress);
        fn hfp_indicator_query_callback(addr: RawAddress);
        fn hfp_current_calls_query_callback(addr: RawAddress);
    }
}

@@ -134,6 +171,10 @@ impl TelephonyDeviceStatus {
    }
}

pub type CallState = ffi::CallState;
pub type CallInfo = ffi::CallInfo;
pub type PhoneState = ffi::PhoneState;

#[derive(Clone, Debug)]
pub enum HfpCallbacks {
    ConnectionState(BthfConnectionState, RawAddress),
@@ -142,6 +183,7 @@ pub enum HfpCallbacks {
    BatteryLevelUpdate(u8, RawAddress),
    CapsUpdate(bool, RawAddress),
    IndicatorQuery(RawAddress),
    CurrentCallsQuery(RawAddress),
}

pub struct HfpCallbacksDispatcher {
@@ -180,6 +222,11 @@ cb_variant!(
    hfp_indicator_query_callback -> HfpCallbacks::IndicatorQuery,
    RawAddress);

cb_variant!(
    HfpCb,
    hfp_current_calls_query_callback -> HfpCallbacks::CurrentCallsQuery,
    RawAddress);

pub struct Hfp {
    internal: cxx::UniquePtr<ffi::HfpIntf>,
    _is_init: bool,
@@ -273,9 +320,33 @@ impl Hfp {
    pub fn indicator_query_response(
        &mut self,
        device_status: TelephonyDeviceStatus,
        phone_state: PhoneState,
        addr: RawAddress,
    ) -> BtStatus {
        BtStatus::from(self.internal.pin_mut().indicator_query_response(
            device_status,
            phone_state,
            addr,
        ))
    }

    #[profile_enabled_or(BtStatus::NotReady)]
    pub fn current_calls_query_response(
        &mut self,
        call_list: &Vec<CallInfo>,
        addr: RawAddress,
    ) -> BtStatus {
        BtStatus::from(self.internal.pin_mut().current_calls_query_response(call_list, addr))
    }

    #[profile_enabled_or(BtStatus::NotReady)]
    pub fn phone_state_change(
        &mut self,
        phone_state: PhoneState,
        number: &String,
        addr: RawAddress,
    ) -> BtStatus {
        BtStatus::from(self.internal.pin_mut().indicator_query_response(device_status, addr))
        BtStatus::from(self.internal.pin_mut().phone_state_change(phone_state, number, addr))
    }

    #[profile_enabled_or(false)]