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

Commit bdb1347b authored by Yuyang Huang's avatar Yuyang Huang Committed by Gerrit Code Review
Browse files

Merge "move Audio related API outside synchronize to avoid deadlock" into main

parents 44a58e0e 69af9839
Loading
Loading
Loading
Loading
+39 −41
Original line number Diff line number Diff line
@@ -80,7 +80,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.FutureTask;

/**
 * Provides Bluetooth Headset and Handsfree profile, as a service in the Bluetooth application.
@@ -1153,31 +1152,40 @@ public class HeadsetService extends ProfileService {
            } else {
                stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START, device);
            }
            if (!Utils.isScoManagedByAudioEnabled()) {
                stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
            }
        }

        if (Utils.isScoManagedByAudioEnabled()) {
            BluetoothDevice voiceRecognitionDevice = device;
            // when isScoManagedByAudio is on, tell AudioManager to connect SCO
            AudioManager am = mSystemInterface.getAudioManager();
                BluetoothDevice finalDevice = device;
            Optional<AudioDeviceInfo> audioDeviceInfo =
                    am.getAvailableCommunicationDevices().stream()
                            .filter(
                                    x ->
                                            x.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO
                                                    && x.getAddress()
                                                                .equals(finalDevice.getAddress()))
                                                            .equals(
                                                                    voiceRecognitionDevice
                                                                            .getAddress()))
                            .findFirst();
            if (audioDeviceInfo.isPresent()) {
                mHandler.post(
                        () -> {
                            am.setCommunicationDevice(audioDeviceInfo.get());
                    Log.i(TAG, "Audio Manager will initiate the SCO connection");
                            Log.i(TAG, "Audio Manager will initiate the SCO for Voice Recognition");
                        });
                return true;
                }
            } else {
                Log.w(
                        TAG,
                        "Cannot find audioDeviceInfo that matches device="
                                + device
                                + voiceRecognitionDevice
                                + " to create the SCO");
                return false;
            }
            stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
        }
        enableSwbCodec(HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, true, device);
        return true;
@@ -1213,12 +1221,19 @@ public class HeadsetService extends ProfileService {
            }
            mVoiceRecognitionStarted = false;
            stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP, device);
            if (!Utils.isScoManagedByAudioEnabled()) {
                stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
            }
        }

        if (Utils.isScoManagedByAudioEnabled()) {
            // do the task outside synchronized to avoid deadlock with Audio Fwk
            mHandler.post(
                    () -> {
                        mSystemInterface.getAudioManager().clearCommunicationDevice();
                    });
            return true;
        }
            stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
        }
        enableSwbCodec(HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, false, device);
        return true;
    }
@@ -2004,23 +2019,6 @@ public class HeadsetService extends ProfileService {
                                HeadsetStateMachine.CALL_STATE_CHANGED,
                                new HeadsetCallState(
                                        numActive, numHeld, callState, number, type, name)));
        if (Utils.isScoManagedByAudioEnabled()) {
            if (mActiveDevice == null) {
                Log.i(TAG, "HeadsetService's active device is null");
            } else {
                // wait until mActiveDevice's state machine processed CALL_STATE_CHANGED message,
                // then Audio Framework starts the SCO connection
                FutureTask task = new FutureTask(() -> {}, null);
                mStateMachines.get(mActiveDevice).getHandler().post(task);
                try {
                    task.get();
                } catch (Exception e) {
                    Log.e(
                            TAG,
                            "Exception when waiting for CALL_STATE_CHANGED message" + e.toString());
                }
            }
        }
        mStateMachinesThreadHandler.post(
                () -> {
                    if (callState == HeadsetHalConstants.CALL_STATE_IDLE
+8 −0
Original line number Diff line number Diff line
@@ -106,6 +106,14 @@ BluetoothAudioCtrlAck HfpTransport::StartRequest() {

  /* Post start SCO event and wait for sco to open */
  hfp_pending_cmd_ = HFP_CTRL_CMD_START;
  bool is_call_idle = bluetooth::headset::IsCallIdle();
  bool is_during_vr = bluetooth::headset::IsDuringVoiceRecognition(&(cb->peer_addr));
  if (is_call_idle && !is_during_vr) {
    log::warn("Call ongoing={}, voice recognition ongoing={}, wait for retry", !is_call_idle,
              is_during_vr);
    hfp_pending_cmd_ = HFP_CTRL_CMD_NONE;
    return BluetoothAudioCtrlAck::PENDING;
  }
  // as ConnectAudio only queues the command into main thread, keep PENDING
  // status
  auto status = bluetooth::headset::GetInterface()->ConnectAudio(&cb->peer_addr, 0);
