hal: Handle A2dp suspend scenario

Avoid writing data to A2dp output in case of non-offload and
mute offload streams if A2dp is suspended.
Restore device and unmute streams if muted as part of suspend.

CRs-Fixed: 2066578
Change-Id: I3a222f128659de602ba1f6bed4d4e3cdcc7a379a
diff --git a/hal/audio_extn/a2dp.c b/hal/audio_extn/a2dp.c
index 5239a30..b96a147 100644
--- a/hal/audio_extn/a2dp.c
+++ b/hal/audio_extn/a2dp.c
@@ -765,6 +765,8 @@
 {
      int ret, val;
      char value[32]={0};
+     struct audio_usecase *uc_info;
+     struct listnode *node;
 
      if(a2dp.is_a2dp_offload_supported == false) {
         ALOGV("no supported encoders identified,ignoring a2dp setparam");
@@ -801,6 +803,14 @@
              if ((!strncmp(value,"true",sizeof(value)))) {
                 ALOGD("Setting a2dp to suspend state");
                 a2dp.a2dp_suspended = true;
+                list_for_each(node, &a2dp.adev->usecase_list) {
+                    uc_info = node_to_item(node, struct audio_usecase, list);
+                    if (uc_info->type == PCM_PLAYBACK) {
+                        pthread_mutex_unlock(&a2dp.adev->lock);
+                        check_a2dp_restore(a2dp.adev, uc_info->stream.out, false);
+                        pthread_mutex_lock(&a2dp.adev->lock);
+                    }
+                }
                 reset_a2dp_enc_config_params();
                 if(a2dp.audio_suspend_stream)
                    a2dp.audio_suspend_stream();
@@ -832,12 +842,13 @@
                         }
                     }
                 }
-                // restore A2DP device for active usecases
                 list_for_each(node, &a2dp.adev->usecase_list) {
                     uc_info = node_to_item(node, struct audio_usecase, list);
-                    if ((uc_info->stream.out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
-                            (uc_info->out_snd_device != SND_DEVICE_OUT_BT_A2DP))
-                        select_devices(a2dp.adev, uc_info->id);
+                    if (uc_info->type == PCM_PLAYBACK) {
+                        pthread_mutex_unlock(&a2dp.adev->lock);
+                        check_a2dp_restore(a2dp.adev, uc_info->stream.out, true);
+                        pthread_mutex_lock(&a2dp.adev->lock);
+                    }
                 }
             }
         }
@@ -874,6 +885,9 @@
 {
     bool ret = false;
 
+    if (a2dp.a2dp_suspended)
+        return ret;
+
     if ((a2dp.bt_state != A2DP_STATE_DISCONNECTED) &&
         (a2dp.is_a2dp_offload_supported) &&
         (a2dp.audio_check_a2dp_ready))
@@ -881,6 +895,11 @@
     return ret;
 }
 
+bool audio_extn_a2dp_is_suspended()
+{
+    return a2dp.a2dp_suspended;
+}
+
 void audio_extn_a2dp_init (void *adev)
 {
   a2dp.adev = (struct audio_device*)adev;
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index d0c3395..c2a5484 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -222,6 +222,7 @@
 #define audio_extn_a2dp_get_apptype_params(sample_rate,bit_width)    (0)
 #define audio_extn_a2dp_get_encoder_latency()            (0)
 #define audio_extn_a2dp_is_ready()                       (0)
+#define audio_extn_a2dp_is_suspended()                   (0)
 #else
 void audio_extn_a2dp_init(void *adev);
 int audio_extn_a2dp_start_playback();
@@ -233,6 +234,7 @@
                                         uint32_t *bit_width);
 uint32_t audio_extn_a2dp_get_encoder_latency();
 bool audio_extn_a2dp_is_ready();
+bool audio_extn_a2dp_is_suspended();
 #endif
 
 #ifndef SSR_ENABLED
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 2531b85..ace2a71 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -346,6 +346,9 @@
 //cache last MBDRC cal step level
 static int last_known_cal_step = -1 ;
 
