libgui: add BQ consumer buffer free notifications

This change adds a new callback for BufferQueue consumers to be notified
when the BufferQueue frees some or all of its buffers.  This is needed
to retain SurfaceTexture behavior where all buffers would be freed when
the producer disconnects.  This change also modifies the
SurfaceTextureGLToGLTest.EglDestroySurfaceUnrefsBuffers test to catch
when the buffers are not freed.

The implementation is a little complicated because it needs to avoid
circular sp<> references across what will be a binder interface (so wp<>
can't be used directly).  It also needs to avoid the possibility of
locking the BufferQueue and consumer (e.g. SurfaceTexture) mutexes in
the wrong order.

This change also includes a few additional fixes and test cleanups.

Change-Id: I27b77d0af15cb4b135f4b63573f634f5f0da2182
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 0f9c7c5..ffc5fa0 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -15,8 +15,8 @@
  */
 
 #define LOG_TAG "BufferQueue"
-//#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
 
 #define GL_GLEXT_PROTOTYPES
 #define EGL_EGLEXT_PROTOTYPES
@@ -146,13 +146,6 @@
     mConsumerName = name;
 }
 
-void BufferQueue::setFrameAvailableListener(
-        const sp<FrameAvailableListener>& listener) {
-    ST_LOGV("setFrameAvailableListener");
-    Mutex::Autolock lock(mMutex);
-    mFrameAvailableListener = listener;
-}
-
 status_t BufferQueue::setDefaultBufferFormat(uint32_t defaultFormat) {
     Mutex::Autolock lock(mMutex);
     mDefaultBufferFormat = defaultFormat;
@@ -531,7 +524,7 @@
 
     ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
 
-    sp<FrameAvailableListener> listener;
+    sp<ConsumerListener> listener;
 
     { // scope for the lock
         Mutex::Autolock lock(mMutex);
@@ -559,7 +552,7 @@
 
             // Synchronous mode always signals that an additional frame should
             // be consumed.
-            listener = mFrameAvailableListener;
+            listener = mConsumerListener;
         } else {
             // In asynchronous mode we only keep the most recent buffer.
             if (mQueue.empty()) {
@@ -568,7 +561,7 @@
                 // Asynchronous mode only signals that a frame should be
                 // consumed if no previous frame was pending. If a frame were
                 // pending then the consumer would have already been notified.
-                listener = mFrameAvailableListener;
+                listener = mConsumerListener;
             } else {
                 Fifo::iterator front(mQueue.begin());
                 // buffer currently queued is freed
@@ -682,6 +675,11 @@
         return NO_INIT;
     }
 
+    if (mConsumerListener == NULL) {
+        ST_LOGE("connect: BufferQueue has no consumer!");
+        return NO_INIT;
+    }
+
     int err = NO_ERROR;
     switch (api) {
         case NATIVE_WINDOW_API_EGL:
@@ -712,38 +710,49 @@
 status_t BufferQueue::disconnect(int api) {
     ATRACE_CALL();
     ST_LOGV("disconnect: api=%d", api);
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        // it is not really an error to disconnect after the surface
-        // has been abandoned, it should just be a no-op.
-        return NO_ERROR;
-    }
 
     int err = NO_ERROR;
-    switch (api) {
-        case NATIVE_WINDOW_API_EGL:
-        case NATIVE_WINDOW_API_CPU:
-        case NATIVE_WINDOW_API_MEDIA:
-        case NATIVE_WINDOW_API_CAMERA:
-            if (mConnectedApi == api) {
-                drainQueueAndFreeBuffersLocked();
-                mConnectedApi = NO_CONNECTED_API;
-                mNextCrop.makeInvalid();
-                mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
-                mNextTransform = 0;
-                mDequeueCondition.broadcast();
-            } else {
-                ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)",
-                        mConnectedApi, api);
+    sp<ConsumerListener> listener;
+
+    { // Scope for the lock
+        Mutex::Autolock lock(mMutex);
+
+        if (mAbandoned) {
+            // it is not really an error to disconnect after the surface
+            // has been abandoned, it should just be a no-op.
+            return NO_ERROR;
+        }
+
+        switch (api) {
+            case NATIVE_WINDOW_API_EGL:
+            case NATIVE_WINDOW_API_CPU:
+            case NATIVE_WINDOW_API_MEDIA:
+            case NATIVE_WINDOW_API_CAMERA:
+                if (mConnectedApi == api) {
+                    drainQueueAndFreeBuffersLocked();
+                    mConnectedApi = NO_CONNECTED_API;
+                    mNextCrop.makeInvalid();
+                    mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
+                    mNextTransform = 0;
+                    mDequeueCondition.broadcast();
+                    listener = mConsumerListener;
+                } else {
+                    ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)",
+                            mConnectedApi, api);
+                    err = -EINVAL;
+                }
+                break;
+            default:
+                ST_LOGE("disconnect: unknown API %d", api);
                 err = -EINVAL;
-            }
-            break;
-        default:
-            ST_LOGE("disconnect: unknown API %d", api);
-            err = -EINVAL;
-            break;
+                break;
+        }
     }
