BQ: Improved buffer/slot tracking

- Explicitly track active buffers and unused slots on top of the
  already existing tracking for free slots and free buffers.

Change-Id: Ife2678678e96f0eb0b3fb21571058378134bd868
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index 370f5d5..a515f39 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -125,6 +125,10 @@
     // Indicates that this buffer was queued by the producer. When in single
     // buffer mode acquire() can return a BufferItem that wasn't in the queue.
     bool mQueuedBuffer;
+
+    // Indicates that this BufferItem contains a stale buffer which has already
+    // been released by the BufferQueue.
+    bool mIsStale;
 };
 
 } // namespace android
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index fbd5114..23cd066 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -105,17 +105,23 @@
     // connected, mDequeueCondition must be broadcast.
     int getMaxBufferCountLocked() const;
 
-    // freeBufferLocked frees the GraphicBuffer and sync resources for the
+    // This performs the same computation but uses the given arguments instead
+    // of the member variables for mMaxBufferCount, mAsyncMode, and
+    // mDequeueBufferCannotBlock.
+    int getMaxBufferCountLocked(bool asyncMode,
+            bool dequeueBufferCannotBlock, int maxBufferCount) const;
+
+    // clearBufferSlotLocked frees the GraphicBuffer and sync resources for the
     // given slot.
-    void freeBufferLocked(int slot, bool validate = true);
+    void clearBufferSlotLocked(int slot);
 
     // freeAllBuffersLocked frees the GraphicBuffer and sync resources for
     // all slots, even if they're currently dequeued, queued, or acquired.
     void freeAllBuffersLocked();
 
-    // stillTracking returns true iff the buffer item is still being tracked
-    // in one of the slots.
-    bool stillTracking(const BufferItem* item) const;
+    // If delta is positive, makes more slots available. If negative, takes
+    // away slots. Returns false if the request can't be met.
+    bool adjustAvailableSlotsLocked(int delta);
 
     // waitWhileAllocatingLocked blocks until mIsAllocating is false.
     void waitWhileAllocatingLocked() const;
@@ -179,13 +185,20 @@
     Fifo mQueue;
 
     // mFreeSlots contains all of the slots which are FREE and do not currently
-    // have a buffer attached
+    // have a buffer attached.
     std::set<int> mFreeSlots;
 
     // mFreeBuffers contains all of the slots which are FREE and currently have
-    // a buffer attached
+    // a buffer attached.
     std::list<int> mFreeBuffers;
 
+    // mUnusedSlots contains all slots that are currently unused. They should be
+    // free and not have a buffer attached.
+    std::list<int> mUnusedSlots;
+
+    // mActiveBuffers contains all slots which have a non-FREE buffer attached.
+    std::set<int> mActiveBuffers;
+
     // mDequeueCondition is a condition variable used for dequeueBuffer in
     // synchronous mode.
     mutable Condition mDequeueCondition;
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 645a07b..dc05e98 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -187,9 +187,9 @@
     // BufferQueueCore::INVALID_BUFFER_SLOT otherwise
     int getFreeBufferLocked() const;
 
-    // Returns the next free slot if one less than or equal to maxBufferCount
-    // is available or BufferQueueCore::INVALID_BUFFER_SLOT otherwise
-    int getFreeSlotLocked(int maxBufferCount) const;
+    // Returns the next free slot if one is available or
+    // BufferQueueCore::INVALID_BUFFER_SLOT otherwise
+    int getFreeSlotLocked() const;
 
     // waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
     // block if there are no available slots and we are not in non-blocking
@@ -200,8 +200,7 @@
         Dequeue,
         Attach,
     };
-    status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found,
-            status_t* returnFlags) const;
+    status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found) const;
 
     sp<BufferQueueCore> mCore;
 
diff --git a/include/gui/BufferSlot.h b/include/gui/BufferSlot.h
index 17a654a..943fa82 100644
--- a/include/gui/BufferSlot.h
+++ b/include/gui/BufferSlot.h
@@ -174,14 +174,15 @@
 struct BufferSlot {
 
     BufferSlot()
-    : mEglDisplay(EGL_NO_DISPLAY),
+    : mGraphicBuffer(nullptr),
+      mEglDisplay(EGL_NO_DISPLAY),
       mBufferState(),
       mRequestBufferCalled(false),
       mFrameNumber(0),
       mEglFence(EGL_NO_SYNC_KHR),
+      mFence(Fence::NO_FENCE),
       mAcquireCalled(false),
-      mNeedsCleanupOnRelease(false),
-      mAttachedByConsumer(false) {
+      mNeedsReallocation(false) {
     }
 
     // mGraphicBuffer points to the buffer allocated for this slot or is NULL
@@ -191,8 +192,6 @@
     // mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects.
     EGLDisplay mEglDisplay;
 
-    static const char* bufferStateName(BufferState state);
-
     // mBufferState is the current state of this buffer slot.
     BufferState mBufferState;
 
@@ -227,15 +226,10 @@
     // Indicates whether this buffer has been seen by a consumer yet
     bool mAcquireCalled;
 
-    // Indicates whether this buffer needs to be cleaned up by the
-    // consumer.  This is set when a buffer in ACQUIRED state is freed.
-    // It causes releaseBuffer to return STALE_BUFFER_SLOT.
-    bool mNeedsCleanupOnRelease;
-
-    // Indicates whether the buffer was attached on the consumer side.
-    // If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when dequeued
-    // to prevent the producer from using a stale cached buffer.
-    bool mAttachedByConsumer;
+    // Indicates whether the buffer was re-allocated without notifying the
+    // producer. If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when
+    // dequeued to prevent the producer from using a stale cached buffer.
+    bool mNeedsReallocation;
 };
 
 } // namespace android
diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h
index d4c9ee5..ebce7fb 100644
--- a/include/gui/IGraphicBufferConsumer.h
+++ b/include/gui/IGraphicBufferConsumer.h
@@ -199,7 +199,9 @@
     // cannot be less than maxAcquiredBufferCount.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * BAD_VALUE - bufferCount was out of range (see above).
