AudioPolicyService: fix race in AudioCommandThread

Fixe race condition in AudioCommandThread::threadLoop() where a
command can be inserted in first position in the queue after the sleep
time has been calculated causing a longer delay than expected.

Also fix a failure to hold a wake lock while commands are still in the queue.

Bug: 22707905.
Change-Id: I813626986677bf00106acb37ee20d3dd75d5cf33
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index eefff3d..c77cc45 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -577,22 +577,28 @@
                     }
                 }
                 waitTime = INT64_MAX;
+                // release mLock before releasing strong reference on the service as
+                // AudioPolicyService destructor calls AudioCommandThread::exit() which
+                // acquires mLock.
+                mLock.unlock();
+                svc.clear();
+                mLock.lock();
             } else {
                 waitTime = mAudioCommands[0]->mTime - curTime;
                 break;
             }
         }
-        // release mLock before releasing strong reference on the service as
-        // AudioPolicyService destructor calls AudioCommandThread::exit() which acquires mLock.
-        mLock.unlock();
-        svc.clear();
-        mLock.lock();
-        if (!exitPending() && (mAudioCommands.isEmpty() || waitTime != INT64_MAX)) {
-            // release delayed commands wake lock
+
+        // release delayed commands wake lock if the queue is empty
+        if (mAudioCommands.isEmpty()) {
             release_wake_lock(mName.string());
+        }
+
+        // At this stage we have either an empty command queue or the first command in the queue
+        // has a finite delay. So unless we are exiting it is safe to wait.
+        if (!exitPending()) {
             ALOGV("AudioCommandThread() going to sleep");
             mWaitWorkCV.waitRelative(mLock, waitTime);
-            ALOGV("AudioCommandThread() waking up");
         }
     }
     // release delayed commands wake lock before quitting
@@ -1003,6 +1009,8 @@
         requestExit();
         mWaitWorkCV.signal();
     }
+    // Note that we can call it from the thread loop if all other references have been released
+    // but it will safely return WOULD_BLOCK in this case
     requestExitAndWait();
 }