+
+    if (listener != NULL) {
+        listener->onBuffersReleased();
+    }
+
     return err;
 }
 
@@ -841,7 +850,7 @@
     }
 }
 
-status_t BufferQueue::acquire(BufferItem *buffer) {
+status_t BufferQueue::acquireBuffer(BufferItem *buffer) {
     ATRACE_CALL();
     Mutex::Autolock _l(mMutex);
     // check if queue is empty
@@ -855,8 +864,7 @@
 
         if (mSlots[buf].mAcquireCalled) {
             buffer->mGraphicBuffer = NULL;
-        }
-        else {
+        } else {
             buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer;
         }
         buffer->mCrop = mSlots[buf].mCrop;
@@ -872,8 +880,7 @@
         mDequeueCondition.broadcast();
 
         ATRACE_INT(mConsumerName.string(), mQueue.size());
-    }
-    else {
+    } else {
         // should be a better return code?
         return -EINVAL;
     }
@@ -907,17 +914,58 @@
     return OK;
 }
 
-status_t BufferQueue::consumerDisconnect() {
+status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListener) {
+    ST_LOGV("consumerConnect");
     Mutex::Autolock lock(mMutex);
 
-    mAbandoned = true;
+    if (mAbandoned) {
+        ST_LOGE("consumerConnect: BufferQueue has been abandoned!");
+        return NO_INIT;
+    }
 
+    mConsumerListener = consumerListener;
+
+    return OK;
+}
+
+status_t BufferQueue::consumerDisconnect() {
+    ST_LOGV("consumerDisconnect");
+    Mutex::Autolock lock(mMutex);
+
+    if (mConsumerListener == NULL) {
+        ST_LOGE("consumerDisconnect: No consumer is connected!");
+        return -EINVAL;
+    }
+
+    mAbandoned = true;
+    mConsumerListener = NULL;
     mQueue.clear();
     freeAllBuffersLocked();
     mDequeueCondition.broadcast();
     return OK;
 }
 
+status_t BufferQueue::getReleasedBuffers(uint32_t* slotMask) {
+    ST_LOGV("getReleasedBuffers");
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        ST_LOGE("getReleasedBuffers: BufferQueue has been abandoned!");
+        return NO_INIT;
+    }
+
+    uint32_t mask = 0;
+    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+        if (!mSlots[i].mAcquireCalled) {
+            mask |= 1 << i;
+        }
+    }
+    *slotMask = mask;
+
+    ST_LOGV("getReleasedBuffers: returning mask %#x", mask);
+    return NO_ERROR;
+}
+
 status_t BufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h)
 {
     ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
@@ -982,4 +1030,24 @@
     return err;
 }
 
+BufferQueue::ProxyConsumerListener::ProxyConsumerListener(
+        const wp<BufferQueue::ConsumerListener>& consumerListener):
+        mConsumerListener(consumerListener) {}
+
+BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
+
+void BufferQueue::ProxyConsumerListener::onFrameAvailable() {
+    sp<BufferQueue::ConsumerListener> listener(mConsumerListener.promote());
+    if (listener != NULL) {
+        listener->onFrameAvailable();
+    }
+}
+
+void BufferQueue::ProxyConsumerListener::onBuffersReleased() {
+    sp<BufferQueue::ConsumerListener> listener(mConsumerListener.promote());
+    if (listener != NULL) {
+        listener->onBuffersReleased();
+    }
+}
+
 }; // namespace android