+static int check_a2dp_restore_l(struct audio_device *adev, struct stream_out *out, bool restore);
+static int out_set_compr_volume(struct audio_stream_out *stream, float left, float right);
+
 static bool may_use_noirq_mode(struct audio_device *adev, audio_usecase_t uc_id,
                                int flags __unused)
 {
@@ -1728,6 +1731,12 @@
             voice_check_and_update_aanc_path(adev, usecase->out_snd_device, false);
     }
 
+    if ((usecase->out_snd_device == SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP) &&
+        (!audio_extn_a2dp_is_ready())) {
+        ALOGW("%s: A2DP profile is not ready, routing to speaker only", __func__);
+        usecase->out_snd_device = SND_DEVICE_OUT_SPEAKER;
+    }
+
     /* Disable current sound devices */
     if (usecase->out_snd_device != SND_DEVICE_NONE) {
         disable_audio_route(adev, usecase);
@@ -2355,6 +2364,7 @@
     char mixer_ctl_name[128];
     struct mixer_ctl *ctl = NULL;
     char* perf_mode[] = {"ULL", "ULL_PP", "LL"};
+    bool a2dp_combo = false;
 
     if ((out->usecase < 0) || (out->usecase >= AUDIO_USECASE_MAX)) {
         ret = -EINVAL;
@@ -2375,13 +2385,13 @@
     if (out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) {
         if (!audio_extn_a2dp_is_ready()) {
             if (out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
-                //combo usecase just by pass a2dp
-                ALOGW("%s: A2DP profile is not ready, route it to speaker", __func__);
-                out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+                a2dp_combo = true;
             } else {
-                ALOGE("%s: A2DP profile is not ready, return error", __func__);
-                ret = -EAGAIN;
-                goto error_config;
+                if (!(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) {
+                    ALOGE("%s: A2DP profile is not ready, return error", __func__);
+                    ret = -EAGAIN;
+                    goto error_config;
+                }
             }
         }
     }
@@ -2434,7 +2444,19 @@
         }
     }
 
-    select_devices(adev, out->usecase);
+    if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
+        (!audio_extn_a2dp_is_ready())) {
+        if (!a2dp_combo) {
+            check_a2dp_restore_l(adev, out, false);
+        } else {
+            audio_devices_t dev = out->devices;
+            out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+            select_devices(adev, out->usecase);
+            out->devices = dev;
+        }
+    } else {
+         select_devices(adev, out->usecase);
+    }
 
     ALOGV("%s: Opening PCM device card_id(%d) device_id(%d) format(%#x)",
           __func__, adev->snd_card, out->pcm_device_id, out->config.format);
@@ -2976,6 +2998,7 @@
     struct str_parms *parms;
     char value[32];
     int ret = 0, val = 0, err;
+    bool bypass_a2dp = false;
 
     ALOGD("%s: enter: usecase(%d: %s) kvpairs: %s",
           __func__, out->usecase, use_case_table[out->usecase], kvpairs);
