BQ: add setMaxDequeuedBufferCount

Adds the new setMaxDequeuedBufferCount() function to
BufferQueueProducer. This will eventually replace setBufferCount.

Also add setAsyncMode.

Bug 13174928

Change-Id: Iea1adcd5d74a75f67d8e9dde06d521695628ad5a
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a440f6e..5ea4a10 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -118,6 +118,7 @@
         // clear the queue, however, so that currently queued buffers still
         // get displayed.
         mCore->freeAllBuffersLocked();
+        mCore->mMaxDequeuedBufferCount = bufferCount - minBufferSlots + 1;
         mCore->mOverrideMaxBufferCount = bufferCount;
         mCore->mDequeueCondition.broadcast();
         listener = mCore->mConsumerListener;
@@ -131,6 +132,102 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueProducer::setMaxDequeuedBufferCount(
+        int maxDequeuedBuffers) {
+    ATRACE_CALL();
+    BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d",
+            maxDequeuedBuffers);
+
+    sp<IConsumerListener> listener;
+    { // Autolock scope
+        Mutex::Autolock lock(mCore->mMutex);
+        mCore->waitWhileAllocatingLocked();
+
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("setMaxDequeuedBufferCount: BufferQueue has been "
+                    "abandoned");
+            return NO_INIT;
+        }
+
+        // There must be no dequeued buffers when changing the buffer count.
+        for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+            if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) {
+                BQ_LOGE("setMaxDequeuedBufferCount: buffer owned by producer");
+                return BAD_VALUE;
+            }
+        }
+
+        int bufferCount = mCore->getMinUndequeuedBufferCountLocked(
+                mCore->mAsyncMode);
+        bufferCount += maxDequeuedBuffers;
+
+        if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) {
+            BQ_LOGE("setMaxDequeuedBufferCount: bufferCount %d too large "
+                    "(max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS);
+            return BAD_VALUE;
+        }
+
+        const int minBufferSlots = mCore->getMinMaxBufferCountLocked(
+                mCore->mAsyncMode);
+        if (bufferCount < minBufferSlots) {
+            BQ_LOGE("setMaxDequeuedBufferCount: requested buffer count %d is "
+                    "less than minimum %d", bufferCount, minBufferSlots);
+            return BAD_VALUE;
+        }
+
+        // Here we are guaranteed that the producer doesn't have any dequeued
+        // buffers and will release all of its buffer references. We don't
+        // clear the queue, however, so that currently queued buffers still
+        // get displayed.
+        mCore->freeAllBuffersLocked();
+        mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers;
+        mCore->mOverrideMaxBufferCount = bufferCount;
+        mCore->mDequeueCondition.broadcast();
+        listener = mCore->mConsumerListener;
+    } // Autolock scope
+
+    // Call back without lock held
+    if (listener != NULL) {
+        listener->onBuffersReleased();
+    }
+
+    return NO_ERROR;
+}
+
+status_t BufferQueueProducer::setAsyncMode(bool async) {
+    ATRACE_CALL();
+    BQ_LOGV("setAsyncMode: async = %d", async);
+
+    sp<IConsumerListener> listener;
+    { // Autolock scope
+        Mutex::Autolock lock(mCore->mMutex);
+        mCore->waitWhileAllocatingLocked();
+
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("setAsyncMode: BufferQueue has been abandoned");
+            return NO_INIT;
+        }
+
+        // There must be no dequeued buffers when changing the async mode.
+        for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+            if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) {
+                BQ_LOGE("setAsyncMode: buffer owned by producer");
+                return BAD_VALUE;
+            }
+        }
+
+        mCore->mAsyncMode = async;
+        mCore->mDequeueCondition.broadcast();
+        listener = mCore->mConsumerListener;
+    } // Autolock scope
+
+    // Call back without lock held
+    if (listener != NULL) {
+        listener->onBuffersReleased();
+    }
+    return NO_ERROR;
+}
+
 status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
         bool async, int* found, status_t* returnFlags) const {
     bool tryAgain = true;
@@ -241,7 +338,7 @@
             // buffer (which could cause us to have to wait here), which is
             // okay, since it is only used to implement an atomic acquire +
             // release (e.g., in GLConsumer::updateTexImage())
-            if (mCore->mDequeueBufferCannotBlock &&
+            if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) &&
                     (acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
                 return WOULD_BLOCK;
             }