Revert "Partial revert of "Fix Surface slot caching""

This reverts commit 981066c3a5be3e4775b6537ef32268f7e108c912.
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index e2e73a0..7b90aa1 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -32,6 +32,7 @@
 
 #include <list>
 #include <set>
+#include <vector>
 
 #define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
 #define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
@@ -120,8 +121,9 @@
     void freeAllBuffersLocked();
 
     // 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);
+    // away slots. Returns false if the request can't be met. Any slots that
+    // were freed will be appended to freedSlots.
+    bool adjustAvailableSlotsLocked(int delta, std::vector<int>* freedSlots);
 
     // waitWhileAllocatingLocked blocks until mIsAllocating is false.
     void waitWhileAllocatingLocked() const;
diff --git a/include/gui/IProducerListener.h b/include/gui/IProducerListener.h
index 3848a6c..895da4b 100644
--- a/include/gui/IProducerListener.h
+++ b/include/gui/IProducerListener.h
@@ -41,6 +41,9 @@
     // This is called without any lock held and can be called concurrently by
     // multiple threads.
     virtual void onBufferReleased() = 0; // Asynchronous
+
+    // onSlotFreed is called when the BufferQueue frees a buffer in a slot.
+    virtual void onSlotFreed(int /*slot*/) {}; // Asynchronous
 };
 
 class IProducerListener : public ProducerListener, public IInterface
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 92285e5..b025280 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -274,35 +274,51 @@
     ATRACE_CALL();
     ATRACE_BUFFER_INDEX(slot);
     BQ_LOGV("detachBuffer: slot %d", slot);
-    Mutex::Autolock lock(mCore->mMutex);
 
-    if (mCore->mIsAbandoned) {
-        BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
-        return NO_INIT;
+    sp<IConsumerListener> consumerListener;
+    sp<IProducerListener> producerListener;
+    {
+        Mutex::Autolock lock(mCore->mMutex);
+
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
+            return NO_INIT;
+        }
+
+        if (mCore->mSingleBufferMode || slot == mCore->mSingleBufferSlot) {
+            BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer"
+                    "mode");
+            return BAD_VALUE;
+        }
+
+        if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+            BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
+                    slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+            return BAD_VALUE;
+        } else if (!mSlots[slot].mBufferState.isAcquired()) {
+            BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
+                    "(state = %s)", slot, mSlots[slot].mBufferState.string());
+            return BAD_VALUE;
+        }
+
+        mSlots[slot].mBufferState.detachConsumer();
+        mCore->mActiveBuffers.erase(slot);
+        mCore->mFreeSlots.insert(slot);
+        mCore->clearBufferSlotLocked(slot);
+        mCore->mDequeueCondition.broadcast();
+        VALIDATE_CONSISTENCY();
+        producerListener = mCore->mConnectedProducerListener;
+        consumerListener = mCore->mConsumerListener;
     }
 
-    if (mCore->mSingleBufferMode || slot == mCore->mSingleBufferSlot) {
-        BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer"
-                "mode");
-        return BAD_VALUE;
+    // Call back without lock held
+    if (producerListener != NULL) {
+        producerListener->onSlotFreed(slot);
+    }
+    if (consumerListener != NULL) {
+        consumerListener->onBuffersReleased();
     }
 
-    if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
-        BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
-                slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
-        return BAD_VALUE;
-    } else if (!mSlots[slot].mBufferState.isAcquired()) {
-        BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
-                "(state = %s)", slot, mSlots[slot].mBufferState.string());
-        return BAD_VALUE;
-    }
-
-    mSlots[slot].mBufferState.detachConsumer();
-    mCore->mActiveBuffers.erase(slot);
-    mCore->mFreeSlots.insert(slot);
-    mCore->clearBufferSlotLocked(slot);
-    mCore->mDequeueCondition.broadcast();
-    VALIDATE_CONSISTENCY();
 
     return NO_ERROR;
 }
@@ -573,30 +589,40 @@
         return BAD_VALUE;
     }
 