+    // * BAD_VALUE - one of the below conditions occurred:
+    //             * bufferCount was out of range (see above).
+    //             * failure to adjust the number of available slots.
     // * INVALID_OPERATION - attempting to call this after a producer connected.
     virtual status_t setMaxBufferCount(int bufferCount) = 0;
 
@@ -212,7 +214,9 @@
     // to be exceeded.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * BAD_VALUE - maxAcquiredBuffers was out of range (see above).
+    // * BAD_VALUE - one of the below conditions occurred:
+    //             * maxAcquiredBuffers was out of range (see above).
+    //             * failure to adjust the number of available slots.
     // * INVALID_OPERATION - attempting to call this after a producer connected.
     virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
 
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 8646981..1f4c8ac 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -101,8 +101,9 @@
     // * NO_INIT - the buffer queue has been abandoned.
     // * BAD_VALUE - one of the below conditions occurred:
     //     * bufferCount was out of range (see above)
-    //     * client has one or more buffers dequeued
+    //     * client has too many buffers dequeued
     //     * this call would cause the maxBufferCount value to be exceeded
+    //     * failure to adjust the number of available slots.
     virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) = 0;
 
     // Set the async flag if the producer intends to asynchronously queue
@@ -115,8 +116,10 @@
     //
     // Return of a value other than NO_ERROR means an error has occurred:
     // * NO_INIT - the buffer queue has been abandoned.
-    // * BAD_VALUE - this call would cause the maxBufferCount value to be
+    // * BAD_VALUE - one of the following has occurred:
+    //             * this call would cause the maxBufferCount value to be
     //               exceeded
+    //             * failure to adjust the number of available slots.
     virtual status_t setAsyncMode(bool async) = 0;
 
     // dequeueBuffer requests a new buffer slot for the client to use. Ownership
@@ -436,6 +439,9 @@
     //             * the producer is already connected
     //             * api was out of range (see above).
     //             * output was NULL.
+    //             * Failure to adjust the number of available slots. This can
+    //               happen because of trying to allocate/deallocate the async
+    //               buffer in response to the value of producerControlledByApp.
     // * DEAD_OBJECT - the token is hosted by an already-dead process
     //
     // Additional negative errors may be returned by the internals, they
@@ -534,6 +540,11 @@
     // timeout of -1. If set (to a value other than -1), this will disable
     // non-blocking mode and its corresponding spare buffer (which is used to
     // ensure a buffer is always available).
+    //
+    // Return of a value other than NO_ERROR means an error has occurred:
+    // * BAD_VALUE - Failure to adjust the number of available slots. This can
+    //               happen because of trying to allocate/deallocate the async
+    //               buffer.
     virtual status_t setDequeueTimeout(nsecs_t timeout) = 0;
 };
 
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index de8ff70..036ef1e 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -39,7 +39,8 @@
     mTransformToDisplayInverse(false),
     mSurfaceDamage(),
     mSingleBufferMode(false),
-    mQueuedBuffer(true) {
+    mQueuedBuffer(true),
+    mIsStale(false) {
 }
 
 BufferItem::~BufferItem() {}
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 6f9f21f..d182f6b 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -49,7 +49,7 @@
         // buffer so that the consumer can successfully set up the newly acquired
         // buffer before releasing the old one.
         int numAcquiredBuffers = 0;
-        for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+        for (int s : mCore->mActiveBuffers) {
             if (mSlots[s].mBufferState.isAcquired()) {
                 ++numAcquiredBuffers;
             }
@@ -133,7 +133,8 @@
                 BQ_LOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64
                         " size=%zu",
                         desiredPresent, expectedPresent, mCore->mQueue.size());
-                if (mCore->stillTracking(front)) {
+
+                if (!front->mIsStale) {
                     // Front buffer is still in mSlots, so mark the slot as free
                     mSlots[front->mSlot].mBufferState.freeQueued();
 
@@ -144,13 +145,17 @@
                             mSlots[front->mSlot].mBufferState.isFree()) {
                         mSlots[front->mSlot].mBufferState.mShared = false;
                     }
-                    // Don't put the shared buffer on the free list.
+
+                    // Don't put the shared buffer on the free list
                     if (!mSlots[front->mSlot].mBufferState.isShared()) {
+                        mCore->mActiveBuffers.erase(front->mSlot);
                         mCore->mFreeBuffers.push_back(front->mSlot);
                     }
+
                     listener = mCore->mConnectedProducerListener;
                     ++numDroppedBuffers;
                 }
+
                 mCore->mQueue.erase(front);
                 front = mCore->mQueue.begin();
             }
@@ -205,6 +210,7 @@
             outBuffer->mSurfaceDamage = Region::INVALID_REGION;
             outBuffer->mSingleBufferMode = true;
             outBuffer->mQueuedBuffer = false;
+            outBuffer->mIsStale = false;
         } else {
             slot = front->mSlot;
             *outBuffer = *front;
@@ -216,10 +222,9 @@
 
         BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
                 slot, outBuffer->mFrameNumber, outBuffer->mGraphicBuffer->handle);
