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());
+}
+
}