+2 −0
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ Interface* GetInterface();
 */
bool IsCallIdle();

bool IsDuringVoiceRecognition(RawAddress* bd_addr);

/**
 * Start up or shutdown the service
 *
+27 −0
Original line number Diff line number Diff line
@@ -115,6 +115,7 @@ struct btif_hf_cb_t {
  tBTA_AG_PEER_FEAT peer_feat;
  int num_active;
  int num_held;
  bool is_during_voice_recognition;
  bthf_call_state_t call_setup_state;
};

@@ -135,6 +136,8 @@ static const char* dump_hf_call_state(bthf_call_state_t call_state) {
  }
}

static int btif_hf_idx_by_bdaddr(RawAddress* bd_addr);

/**
 * Check if bd_addr is the current active device.
 *
@@ -821,6 +824,28 @@ bool IsCallIdle() {
  return true;
}

bool IsDuringVoiceRecognition(RawAddress* bd_addr) {
  if (!bt_hf_callbacks) {
    return false;
  }
  if (bd_addr == nullptr) {
    log::error("null address");
    return false;
  }
  int idx = btif_hf_idx_by_bdaddr(bd_addr);
  if ((idx < 0) || (idx >= BTA_AG_MAX_NUM_CLIENTS)) {
    log::error("Invalid index {}", idx);
    return false;
  }
  if (!is_connected(bd_addr)) {
    log::error("{} is not connected", *bd_addr);
    return false;
  }
  bool in_vr = btif_hf_cb[idx].is_during_voice_recognition;
  log::debug("IsDuringVoiceRecognition={}", in_vr);
  return in_vr;
}

class HeadsetInterface : Interface {
public:
  static Interface* GetInstance() {
@@ -992,6 +1017,7 @@ bt_status_t HeadsetInterface::StartVoiceRecognition(RawAddress* bd_addr) {
    log::error("voice recognition not supported, features=0x{:x}", btif_hf_cb[idx].peer_feat);
    return BT_STATUS_UNSUPPORTED;
  }
  btif_hf_cb[idx].is_during_voice_recognition = true;
  tBTA_AG_RES_DATA ag_res = {};
  ag_res.state = true;
  BTA_AgResult(btif_hf_cb[idx].handle, BTA_AG_BVRA_RES, ag_res);
@@ -1014,6 +1040,7 @@ bt_status_t HeadsetInterface::StopVoiceRecognition(RawAddress* bd_addr) {
    log::error("voice recognition not supported, features=0x{:x}", btif_hf_cb[idx].peer_feat);
    return BT_STATUS_UNSUPPORTED;
  }
  btif_hf_cb[idx].is_during_voice_recognition = false;
  tBTA_AG_RES_DATA ag_res = {};
  ag_res.state = false;
  BTA_AgResult(btif_hf_cb[idx].handle, BTA_AG_BVRA_RES, ag_res);
+12 −0
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ namespace btif_hf {

// Function state capture and return values, if needed
struct GetInterface GetInterface;
struct IsCallIdle IsCallIdle;
struct IsDuringVoiceRecognition IsDuringVoiceRecognition;
}  // namespace btif_hf
}  // namespace mock
}  // namespace test
@@ -49,6 +51,16 @@ Interface* GetInterface() {
  inc_func_call_count(__func__);
  return test::mock::btif_hf::GetInterface();
}

bool IsCallIdle() {
  inc_func_call_count(__func__);
  return test::mock::btif_hf::IsCallIdle();
}

bool IsDuringVoiceRecognition(RawAddress* bd_addr) {
  inc_func_call_count(__func__);
  return test::mock::btif_hf::IsDuringVoiceRecognition(bd_addr);
}
}  // namespace headset
}  // namespace bluetooth

Loading