-        // If the front buffer is still being tracked, update its slot state
-        if (mCore->stillTracking(outBuffer)) {
+
+        if (!outBuffer->mIsStale) {
             mSlots[slot].mAcquireCalled = true;
-            mSlots[slot].mNeedsCleanupOnRelease = false;
             // Don't decrease the queue count if the BufferItem wasn't
             // previously in the queue. This happens in single buffer mode when
             // the queue is empty and the BufferItem is created above.
@@ -270,7 +275,7 @@
         return NO_INIT;
     }
 
-    if (mCore->mSingleBufferMode) {
+    if (mCore->mSingleBufferMode || slot == mCore->mSingleBufferSlot) {
         BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer"
                 "mode");
         return BAD_VALUE;
@@ -287,7 +292,9 @@
     }
 
     mSlots[slot].mBufferState.detachConsumer();
-    mCore->freeBufferLocked(slot);
+    mCore->mActiveBuffers.erase(slot);
+    mCore->mFreeSlots.insert(slot);
+    mCore->clearBufferSlotLocked(slot);
     mCore->mDequeueCondition.broadcast();
     mCore->validateConsistencyLocked();
 
@@ -316,7 +323,7 @@
 
     // Make sure we don't have too many acquired buffers
     int numAcquiredBuffers = 0;
-    for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+    for (int s : mCore->mActiveBuffers) {
         if (mSlots[s].mBufferState.isAcquired()) {
             ++numAcquiredBuffers;
         }
@@ -351,14 +358,14 @@
         return NO_MEMORY;
     }
 
+    mCore->mActiveBuffers.insert(found);
     *outSlot = found;
     ATRACE_BUFFER_INDEX(*outSlot);
     BQ_LOGV("attachBuffer: returning slot %d", *outSlot);
 
     mSlots[*outSlot].mGraphicBuffer = buffer;
     mSlots[*outSlot].mBufferState.attachConsumer();
-    mSlots[*outSlot].mAttachedByConsumer = true;
-    mSlots[*outSlot].mNeedsCleanupOnRelease = false;
+    mSlots[*outSlot].mNeedsReallocation = true;
     mSlots[*outSlot].mFence = Fence::NO_FENCE;
     mSlots[*outSlot].mFrameNumber = 0;
 
@@ -411,39 +418,33 @@
             return STALE_BUFFER_SLOT;
         }
 
-
-        if (mSlots[slot].mBufferState.isAcquired()) {
-            mSlots[slot].mEglDisplay = eglDisplay;
-            mSlots[slot].mEglFence = eglFence;
-            mSlots[slot].mFence = releaseFence;
-            mSlots[slot].mBufferState.release();
-
-            // After leaving single buffer mode, the shared buffer will
-            // still be around. Mark it as no longer shared if this
-            // operation causes it to be free.
-            if (!mCore->mSingleBufferMode &&
-                    mSlots[slot].mBufferState.isFree()) {
-                mSlots[slot].mBufferState.mShared = false;
-            }
-            // Don't put the shared buffer on the free list.
-            if (!mSlots[slot].mBufferState.isShared()) {
-                mCore->mFreeBuffers.push_back(slot);
-            }
-
-            listener = mCore->mConnectedProducerListener;
-            BQ_LOGV("releaseBuffer: releasing slot %d", slot);
-        } else if (mSlots[slot].mNeedsCleanupOnRelease) {
-            BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
-                    "(state = %s)", slot, mSlots[slot].mBufferState.string());
-            mSlots[slot].mNeedsCleanupOnRelease = false;
-            return STALE_BUFFER_SLOT;
-        } else {
+        if (!mSlots[slot].mBufferState.isAcquired()) {
             BQ_LOGE("releaseBuffer: attempted to release buffer slot %d "
                     "but its state was %s", slot,
                     mSlots[slot].mBufferState.string());
             return BAD_VALUE;
         }
 
+        mSlots[slot].mEglDisplay = eglDisplay;
+        mSlots[slot].mEglFence = eglFence;
+        mSlots[slot].mFence = releaseFence;
+        mSlots[slot].mBufferState.release();
+
+        // After leaving single buffer mode, the shared buffer will
+        // still be around. Mark it as no longer shared if this
+        // operation causes it to be free.
+        if (!mCore->mSingleBufferMode && mSlots[slot].mBufferState.isFree()) {
+            mSlots[slot].mBufferState.mShared = false;
+        }
+        // Don't put the shared buffer on the free list.
+        if (!mSlots[slot].mBufferState.isShared()) {
+            mCore->mActiveBuffers.erase(slot);
+            mCore->mFreeBuffers.push_back(slot);
+        }
+
+        listener = mCore->mConnectedProducerListener;
+        BQ_LOGV("releaseBuffer: releasing slot %d", slot);
+
         mCore->mDequeueCondition.broadcast();
         mCore->validateConsistencyLocked();
     } // Autolock scope
@@ -497,6 +498,7 @@
     mCore->mConsumerListener = NULL;
     mCore->mQueue.clear();
     mCore->freeAllBuffersLocked();
+    mCore->mSingleBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
     mCore->mDequeueCondition.broadcast();
     return NO_ERROR;
 }
@@ -579,6 +581,15 @@
         return BAD_VALUE;
     }
 
+    int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
+            mCore->mDequeueBufferCannotBlock, bufferCount) -
+            mCore->getMaxBufferCountLocked();
+    if (!mCore->adjustAvailableSlotsLocked(delta)) {
+        BQ_LOGE("setMaxBufferCount: BufferQueue failed to adjust the number of "
+                "available slots. Delta = %d", delta);
+        return BAD_VALUE;
+    }
+
     mCore->mMaxBufferCount = bufferCount;
     return NO_ERROR;
 }
@@ -612,8 +623,17 @@
         return BAD_VALUE;
     }
 
+    if (!mCore->adjustAvailableSlotsLocked(
+            maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount)) {
+        BQ_LOGE("setMaxAcquiredBufferCount: BufferQueue failed to adjust the "
+                "number of available slots. Delta = %d",
+                maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount);
+        return BAD_VALUE;
+    }
+
     BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
     mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
+    mCore->validateConsistencyLocked();
     return NO_ERROR;
 }
 
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index c24ad19..f02ff5f 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -52,6 +52,8 @@
     mQueue(),
     mFreeSlots(),
     mFreeBuffers(),
+    mUnusedSlots(),
+    mActiveBuffers(),
     mDequeueCondition(),
     mDequeueBufferCannotBlock(false),
     mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
@@ -82,8 +84,14 @@
             BQ_LOGE("createGraphicBufferAlloc failed");
         }
     }
