Revert "SurfaceTexture: inherit from ConsumerBase"

This reverts commit ed059a8d754770c3cf28b78dba30f7a6ba475dbe

Change-Id: I72542c2595771a40c2c88251e0d6eb54e305b99b
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 451ccc2..6666081 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -26,8 +26,6 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
-#include <hardware/hardware.h>
-
 #include <gui/IGraphicBufferAlloc.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
@@ -98,10 +96,14 @@
 
 static void mtxMul(float out[16], const float a[16], const float b[16]);
 
+// Get an ID that's unique within this process.
+static int32_t createProcessUniqueId() {
+    static volatile int32_t globalCounter = 0;
+    return android_atomic_inc(&globalCounter);
+}
 
 SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
         GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
-    ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue),
     mCurrentTransform(0),
     mCurrentTimestamp(0),
     mFilteringEnabled(true),
@@ -114,15 +116,47 @@
     mTexTarget(texTarget),
     mEglDisplay(EGL_NO_DISPLAY),
     mEglContext(EGL_NO_CONTEXT),
+    mAbandoned(false),
     mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
     mAttached(true)
 {
+    // Choose a name using the PID and a process-unique ID.
+    mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
     ST_LOGV("SurfaceTexture");
+    if (bufferQueue == 0) {
+        ST_LOGV("Creating a new BufferQueue");
+        mBufferQueue = new BufferQueue(allowSynchronousMode);
+    }
+    else {
+        mBufferQueue = bufferQueue;
+    }
 
     memcpy(mCurrentTransformMatrix, mtxIdentity,
             sizeof(mCurrentTransformMatrix));
 
-    mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+    // Note that we can't create an sp<...>(this) in a ctor that will not keep a
+    // reference once the ctor ends, as that would cause the refcount of 'this'
+    // dropping to 0 at the end of the ctor.  Since all we need is a wp<...>
+    // that's what we create.
+    wp<BufferQueue::ConsumerListener> listener;
+    sp<BufferQueue::ConsumerListener> proxy;
+    listener = static_cast<BufferQueue::ConsumerListener*>(this);
+    proxy = new BufferQueue::ProxyConsumerListener(listener);
+
+    status_t err = mBufferQueue->consumerConnect(proxy);
+    if (err != NO_ERROR) {
+        ST_LOGE("SurfaceTexture: error connecting to BufferQueue: %s (%d)",
+                strerror(-err), err);
+    } else {
+        mBufferQueue->setConsumerName(mName);
+        mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+    }
+}
+
+SurfaceTexture::~SurfaceTexture() {
+    ST_LOGV("~SurfaceTexture");
+
+    abandon();
 }
 
 status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
@@ -143,42 +177,6 @@
     return SurfaceTexture::updateTexImage(NULL);
 }
 
-status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {
-    status_t err = ConsumerBase::acquireBufferLocked(item);
-    if (err != NO_ERROR) {
-        return err;
-    }
-
-    int slot = item->mBuf;
-    if (item->mGraphicBuffer != NULL) {
-        if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
-            eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage);
-            mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
-        }
-    }
-
-    // Update the GL texture object. We may have to do this even when
-    // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when
-    // detaching from a context but the buffer has not been re-allocated.
-    EGLImageKHR image = createImage(mEglDisplay, mSlots[slot].mGraphicBuffer);
-    if (image == EGL_NO_IMAGE_KHR) {
-        return UNKNOWN_ERROR;
-    }
-    mEglSlots[slot].mEglImage = image;
-
-    return NO_ERROR;
-}
-
-status_t SurfaceTexture::releaseBufferLocked(int buf, EGLDisplay display,
-       EGLSyncKHR eglFence, const sp<Fence>& fence) {
-    status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay,
-           eglFence, fence);
-
-    mEglSlots[mCurrentTexture].mEglFence = EGL_NO_SYNC_KHR;
-
-    return err;
-}
-
 status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
     ATRACE_CALL();
     ST_LOGV("updateTexImage");
@@ -219,65 +217,97 @@
 
     // In asynchronous mode the list is guaranteed to be one buffer
     // deep, while in synchronous mode we use the oldest buffer.
