audio hal: fix audio leak when disconnecting A2DP sink am: f030fe8ae0

Change-Id: Id954aaab1f06073f5fed6cc72b7081cc32f21089
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 035b14e..0f868ca 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -72,6 +72,8 @@
 /* treat as unsigned Q1.13 */
 #define APP_TYPE_GAIN_DEFAULT         0x2000
 #define COMPRESS_PLAYBACK_VOLUME_MAX 0x2000
+#define PCM_PLAYBACK_VOLUME_MAX 0x2000
+#define INVALID_OUT_VOLUME -1
 
 /* treat as unsigned Q1.13 */
 #define VOIP_PLAYBACK_VOLUME_MAX 0x2000
@@ -359,7 +361,8 @@
 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 set_compr_volume(struct audio_stream_out *stream, float left, float right);
+static int out_set_compr_volume(struct audio_stream_out *stream, float left, float right);
+static int out_set_pcm_volume(struct audio_stream_out *stream, float left, float right);
 
 static int in_set_microphone_direction(const struct audio_stream_in *stream,
                                            audio_microphone_direction_t dir);
@@ -2602,6 +2605,11 @@
                 goto error_open;
             }
         }
+        if ((out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY
+                || out->usecase == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER
+                || out->usecase == USECASE_AUDIO_PLAYBACK_ULL)) {
+           out_set_pcm_volume(&out->stream, out->volume_l, out->volume_r);
+         }
     }
 
     register_out_stream(out);
@@ -2969,6 +2977,7 @@
     bool select_new_device = false;
     int status = 0;
     bool bypass_a2dp = false;
+    bool forced_speaker_fallback = false;
 
     ALOGD("%s: enter: usecase(%d: %s) kvpairs: %s",
           __func__, out->usecase, use_case_table[out->usecase], kvpairs);
@@ -2990,6 +2999,7 @@
                 out_standby_l(&out->stream.common);
             }
             val = AUDIO_DEVICE_OUT_SPEAKER;
+            forced_speaker_fallback = true;
         }
 
         pthread_mutex_lock(&adev->lock);