-    Mutex::Autolock lock(mCore->mMutex);
+    sp<IConsumerListener> listener;
+    {
+        Mutex::Autolock lock(mCore->mMutex);
+        if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+            BQ_LOGE("setMaxBufferCount: producer is already connected");
+            return INVALID_OPERATION;
+        }
 
-    if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
-        BQ_LOGE("setMaxBufferCount: producer is already connected");
-        return INVALID_OPERATION;
+        if (bufferCount < mCore->mMaxAcquiredBufferCount) {
+            BQ_LOGE("setMaxBufferCount: invalid buffer count (%d) less than"
+                    "mMaxAcquiredBufferCount (%d)", bufferCount,
+                    mCore->mMaxAcquiredBufferCount);
+            return BAD_VALUE;
+        }
+
+        int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
+                mCore->mDequeueBufferCannotBlock, bufferCount) -
+                mCore->getMaxBufferCountLocked();
+        if (!mCore->adjustAvailableSlotsLocked(delta, nullptr)) {
+            BQ_LOGE("setMaxBufferCount: BufferQueue failed to adjust the number"
+                    " of available slots. Delta = %d", delta);
+            return BAD_VALUE;
+        }
+
+        mCore->mMaxBufferCount = bufferCount;
+        if (delta < 0) {
+            listener = mCore->mConsumerListener;
+        }
     }
 
-    if (bufferCount < mCore->mMaxAcquiredBufferCount) {
-        BQ_LOGE("setMaxBufferCount: invalid buffer count (%d) less than"
-                "mMaxAcquiredBufferCount (%d)", bufferCount,
-                mCore->mMaxAcquiredBufferCount);
-        return BAD_VALUE;
+    // Call back without lock held
+    if (listener != NULL) {
+        listener->onBuffersReleased();
     }
-
-    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;
 }
 
@@ -611,7 +637,9 @@
         return BAD_VALUE;
     }
 
-    sp<IConsumerListener> listener;
+    sp<IConsumerListener> consumerListener;
+    sp<IProducerListener> producerListener;
+    std::vector<int> freedSlots;
     { // Autolock scope
         Mutex::Autolock lock(mCore->mMutex);
         mCore->waitWhileAllocatingLocked();
@@ -648,7 +676,7 @@
         }
 
         int delta = maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount;
-        if (!mCore->adjustAvailableSlotsLocked(delta)) {
+        if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
             return BAD_VALUE;
         }
 
@@ -656,12 +684,19 @@
         mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
         VALIDATE_CONSISTENCY();
         if (delta < 0) {
-            listener = mCore->mConsumerListener;
+            consumerListener = mCore->mConsumerListener;
+            producerListener = mCore->mConnectedProducerListener;
         }
     }
+
     // Call back without lock held
-    if (listener != NULL) {
-        listener->onBuffersReleased();
+    if (consumerListener != NULL) {
+        consumerListener->onBuffersReleased();
+    }
+    if (producerListener != NULL) {
+        for (int i : freedSlots) {
+            producerListener->onSlotFreed(i);
+        }
     }
 
     return NO_ERROR;
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index f785db0..df7376e 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -225,7 +225,8 @@
     VALIDATE_CONSISTENCY();
 }
 
-bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) {
+bool BufferQueueCore::adjustAvailableSlotsLocked(int delta,
+        std::vector<int>* freedSlots) {
     if (delta >= 0) {
         // If we're going to fail, do so before modifying anything
         if (delta > static_cast<int>(mUnusedSlots.size())) {
@@ -252,11 +253,17 @@
                 clearBufferSlotLocked(*slot);
                 mUnusedSlots.push_back(*slot);
                 mFreeSlots.erase(slot);
+                if (freedSlots) {
+                    freedSlots->push_back(*slot);
+                }
             } else if (!mFreeBuffers.empty()) {
                 int slot = mFreeBuffers.back();
                 clearBufferSlotLocked(slot);
                 mUnusedSlots.push_back(slot);
                 mFreeBuffers.pop_back();
+                if (freedSlots) {
+                    freedSlots->push_back(slot);
+                }
             } else {
                 return false;
             }
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 136a14a..46e7591 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -90,7 +90,9 @@
     BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d",
             maxDequeuedBuffers);
 
-    sp<IConsumerListener> listener;
+    sp<IConsumerListener> consumerListener;
+    sp<IProducerListener> producerListener;
+    std::vector<int> freedSlots;
     { // Autolock scope
         Mutex::Autolock lock(mCore->mMutex);
         mCore->waitWhileAllocatingLocked();
@@ -142,20 +144,26 @@
         }
 
         int delta = maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount;
-        if (!mCore->adjustAvailableSlotsLocked(delta)) {
+        if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
             return BAD_VALUE;
         }
         mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers;
         VALIDATE_CONSISTENCY();
         if (delta < 0) {
-            listener = mCore->mConsumerListener;
+            consumerListener = mCore->mConsumerListener;
+            producerListener = mCore->mConnectedProducerListener;
         }
         mCore->mDequeueCondition.broadcast();
     } // Autolock scope
 
     // Call back without lock held
-    if (listener != NULL) {
-        listener->onBuffersReleased();
+    if (consumerListener != NULL) {
+        consumerListener->onBuffersReleased();
+    }
+    if (producerListener != NULL) {
+        for (int i : freedSlots) {
+            producerListener->onSlotFreed(i);
+        }
     }
 
     return NO_ERROR;
@@ -165,7 +173,9 @@
     ATRACE_CALL();
     BQ_LOGV("setAsyncMode: async = %d", async);
 
-    sp<IConsumerListener> listener;
+    sp<IConsumerListener> consumerListener;
+    sp<IProducerListener> producerListener;
+    std::vector<int> freedSlots;
     { // Autolock scope
         Mutex::Autolock lock(mCore->mMutex);
         mCore->waitWhileAllocatingLocked();
@@ -191,7 +201,7 @@
                 mCore->mDequeueBufferCannotBlock, mCore->mMaxBufferCount)
                 - mCore->getMaxBufferCountLocked();
 
-        if (!mCore->adjustAvailableSlotsLocked(delta)) {
+        if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
             BQ_LOGE("setAsyncMode: BufferQueue failed to adjust the number of "
                     "available slots. Delta = %d", delta);
             return BAD_VALUE;
@@ -199,12 +209,20 @@
         mCore->mAsyncMode = async;
         VALIDATE_CONSISTENCY();
         mCore->mDequeueCondition.broadcast();
-        listener = mCore->mConsumerListener;
+        if (delta < 0) {
+            consumerListener = mCore->mConsumerListener;
+            producerListener = mCore->mConnectedProducerListener;
+        }
     } // Autolock scope
 
     // Call back without lock held
-    if (listener != NULL) {
-        listener->onBuffersReleased();
+    if (consumerListener != NULL) {
+        consumerListener->onBuffersReleased();
+    }
+    if (producerListener != NULL) {
+        for (int i : freedSlots) {
+            producerListener->onSlotFreed(i);
+        }
     }
     return NO_ERROR;
 }
@@ -361,6 +379,9 @@
     EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
     bool attachedByConsumer = false;
 
+    sp<IConsumerListener> consumerListener;
+    sp<IProducerListener> producerListener;
+    int found = BufferItem::INVALID_BUFFER_SLOT;
     { // Autolock scope
         Mutex::Autolock lock(mCore->mMutex);
         mCore->waitWhileAllocatingLocked();
@@ -378,7 +399,6 @@
             height = mCore->mDefaultHeight;
         }
 
-        int found = BufferItem::INVALID_BUFFER_SLOT;
         while (found == BufferItem::INVALID_BUFFER_SLOT) {
             status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,
                     &found);
@@ -408,6 +428,8 @@
                     mCore->mFreeSlots.insert(found);
                     mCore->clearBufferSlotLocked(found);
                     found = BufferItem::INVALID_BUFFER_SLOT;
+                    consumerListener = mCore->mConsumerListener;
+                    producerListener = mCore->mConnectedProducerListener;
                     continue;
                 }
             }
