BufferQueueProducer: add detachNextBuffer

Adds a new method, IGBP::detachNextBuffer, that effectively does
dequeue + request + detach in a single call, but does not need to
know anything about the dequeued buffer, and will not block on
dequeue. This is mostly for the upcoming StreamSplitter to use in
its onBufferReleased callback.

Change-Id: Ie88a69de109003acebaa486a5b44c8a455726550
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index d04b67d..da980a7 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -103,6 +103,11 @@
     return mProducer->detachBuffer(slot);
 }
 
+status_t BufferQueue::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+        sp<Fence>* outFence) {
+    return mProducer->detachNextBuffer(outBuffer, outFence);
+}
+
 status_t BufferQueue::attachProducerBuffer(int* slot,
         const sp<GraphicBuffer>& buffer) {
     return mProducer->attachBuffer(slot, buffer);
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index ea37309..61846dd 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -393,6 +393,50 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+        sp<Fence>* outFence) {
+    ATRACE_CALL();
+
+    if (outBuffer == NULL) {
+        BQ_LOGE("detachNextBuffer: outBuffer must not be NULL");
+        return BAD_VALUE;
+    } else if (outFence == NULL) {
+        BQ_LOGE("detachNextBuffer: outFence must not be NULL");
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mCore->mMutex);
+
+    if (mCore->mIsAbandoned) {
+        BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
+        return NO_INIT;
+    }
+
+    // Find the oldest valid slot
+    int found = BufferQueueCore::INVALID_BUFFER_SLOT;
+    for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+        if (mSlots[s].mBufferState == BufferSlot::FREE &&
+                mSlots[s].mGraphicBuffer != NULL) {
+            if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
+                    mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
+                found = s;
+            }
+        }
+    }
+
+    if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+        return NO_MEMORY;
+    }
+
+    BQ_LOGV("detachNextBuffer detached slot %d", found);
+
+    *outBuffer = mSlots[found].mGraphicBuffer;
+    *outFence = mSlots[found].mFence;
+    mCore->freeBufferLocked(found);
+
+    return NO_ERROR;
+}
+
 status_t BufferQueueProducer::attachBuffer(int* outSlot,
         const sp<android::GraphicBuffer>& buffer) {
     ATRACE_CALL();
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index c0b08c1..aa6acb9 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -37,6 +37,7 @@
     SET_BUFFER_COUNT,
     DEQUEUE_BUFFER,
     DETACH_BUFFER,
+    DETACH_NEXT_BUFFER,
     ATTACH_BUFFER,
     QUEUE_BUFFER,
     CANCEL_BUFFER,
@@ -123,6 +124,37 @@
         return result;
     }
 
+    virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+            sp<Fence>* outFence) {
+        if (outBuffer == NULL) {
+            ALOGE("detachNextBuffer: outBuffer must not be NULL");
+            return BAD_VALUE;
+        } else if (outFence == NULL) {
+            ALOGE("detachNextBuffer: outFence must not be NULL");
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        status_t result = remote()->transact(DETACH_NEXT_BUFFER, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readInt32();
+        if (result == NO_ERROR) {
+            bool nonNull = reply.readInt32();
+            if (nonNull) {
+                *outBuffer = new GraphicBuffer;
+                reply.read(**outBuffer);
+            }
+            nonNull = reply.readInt32();
+            if (nonNull) {
+                *outFence = new Fence;
+                reply.read(**outFence);
+            }
+        }
+        return result;
+    }
+
     virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -274,6 +306,24 @@
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
+        case DETACH_NEXT_BUFFER: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            sp<GraphicBuffer> buffer;
+            sp<Fence> fence;
+            int32_t result = detachNextBuffer(&buffer, &fence);
+            reply->writeInt32(result);
+            if (result == NO_ERROR) {
+                reply->writeInt32(buffer != NULL);
+                if (buffer != NULL) {
+                    reply->write(*buffer);
+                }
+                reply->writeInt32(fence != NULL);
+                if (fence != NULL) {
+                    reply->write(*fence);
+                }
+            }
+            return NO_ERROR;
+        } break;
         case ATTACH_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             sp<GraphicBuffer> buffer = new GraphicBuffer();