CodecAsyncHandler: Fix race issue in async mode

Modified await on input/output queues. In earlier design, in a scenario,
async handler thread waits on input/output queues even if the queue is
not empty. This is because of lock-release-relock between isEmpty() and
mCondition.await().

Bug: 149031058
Test:  atest android.mediav2.cts
Change-Id: Iee592e94704c4011d2cd336be5f5fd3a3a03fb78
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index cc58caf..1aeb375 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -793,7 +793,6 @@
      * Test Decoder by Queuing CSD separately
      */
     @LargeTest
-    @Ignore("TODO(b/149031058)")
     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
     public void testSimpleDecodeQueueCSD() throws IOException, InterruptedException {
         MediaFormat format = setUpSource(mTestFile);
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index a09cf5a..739b60a 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -45,22 +45,24 @@
     private static final String LOG_TAG = CodecAsyncHandler.class.getSimpleName();
     private final Lock mLock = new ReentrantLock();
     private final Condition mCondition = mLock.newCondition();
-    private CallBackQueue mCbInputQueue;
-    private CallBackQueue mCbOutputQueue;
+    private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbInputQueue;
+    private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbOutputQueue;
     private MediaFormat mOutFormat;
     private boolean mSignalledOutFormatChanged;
     private volatile boolean mSignalledError;
 
     CodecAsyncHandler() {
-        mCbInputQueue = new CallBackQueue();
-        mCbOutputQueue = new CallBackQueue();
+        mCbInputQueue = new LinkedList<>();
+        mCbOutputQueue = new LinkedList<>();
         mSignalledError = false;
         mSignalledOutFormatChanged = false;
     }
 
     void clearQueues() {
+        mLock.lock();
         mCbInputQueue.clear();
         mCbOutputQueue.clear();
+        mLock.unlock();
     }
 
     void resetContext() {
@@ -73,14 +75,20 @@
     @Override
     public void onInputBufferAvailable(@NonNull MediaCodec codec, int bufferIndex) {
         assertTrue(bufferIndex >= 0);
-        mCbInputQueue.push(new Pair<>(bufferIndex, (MediaCodec.BufferInfo) null));
+        mLock.lock();
+        mCbInputQueue.add(new Pair<>(bufferIndex, (MediaCodec.BufferInfo) null));
+        mCondition.signalAll();
+        mLock.unlock();
     }
 
     @Override
     public void onOutputBufferAvailable(@NonNull MediaCodec codec, int bufferIndex,
             @NonNull MediaCodec.BufferInfo info) {
         assertTrue(bufferIndex >= 0);
-        mCbOutputQueue.push(new Pair<>(bufferIndex, info));
+        mLock.lock();
+        mCbOutputQueue.add(new Pair<>(bufferIndex, info));
+        mCondition.signalAll();
+        mLock.unlock();
     }
 
     @Override
@@ -109,51 +117,52 @@
 
     Pair<Integer, MediaCodec.BufferInfo> getInput() throws InterruptedException {
         Pair<Integer, MediaCodec.BufferInfo> element = null;
+        mLock.lock();
         while (!mSignalledError) {
             if (mCbInputQueue.isEmpty()) {
-                mLock.lock();
                 mCondition.await();
-                mLock.unlock();
             } else {
-                element = mCbInputQueue.pop();
+                element = mCbInputQueue.remove(0);
                 break;
             }
         }
+        mLock.unlock();
         return element;
     }
 
     Pair<Integer, MediaCodec.BufferInfo> getOutput() throws InterruptedException {
         Pair<Integer, MediaCodec.BufferInfo> element = null;
+        mLock.lock();
         while (!mSignalledError) {
             if (mCbOutputQueue.isEmpty()) {
-                mLock.lock();
                 mCondition.await();
-                mLock.unlock();
             } else {
-                element = mCbOutputQueue.pop();
+                element = mCbOutputQueue.remove(0);
                 break;
             }
         }
+        mLock.unlock();
         return element;
     }
 
     Pair<Integer, MediaCodec.BufferInfo> getWork() throws InterruptedException {
         Pair<Integer, MediaCodec.BufferInfo> element = null;
+        mLock.lock();
         while (!mSignalledError) {
             if (mCbInputQueue.isEmpty() && mCbOutputQueue.isEmpty()) {
-                mLock.lock();
                 mCondition.await();
-                mLock.unlock();
-            }
-            if (!mCbOutputQueue.isEmpty()) {
-                element = mCbOutputQueue.pop();
-                break;
-            }
-            if (!mCbInputQueue.isEmpty()) {
-                element = mCbInputQueue.pop();
-                break;
+            } else {
+                if (!mCbOutputQueue.isEmpty()) {
+                    element = mCbOutputQueue.remove(0);
+                    break;
+                }
+                if (!mCbInputQueue.isEmpty()) {
+                    element = mCbInputQueue.remove(0);
+                    break;
+                }
             }
         }
+        mLock.unlock();
         return element;
     }
 
@@ -168,38 +177,6 @@
     MediaFormat getOutputFormat() {
         return mOutFormat;
     }
-
-    private class CallBackQueue {
-        private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mQueue = new LinkedList<>();
-
-        void push(Pair<Integer, MediaCodec.BufferInfo> element) {
-            mLock.lock();
-            mQueue.add(element);
-            mCondition.signalAll();
-            mLock.unlock();
-        }
-
-        Pair<Integer, MediaCodec.BufferInfo> pop() {
-            Pair<Integer, MediaCodec.BufferInfo> element = null;
-            mLock.lock();
-            if (mQueue.size() != 0) element = mQueue.remove(0);
-            mLock.unlock();
-            return element;
-        }
-
-        boolean isEmpty() {
-            mLock.lock();
-            boolean isEmpty = (mQueue.size() == 0);
-            mLock.unlock();
-            return isEmpty;
-        }
-
-        void clear() {
-            mLock.lock();
-            mQueue.clear();
-            mLock.unlock();
-        }
-    }
 }
 
 class OutputManager {
@@ -461,8 +438,10 @@
                     int bufferID = element.first;
                     MediaCodec.BufferInfo info = element.second;
                     if (info != null) {
+                        // <id, info> corresponds to output callback. Handle it accordingly
                         dequeueOutput(bufferID, info);
                     } else {
+                        // <id, null> corresponds to input callback. Handle it accordingly
                         enqueueInput(bufferID);
                         frameCount++;
                     }