@@ -3020,7 +3043,7 @@
                 if (val & AUDIO_DEVICE_OUT_SPEAKER) {
                     //combo usecase just by pass a2dp
                     ALOGW("%s: A2DP profile is not ready,routing to speaker only", __func__);
-                    val = AUDIO_DEVICE_OUT_SPEAKER;
+                    bypass_a2dp = true;
                 } else {
                     ALOGE("%s: A2DP profile is not ready,ignoring routing request", __func__);
                     /* update device to a2dp and don't route as BT returned error
@@ -3079,9 +3102,24 @@
                         adev->adm_on_routing_change(adev->adm_data,
                                                     out->handle);
                 }
-                select_devices(adev, out->usecase);
+                if (!bypass_a2dp) {
+                    select_devices(adev, out->usecase);
+                } else {
+                    out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+                    select_devices(adev, out->usecase);
+                    out->devices = new_dev;
+                }
                 if (!same_dev)
                     audio_extn_perf_lock_release(&adev->perf_lock_handle);
+                if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
+                    out->a2dp_compress_mute &&
+                    (!(out->devices & AUDIO_DEVICE_OUT_ALL_A2DP))) {
+                    pthread_mutex_lock(&out->compr_mute_lock);
+                    out->a2dp_compress_mute = false;
+                    out_set_compr_volume(&out->stream, out->volume_l, out->volume_r);
+                    pthread_mutex_unlock(&out->compr_mute_lock);
+                }
+
             }
         }
 
@@ -3287,11 +3325,40 @@
     return db;
 }
 
+static int out_set_compr_volume(struct audio_stream_out *stream, float left,
+                          float right)
+{
+    struct stream_out *out = (struct stream_out *)stream;
+    int volume[2];
+    char mixer_ctl_name[128];
+    struct audio_device *adev = out->dev;
+    struct mixer_ctl *ctl;
+    int pcm_device_id = platform_get_pcm_device_id(out->usecase,
+                                               PCM_PLAYBACK);
+
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+             "Compress Playback %d Volume", pcm_device_id);
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        return -EINVAL;
+    }
+    ALOGE("%s:ctl for mixer cmd - %s, left %f, right %f",
+           __func__, mixer_ctl_name, left, right);
+    volume[0] = (int)(left * COMPRESS_PLAYBACK_VOLUME_MAX);
+    volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX);
+    mixer_ctl_set_array(ctl, volume, sizeof(volume)/sizeof(volume[0]));
+
+    return 0;
+}
+
 static int out_set_volume(struct audio_stream_out *stream, float left,
                           float right)
 {
     struct stream_out *out = (struct stream_out *)stream;
     int volume[2];
+    int ret = 0;
 
     if (out->usecase == USECASE_AUDIO_PLAYBACK_MULTI_CH) {
         /* only take left channel into account: the API is for stereo anyway */
@@ -3320,24 +3387,14 @@
             mixer_ctl_set_array(ctl, volume, sizeof(volume)/sizeof(volume[0]));
             return 0;
         } else {
-            char mixer_ctl_name[128];
-            struct audio_device *adev = out->dev;
-            struct mixer_ctl *ctl;
-            int pcm_device_id = platform_get_pcm_device_id(out->usecase,
-                                                       PCM_PLAYBACK);
-
-            snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
-                     "Compress Playback %d Volume", pcm_device_id);
-            ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
-            if (!ctl) {
-                ALOGE("%s: Could not get ctl for mixer cmd - %s",
-                      __func__, mixer_ctl_name);
-                return -EINVAL;
-            }
-            volume[0] = (int)(left * COMPRESS_PLAYBACK_VOLUME_MAX);
-            volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX);
-            mixer_ctl_set_array(ctl, volume, sizeof(volume)/sizeof(volume[0]));
-            return 0;
+            pthread_mutex_lock(&out->compr_mute_lock);
+            ALOGE("%s: compress mute %d", __func__, out->a2dp_compress_mute);
+            if (!out->a2dp_compress_mute)
+                ret = out_set_compr_volume(stream, left, right);
+            out->volume_l = left;
+            out->volume_r = right;
+            pthread_mutex_unlock(&out->compr_mute_lock);
+            return ret;
         }
     } else if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP) {
         char mixer_ctl_name[] = "App Type Gain";
@@ -3412,6 +3469,21 @@
             out->is_iec61937_info_available = true;
         }
     }
+
+    if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
+        (audio_extn_a2dp_is_suspended())) {
+        if (!(out->devices & AUDIO_DEVICE_OUT_SPEAKER)) {
+            if (!(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) {
+                size_t bpf = audio_bytes_per_sample(out->format) *
+                    audio_channel_count_from_out_mask(out->channel_mask);
+                if (bpf != 0)
+                    out->written += bytes / bpf;
+                ret = -EIO;
+                goto exit;
+            }
+        }
+    }
+
     if (out->standby) {
         out->standby = false;
         pthread_mutex_lock(&adev->lock);
@@ -4300,6 +4372,7 @@
 
     pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL);
     pthread_mutex_init(&out->pre_lock, (const pthread_mutexattr_t *) NULL);
+    pthread_mutex_init(&out->compr_mute_lock, (const pthread_mutexattr_t *) NULL);
     pthread_cond_init(&out->cond, (const pthread_condattr_t *) NULL);
 
     if (devices == AUDIO_DEVICE_NONE)
@@ -4320,6 +4393,7 @@
     out->non_blocking = 0;
     out->convert_buffer = NULL;
     out->started = 0;
+    out->a2dp_compress_mute = false;
 
     if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL &&
         (flags & AUDIO_OUTPUT_FLAG_DIRECT)) {
@@ -4862,6 +4936,8 @@
             free(out->compr_config.codec);
     }
 
