libgui: Add generation numbers to BufferQueue

This change allows producers to set a generation number on a
BufferQueue. This number will be embedded in any new GraphicBuffers
created in that BufferQueue, and attempts to attach buffers which have
a different generation number will fail.

It also plumbs the setGenerationNumber method through Surface, with the
additional effect that any buffers attached to the Surface after
setting a new generation number will automatically be updated with the
new number (as opposed to failing, as would happen on through IGBP).

Bug: 20923096
Change-Id: I32bf726b035f99c3e5834beaf76afb9f01adcbc2
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 4174676..ae796b1 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -248,6 +248,13 @@
         return INVALID_OPERATION;
     }
 
+    if (buffer->getGenerationNumber() != mCore->mGenerationNumber) {
+        BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] "
+                "[queue %u]", buffer->getGenerationNumber(),
+                mCore->mGenerationNumber);
+        return BAD_VALUE;
+    }
+
     // Find a free slot to put the buffer into
     int found = BufferQueueCore::INVALID_BUFFER_SLOT;
     if (!mCore->mFreeSlots.empty()) {
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index e784644..851a396 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -71,7 +71,8 @@
     mIsAllocating(false),
     mIsAllocatingCondition(),
     mAllowAllocation(true),
-    mBufferAge(0)
+    mBufferAge(0),
+    mGenerationNumber(0)
 {
     if (allocator == NULL) {
         sp<ISurfaceComposer> composer(ComposerService::getComposerService());
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index e318484..73d4261 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -383,6 +383,7 @@
                 return NO_INIT;
             }
 
+            graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
             mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
         } // Autolock scope
     }
@@ -498,6 +499,13 @@
     Mutex::Autolock lock(mCore->mMutex);
     mCore->waitWhileAllocatingLocked();
 
+    if (buffer->getGenerationNumber() != mCore->mGenerationNumber) {
+        BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] "
+                "[queue %u]", buffer->getGenerationNumber(),
+                mCore->mGenerationNumber);
+        return BAD_VALUE;
+    }
+
     status_t returnFlags = NO_ERROR;
     int found;
     // TODO: Should we provide an async flag to attachBuffer? It seems
@@ -1072,6 +1080,15 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueProducer::setGenerationNumber(uint32_t generationNumber) {
+    ATRACE_CALL();
+    BQ_LOGV("setGenerationNumber: %u", generationNumber);
+
+    Mutex::Autolock lock(mCore->mMutex);
+    mCore->mGenerationNumber = generationNumber;
+    return NO_ERROR;
+}
+
 void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
     // If we're here, it means that a producer we were connected to died.
     // We're guaranteed that we are still connected to it because we remove
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 7093ffa..cfe726b 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -47,6 +47,7 @@
     SET_SIDEBAND_STREAM,
     ALLOCATE_BUFFERS,
     ALLOW_ALLOCATION,
+    SET_GENERATION_NUMBER,
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -284,6 +285,17 @@
         result = reply.readInt32();
         return result;
     }
+
+    virtual status_t setGenerationNumber(uint32_t generationNumber) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeUint32(generationNumber);
+        status_t result = remote()->transact(SET_GENERATION_NUMBER, data, &reply);
+        if (result == NO_ERROR) {
+            result = reply.readInt32();
+        }
+        return result;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -448,6 +460,13 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case SET_GENERATION_NUMBER: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            uint32_t generationNumber = data.readUint32();
+            status_t result = setGenerationNumber(generationNumber);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 04ac0f4..aeb56e0 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -42,7 +42,8 @@
 Surface::Surface(
         const sp<IGraphicBufferProducer>& bufferProducer,
         bool controlledByApp)
-    : mGraphicBufferProducer(bufferProducer)
+    : mGraphicBufferProducer(bufferProducer),
+      mGenerationNumber(0)
 {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = hook_setSwapInterval;
@@ -102,6 +103,14 @@
             reqHeight, mReqFormat, mReqUsage);
 }
 