@@ -444,6 +466,10 @@
         if ((buffer == NULL) ||
                 buffer->needsReallocation(width, height, format, usage))
         {
+            if (buffer != NULL) {
+                consumerListener = mCore->mConsumerListener;
+                producerListener = mCore->mConnectedProducerListener;
+            }
             mSlots[found].mAcquireCalled = false;
             mSlots[found].mGraphicBuffer = NULL;
             mSlots[found].mRequestBufferCalled = false;
@@ -531,6 +557,14 @@
             mSlots[*outSlot].mFrameNumber,
             mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
 
+    // Call back without lock held
+    if (consumerListener != NULL) {
+        consumerListener->onBuffersReleased();
+    }
+    if (producerListener != NULL) {
+        producerListener->onSlotFreed(found);
+    }
+
     return returnFlags;
 }
 
@@ -538,44 +572,60 @@
     ATRACE_CALL();
     ATRACE_BUFFER_INDEX(slot);
     BQ_LOGV("detachBuffer: slot %d", slot);
-    Mutex::Autolock lock(mCore->mMutex);
 
-    if (mCore->mIsAbandoned) {
-        BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
-        return NO_INIT;
+    sp<IConsumerListener> consumerListener;
+    sp<IProducerListener> producerListener;
+    {
+        Mutex::Autolock lock(mCore->mMutex);
+
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
+            return NO_INIT;
+        }
+
+        if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+            BQ_LOGE("detachBuffer: BufferQueue has no connected producer");
+            return NO_INIT;
+        }
+
+        if (mCore->mSingleBufferMode || mCore->mSingleBufferSlot == slot) {
+            BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer "
+                    "mode");
+            return BAD_VALUE;
+        }
+
+        if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+            BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
+                    slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+            return BAD_VALUE;
+        } else if (!mSlots[slot].mBufferState.isDequeued()) {
+            BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
+                    "(state = %s)", slot, mSlots[slot].mBufferState.string());
+            return BAD_VALUE;
+        } else if (!mSlots[slot].mRequestBufferCalled) {
+            BQ_LOGE("detachBuffer: buffer in slot %d has not been requested",
+                    slot);
+            return BAD_VALUE;
+        }
+
+        mSlots[slot].mBufferState.detachProducer();
+        mCore->mActiveBuffers.erase(slot);
+        mCore->mFreeSlots.insert(slot);
+        mCore->clearBufferSlotLocked(slot);
+        mCore->mDequeueCondition.broadcast();
+        VALIDATE_CONSISTENCY();
+        producerListener = mCore->mConnectedProducerListener;
+        consumerListener = mCore->mConsumerListener;
     }
 
-    if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
-        BQ_LOGE("detachBuffer: BufferQueue has no connected producer");
-        return NO_INIT;
+    // Call back without lock held
+    if (consumerListener != NULL) {
+        consumerListener->onBuffersReleased();
     }
-
-    if (mCore->mSingleBufferMode || mCore->mSingleBufferSlot == slot) {
-        BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer mode");
-        return BAD_VALUE;
+    if (producerListener != NULL) {
+        producerListener->onSlotFreed(slot);
     }
 
-    if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
-        BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
-                slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
-        return BAD_VALUE;
-    } else if (!mSlots[slot].mBufferState.isDequeued()) {
-        BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
-                "(state = %s)", slot, mSlots[slot].mBufferState.string());
-        return BAD_VALUE;
-    } else if (!mSlots[slot].mRequestBufferCalled) {
-        BQ_LOGE("detachBuffer: buffer in slot %d has not been requested",
-                slot);
-        return BAD_VALUE;
-    }
-
-    mSlots[slot].mBufferState.detachProducer();
-    mCore->mActiveBuffers.erase(slot);
-    mCore->mFreeSlots.insert(slot);
-    mCore->clearBufferSlotLocked(slot);
-    mCore->mDequeueCondition.broadcast();
-    VALIDATE_CONSISTENCY();
-
     return NO_ERROR;
 }
 