@@ -3004,6 +3014,7 @@
         if (out->devices == AUDIO_DEVICE_OUT_AUX_DIGITAL &&
                 val == AUDIO_DEVICE_NONE) {
             val = AUDIO_DEVICE_OUT_SPEAKER;
+            forced_speaker_fallback = true;
         }
 
         /*
@@ -3018,6 +3029,7 @@
                 !audio_extn_a2dp_is_ready() &&
                 !adev->bt_sco_on) {
                 val = AUDIO_DEVICE_OUT_SPEAKER;
+                forced_speaker_fallback = true;
         }
 
         /* To avoid a2dp to sco overlapping / BT device improper state
@@ -3094,6 +3106,42 @@
             }
 
             if (!out->standby) {
+                int volume_delay_us = 0;
+                if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+                    pthread_mutex_lock(&out->compr_mute_lock);
+                    if (out->a2dp_compress_mute &&
+                        (!(new_dev & AUDIO_DEVICE_OUT_ALL_A2DP) ||
+                         audio_extn_a2dp_is_ready())) {
+                        out->a2dp_compress_mute = false;
+                    }
+                    float volume_l = out->volume_l;
+                    float volume_r = out->volume_r;
+                    if (out->a2dp_compress_mute || forced_speaker_fallback) {
+                        volume_l = 0.0;
+                        volume_r = 0.0;
+                    }
+                    if (volume_l != out->applied_volume_l || volume_r != out->applied_volume_r)
+                        volume_delay_us = COMPRESS_OFFLOAD_PLAYBACK_LATENCY * 2000;
+
+                    out_set_compr_volume(&out->stream, volume_l, volume_r);
+                    pthread_mutex_unlock(&out->compr_mute_lock);
+                } else if (out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY ||
+                        out->usecase == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER ||
+                        out->usecase == USECASE_AUDIO_PLAYBACK_ULL) {
+                    float volume_l = out->volume_l;
+                    float volume_r = out->volume_r;
+                    if (forced_speaker_fallback) {
+                        volume_l = 0.0;
+                        volume_r = 0.0;
+                    }
+                    if (volume_l != out->applied_volume_l || volume_r != out->applied_volume_r)
+                        volume_delay_us = (int)platform_render_latency(out) * 2;
+
+                    out_set_pcm_volume(&out->stream, volume_l, volume_r);
+                }
+                if (volume_delay_us > 0)
+                    usleep(volume_delay_us * 2);
+
                 if (!same_dev) {
                     ALOGV("update routing change");
                     // inform adm before actual routing to prevent glitches.
@@ -3120,14 +3168,7 @@
                 if (!same_dev)
                     platform_set_swap_channels(adev, true);
 
-                if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
-                    out->a2dp_compress_mute &&
-                    (!(out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) || audio_extn_a2dp_is_ready())) {
-                    pthread_mutex_lock(&out->compr_mute_lock);
-                    out->a2dp_compress_mute = false;
-                    set_compr_volume(&out->stream, out->volume_l, out->volume_r);
-                    pthread_mutex_unlock(&out->compr_mute_lock);
-                }
+
             }
 
         }
@@ -3299,7 +3340,7 @@
     return latency;
 }
 
-static int set_compr_volume(struct audio_stream_out *stream, float left,
+static int out_set_compr_volume(struct audio_stream_out *stream, float left,
                           float right)
 {
     struct stream_out *out = (struct stream_out *)stream;
@@ -3310,6 +3351,9 @@
     int pcm_device_id = platform_get_pcm_device_id(out->usecase,
                                                PCM_PLAYBACK);
 
+    if (left == out->applied_volume_l && right == out->applied_volume_r)
+       return 0;
+
     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);
@@ -3324,9 +3368,49 @@
     volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX);
     mixer_ctl_set_array(ctl, volume, sizeof(volume) / sizeof(volume[0]));
 
+    out->applied_volume_l = left;
+    out->applied_volume_r = right;
     return 0;
 }
 
+static int out_set_pcm_volume(struct audio_stream_out *stream, float left,
+                              float right)
+{
+    struct stream_out *out = (struct stream_out *)stream;
+
+    if (left == out->applied_volume_l && right == out->applied_volume_r)
+       return 0;
+
+    /* Volume control for pcm playback */
+    if (left != right) {
+        return -EINVAL;
+    } 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), "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;
+        }
+
+        int volume = (int) (left * PCM_PLAYBACK_VOLUME_MAX);
+        int ret = mixer_ctl_set_value(ctl, 0, volume);
+        if (ret < 0) {
+            ALOGE("%s: Could not set ctl, error:%d ", __func__, ret);
+            return -EINVAL;
+        }
+
+        ALOGV("%s : Pcm set volume value %d left %f", __func__, volume, left);
+
+        out->applied_volume_l = left;
+        out->applied_volume_r = right;
+        return 0;
+    }
+}
+
 static int out_set_volume(struct audio_stream_out *stream, float left,
                           float right)
 {
@@ -3341,7 +3425,7 @@
         pthread_mutex_lock(&out->compr_mute_lock);
         ALOGV("%s: compress mute %d", __func__, out->a2dp_compress_mute);
         if (!out->a2dp_compress_mute)
-            ret = set_compr_volume(stream, left, right);
+            ret = out_set_compr_volume(stream, left, right);
         out->volume_l = left;
         out->volume_r = right;
         pthread_mutex_unlock(&out->compr_mute_lock);
@@ -5293,6 +5377,13 @@
             }
         }
 
+        if (out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY ||
+               out->usecase == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER ||
+               out->usecase == USECASE_AUDIO_PLAYBACK_ULL) {
+            out->volume_l = 1.0;
+            out->volume_r = 1.0;
+        }
+
         if (config->sample_rate == 0) {
             out->sample_rate = out->config.rate;
         } else {
@@ -5375,6 +5466,9 @@
     }
     pthread_mutex_unlock(&adev->lock);
 
+    out->applied_volume_l = INVALID_OUT_VOLUME;
+    out->applied_volume_r = INVALID_OUT_VOLUME;
+
     out->stream.common.get_sample_rate = out_get_sample_rate;
     out->stream.common.set_sample_rate = out_set_sample_rate;
     out->stream.common.get_buffer_size = out_get_buffer_size;
@@ -6387,7 +6481,7 @@
             if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
                 (out->a2dp_compress_mute)) {
                 out->a2dp_compress_mute = false;
-                set_compr_volume(&out->stream, out->volume_l, out->volume_r);
+                out_set_compr_volume(&out->stream, out->volume_l, out->volume_r);
             }
             pthread_mutex_unlock(&out->compr_mute_lock);
         }
@@ -6404,7 +6498,7 @@
                 right_p = out->volume_r;
                 if (out->offload_state == OFFLOAD_STATE_PLAYING)
                     compress_pause(out->compr);
-                set_compr_volume(&out->stream, 0.0f, 0.0f);
+                out_set_compr_volume(&out->stream, 0.0f, 0.0f);
                 out->a2dp_compress_mute = true;
                 select_devices(adev, out->usecase);
                 if (out->offload_state == OFFLOAD_STATE_PLAYING)
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 15cfa60..e980e88 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -256,6 +256,8 @@
     bool a2dp_compress_mute;
     float volume_l;
     float volume_r;
+    float applied_volume_l;
+    float applied_volume_r;
 
     error_log_t *error_log;