-    err = acquireBufferLocked(&item);
+    err = mBufferQueue->acquireBuffer(&item);
     if (err == NO_ERROR) {
         int buf = item.mBuf;
+        // This buffer was newly allocated, so we need to clean up on our side
+        if (item.mGraphicBuffer != NULL) {
+            mEGLSlots[buf].mGraphicBuffer = 0;
+            if (mEGLSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
+                eglDestroyImageKHR(dpy, mEGLSlots[buf].mEglImage);
+                mEGLSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
+            }
+            mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer;
+        }
 
         // we call the rejecter here, in case the caller has a reason to
         // not accept this buffer. this is used by SurfaceFlinger to
         // reject buffers which have the wrong size
-        if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) {
-            releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
+        if (rejecter && rejecter->reject(mEGLSlots[buf].mGraphicBuffer, item)) {
+            mBufferQueue->releaseBuffer(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
             glBindTexture(mTexTarget, mTexName);
             return NO_ERROR;
         }
 
-        GLint error;
-        while ((error = glGetError()) != GL_NO_ERROR) {
-            ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
-        }
-
-        EGLImageKHR image = mEglSlots[buf].mEglImage;
-        glBindTexture(mTexTarget, mTexName);
-        glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
-
-        while ((error = glGetError()) != GL_NO_ERROR) {
-            ST_LOGE("updateTexImage: error binding external texture image %p "
-                    "(slot %d): %#04x", image, buf, error);
-            err = UNKNOWN_ERROR;
+        // Update the GL texture object. We may have to do this even when
+        // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when
+        // detaching from a context but the buffer has not been re-allocated.
+        EGLImageKHR image = mEGLSlots[buf].mEglImage;
+        if (image == EGL_NO_IMAGE_KHR) {
+            if (mEGLSlots[buf].mGraphicBuffer == NULL) {
+                ST_LOGE("updateTexImage: buffer at slot %d is null", buf);
+                err = BAD_VALUE;
+            } else {
+                image = createImage(dpy, mEGLSlots[buf].mGraphicBuffer);
+                mEGLSlots[buf].mEglImage = image;
+                if (image == EGL_NO_IMAGE_KHR) {
+                    // NOTE: if dpy was invalid, createImage() is guaranteed to
+                    // fail. so we'd end up here.
+                    err = UNKNOWN_ERROR;
+                }
+            }
         }
 
         if (err == NO_ERROR) {
-            err = syncForReleaseLocked(dpy);
+            GLint error;
+            while ((error = glGetError()) != GL_NO_ERROR) {
+                ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
+            }
+
+            glBindTexture(mTexTarget, mTexName);
+            glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
+
+            while ((error = glGetError()) != GL_NO_ERROR) {
+                ST_LOGE("updateTexImage: error binding external texture image %p "
+                        "(slot %d): %#04x", image, buf, error);
+                err = UNKNOWN_ERROR;
+            }
+
+            if (err == NO_ERROR) {
+                err = syncForReleaseLocked(dpy);
+            }
         }
 
         if (err != NO_ERROR) {
             // Release the buffer we just acquired.  It's not safe to
             // release the old buffer, so instead we just drop the new frame.
-            releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
+            mBufferQueue->releaseBuffer(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
             return err;
         }
 
         ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
                 mCurrentTexture,
                 mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
-                buf, mSlots[buf].mGraphicBuffer->handle);
+                buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0);
 
         // release old buffer
         if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
-            status_t status = releaseBufferLocked(mCurrentTexture, dpy,
-                    mEglSlots[mCurrentTexture].mEglFence,
-                    mSlots[mCurrentTexture].mFence);
-            if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
-                ST_LOGE("updateTexImage: failed to release buffer: %s (%d)",
-                       strerror(-status), status);
+            status_t status = mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
+                    mEGLSlots[mCurrentTexture].mFence,
+                    mEGLSlots[mCurrentTexture].mReleaseFence);
+            mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR;
+            mEGLSlots[mCurrentTexture].mReleaseFence.clear();
+            if (status == BufferQueue::STALE_BUFFER_SLOT) {
+                freeBufferLocked(mCurrentTexture);
+            } else if (status != NO_ERROR) {
+                ST_LOGE("updateTexImage: released invalid buffer");
                 err = status;
             }
         }
 
         // Update the SurfaceTexture state.
         mCurrentTexture = buf;
-        mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
+        mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer;
         mCurrentCrop = item.mCrop;
         mCurrentTransform = item.mTransform;
         mCurrentScalingMode = item.mScalingMode;
@@ -300,20 +330,20 @@
     sp<Fence> fence(new Fence(fenceFd));
     if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
         return;
-    if (!mSlots[mCurrentTexture].mFence.get()) {
-        mSlots[mCurrentTexture].mFence = fence;
+    if (!mEGLSlots[mCurrentTexture].mReleaseFence.get()) {
+        mEGLSlots[mCurrentTexture].mReleaseFence = fence;
     } else {
         sp<Fence> mergedFence = Fence::merge(
                 String8("SurfaceTexture merged release"),
-                mSlots[mCurrentTexture].mFence, fence);
+                mEGLSlots[mCurrentTexture].mReleaseFence, fence);
         if (!mergedFence.get()) {
             ST_LOGE("failed to merge release fences");
             // synchronization is broken, the best we can do is hope fences
             // signal in order so the new fence will act like a union
-            mSlots[mCurrentTexture].mFence = fence;
+            mEGLSlots[mCurrentTexture].mReleaseFence = fence;
             return;
         }
-        mSlots[mCurrentTexture].mFence = mergedFence;
+        mEGLSlots[mCurrentTexture].mReleaseFence = mergedFence;
     }
 }
 
@@ -360,10 +390,10 @@
     // SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a
     // new EGLDisplay).
     for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        EGLImageKHR img = mEglSlots[i].mEglImage;
+        EGLImageKHR img = mEGLSlots[i].mEglImage;
         if (img != EGL_NO_IMAGE_KHR) {
             eglDestroyImageKHR(mEglDisplay, img);
-            mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
+            mEGLSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
         }
     }
 
