audio: Add check to confirm if routing to BT SCO is successfull
-Routing to BT-SCO can fail if the BT SOC gets disconnected or
the remote disconnects
-In such scenarios if audio is in process of routing streams to
BT SCO then those routing requests fail, hence the complete
data chain gets stalled.
-Add a mechanism to confirm with BT Codec driver if the last
routing request was successfull, if not close the stream
to avoid hang.
Change-Id: Ic78ba90ad55f30d99cdd76a6c0a0dc5398706b03
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 59d14aa..df8a9c4 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1485,6 +1485,48 @@
return ret;
}
+bool is_btsco_device(snd_device_t out_snd_device, snd_device_t in_snd_device)
+{
+ bool ret=false;
+ if ((out_snd_device == SND_DEVICE_OUT_BT_SCO ||
+ out_snd_device == SND_DEVICE_OUT_BT_SCO_WB) ||
+ in_snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB_NREC ||
+ in_snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB ||
+ in_snd_device == SND_DEVICE_IN_BT_SCO_MIC_NREC ||
+ in_snd_device == SND_DEVICE_IN_BT_SCO_MIC)
+ ret = true;
+
+ return ret;
+}
+
+bool is_a2dp_device(snd_device_t out_snd_device)
+{
+ bool ret=false;
+ if (out_snd_device == SND_DEVICE_OUT_BT_A2DP)
+ ret = true;
+
+ return ret;
+}
+
+bool is_bt_soc_on(struct audio_device *adev)
+{
+ struct mixer_ctl *ctl;
+ char *mixer_ctl_name = "BT SOC status";
+ ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+ bool bt_soc_status = true;
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s",
+ __func__, mixer_ctl_name);
+ /*This is to ensure we dont break targets which dont have the kernel change*/
+ return true;
+ }
+ bt_soc_status = mixer_ctl_get_value(ctl, 0);
+ ALOGD("BT SOC status: %d",bt_soc_status);
+ return bt_soc_status;
+}
+
+int out_standby_l(struct audio_stream *stream);
+
int select_devices(struct audio_device *adev, audio_usecase_t uc_id)
{
snd_device_t out_snd_device = SND_DEVICE_NONE;
@@ -1611,6 +1653,12 @@
return 0;
}
+ if ((is_btsco_device(out_snd_device,in_snd_device) && !adev->bt_sco_on) ||
+ (is_a2dp_device(out_snd_device) && !audio_extn_a2dp_is_ready())) {
+ ALOGD("SCO/A2DP is selected but they are not connected/ready hence dont route");
+ return 0;
+ }
+
ALOGD("%s: out_snd_device(%d: %s) in_snd_device(%d: %s)", __func__,
out_snd_device, platform_get_snd_device_name(out_snd_device),
in_snd_device, platform_get_snd_device_name(in_snd_device));
@@ -1740,6 +1788,35 @@
status = platform_switch_voice_call_usecase_route_post(adev->platform,
out_snd_device,
in_snd_device);
+
+ if (is_btsco_device(out_snd_device, in_snd_device) || is_a2dp_device(out_snd_device)) {
+
+ if (usecase->type == VOIP_CALL) {
+ if (adev->active_input != NULL &&
+ !adev->active_input->standby) {
+ if (is_bt_soc_on(adev) == false){
+ ALOGD("BT SCO MIC disconnected while in connection");
+ if (adev->active_input->pcm != NULL)
+ pcm_stop(adev->active_input->pcm);
+ }
+ }
+ if ((usecase->stream.out != NULL) && (usecase->stream.out != adev->primary_output)
+ && usecase->stream.out->started) {
+ if (is_bt_soc_on(adev) == false) {
+ ALOGD("BT SCO/A2DP disconnected while in connection");
+ out_standby_l(&usecase->stream.out->stream.common);
+ }
+ }
+ } else if ((usecase->stream.out != NULL) &&
+ !(usecase->stream.out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
+ usecase->stream.out->started) {
+ if (is_bt_soc_on(adev) == false) {
+ ALOGD("BT SCO/A2dp disconnected while in connection");
+ out_standby_l(&usecase->stream.out->stream.common);
+ }
+ }
+ }
+
ALOGD("%s: done",__func__);
return status;
@@ -1799,6 +1876,14 @@
goto error_config;
}
+ if (audio_is_bluetooth_sco_device(in->device)) {
+ if (!adev->bt_sco_on) {
+ ALOGE("%s: SCO profile is not ready, return error", __func__);
+ ret = -EIO;
+ goto error_config;
+ }
+ }
+
/* Check if source matches incall recording usecase criteria */
ret = voice_check_and_set_incall_rec_usecase(adev, in);
if (ret)
@@ -2197,7 +2282,7 @@
list_remove(&uc_info->list);
free(uc_info);
-
+ out->started = 0;
if (is_offload_usecase(out->usecase) &&
(audio_extn_passthru_is_passthrough_stream(out))) {
ALOGV("Disable passthrough , reset mixer to pcm");
@@ -2259,6 +2344,20 @@
}
}
}
+ if (out->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
+ if (!adev->bt_sco_on) {
+ if (out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+ //combo usecase just by pass a2dp
+ ALOGW("%s: SCO is not connected, route it to speaker", __func__);
+ out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+ } else {
+ ALOGE("%s: SCO profile is not ready, return error", __func__);
+ ret = -EAGAIN;
+ goto error_config;
+ }
+ }
+ }
+
out->pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
if (out->pcm_device_id < 0) {
ALOGE("%s: Invalid PCM device id(%d) for the usecase(%d)",
@@ -2647,6 +2746,7 @@
out->standby = true;
if (out->usecase == USECASE_COMPRESS_VOIP_CALL) {
voice_extn_compress_voip_close_output_stream(stream);
+ out->started = 0;
pthread_mutex_unlock(&adev->lock);
pthread_mutex_unlock(&out->lock);
ALOGD("VOIP output entered standby");
@@ -2696,6 +2796,53 @@
return 0;
}
+/*
+ *standby implementation without locks, assumes that the callee already
+ *has taken adev and out lock.
+ */
+int out_standby_l(struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->dev;
+
+ ALOGD("%s: enter: stream (%p) usecase(%d: %s)", __func__,
+ stream, out->usecase, use_case_table[out->usecase]);
+
+ if (!out->standby) {
+ if (adev->adm_deregister_stream)
+ adev->adm_deregister_stream(adev->adm_data, out->handle);
+
+ if (is_offload_usecase(out->usecase))
+ stop_compressed_output_l(out);
+
+ out->standby = true;
+ if (out->usecase == USECASE_COMPRESS_VOIP_CALL) {
+ voice_extn_compress_voip_close_output_stream(stream);
+ out->started = 0;
+ ALOGD("VOIP output entered standby");
+ return 0;
+ } else if (!is_offload_usecase(out->usecase)) {
+ if (out->pcm) {
+ pcm_close(out->pcm);
+ out->pcm = NULL;
+ }
+ } else {
+ ALOGD("copl(%p):standby", out);
+ out->send_next_track_params = false;
+ out->is_compr_metadata_avail = false;
+ out->gapless_mdata.encoder_delay = 0;
+ out->gapless_mdata.encoder_padding = 0;
+ if (out->compr != NULL) {
+ compress_close(out->compr);
+ out->compr = NULL;
+ }
+ }
+ stop_output_stream(out);
+ }
+ ALOGD("%s: exit", __func__);
+ return 0;
+}
+
static int out_dump(const struct audio_stream *stream __unused,
int fd __unused)
{
@@ -3203,7 +3350,7 @@
out->standby = true;
goto exit;
}
-
+ out->started = 1;
if (last_known_cal_step != -1) {
ALOGD("%s: retry previous failed cal level set", __func__);
audio_hw_send_gain_dep_calibration(last_known_cal_step);
@@ -3372,6 +3519,7 @@
if (out->usecase == USECASE_COMPRESS_VOIP_CALL) {
pthread_mutex_lock(&adev->lock);
voice_extn_compress_voip_close_output_stream(&out->stream.common);
+ out->started = 0;
pthread_mutex_unlock(&adev->lock);
out->standby = true;
}
@@ -4075,6 +4223,7 @@
out->bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
out->non_blocking = 0;
out->convert_buffer = NULL;
+ out->started = 0;
if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL &&
(flags & AUDIO_OUTPUT_FLAG_DIRECT)) {
@@ -4575,6 +4724,7 @@
if (out->usecase == USECASE_COMPRESS_VOIP_CALL) {
pthread_mutex_lock(&adev->lock);
ret = voice_extn_compress_voip_close_output_stream(&stream->common);
+ out->started = 0;
pthread_mutex_unlock(&adev->lock);
if(ret != 0)
ALOGE("%s: Compress voip output cannot be closed, error:%d",
@@ -4622,6 +4772,15 @@
if (!parms)
goto error;
+ ret = str_parms_get_str(parms, "BT_SCO", value, sizeof(value));
+ if (ret >= 0) {
+ /* When set to false, HAL should disable EC and NS */
+ if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
+ adev->bt_sco_on = true;
+ else
+ adev->bt_sco_on = false;
+ }
+
pthread_mutex_lock(&adev->lock);
status = voice_set_parameters(adev, parms);
if (status != 0)
@@ -5355,6 +5514,7 @@
adev->bluetooth_nrec = true;
adev->acdb_settings = TTY_MODE_OFF;
adev->allow_afe_proxy_usage = true;
+ adev->bt_sco_on = false;
/* adev->cur_hdmi_channels = 0; by calloc() */
adev->snd_dev_ref_cnt = calloc(SND_DEVICE_MAX, sizeof(int));
voice_init(adev);