-    for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
-        mFreeSlots.insert(slot);
+
+    int numStartingBuffers = getMaxBufferCountLocked();
+    for (int s = 0; s < numStartingBuffers; s++) {
+        mFreeSlots.insert(s);
+    }
+    for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS;
+            s++) {
+        mUnusedSlots.push_front(s);
     }
 }
 
@@ -113,32 +121,26 @@
             mDefaultHeight, mDefaultBufferFormat, mTransformHint, mQueue.size(),
             fifo.string());
 
-    // Trim the free buffers so as to not spam the dump
-    int maxBufferCount = 0;
-    for (int s = BufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) {
-        const BufferSlot& slot(mSlots[s]);
-        if (!slot.mBufferState.isFree() ||
-                slot.mGraphicBuffer != NULL) {
-            maxBufferCount = s + 1;
-            break;
-        }
+    for (int s : mActiveBuffers) {
+        const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
+        result.appendFormat("%s%s[%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
+                prefix, (mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s,
+                buffer.get(), mSlots[s].mBufferState.string(), buffer->handle,
+                buffer->width, buffer->height, buffer->stride, buffer->format);
+
+    }
+    for (int s : mFreeBuffers) {
+        const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
+        result.appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
+                prefix, s, buffer.get(), mSlots[s].mBufferState.string(),
+                buffer->handle, buffer->width, buffer->height, buffer->stride,
+                buffer->format);
     }
 
-    for (int s = 0; s < maxBufferCount; ++s) {
-        const BufferSlot& slot(mSlots[s]);
-        const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer);
-        result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix,
-                (slot.mBufferState.isAcquired()) ? ">" : " ",
-                s, buffer.get(),
-                slot.mBufferState.string());
-
-        if (buffer != NULL) {
-            result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle,
-                    buffer->width, buffer->height, buffer->stride,
-                    buffer->format);
-        }
-
-        result.append("\n");
+    for (int s : mFreeSlots) {
+        const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
+        result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s,
+                buffer.get(), mSlots[s].mBufferState.string());
     }
 }
 
@@ -156,44 +158,33 @@
     return getMinUndequeuedBufferCountLocked() + 1;
 }
 
+int BufferQueueCore::getMaxBufferCountLocked(bool asyncMode,
+        bool dequeueBufferCannotBlock, int maxBufferCount) const {
+    int maxCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount +
+            ((asyncMode || dequeueBufferCannotBlock) ? 1 : 0);
+    maxCount = std::min(maxBufferCount, maxCount);
+    return maxCount;
+}
+
 int BufferQueueCore::getMaxBufferCountLocked() const {
     int maxBufferCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount +
-            (mAsyncMode || mDequeueBufferCannotBlock ? 1 : 0);
+            ((mAsyncMode || mDequeueBufferCannotBlock) ? 1 : 0);
 
     // limit maxBufferCount by mMaxBufferCount always
     maxBufferCount = std::min(mMaxBufferCount, maxBufferCount);
 
-    // Any buffers that are dequeued by the producer or sitting in the queue
-    // waiting to be consumed need to have their slots preserved. Such buffers
-    // will temporarily keep the max buffer count up until the slots no longer
-    // need to be preserved.
-    for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-        BufferState state = mSlots[s].mBufferState;
-        if (state.isQueued() || state.isDequeued()) {
-            maxBufferCount = s + 1;
-        }
-    }
-
     return maxBufferCount;
 }
 
-void BufferQueueCore::freeBufferLocked(int slot, bool validate) {
-    BQ_LOGV("freeBufferLocked: slot %d", slot);
-    bool hadBuffer = mSlots[slot].mGraphicBuffer != NULL;
+void BufferQueueCore::clearBufferSlotLocked(int slot) {
+    BQ_LOGV("clearBufferSlotLocked: slot %d", slot);
+
     mSlots[slot].mGraphicBuffer.clear();
-    if (mSlots[slot].mBufferState.isAcquired()) {
-        mSlots[slot].mNeedsCleanupOnRelease = true;
-    }
-    if (!mSlots[slot].mBufferState.isFree()) {
-        mFreeSlots.insert(slot);
-    } else if (hadBuffer) {
-        // If the slot was FREE, but we had a buffer, we need to move this slot
-        // from the free buffers list to the the free slots list
-        mFreeBuffers.remove(slot);
-        mFreeSlots.insert(slot);
-    }
-    mSlots[slot].mAcquireCalled = false;
+    mSlots[slot].mBufferState.reset();
+    mSlots[slot].mRequestBufferCalled = false;
     mSlots[slot].mFrameNumber = 0;
+    mSlots[slot].mAcquireCalled = false;
+    mSlots[slot].mNeedsReallocation = true;
 
     // Destroy fence as BufferQueue now takes ownership
     if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) {
@@ -201,35 +192,63 @@
         mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
     }
     mSlots[slot].mFence = Fence::NO_FENCE;
-    if (validate) {
-        validateConsistencyLocked();
-    }
+    mSlots[slot].mEglDisplay = EGL_NO_DISPLAY;
 }
 
 void BufferQueueCore::freeAllBuffersLocked() {
-    mBufferHasBeenQueued = false;
-    for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-        freeBufferLocked(s, false);
-        mSlots[s].mBufferState.reset();
+    for (int s : mFreeSlots) {
+        clearBufferSlotLocked(s);
     }
-    mSingleBufferSlot = INVALID_BUFFER_SLOT;
+
+    for (int s : mFreeBuffers) {
+        mFreeSlots.insert(s);
+        clearBufferSlotLocked(s);
+    }
+    mFreeBuffers.clear();
+
+    for (int s : mActiveBuffers) {
+        mFreeSlots.insert(s);
+        clearBufferSlotLocked(s);
+    }
+    mActiveBuffers.clear();
+
+    for (auto& b : mQueue) {
+        b.mIsStale = true;
+    }
+
     validateConsistencyLocked();
 }
 
