hal: QAF: Fix to resolve hang in dual AAC mix case

-Hang, because Drain-Ready not sent for Assoc-AAC
--Fixed by adding Drain-Ready event for Assoc-AAC

-Hang, because data is written on assoc stream
event when no main or pcm stream is active. Module will
not process the associated data alone
--Fixed by dropping the data on assoc stream if no main or
pcm stream is active

-Hang, because EOS is not raised by module if only
assoc stream is running. Module will keep on waiting for
main or pcm data
--Fixed by sending the Drain-Ready event immediately on assoc
stream if no main or pcm stream is active

-Hang, due to race condition when main and associated
streams are closed simulteneously
--Fixed by protecting with lock.

-Hang in test app if drain ready event is received synchronously
--Fixed by adding a flag to indicated drain-ready event.

Change-Id: I19c0de43657a8de8845b440f8c18f03149e549e5
diff --git a/hal/audio_extn/qaf.c b/hal/audio_extn/qaf.c
index 7a1c65f..9c57b2c 100644
--- a/hal/audio_extn/qaf.c
+++ b/hal/audio_extn/qaf.c
@@ -354,6 +354,22 @@
    return (qaf_mod->stream_in[QAF_IN_MAIN] && qaf_mod->stream_in[QAF_IN_MAIN_2]);
 }
 
+//Checks if any main or pcm stream is running in the session.
+static bool is_any_stream_running(struct qaf_module* qaf_mod)
+{
+    //Not checking associated stream.
+    struct stream_out *out = qaf_mod->stream_in[QAF_IN_MAIN];
+    struct stream_out *out_pcm = qaf_mod->stream_in[QAF_IN_PCM];
+    struct stream_out *out_main2 = qaf_mod->stream_in[QAF_IN_MAIN_2];
+
+    if ((out == NULL || (out != NULL && check_stream_state(out, STOPPED)))
+        && (out_main2 == NULL || (out_main2 != NULL && check_stream_state(out_main2, STOPPED)))
+        && (out_pcm == NULL || (out_pcm != NULL && check_stream_state(out_pcm, STOPPED)))) {
+        return false;
+    }
+    return true;
+}
+
 /* Gets the pcm output buffer size(in samples) for the mm module. */
 static uint32_t get_pcm_output_buffer_size_samples(struct qaf_module *qaf_mod)
 {
@@ -713,6 +729,10 @@
         return ret;
     }
 
+    //If data received on associated stream when all other stream are stopped then drop the data.
+    if (out == qaf_mod->stream_in[QAF_IN_ASSOC] && !is_any_stream_running(qaf_mod))
+        return bytes;
+
     if (out->qaf_stream_handle) {
         ret = qaf_mod->qaf_audio_stream_write(out->qaf_stream_handle, buffer, bytes);
         if(ret > 0) set_stream_state(out, RUN);
@@ -1049,6 +1069,9 @@
 {
     struct stream_out *out = (struct stream_out *)stream;
     int status = 0;
+    struct qaf_module *qaf_mod = NULL;
+
+    qaf_mod = get_qaf_module_for_input_stream(out);
     DEBUG_MSG("Output Stream %p", out);
 
     lock_output_stream(out);
@@ -1062,11 +1085,11 @@
                     (struct audio_stream_out *)p_qaf->passthrough_out, type);
         }
         pthread_mutex_unlock(&p_qaf->lock);