@@ -591,41 +641,55 @@
         return BAD_VALUE;
     }
 
-    Mutex::Autolock lock(mCore->mMutex);
+    sp<IConsumerListener> consumerListener;
+    sp<IProducerListener> producerListener;
+    int found = BufferQueueCore::INVALID_BUFFER_SLOT;
+    {
+        Mutex::Autolock lock(mCore->mMutex);
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
+            return NO_INIT;
+        }
 
-    if (mCore->mIsAbandoned) {
-        BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
-        return NO_INIT;
+        if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+            BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
+            return NO_INIT;
+        }
+
+        if (mCore->mSingleBufferMode) {
+            BQ_LOGE("detachNextBuffer: cannot detach a buffer in single buffer"
+                    "mode");
+            return BAD_VALUE;
+        }
+
+        mCore->waitWhileAllocatingLocked();
+
+        if (mCore->mFreeBuffers.empty()) {
+            return NO_MEMORY;
+        }
+
+        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->clearBufferSlotLocked(found);
+        VALIDATE_CONSISTENCY();
+        consumerListener = mCore->mConsumerListener;
+        producerListener = mCore->mConnectedProducerListener;
     }
 
-    if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
-        BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
-        return NO_INIT;
+    // Call back without lock held
+    if (consumerListener != NULL) {
+        consumerListener->onBuffersReleased();
     }
-
-    if (mCore->mSingleBufferMode) {
-        BQ_LOGE("detachNextBuffer: cannot detach a buffer in single buffer"
-                "mode");
-        return BAD_VALUE;
+    if (producerListener != NULL) {
+        producerListener->onSlotFreed(found);
     }
 
-    mCore->waitWhileAllocatingLocked();
-
-    if (mCore->mFreeBuffers.empty()) {
-        return NO_MEMORY;
-    }
-
-    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->clearBufferSlotLocked(found);
-    VALIDATE_CONSISTENCY();
-
     return NO_ERROR;
 }
 