-bool BufferQueueCore::stillTracking(const BufferItem* item) const {
-    const BufferSlot& slot = mSlots[item->mSlot];
-
-    BQ_LOGV("stillTracking: item { slot=%d/%" PRIu64 " buffer=%p } "
-            "slot { slot=%d/%" PRIu64 " buffer=%p }",
-            item->mSlot, item->mFrameNumber,
-            (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0),
-            item->mSlot, slot.mFrameNumber,
-            (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0));
-
-    // Compare item with its original buffer slot. We can check the slot as
-    // the buffer would not be moved to a different slot by the producer.
-    return (slot.mGraphicBuffer != NULL) &&
-           (item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle);
+bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) {
+    if (delta >= 0) {
+        while (delta > 0) {
+            if (mUnusedSlots.empty()) {
+                return false;
+            }
+            int slot = mUnusedSlots.back();
+            mUnusedSlots.pop_back();
+            mFreeSlots.insert(slot);
+            delta--;
+        }
+    } else {
+        while (delta < 0) {
+            if (!mFreeSlots.empty()) {
+                auto slot = mFreeSlots.begin();
+                clearBufferSlotLocked(*slot);
+                mUnusedSlots.push_back(*slot);
+                mFreeSlots.erase(slot);
+            } else if (!mFreeBuffers.empty()) {
+                int slot = mFreeBuffers.back();
+                clearBufferSlotLocked(slot);
+                mUnusedSlots.push_back(slot);
+                mFreeBuffers.pop_back();
+            } else {
+                return false;
+            }
+            delta++;
+        }
+    }
+    return true;
 }
 
 void BufferQueueCore::waitWhileAllocatingLocked() const {
@@ -241,47 +260,127 @@
 
 void BufferQueueCore::validateConsistencyLocked() const {
     static const useconds_t PAUSE_TIME = 0;
+    int allocatedSlots = 0;
     for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
         bool isInFreeSlots = mFreeSlots.count(slot) != 0;
         bool isInFreeBuffers =
                 std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) !=
                 mFreeBuffers.cend();
-        if (mSlots[slot].mBufferState.isFree() &&
-                !mSlots[slot].mBufferState.isShared()) {
-            if (mSlots[slot].mGraphicBuffer == NULL) {
-                if (!isInFreeSlots) {
-                    BQ_LOGE("Slot %d is FREE but is not in mFreeSlots", slot);
-                    usleep(PAUSE_TIME);
-                }
-                if (isInFreeBuffers) {
-                    BQ_LOGE("Slot %d is in mFreeSlots "
-                            "but is also in mFreeBuffers", slot);
-                    usleep(PAUSE_TIME);
-                }
-            } else {
-                if (!isInFreeBuffers) {
-                    BQ_LOGE("Slot %d is FREE but is not in mFreeBuffers", slot);
-                    usleep(PAUSE_TIME);
-                }
-                if (isInFreeSlots) {
-                    BQ_LOGE("Slot %d is in mFreeBuffers "
-                            "but is also in mFreeSlots", slot);
-                    usleep(PAUSE_TIME);
-                }
-            }
-        } else {
+        bool isInActiveBuffers = mActiveBuffers.count(slot) != 0;
+        bool isInUnusedSlots =
+                std::find(mUnusedSlots.cbegin(), mUnusedSlots.cend(), slot) !=
+                mUnusedSlots.cend();
+
+        if (isInFreeSlots || isInFreeBuffers || isInActiveBuffers) {
+            allocatedSlots++;
+        }
+
+        if (isInUnusedSlots) {
             if (isInFreeSlots) {
-                BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%s)",
-                        slot, mSlots[slot].mBufferState.string());
+                BQ_LOGE("Slot %d is in mUnusedSlots and in mFreeSlots", slot);
                 usleep(PAUSE_TIME);
             }
             if (isInFreeBuffers) {
-                BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%s)",
-                        slot, mSlots[slot].mBufferState.string());
+                BQ_LOGE("Slot %d is in mUnusedSlots and in mFreeBuffers", slot);
                 usleep(PAUSE_TIME);
             }