+    out->a2dp_compress_mute = false;
+
     if (out->convert_buffer != NULL) {
         free(out->convert_buffer);
         out->convert_buffer = NULL;
@@ -5626,6 +5702,81 @@
     return;
 }
 
+/* out and adev lock held */
+static int check_a2dp_restore_l(struct audio_device *adev, struct stream_out *out, bool restore)
+{
+    struct audio_usecase *uc_info;
+    float left_p;
+    float right_p;
+    audio_devices_t devices;
+
+    uc_info = get_usecase_from_list(adev, out->usecase);
+    if (uc_info == NULL) {
+        ALOGE("%s: Could not find the usecase (%d) in the list",
+              __func__, out->usecase);
+        return -EINVAL;
+    }
+
+    ALOGD("%s: enter: usecase(%d: %s)", __func__,
+          out->usecase, use_case_table[out->usecase]);
+
+    if (restore) {
+        // restore A2DP device for active usecases and unmute if required
+        if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
+            (uc_info->out_snd_device != SND_DEVICE_OUT_BT_A2DP)) {
+            ALOGD("%s: restoring A2dp and unmuting stream", __func__);
+            select_devices(adev, uc_info->id);
+            pthread_mutex_lock(&out->compr_mute_lock);
+            if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
+                (out->a2dp_compress_mute)) {
+                out->a2dp_compress_mute = false;
+                out_set_compr_volume(&out->stream, out->volume_l, out->volume_r);
+            }
+            pthread_mutex_unlock(&out->compr_mute_lock);
+        }
+    } else {
+        // mute compress stream if suspended
+        pthread_mutex_lock(&out->compr_mute_lock);
+        if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
+            (!out->a2dp_compress_mute)) {
+            if (!out->standby) {
+                ALOGD("%s: selecting speaker and muting stream", __func__);
+                devices = out->devices;
+                out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+                left_p = out->volume_l;
+                right_p = out->volume_r;
+                if (out->offload_state == OFFLOAD_STATE_PLAYING)
+                    compress_pause(out->compr);
+                out_set_compr_volume(&out->stream, (float)0, (float)0);
+                out->a2dp_compress_mute = true;
+                select_devices(adev, out->usecase);
+                if (out->offload_state == OFFLOAD_STATE_PLAYING)
+                    compress_resume(out->compr);
+                out->devices = devices;
+                out->volume_l = left_p;
+                out->volume_r = right_p;
+            }
+        }
+        pthread_mutex_unlock(&out->compr_mute_lock);
+    }
+    ALOGV("%s: exit", __func__);
+    return 0;
+}
+
+int check_a2dp_restore(struct audio_device *adev, struct stream_out *out, bool restore)
+{
+    int ret = 0;
+
+    lock_output_stream(out);
+    pthread_mutex_lock(&adev->lock);
+
+    ret = check_a2dp_restore_l(adev, out, restore);
+
+    pthread_mutex_unlock(&adev->lock);
+    pthread_mutex_unlock(&out->lock);
+    return ret;
+}
+
 static int adev_open(const hw_module_t *module, const char *name,
                      hw_device_t **device)
 {
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 91bf954..561a967 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -236,6 +236,7 @@
     struct audio_stream_out stream;
     pthread_mutex_t lock; /* see note below on mutex acquisition order */
     pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by playback thread */
+    pthread_mutex_t compr_mute_lock; /* acquire before setting compress volume */
     pthread_cond_t  cond;
     struct pcm_config config;
     struct compr_config compr_config;
@@ -301,6 +302,9 @@
     qahwi_stream_out_t qahwi_out;
 
     bool is_iec61937_info_available;
+    bool a2dp_compress_mute;
+    float volume_l;
+    float volume_r;
 };
 
 struct stream_in {
@@ -502,6 +506,8 @@
 audio_usecase_t get_usecase_id_from_usecase_type(const struct audio_device *adev,
                                                  usecase_type_t type);
 
+int check_a2dp_restore(struct audio_device *adev, struct stream_out *out, bool restore);
+
 int adev_open_output_stream(struct audio_hw_device *dev,
                             audio_io_handle_t handle,
                             audio_devices_t devices,