-    } else if (check_stream_state(out, STOPPED)) {
+    } else if (!is_any_stream_running(qaf_mod)) {
         //If stream is already stopped then send the drain ready.
         out->client_callback(STREAM_CBK_EVENT_DRAIN_READY, NULL, out->client_cookie);
+        set_stream_state(out, STOPPED);
     } else {
-
         //Drain the module input stream.
         /* Stream stop will trigger EOS and on EOS_EVENT received
          from callback DRAIN_READY command is sent */
@@ -1244,6 +1267,8 @@
     audio_qaf_media_format_t *media_fmt = NULL;
 
     DEBUG_MSG_VV("Device 0x%X, Event = 0x%X, Bytes to write %d", device, event_id, size);
+
+
     pthread_mutex_lock(&p_qaf->lock);
 
     /* Default config initialization. */
@@ -1674,7 +1699,9 @@
                || event_id == AUDIO_EOS_MAIN_2_DD_DDP_EVENT
                || event_id == AUDIO_EOS_MAIN_AAC_EVENT
                || event_id == AUDIO_EOS_MAIN_AC4_EVENT
-               || event_id == AUDIO_EOS_ASSOC_DD_DDP_EVENT) {
+               || event_id == AUDIO_EOS_ASSOC_DD_DDP_EVENT
+               || event_id == AUDIO_EOS_ASSOC_AAC_EVENT
+               || event_id == AUDIO_EOS_ASSOC_AC4_EVENT) {
         struct stream_out *out = qaf_mod->stream_in[QAF_IN_MAIN];
         struct stream_out *out_pcm = qaf_mod->stream_in[QAF_IN_PCM];
         struct stream_out *out_main2 = qaf_mod->stream_in[QAF_IN_MAIN_2];
@@ -1693,7 +1720,9 @@
             set_stream_state(out_pcm, STOPPED);
             unlock_output_stream(out_pcm);
             DEBUG_MSG("sent pcm DRAIN_READY");
-        } else if (event_id == AUDIO_EOS_ASSOC_DD_DDP_EVENT
+        } else if ( (event_id == AUDIO_EOS_ASSOC_DD_DDP_EVENT
+                || event_id == AUDIO_EOS_ASSOC_AAC_EVENT
+                || event_id == AUDIO_EOS_ASSOC_AC4_EVENT)
                 && (out_assoc != NULL)
                 && (check_stream_state(out_assoc, STOPPING))) {
 
@@ -1799,6 +1828,8 @@
         return -EINVAL;
     }
 
+    pthread_mutex_lock(&p_qaf->lock);
+
     set_stream_state(out,STOPPED);
     qaf_mod->stream_in[index] = NULL;
     memset(&qaf_mod->adsp_hdlr_config[index], 0, sizeof(struct qaf_adsp_hdlr_config_state));
@@ -1813,6 +1844,8 @@
     //If all streams are closed then close the session.
     qaf_session_close(qaf_mod);
 
+    pthread_mutex_unlock(&p_qaf->lock);
+
     DEBUG_MSG();
     return ret;
 }
diff --git a/qahw_api/test/qahw_playback_test.c b/qahw_api/test/qahw_playback_test.c
index 0fada71..f7f9e34 100644
--- a/qahw_api/test/qahw_playback_test.c
+++ b/qahw_api/test/qahw_playback_test.c
@@ -322,6 +322,7 @@
     case QAHW_STREAM_CBK_EVENT_DRAIN_READY:
         fprintf(log_file, "stream %d: received event - QAHW_STREAM_CBK_EVENT_DRAIN_READY\n", params->stream_index);
         pthread_mutex_lock(&params->drain_lock);
+        params->drain_received = true;
         pthread_cond_signal(&params->drain_cond);
         pthread_mutex_unlock(&params->drain_lock);
         break;
@@ -780,11 +781,14 @@
             if ((!read_complete_file && (bytes_to_read <= 0)) || (bytes_read <= 0)) {
                 fprintf(log_file, "stream %d: end of file\n", params->stream_index);
                 if (is_offload) {
-                    pthread_mutex_lock(&params->drain_lock);
+                    params->drain_received = false;
                     qahw_out_drain(params->out_handle, QAHW_DRAIN_ALL);
-                    pthread_cond_wait(&params->drain_cond, &params->drain_lock);
+                    if(!params->drain_received) {
+                        pthread_mutex_lock(&params->drain_lock);
+                        pthread_cond_wait(&params->drain_cond, &params->drain_lock);
+                        pthread_mutex_unlock(&params->drain_lock);
+                    }
                     fprintf(log_file, "stream %d: out of compress drain\n", params->stream_index);
-                    pthread_mutex_unlock(&params->drain_lock);
                 }
                 /*
                  * Caution: Below ADL log shouldnt be altered without notifying
diff --git a/qahw_api/test/qahw_playback_test.h b/qahw_api/test/qahw_playback_test.h
index 1f524b9..3ec8f25 100644
--- a/qahw_api/test/qahw_playback_test.h
+++ b/qahw_api/test/qahw_playback_test.h
@@ -134,6 +134,7 @@
     pthread_mutex_t write_lock;
     pthread_cond_t drain_cond;
     pthread_mutex_t drain_lock;
+    bool drain_received;
     bool interactive_strm;
     qahw_mix_matrix_params_t mm_params_pan_scale;
     qahw_mix_matrix_params_t mm_params_downmix;