+            if (isInActiveBuffers) {
+                BQ_LOGE("Slot %d is in mUnusedSlots and in mActiveBuffers",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+            if (!mSlots[slot].mBufferState.isFree()) {
+                BQ_LOGE("Slot %d is in mUnusedSlots but is not FREE", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (mSlots[slot].mGraphicBuffer != NULL) {
+                BQ_LOGE("Slot %d is in mUnusedSluts but has an active buffer",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+        } else if (isInFreeSlots) {
+            if (isInUnusedSlots) {
+                BQ_LOGE("Slot %d is in mFreeSlots and in mUnusedSlots", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInFreeBuffers) {
+                BQ_LOGE("Slot %d is in mFreeSlots and in mFreeBuffers", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInActiveBuffers) {
+                BQ_LOGE("Slot %d is in mFreeSlots and in mActiveBuffers", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (!mSlots[slot].mBufferState.isFree()) {
+                BQ_LOGE("Slot %d is in mFreeSlots but is not FREE", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (mSlots[slot].mGraphicBuffer != NULL) {
+                BQ_LOGE("Slot %d is in mFreeSlots but has a buffer",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+        } else if (isInFreeBuffers) {
+            if (isInUnusedSlots) {
+                BQ_LOGE("Slot %d is in mFreeBuffers and in mUnusedSlots", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInFreeSlots) {
+                BQ_LOGE("Slot %d is in mFreeBuffers and in mFreeSlots", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInActiveBuffers) {
+                BQ_LOGE("Slot %d is in mFreeBuffers and in mActiveBuffers",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+            if (!mSlots[slot].mBufferState.isFree()) {
+                BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (mSlots[slot].mGraphicBuffer == NULL) {
+                BQ_LOGE("Slot %d is in mFreeBuffers but has no buffer", slot);
+                usleep(PAUSE_TIME);
+            }
+        } else if (isInActiveBuffers) {
+            if (isInUnusedSlots) {
+                BQ_LOGE("Slot %d is in mActiveBuffers and in mUnusedSlots",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInFreeSlots) {
+                BQ_LOGE("Slot %d is in mActiveBuffers and in mFreeSlots", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInFreeBuffers) {
+                BQ_LOGE("Slot %d is in mActiveBuffers and in mFreeBuffers",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+            if (mSlots[slot].mBufferState.isFree() &&
+                    !mSlots[slot].mBufferState.isShared()) {
+                BQ_LOGE("Slot %d is in mActiveBuffers but is FREE", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (mSlots[slot].mGraphicBuffer == NULL && !mIsAllocating) {
+                BQ_LOGE("Slot %d is in mActiveBuffers but has no buffer", slot);
+                usleep(PAUSE_TIME);
+            }
+        } else {
+            BQ_LOGE("Slot %d isn't in any of mUnusedSlots, mFreeSlots, "
+                    "mFreeBuffers, or mActiveBuffers", slot);
+            usleep(PAUSE_TIME);
         }
     }
+
+    if (allocatedSlots != getMaxBufferCountLocked()) {
+        BQ_LOGE("Number of allocated slots is incorrect. Allocated = %d, "
+                "Should be %d (%zu free slots, %zu free buffers, "
+                "%zu activeBuffers, %zu unusedSlots)", allocatedSlots,
+                getMaxBufferCountLocked(), mFreeSlots.size(),
+                mFreeBuffers.size(), mActiveBuffers.size(),
+                mUnusedSlots.size());
+    }
 }
 
 } // namespace android
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 5b1aaa0..22a2d79 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -96,7 +96,7 @@
         }
 
         // There must be no dequeued buffers when changing the buffer count.
-        for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+        for (int s : mCore->mActiveBuffers) {
             if (mSlots[s].mBufferState.isDequeued()) {
                 BQ_LOGE("setMaxDequeuedBufferCount: buffer owned by producer");
                 return BAD_VALUE;
@@ -132,8 +132,15 @@
         // 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();
+        if (!mCore->adjustAvailableSlotsLocked(
+                maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount)) {
+            BQ_LOGE("setMaxDequeuedBufferCount: BufferQueue failed to adjust "
+                    "the number of available slots. Delta = %d",
+                    maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount);
+            return BAD_VALUE;
+        }
         mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers;
+        mCore->validateConsistencyLocked();
         mCore->mDequeueCondition.broadcast();
         listener = mCore->mConsumerListener;
     } // Autolock scope
@@ -172,7 +179,17 @@
             return BAD_VALUE;
         }
 
+        int delta = mCore->getMaxBufferCountLocked(async,
+                mCore->mDequeueBufferCannotBlock, mCore->mMaxBufferCount)
+                - mCore->getMaxBufferCountLocked();
+
+        if (!mCore->adjustAvailableSlotsLocked(delta)) {
+            BQ_LOGE("setAsyncMode: BufferQueue failed to adjust the number of "
+                    "available slots. Delta = %d", delta);
+            return BAD_VALUE;
+        }
         mCore->mAsyncMode = async;
+        mCore->validateConsistencyLocked();
         mCore->mDequeueCondition.broadcast();
         listener = mCore->mConsumerListener;
     } // Autolock scope
@@ -188,25 +205,22 @@
     if (mCore->mFreeBuffers.empty()) {
         return BufferQueueCore::INVALID_BUFFER_SLOT;
     }
-    auto slot = mCore->mFreeBuffers.front();
+    int slot = mCore->mFreeBuffers.front();
     mCore->mFreeBuffers.pop_front();
     return slot;
 }
 
-int BufferQueueProducer::getFreeSlotLocked(int maxBufferCount) const {
+int BufferQueueProducer::getFreeSlotLocked() const {
     if (mCore->mFreeSlots.empty()) {
         return BufferQueueCore::INVALID_BUFFER_SLOT;
     }
-    auto slot = *(mCore->mFreeSlots.begin());
-    if (slot < maxBufferCount) {
-        mCore->mFreeSlots.erase(slot);
-        return slot;
-    }
-    return BufferQueueCore::INVALID_BUFFER_SLOT;
+    auto slot = mCore->mFreeSlots.begin();
+    mCore->mFreeSlots.erase(slot);
+    return *slot;
 }
 
 status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
-        int* found, status_t* returnFlags) const {
+        int* found) const {
     auto callerString = (caller == FreeSlotCaller::Dequeue) ?
             "dequeueBuffer" : "attachBuffer";
     bool tryAgain = true;
@@ -216,20 +230,9 @@
             return NO_INIT;
         }
 
-        const int maxBufferCount = mCore->getMaxBufferCountLocked();
-
-        // Free up any buffers that are in slots beyond the max buffer count
-        for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-            assert(mSlots[s].mBufferState.isFree());
-            if (mSlots[s].mGraphicBuffer != NULL) {
-                mCore->freeBufferLocked(s);
-                *returnFlags |= RELEASE_ALL_BUFFERS;
-            }
-        }
-
         int dequeuedCount = 0;
         int acquiredCount = 0;
-        for (int s = 0; s < maxBufferCount; ++s) {
+        for (int s : mCore->mActiveBuffers) {
             if (mSlots[s].mBufferState.isDequeued()) {
                 ++dequeuedCount;
             }
@@ -254,6 +257,7 @@
         // our slots are empty but we have many buffers in the queue. This can
         // cause us to run out of memory if we outrun the consumer. Wait here if
         // it looks like we have too many buffers queued up.
+        const int maxBufferCount = mCore->getMaxBufferCountLocked();
         bool tooManyBuffers = mCore->mQueue.size()
                             > static_cast<size_t>(maxBufferCount);
         if (tooManyBuffers) {
@@ -268,15 +272,15 @@
             } else {
                 if (caller == FreeSlotCaller::Dequeue) {
                     // If we're calling this from dequeue, prefer free buffers
-                    auto slot = getFreeBufferLocked();
+                    int slot = getFreeBufferLocked();
                     if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                         *found = slot;
                     } else if (mCore->mAllowAllocation) {
-                        *found = getFreeSlotLocked(maxBufferCount);
+                        *found = getFreeSlotLocked();
                     }
                 } else {
                     // If we're calling this from attach, prefer free slots
-                    auto slot = getFreeSlotLocked(maxBufferCount);
+                    int slot = getFreeSlotLocked();
                     if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                         *found = slot;
                     } else {
@@ -369,7 +373,7 @@
         int found = BufferItem::INVALID_BUFFER_SLOT;
         while (found == BufferItem::INVALID_BUFFER_SLOT) {
             status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,
-                    &found, &returnFlags);
+                    &found);
             if (status != NO_ERROR) {
                 return status;
             }
@@ -388,24 +392,36 @@
             // requested attributes, we free it and attempt to get another one.
             if (!mCore->mAllowAllocation) {
                 if (buffer->needsReallocation(width, height, format, usage)) {
-                    if (mCore->mSingleBufferMode &&
-                            mCore->mSingleBufferSlot == found) {
+                    if (mCore->mSingleBufferSlot == found) {
                         BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
                                 "buffer");
                         return BAD_VALUE;
                     }
-
-                    mCore->freeBufferLocked(found);
+                    mCore->mFreeSlots.insert(found);
+                    mCore->clearBufferSlotLocked(found);
                     found = BufferItem::INVALID_BUFFER_SLOT;
                     continue;
                 }
             }
         }
 
+        const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
+        if (mCore->mSingleBufferSlot == found &&
+                buffer->needsReallocation(width,  height, format, usage)) {
+            BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
+                    "buffer");
+
+            return BAD_VALUE;
+        }
+
+        if (mCore->mSingleBufferSlot != found) {
+            mCore->mActiveBuffers.insert(found);
+        }
         *outSlot = found;
         ATRACE_BUFFER_INDEX(found);
 
-        attachedByConsumer = mSlots[found].mAttachedByConsumer;
+        attachedByConsumer = mSlots[found].mNeedsReallocation;
+        mSlots[found].mNeedsReallocation = false;
 
         mSlots[found].mBufferState.dequeue();
 
@@ -417,7 +433,6 @@
             mSlots[found].mBufferState.mShared = true;
         }
 
-        const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
         if ((buffer == NULL) ||
                 buffer->needsReallocation(width, height, format, usage))
         {
@@ -452,8 +467,6 @@
         *outFence = mSlots[found].mFence;
         mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
         mSlots[found].mFence = Fence::NO_FENCE;
-
-        mCore->validateConsistencyLocked();
     } // Autolock scope
 
     if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
@@ -481,6 +494,8 @@
                 BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
                 return NO_INIT;
             }
+
+            mCore->validateConsistencyLocked();
         } // Autolock scope
     }
 
@@ -527,9 +542,8 @@
         return NO_INIT;
     }
 
-    if (mCore->mSingleBufferMode) {
-        BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer"
-                "mode");
+    if (mCore->mSingleBufferMode || mCore->mSingleBufferSlot == slot) {
+        BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer mode");
         return BAD_VALUE;
     }
 
@@ -548,7 +562,9 @@
     }
 
     mSlots[slot].mBufferState.detachProducer();
-    mCore->freeBufferLocked(slot);
+    mCore->mActiveBuffers.erase(slot);
+    mCore->mFreeSlots.insert(slot);
+    mCore->clearBufferSlotLocked(slot);
     mCore->mDequeueCondition.broadcast();
     mCore->validateConsistencyLocked();
 
@@ -593,12 +609,13 @@
 
     int found = mCore->mFreeBuffers.front();
     mCore->mFreeBuffers.remove(found);
+    mCore->mFreeSlots.insert(found);
 
     BQ_LOGV("detachNextBuffer detached slot %d", found);
 
     *outBuffer = mSlots[found].mGraphicBuffer;
     *outFence = mSlots[found].mFence;
-    mCore->freeBufferLocked(found);
+    mCore->clearBufferSlotLocked(found);
     mCore->validateConsistencyLocked();
 
     return NO_ERROR;
@@ -644,8 +661,7 @@
 
     status_t returnFlags = NO_ERROR;
     int found;
-    status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found,
-            &returnFlags);
+    status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found);
     if (status != NO_ERROR) {
         return status;
     }
@@ -666,7 +682,8 @@
     mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR;
     mSlots[*outSlot].mFence = Fence::NO_FENCE;
     mSlots[*outSlot].mRequestBufferCalled = true;
-
+    mSlots[*outSlot].mAcquireCalled = false;
+    mCore->mActiveBuffers.insert(found);
     mCore->validateConsistencyLocked();
 
     return returnFlags;
@@ -722,11 +739,9 @@
             return NO_INIT;
         }
 
-        const int maxBufferCount = mCore->getMaxBufferCountLocked();
-
-        if (slot < 0 || slot >= maxBufferCount) {
+        if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
             BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
-                    slot, maxBufferCount);
+                    slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
             return BAD_VALUE;
         } else if (!mSlots[slot].mBufferState.isDequeued()) {
             BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
@@ -807,9 +822,8 @@
             // state to see if we need to replace it
             BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
             if (front->mIsDroppable) {
-                // If the front queued buffer is still being tracked, we first
-                // mark it as freed
-                if (mCore->stillTracking(front)) {
+
+                if (!front->mIsStale) {
                     mSlots[front->mSlot].mBufferState.freeQueued();
 
                     // After leaving single buffer mode, the shared buffer will
@@ -821,9 +835,11 @@
                     }
                     // Don't put the shared buffer on the free list.
                     if (!mSlots[front->mSlot].mBufferState.isShared()) {
-                        mCore->mFreeBuffers.push_front(front->mSlot);
+                        mCore->mActiveBuffers.erase(front->mSlot);
+                        mCore->mFreeBuffers.push_back(front->mSlot);
                     }
                 }
+
                 // Overwrite the droppable buffer with the incoming one
                 *front = item;
                 frameReplacedListener = mCore->mConsumerListener;
@@ -926,8 +942,10 @@
 
     // Don't put the shared buffer on the free list.
     if (!mSlots[slot].mBufferState.isShared()) {
-        mCore->mFreeBuffers.push_front(slot);
+        mCore->mActiveBuffers.erase(slot);
+        mCore->mFreeBuffers.push_back(slot);
     }
+
     mSlots[slot].mFence = fence;
     mCore->mDequeueCondition.broadcast();
     mCore->validateConsistencyLocked();
@@ -1020,6 +1038,17 @@
         return BAD_VALUE;
     }
 
+    int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
+            mDequeueTimeout < 0 ?
+            mCore->mConsumerControlledByApp && producerControlledByApp : false,
+            mCore->mMaxBufferCount) -
+            mCore->getMaxBufferCountLocked();
+    if (!mCore->adjustAvailableSlotsLocked(delta)) {
+        BQ_LOGE("connect: BufferQueue failed to adjust the number of available "
+                "slots. Delta = %d", delta);
+        return BAD_VALUE;
+    }
+
     int status = NO_ERROR;
     switch (api) {
         case NATIVE_WINDOW_API_EGL:
@@ -1056,8 +1085,9 @@
         mCore->mDequeueBufferCannotBlock =
                 mCore->mConsumerControlledByApp && producerControlledByApp;
     }
-    mCore->mAllowAllocation = true;
 
+    mCore->mAllowAllocation = true;
+    mCore->validateConsistencyLocked();
     return status;
 }
 
@@ -1094,6 +1124,8 @@
                         token->unlinkToDeath(
                                 static_cast<IBinder::DeathRecipient*>(this));
                     }
+                    mCore->mSingleBufferSlot =
+                            BufferQueueCore::INVALID_BUFFER_SLOT;
                     mCore->mConnectedProducerListener = NULL;
                     mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
                     mCore->mSidebandStream.clear();
@@ -1138,7 +1170,6 @@
         PixelFormat format, uint32_t usage) {
     ATRACE_CALL();
     while (true) {
-        Vector<int> freeSlots;
         size_t newBufferCount = 0;
         uint32_t allocWidth = 0;
         uint32_t allocHeight = 0;
@@ -1154,32 +1185,11 @@
                 return;
             }
 
-            int currentBufferCount = 0;
-            for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
-                if (mSlots[slot].mGraphicBuffer != NULL) {
-                    ++currentBufferCount;
-                } else {
-                    if (!mSlots[slot].mBufferState.isFree()) {
-                        BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
-                                slot);
-                        continue;
-                    }
-
-                    freeSlots.push_back(slot);
-                }
-            }
-
-            int maxBufferCount = mCore->getMaxBufferCountLocked();
-            BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers",
-                    currentBufferCount, maxBufferCount);
-            if (maxBufferCount <= currentBufferCount)
-                return;
-            newBufferCount =
-                    static_cast<size_t>(maxBufferCount - currentBufferCount);
-            if (freeSlots.size() < newBufferCount) {
-                BQ_LOGE("allocateBuffers: ran out of free slots");
+            newBufferCount = mCore->mFreeSlots.size();
+            if (newBufferCount == 0) {
                 return;
             }
+
             allocWidth = width > 0 ? width : mCore->mDefaultWidth;
             allocHeight = height > 0 ? height : mCore->mDefaultHeight;
             allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
@@ -1221,24 +1231,23 @@
             }
 
             for (size_t i = 0; i < newBufferCount; ++i) {
-                int slot = freeSlots[i];
-                if (!mSlots[slot].mBufferState.isFree()) {
-                    // A consumer allocated the FREE slot with attachBuffer. Discard the buffer we
-                    // allocated.
-                    BQ_LOGV("allocateBuffers: slot %d was acquired while allocating. "
-                            "Dropping allocated buffer.", slot);
+                if (mCore->mFreeSlots.empty()) {
+                    BQ_LOGV("allocateBuffers: a slot was occupied while "
+                            "allocating. Dropping allocated buffer.");
                     continue;
                 }
-                mCore->freeBufferLocked(slot); // Clean up the slot first
-                mSlots[slot].mGraphicBuffer = buffers[i];
-                mSlots[slot].mFence = Fence::NO_FENCE;
+                auto slot = mCore->mFreeSlots.begin();
+                mCore->clearBufferSlotLocked(*slot); // Clean up the slot first
+                mSlots[*slot].mGraphicBuffer = buffers[i];
+                mSlots[*slot].mFence = Fence::NO_FENCE;
 
                 // freeBufferLocked puts this slot on the free slots list. Since
                 // we then attached a buffer, move the slot to free buffer list.
                 mCore->mFreeSlots.erase(slot);
-                mCore->mFreeBuffers.push_front(slot);
+                mCore->mFreeBuffers.push_front(*slot);
 
-                BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot);
+                BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d",
+                        *slot);
             }
 
             mCore->mIsAllocating = false;
@@ -1297,8 +1306,17 @@
     BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
 
     Mutex::Autolock lock(mCore->mMutex);
+    int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false,
+            mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked();
+    if (!mCore->adjustAvailableSlotsLocked(delta)) {
+        BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number of "
+                "available slots. Delta = %d", delta);
+        return BAD_VALUE;
+    }
+
     mDequeueTimeout = timeout;
     mCore->mDequeueBufferCannotBlock = false;
+    mCore->validateConsistencyLocked();
     return NO_ERROR;
 }
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 9e90ad0..6fc55c3 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -759,6 +759,13 @@
         *outFence = Fence::NO_FENCE;
     }
 
+    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+        if (mSlots[i].buffer != NULL &&
+                mSlots[i].buffer->handle == buffer->handle) {
+            mSlots[i].buffer = NULL;
+        }
+    }
+
     return NO_ERROR;
 }