audio HAL: fix thread starvation

Fix thread starvation issue where the capture or playback threads
running in FIFO priority would constantly acquire the stream mutex preventing
other threads to complete routing commands.

Bug: 21880828.
Change-Id: I36e9c609857dd1bc959809326350a04c3af92228
diff --git a/audio/hal/audio_hw.c b/audio/hal/audio_hw.c
index ff785b2..83a623d 100644
--- a/audio/hal/audio_hw.c
+++ b/audio/hal/audio_hw.c
@@ -1245,6 +1245,20 @@
     return ret;
 }
 
+void lock_input_stream(struct stream_in *in)
+{
+    pthread_mutex_lock(&in->pre_lock);
+    pthread_mutex_lock(&in->lock);
+    pthread_mutex_unlock(&in->pre_lock);
+}
+
+void lock_output_stream(struct stream_out *out)
+{
+    pthread_mutex_lock(&out->pre_lock);
+    pthread_mutex_lock(&out->lock);
+    pthread_mutex_unlock(&out->pre_lock);
+}
+
 static int uc_release_pcm_devices(struct audio_usecase *usecase)
 {
     struct stream_out *out = (struct stream_out *)usecase->stream;
@@ -1629,7 +1643,7 @@
 
     ALOGV("%s: enter: usecase(%d: %s)", __func__,
           out->usecase, use_case_table[out->usecase]);
-    pthread_mutex_lock(&out->lock);
+    lock_output_stream(out);
     if (!out->standby) {
         pthread_mutex_lock(&adev->lock);
         do_out_standby_l(out);
@@ -1669,7 +1683,7 @@
     if (ret >= 0) {
         val = atoi(value);
         pthread_mutex_lock(&adev->lock_inputs);
-        pthread_mutex_lock(&out->lock);
+        lock_output_stream(out);
         pthread_mutex_lock(&adev->lock);
         if (val != 0) {
             out->devices = val;
@@ -1815,7 +1829,7 @@
     unsigned char *data = NULL;
     struct pcm_config config;
 
-    pthread_mutex_lock(&out->lock);
+    lock_output_stream(out);
     if (out->standby) {
         pthread_mutex_lock(&adev->lock);
         ret = start_output_stream(out);
@@ -1932,7 +1946,7 @@
     int ret = -1;
     unsigned long dsp_frames;
 
-    pthread_mutex_lock(&out->lock);
+    lock_output_stream(out);
 
     /* FIXME: which device to read from? */
     if (!list_empty(&out->pcm_dev_list)) {
@@ -2056,7 +2070,7 @@
 {
     struct audio_device *adev = in->dev;
     int status = 0;
-    pthread_mutex_lock(&in->lock);
+    lock_input_stream(in);
     if (!in->standby) {
         pthread_mutex_lock(&adev->lock);
         status = do_in_standby_l(in);
@@ -2107,7 +2121,7 @@
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE, value, sizeof(value));
 
     pthread_mutex_lock(&adev->lock_inputs);
-    pthread_mutex_lock(&in->lock);
+    lock_input_stream(in);
     pthread_mutex_lock(&adev->lock);
     if (ret >= 0) {
         val = atoi(value);
@@ -2202,11 +2216,11 @@
     size_t frames_rq = bytes / audio_stream_in_frame_size(stream);
 
     /* no need to acquire adev->lock_inputs because API contract prevents a close */
-    pthread_mutex_lock(&in->lock);
+    lock_input_stream(in);
     if (in->standby) {
         pthread_mutex_unlock(&in->lock);
         pthread_mutex_lock(&adev->lock_inputs);
-        pthread_mutex_lock(&in->lock);
+        lock_input_stream(in);
         if (!in->standby) {
             pthread_mutex_unlock(&adev->lock_inputs);
             goto false_alarm;
@@ -2358,6 +2372,7 @@
     /* out->written = 0; by calloc() */
 
     pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL);
+    pthread_mutex_init(&out->pre_lock, (const pthread_mutexattr_t *) NULL);
     pthread_cond_init(&out->cond, (const pthread_condattr_t *) NULL);
 
     config->format = out->stream.common.get_format(&out->stream.common);
@@ -2662,6 +2677,9 @@
     }
     in->usecase_type = usecase_type;
 
+    pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL);
+    pthread_mutex_init(&in->pre_lock, (const pthread_mutexattr_t *) NULL);
+
     *stream_in = &in->stream;
     ALOGV("%s: exit", __func__);
     return 0;
diff --git a/audio/hal/audio_hw.h b/audio/hal/audio_hw.h
index 775df1b..92aff51 100644
--- a/audio/hal/audio_hw.h
+++ b/audio/hal/audio_hw.h
@@ -206,6 +206,7 @@
 struct stream_out {
     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_cond_t              cond;
     struct pcm_config           config;
     struct listnode             pcm_dev_list;
@@ -234,6 +235,8 @@
 struct stream_in {
     struct audio_stream_in              stream;
     pthread_mutex_t                     lock; /* see note below on mutex acquisition order */
+    pthread_mutex_t                     pre_lock; /* acquire before lock to avoid DOS by
+                                                     capture thread */
     struct pcm_config                   config;
     struct listnode                     pcm_dev_list;
     int                                 standby;