@@ -451,7 +481,7 @@
     ST_LOGV("syncForReleaseLocked");
 
     if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
-        EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence;
+        EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence;
         if (fence != EGL_NO_SYNC_KHR) {
             // There is already a fence for the current slot.  We need to wait
             // on that before replacing it with another fence to ensure that all
@@ -479,7 +509,7 @@
             return UNKNOWN_ERROR;
         }
         glFlush();
-        mEglSlots[mCurrentTexture].mEglFence = fence;
+        mEGLSlots[mCurrentTexture].mFence = fence;
     }
 
     return OK;
@@ -577,12 +607,10 @@
                     // only need to shrink by a half a pixel.
                     shrinkAmount = 0.5;
                     break;
-
                 default:
                     // If we don't recognize the format, we must assume the
                     // worst case (that we care about), which is YUV420.
                     shrinkAmount = 1.0;
-                    break;
             }
         }
 
@@ -622,6 +650,13 @@
     return mCurrentTimestamp;
 }
 
+void SurfaceTexture::setFrameAvailableListener(
+        const sp<FrameAvailableListener>& listener) {
+    ST_LOGV("setFrameAvailableListener");
+    Mutex::Autolock lock(mMutex);
+    mFrameAvailableListener = listener;
+}
+
 EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
         const sp<GraphicBuffer>& graphicBuffer) {
     EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
@@ -701,21 +736,35 @@
 
 void SurfaceTexture::freeBufferLocked(int slotIndex) {
     ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+    mEGLSlots[slotIndex].mGraphicBuffer = 0;
     if (slotIndex == mCurrentTexture) {
         mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
     }
-    EGLImageKHR img = mEglSlots[slotIndex].mEglImage;
+    EGLImageKHR img = mEGLSlots[slotIndex].mEglImage;
     if (img != EGL_NO_IMAGE_KHR) {
         ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img);
         eglDestroyImageKHR(mEglDisplay, img);
     }
-    mEglSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
+    mEGLSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
 }
 
-void SurfaceTexture::abandonLocked() {
-    ST_LOGV("abandonLocked");
-    mCurrentTextureBuf.clear();
-    ConsumerBase::abandonLocked();
+void SurfaceTexture::abandon() {
+    ST_LOGV("abandon");
+    Mutex::Autolock lock(mMutex);
+
+    if (!mAbandoned) {
+        mAbandoned = true;
+        mCurrentTextureBuf.clear();
+
+        // destroy all egl buffers
+        for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+            freeBufferLocked(i);
+        }
+
+        // disconnect from the BufferQueue
+        mBufferQueue->consumerDisconnect();
+        mBufferQueue.clear();
+    }
 }
 
 void SurfaceTexture::setName(const String8& name) {
@@ -747,18 +796,71 @@
     return mBufferQueue->setSynchronousMode(enabled);
 }
 
-void SurfaceTexture::dumpLocked(String8& result, const char* prefix,
-        char* buffer, size_t size) const
+// Used for refactoring, should not be in final interface
+sp<BufferQueue> SurfaceTexture::getBufferQueue() const {
+    Mutex::Autolock lock(mMutex);
+    return mBufferQueue;
+}
+
+void SurfaceTexture::onFrameAvailable() {
+    ST_LOGV("onFrameAvailable");
+
+    sp<FrameAvailableListener> listener;
+    { // scope for the lock
+        Mutex::Autolock lock(mMutex);
+        listener = mFrameAvailableListener;
+    }
+
+    if (listener != NULL) {
+        ST_LOGV("actually calling onFrameAvailable");
+        listener->onFrameAvailable();
+    }
+}
+
+void SurfaceTexture::onBuffersReleased() {
+    ST_LOGV("onBuffersReleased");
+
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        // Nothing to do if we're already abandoned.
+        return;
+    }
+
+    uint32_t mask = 0;
+    mBufferQueue->getReleasedBuffers(&mask);
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        if (mask & (1 << i)) {
+            freeBufferLocked(i);
+        }
+    }
+}
+
+void SurfaceTexture::dump(String8& result) const
 {
-    snprintf(buffer, size,
-       "%smTexName=%d mCurrentTexture=%d\n"
-       "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
-       prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
-       mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
-       mCurrentTransform);
+    char buffer[1024];
+    dump(result, "", buffer, 1024);
+}
+
+void SurfaceTexture::dump(String8& result, const char* prefix,
+        char* buffer, size_t SIZE) const
+{
+    Mutex::Autolock _l(mMutex);
+    snprintf(buffer, SIZE, "%smTexName=%d, mAbandoned=%d\n", prefix, mTexName,
+            int(mAbandoned));
     result.append(buffer);
 
-    ConsumerBase::dumpLocked(result, prefix, buffer, size);
+    snprintf(buffer, SIZE,
+            "%snext   : {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n",
+            prefix, mCurrentCrop.left,
+            mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
+            mCurrentTransform, mCurrentTexture
+    );
+    result.append(buffer);
+
+    if (!mAbandoned) {
+        mBufferQueue->dump(result, prefix, buffer, SIZE);
+    }
 }
 
 static void mtxMul(float out[16], const float a[16], const float b[16]) {