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);