@@ -1020,82 +1084,102 @@
 status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
         int api, bool producerControlledByApp, QueueBufferOutput *output) {
     ATRACE_CALL();
-    Mutex::Autolock lock(mCore->mMutex);
-    mConsumerName = mCore->mConsumerName;
-    BQ_LOGV("connect: api=%d producerControlledByApp=%s", api,
-            producerControlledByApp ? "true" : "false");
-
-    if (mCore->mIsAbandoned) {
-        BQ_LOGE("connect: BufferQueue has been abandoned");
-        return NO_INIT;
-    }
-
-    if (mCore->mConsumerListener == NULL) {
-        BQ_LOGE("connect: BufferQueue has no consumer");
-        return NO_INIT;
-    }
-
-    if (output == NULL) {
-        BQ_LOGE("connect: output was NULL");
-        return BAD_VALUE;
-    }
-
-    if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
-        BQ_LOGE("connect: already connected (cur=%d req=%d)",
-                mCore->mConnectedApi, api);
-        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:
-        case NATIVE_WINDOW_API_CPU:
-        case NATIVE_WINDOW_API_MEDIA:
-        case NATIVE_WINDOW_API_CAMERA:
-            mCore->mConnectedApi = api;
-            output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
-                    mCore->mTransformHint,
-                    static_cast<uint32_t>(mCore->mQueue.size()));
+    sp<IConsumerListener> consumerListener;
+    sp<IProducerListener> producerListener;
+    std::vector<int> freedSlots;
+    {
+        Mutex::Autolock lock(mCore->mMutex);
+        mConsumerName = mCore->mConsumerName;
+        BQ_LOGV("connect: api=%d producerControlledByApp=%s", api,
+                producerControlledByApp ? "true" : "false");
 
-            // Set up a death notification so that we can disconnect
-            // automatically if the remote producer dies
-            if (listener != NULL &&
-                    IInterface::asBinder(listener)->remoteBinder() != NULL) {
-                status = IInterface::asBinder(listener)->linkToDeath(
-                        static_cast<IBinder::DeathRecipient*>(this));
-                if (status != NO_ERROR) {
-                    BQ_LOGE("connect: linkToDeath failed: %s (%d)",
-                            strerror(-status), status);
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("connect: BufferQueue has been abandoned");
+            return NO_INIT;
+        }
+
+        if (mCore->mConsumerListener == NULL) {
+            BQ_LOGE("connect: BufferQueue has no consumer");
+            return NO_INIT;
+        }
+
+        if (output == NULL) {
+            BQ_LOGE("connect: output was NULL");
+            return BAD_VALUE;
+        }
+
+        if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+            BQ_LOGE("connect: already connected (cur=%d req=%d)",
+                    mCore->mConnectedApi, api);
+            return BAD_VALUE;
+        }
+
+        bool dequeueBufferCannotBlock = mDequeueTimeout < 0 ?
+                mCore->mConsumerControlledByApp && producerControlledByApp :
+                false;
+        int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
+                dequeueBufferCannotBlock, mCore->mMaxBufferCount) -
+                mCore->getMaxBufferCountLocked();
+
+        if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
+            BQ_LOGE("connect: BufferQueue failed to adjust the number of "
+                    "available slots. Delta = %d", delta);
+            return BAD_VALUE;
+        }
+
+        switch (api) {
+            case NATIVE_WINDOW_API_EGL:
+            case NATIVE_WINDOW_API_CPU:
+            case NATIVE_WINDOW_API_MEDIA:
+            case NATIVE_WINDOW_API_CAMERA:
+                mCore->mConnectedApi = api;
+                output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
+                        mCore->mTransformHint,
+                        static_cast<uint32_t>(mCore->mQueue.size()));
+
+                // Set up a death notification so that we can disconnect
+                // automatically if the remote producer dies
+                if (listener != NULL &&
+                        IInterface::asBinder(listener)->remoteBinder() !=
+                        NULL) {
+                    status = IInterface::asBinder(listener)->linkToDeath(
+                            static_cast<IBinder::DeathRecipient*>(this));
+                    if (status != NO_ERROR) {
+                        BQ_LOGE("connect: linkToDeath failed: %s (%d)",
+                                strerror(-status), status);
+                    }
                 }
-            }
-            mCore->mConnectedProducerListener = listener;
-            break;
-        default:
-            BQ_LOGE("connect: unknown API %d", api);
-            status = BAD_VALUE;
-            break;
+                mCore->mConnectedProducerListener = listener;
+                break;
+            default:
+                BQ_LOGE("connect: unknown API %d", api);
+                status = BAD_VALUE;
+                break;
+        }
+
+        mCore->mBufferHasBeenQueued = false;
+        mCore->mDequeueBufferCannotBlock = dequeueBufferCannotBlock;
+
+        mCore->mAllowAllocation = true;
+        VALIDATE_CONSISTENCY();
+
+        if (delta < 0) {
+            consumerListener = mCore->mConsumerListener;
+            producerListener = listener;
+        }
     }
 
-    mCore->mBufferHasBeenQueued = false;
-    mCore->mDequeueBufferCannotBlock = false;
-    if (mDequeueTimeout < 0) {
-        mCore->mDequeueBufferCannotBlock =
-                mCore->mConsumerControlledByApp && producerControlledByApp;
+    // Call back without lock held
+    if (consumerListener != NULL) {
+        consumerListener->onBuffersReleased();
+    }
+    if (producerListener != NULL) {
+        for (int i : freedSlots) {
+            producerListener->onSlotFreed(i);
+        }
     }
 
-    mCore->mAllowAllocation = true;
-    VALIDATE_CONSISTENCY();
     return status;
 }
 
@@ -1313,19 +1397,40 @@
     ATRACE_CALL();
     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;
