Merge "hal: mute stream and switch to speaker during a2dp suspend"
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 2a6cdfa..f1ff4ef 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -102,6 +102,7 @@
 #define INVALID_OUT_VOLUME -1
 #define AUDIO_IO_PORTS_MAX 32
 
+#define PLAYBACK_GAIN_MAX 1.0f
 #define RECORD_GAIN_MIN 0.0f
 #define RECORD_GAIN_MAX 1.0f
 #define RECORD_VOLUME_CTL_MAX 0x2000
@@ -1227,15 +1228,10 @@
     if (audio_extn_is_maxx_audio_enabled())
         audio_extn_ma_set_device(usecase);
     audio_extn_utils_send_audio_calibration(adev, usecase);
-    if ((usecase->type == PCM_PLAYBACK) &&
-            ((out = usecase->stream.out) != NULL)) {
-        if (!is_offload_usecase(out->usecase)) {
-            pthread_mutex_lock(&out->latch_lock);
-            out->muted = false;
-            pthread_mutex_unlock(&out->latch_lock);
-        } else if (out->compr) {
+    if ((usecase->type == PCM_PLAYBACK) && is_offload_usecase(usecase->id)) {
+        out = usecase->stream.out;
+        if (out && out->compr)
             audio_extn_utils_compress_set_clk_rec_mode(usecase);
-        }
     }
 
     if (usecase->type == PCM_CAPTURE) {
@@ -3641,6 +3637,8 @@
         return -EINVAL;
     }
 
+    out->a2dp_muted = false;
+
     if (audio_extn_ext_hw_plugin_usecase_stop(adev->ext_hw_plugin, uc_info))
         ALOGE("%s: failed to stop ext hw plugin", __func__);
 
@@ -3689,9 +3687,6 @@
 
     list_remove(&uc_info->list);
     out->started = 0;
-    pthread_mutex_lock(&out->latch_lock);
-    out->muted = false;
-    pthread_mutex_unlock(&out->latch_lock);
     if (is_offload_usecase(out->usecase) &&
         (audio_extn_passthru_is_passthrough_stream(out))) {
         ALOGV("Disable passthrough , reset mixer to pcm");
@@ -4979,17 +4974,19 @@
                 platform_set_swap_channels(adev, true);
                 audio_extn_perf_lock_release(&adev->perf_lock_handle);
             }
-            if (is_offload_usecase(out->usecase) &&
-                 (!is_a2dp_out_device_type(&out->device_list) || audio_extn_a2dp_source_is_ready())) {
-                pthread_mutex_lock(&out->latch_lock);
-                if (out->a2dp_compress_mute) {
-                    out->a2dp_compress_mute = false;
-                    out_set_compr_volume(&out->stream, out->volume_l, out->volume_r);
+            pthread_mutex_lock(&out->latch_lock);
+            if (!is_a2dp_out_device_type(&out->device_list) || audio_extn_a2dp_source_is_ready()) {
+                if (out->a2dp_muted) {
+                    out->a2dp_muted = false;
+                    if (is_offload_usecase(out->usecase))
+                        out_set_compr_volume(&out->stream, out->volume_l, out->volume_r);
+                    else if (out->usecase != USECASE_AUDIO_PLAYBACK_VOIP)
+                        out_set_pcm_volume(&out->stream, out->volume_l, out->volume_r);
                 }
-                pthread_mutex_unlock(&out->latch_lock);
-            } else if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP) {
-                out_set_voip_volume(&out->stream, out->volume_l, out->volume_r);
             }
+            if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP && !out->a2dp_muted)
+                out_set_voip_volume(&out->stream, out->volume_l, out->volume_r);
+            pthread_mutex_unlock(&out->latch_lock);
         }
     }
 
@@ -5541,9 +5538,7 @@
     ALOGD("%s: called with left_vol=%f, right_vol=%f", __func__, left, right);
     if (out->usecase == USECASE_AUDIO_PLAYBACK_MULTI_CH) {
         /* only take left channel into account: the API is for stereo anyway */
-        pthread_mutex_lock(&out->latch_lock);
         out->muted = (left == 0.0f);
-        pthread_mutex_unlock(&out->latch_lock);
         return 0;
     } else if (is_offload_usecase(out->usecase)) {
         if (audio_extn_passthru_is_passthrough_stream(out)) {
@@ -5581,15 +5576,15 @@
                 }
             }
             pthread_mutex_lock(&out->latch_lock);