+status_t Surface::setGenerationNumber(uint32_t generation) {
+    status_t result = mGraphicBufferProducer->setGenerationNumber(generation);
+    if (result == NO_ERROR) {
+        mGenerationNumber = generation;
+    }
+    return result;
+}
+
 int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
     Surface* c = getSelf(window);
     return c->setSwapInterval(interval);
@@ -698,11 +707,14 @@
     Mutex::Autolock lock(mMutex);
 
     sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer));
+    uint32_t priorGeneration = graphicBuffer->mGenerationNumber;
+    graphicBuffer->mGenerationNumber = mGenerationNumber;
     int32_t attachedSlot = -1;
     status_t result = mGraphicBufferProducer->attachBuffer(
             &attachedSlot, graphicBuffer);
     if (result != NO_ERROR) {
         ALOGE("attachBuffer: IGraphicBufferProducer call failed (%d)", result);
+        graphicBuffer->mGenerationNumber = priorGeneration;
         return result;
     }
     mSlots[attachedSlot].buffer = graphicBuffer;
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 1584fef..3d1139d 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -402,4 +402,46 @@
             WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
 }
 
+TEST_F(BufferQueueTest, TestGenerationNumbers) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+            NATIVE_WINDOW_API_CPU, true, &output));
+
+    ASSERT_EQ(OK, mProducer->setGenerationNumber(1));
+
+    // Get one buffer to play with
+    int slot;
+    sp<Fence> fence;
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+            mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0, 0));
+
+    sp<GraphicBuffer> buffer;
+    ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+
+    // Ensure that the generation number we set propagates to allocated buffers
+    ASSERT_EQ(1U, buffer->getGenerationNumber());
+
+    ASSERT_EQ(OK, mProducer->detachBuffer(slot));
+
+    ASSERT_EQ(OK, mProducer->setGenerationNumber(2));
+
+    // These should fail, since we've changed the generation number on the queue
+    int outSlot;
+    ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&outSlot, buffer));
+    ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&outSlot, buffer));
+
+    buffer->setGenerationNumber(2);
+
+    // This should succeed now that we've changed the buffer's generation number
+    ASSERT_EQ(OK, mProducer->attachBuffer(&outSlot, buffer));
+
+    ASSERT_EQ(OK, mProducer->detachBuffer(outSlot));
+
+    // This should also succeed with the new generation number
+    ASSERT_EQ(OK, mConsumer->attachBuffer(&outSlot, buffer));
+}
+
 } // namespace android
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 4f87824..cf0043d 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -177,4 +177,37 @@
     ASSERT_EQ(TEST_DATASPACE, dataSpace);
 }
 
+TEST_F(SurfaceTest, SettingGenerationNumber) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+    sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+    sp<Surface> surface = new Surface(producer);
+    sp<ANativeWindow> window(surface);
+
+    // Allocate a buffer with a generation number of 0
+    ANativeWindowBuffer* buffer;
+    int fenceFd;
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fenceFd));
+    ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fenceFd));
+
+    // Detach the buffer and check its generation number
+    sp<GraphicBuffer> graphicBuffer;
+    sp<Fence> fence;
+    ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&graphicBuffer, &fence));
+    ASSERT_EQ(0U, graphicBuffer->getGenerationNumber());
+
+    ASSERT_EQ(NO_ERROR, surface->setGenerationNumber(1));
+    buffer = static_cast<ANativeWindowBuffer*>(graphicBuffer.get());
+
+    // This should change the generation number of the GraphicBuffer
+    ASSERT_EQ(NO_ERROR, surface->attachBuffer(buffer));
+
+    // Check that the new generation number sticks with the buffer
+    ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, -1));
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fenceFd));
+    graphicBuffer = static_cast<GraphicBuffer*>(buffer);
+    ASSERT_EQ(1U, graphicBuffer->getGenerationNumber());
+}
+
 }