+    sp<IConsumerListener> consumerListener;
+    sp<IProducerListener> producerListener;
+    std::vector<int> freedSlots;
+    {
+        Mutex::Autolock lock(mCore->mMutex);
+        int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false,
+                mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked();
+        if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
+            BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number"
+                    " of available slots. Delta = %d", delta);
+            return BAD_VALUE;
+        }
+
+        mDequeueTimeout = timeout;
+        mCore->mDequeueBufferCannotBlock = false;
+
+        VALIDATE_CONSISTENCY();
+
+        if (delta < 0) {
+            consumerListener = mCore->mConsumerListener;
+            producerListener = mCore->mConnectedProducerListener;
+        }
     }
 
-    mDequeueTimeout = timeout;
-    mCore->mDequeueBufferCannotBlock = false;
+    // Call back without lock held
+    if (consumerListener != NULL) {
+        consumerListener->onBuffersReleased();
+    }
+    if (producerListener != NULL) {
+        for (int i : freedSlots) {
+            producerListener->onSlotFreed(i);
+        }
+    }
 
-    VALIDATE_CONSISTENCY();
     return NO_ERROR;
 }
 
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 81adc95..39c1da8 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -22,6 +22,7 @@
 
 enum {
     ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION,
+    ON_SLOT_FREED,
 };
 
 class BpProducerListener : public BpInterface<IProducerListener>
@@ -37,6 +38,17 @@
         data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
         remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY);
     }
+
+    virtual void onSlotFreed(int slot) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
+        data.writeInt32(slot);
+        status_t err = remote()->transact(ON_SLOT_FREED, data, &reply,
+                IBinder::FLAG_ONEWAY);
+        if (err != NO_ERROR) {
+            ALOGE("onSlotFreed failed to transact %d", err);
+        }
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -52,6 +64,12 @@
             CHECK_INTERFACE(IProducerListener, data, reply);
             onBufferReleased();
             return NO_ERROR;
+        case ON_SLOT_FREED: {
+            CHECK_INTERFACE(IProducerListener, data, reply);
+            int slot = data.readInt32();
+            onSlotFreed(slot);
+            return NO_ERROR;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 45b6463..fad0baa 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -727,4 +727,55 @@
     ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer));
 }
 
+struct TestListener : public BnProducerListener {
+    virtual void onBufferReleased() {}
+    virtual void onSlotFreed(int slot) {
+        ASSERT_EQ(1, slot);
+    }
+};
+
+TEST_F(IGraphicBufferProducerTest, SlotFreedListenerReturnsCorrectSlot) {
+    const ::testing::TestInfo* const testInfo =
+        ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
+            testInfo->name());
+
+    BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+
+    sp<DummyConsumer> consumerListener = new DummyConsumer;
+    ASSERT_OK(mConsumer->consumerConnect(consumerListener, false));
+
+    sp<TestListener> producerListener = new TestListener;
+    IGraphicBufferProducer::QueueBufferOutput output;
+    ASSERT_OK(mProducer->connect(producerListener, TEST_API,
+            TEST_CONTROLLED_BY_APP, &output));
+
+    ASSERT_OK(mProducer->setMaxDequeuedBufferCount(2));
+
+    DequeueBufferResult buffer0;
+    sp<GraphicBuffer> buf;
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+            dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
+            TEST_PRODUCER_USAGE_BITS, &buffer0));
+    ASSERT_OK(mProducer->requestBuffer(buffer0.slot, &buf));
+
+    DequeueBufferResult buffer1;
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+            dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
+            TEST_PRODUCER_USAGE_BITS, &buffer1));
+
+    IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+    ASSERT_OK(mProducer->queueBuffer(buffer0.slot, input, &output));
+
+    DequeueBufferResult buffer2;
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+            dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
+            TEST_PRODUCER_USAGE_BITS, &buffer2));
+
+    ASSERT_OK(mProducer->cancelBuffer(buffer1.slot, Fence::NO_FENCE));
+
+    ASSERT_OK(mProducer->setMaxDequeuedBufferCount(1));
+}
+
+
 } // namespace android