-            if (!out->a2dp_compress_mute) {
+            if (!out->a2dp_muted) {
                 ret = out_set_compr_volume(&out->stream, out->volume_l, out->volume_r);
             }
             pthread_mutex_unlock(&out->latch_lock);
             return ret;
         } else {
             pthread_mutex_lock(&out->latch_lock);
-            ALOGV("%s: compress mute %d", __func__, out->a2dp_compress_mute);
-            if (!out->a2dp_compress_mute)
+            ALOGV("%s: compress mute %d", __func__, out->a2dp_muted);
+            if (!out->a2dp_muted)
                 ret = out_set_compr_volume(stream, left, right);
             out->volume_l = left;
             out->volume_r = right;
@@ -5599,14 +5594,17 @@
     } else if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP) {
         out->app_type_cfg.gain[0] = (int)(left * VOIP_PLAYBACK_VOLUME_MAX);
         out->app_type_cfg.gain[1] = (int)(right * VOIP_PLAYBACK_VOLUME_MAX);
+        pthread_mutex_lock(&out->latch_lock);
         if (!out->standby) {
             audio_extn_utils_send_app_type_gain(out->dev,
                                                 out->app_type_cfg.app_type,
                                                 &out->app_type_cfg.gain[0]);
-            ret = out_set_voip_volume(stream, left, right);
+            if (!out->a2dp_muted)
+                ret = out_set_voip_volume(stream, left, right);
         }
         out->volume_l = left;
         out->volume_r = right;
+        pthread_mutex_unlock(&out->latch_lock);
         return ret;
     } else if (out->usecase == USECASE_AUDIO_PLAYBACK_MMAP) {
         ALOGV("%s: MMAP set volume called", __func__);
@@ -5618,21 +5616,25 @@
     } else if (out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY ||
                out->usecase == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER ||
                out->usecase == USECASE_AUDIO_PLAYBACK_ULL) {
+        pthread_mutex_lock(&out->latch_lock);
         /* Volume control for pcm playback */
-        if (!out->standby)
+        if (!out->standby && !out->a2dp_muted)
             ret = out_set_pcm_volume(stream, left, right);
         else
             out->apply_volume = true;
 
         out->volume_l = left;
         out->volume_r = right;
+        pthread_mutex_unlock(&out->latch_lock);
         return ret;
     } else if (audio_extn_auto_hal_is_bus_device_usecase(out->usecase)) {
         ALOGV("%s: bus device set volume called", __func__);
-        if (!out->standby)
+        pthread_mutex_lock(&out->latch_lock);
+        if (!out->standby && !out->a2dp_muted)
             ret = out_set_pcm_volume(stream, left, right);
         out->volume_l = left;
         out->volume_r = right;
+        pthread_mutex_unlock(&out->latch_lock);
         return ret;
     }
 
@@ -6018,10 +6020,8 @@
     } else {
         if (out->pcm) {
             size_t bytes_to_write = bytes;
-            pthread_mutex_lock(&out->latch_lock);
             if (out->muted)
                 memset((void *)buffer, 0, bytes);
-            pthread_mutex_unlock(&out->latch_lock);
             ALOGV("%s: frames=%zu, frame_size=%zu, bytes_to_write=%zu",
                      __func__, frames, frame_size, bytes_to_write);
 
@@ -7773,7 +7773,7 @@
     out->non_blocking = 0;
     out->convert_buffer = NULL;
     out->started = 0;
-    out->a2dp_compress_mute = false;
+    out->a2dp_muted = false;
     out->hal_output_suspend_supported = 0;
     out->dynamic_pm_qos_config_supported = 0;
     out->set_dual_mono = false;
@@ -8504,6 +8504,8 @@
     out->kernel_buffer_size = out->config.period_size * out->config.period_count;
 
     out->standby = 1;
+    out->volume_l = PLAYBACK_GAIN_MAX;
+    out->volume_r = PLAYBACK_GAIN_MAX;
     /* out->muted = false; by calloc() */
     /* out->written = 0; by calloc() */
 
@@ -8644,7 +8646,7 @@
             free(out->compr_config.codec);
     }
 
-    out->a2dp_compress_mute = false;
+    out->a2dp_muted = false;
 
     if (is_interactive_usecase(out->usecase))
         free_interactive_usecase(adev, out->usecase);
@@ -8897,17 +8899,12 @@
                 //force device switch to re configure encoder
                 select_devices(adev, usecase->id);
                 ALOGD("Unmuting the stream after select_devices");
-                pthread_mutex_lock(&usecase->stream.out->latch_lock);
-                usecase->stream.out->a2dp_compress_mute = false;
-                out_set_compr_volume(&usecase->stream.out->stream,
-                                     usecase->stream.out->volume_l,
-                                     usecase->stream.out->volume_r);
-                pthread_mutex_unlock(&usecase->stream.out->latch_lock);
+                check_a2dp_restore_l(adev, usecase->stream.out, true);
                 audio_extn_a2dp_set_handoff_mode(false);
                 break;
             } else if (is_offload_usecase(usecase->stream.out->usecase)) {
                 pthread_mutex_lock(&usecase->stream.out->latch_lock);
-                if (usecase->stream.out->a2dp_compress_mute) {
+                if (usecase->stream.out->a2dp_muted) {
                     pthread_mutex_unlock(&usecase->stream.out->latch_lock);
                     reassign_device_list(&usecase->stream.out->device_list,
                                          AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, "");
@@ -10358,8 +10355,8 @@
     }
     list_init(&devices);
 
-    ALOGD("%s: enter: usecase(%d: %s)", __func__,
-          out->usecase, use_case_table[out->usecase]);
+    ALOGD("%s: enter: usecase(%d: %s), a2dp muted %d", __func__,
+          out->usecase, use_case_table[out->usecase], out->a2dp_muted);
 
     if (restore) {
         pthread_mutex_lock(&out->latch_lock);
@@ -10368,42 +10365,50 @@
             ALOGD("%s: restoring A2dp and unmuting stream", __func__);
             if (uc_info->out_snd_device != SND_DEVICE_OUT_BT_A2DP)
                 select_devices(adev, uc_info->id);
-            if (is_offload_usecase(out->usecase) &&
-                (uc_info->out_snd_device == SND_DEVICE_OUT_BT_A2DP)) {
-                if (out->a2dp_compress_mute) {
-                    out->a2dp_compress_mute = false;
+
+            if (is_offload_usecase(out->usecase)) {
+                if (uc_info->out_snd_device == SND_DEVICE_OUT_BT_A2DP)
                     out_set_compr_volume(&out->stream, out->volume_l, out->volume_r);
-                }
+            } else if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP) {
+                out_set_voip_volume(&out->stream, out->volume_l, out->volume_r);
+            } else {
+                out_set_pcm_volume(&out->stream, out->volume_l, out->volume_r);
             }
+            out->a2dp_muted = false;
         }
-        out->muted = false;
         pthread_mutex_unlock(&out->latch_lock);
     } else {
         pthread_mutex_lock(&out->latch_lock);
-        if (is_offload_usecase(out->usecase)) {
-            // mute compress stream if suspended
-            if (!out->a2dp_compress_mute && !out->standby) {
-                ALOGD("%s: selecting speaker and muting stream", __func__);
-                assign_devices(&devices, &out->device_list);
-                reassign_device_list(&out->device_list, AUDIO_DEVICE_OUT_SPEAKER, "");
-                left_p = out->volume_l;
-                right_p = out->volume_r;
+        // mute stream and switch to speaker if suspended
+        if (!out->a2dp_muted && !out->standby) {
+            ALOGD("%s: selecting speaker and muting stream", __func__);
+            assign_devices(&devices, &out->device_list);
+            reassign_device_list(&out->device_list, AUDIO_DEVICE_OUT_SPEAKER, "");
+            left_p = out->volume_l;
+            right_p = out->volume_r;
+            out->a2dp_muted = true;
+            if (is_offload_usecase(out->usecase)) {
                 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);
+            } else if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP) {
+                out_set_voip_volume(&out->stream, (float)0, (float)0);
+            } else {
+                out_set_pcm_volume(&out->stream, (float)0, (float)0);
+                /* wait for stale pcm drained before switching to speaker */
+                uint32_t latency =
+                    (out->config.period_count * out->config.period_size * 1000) /
+                    (out->config.rate);
+                usleep(latency * 1000);
+            }
+            select_devices(adev, out->usecase);
+            if (is_offload_usecase(out->usecase)) {
                 if (out->offload_state == OFFLOAD_STATE_PLAYING)
                     compress_resume(out->compr);
-                assign_devices(&out->device_list, &devices);
-                out->volume_l = left_p;
-                out->volume_r = right_p;
             }
-        } else {
-            // mute for non offloaded streams
-            if (audio_extn_a2dp_source_is_suspended()) {
-                out->muted = true;
-            }
+            assign_devices(&out->device_list, &devices);
+            out->volume_l = left_p;
+            out->volume_r = right_p;
         }
         pthread_mutex_unlock(&out->latch_lock);
     }
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index f1e8672..32271bb 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -442,7 +442,7 @@
     qahwi_stream_out_t qahwi_out;
 
     bool is_iec61937_info_available;
-    bool a2dp_compress_mute;
+    bool a2dp_muted;
     float volume_l;
     float volume_r;
     bool apply_volume;