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
(cherry picked from commit 812ed0644f)
This commit is contained in:
Dan Stoza 2015-06-02 15:45:22 -07:00
parent c19fdee38a
commit 993772a60a
18 changed files with 199 additions and 16 deletions

View File

@ -275,6 +275,11 @@ private:
// buffer as the number of frames that have elapsed since it was last queued
uint64_t mBufferAge;
// mGenerationNumber stores the current generation number of the attached
// producer. Any attempt to attach a buffer with a different generation
// number will fail.
uint32_t mGenerationNumber;
}; // class BufferQueueCore
} // namespace android

View File

@ -175,6 +175,9 @@ public:
// See IGraphicBufferProducer::allowAllocation
virtual status_t allowAllocation(bool allow);
// See IGraphicBufferProducer::setGenerationNumber
virtual status_t setGenerationNumber(uint32_t generationNumber);
private:
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);

View File

@ -110,7 +110,8 @@ public:
// will be deallocated as stale.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - outSlot or buffer were NULL
// * BAD_VALUE - outSlot or buffer were NULL, or the generation number of
// the buffer did not match the buffer queue.
// * INVALID_OPERATION - cannot attach the buffer because it would cause too
// many buffers to be acquired.
// * NO_MEMORY - no free slots available

View File

@ -218,8 +218,9 @@ public:
//
// Return of a negative value means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned.
// * BAD_VALUE - outSlot or buffer were NULL or invalid combination of
// async mode and buffer count override.
// * BAD_VALUE - outSlot or buffer were NULL, invalid combination of
// async mode and buffer count override, or the generation
// number of the buffer did not match the buffer queue.
// * INVALID_OPERATION - cannot attach the buffer because it would cause
// too many buffers to be dequeued, either because
// the producer already has a single buffer dequeued
@ -470,6 +471,15 @@ public:
// eligible slot is available, dequeueBuffer will block or return an error
// as usual.
virtual status_t allowAllocation(bool allow) = 0;
// Sets the current generation number of the BufferQueue.
//
// This generation number will be inserted into any buffers allocated by the
// BufferQueue, and any attempts to attach a buffer with a different
// generation number will fail. Buffers already in the queue are not
// affected and will retain their current generation number. The generation
// number defaults to 0.
virtual status_t setGenerationNumber(uint32_t generationNumber) = 0;
};
// ----------------------------------------------------------------------------

View File

@ -101,6 +101,11 @@ public:
*/
void allocateBuffers();
/* Sets the generation number on the IGraphicBufferProducer and updates the
* generation number on any buffers attached to the Surface after this call.
* See IGBP::setGenerationNumber for more information. */
status_t setGenerationNumber(uint32_t generationNumber);
protected:
virtual ~Surface();
@ -305,6 +310,10 @@ private:
// When a non-CPU producer is attached, this reflects the surface damage
// (the change since the previous frame) passed in by the producer.
Region mDirtyRegion;
// Stores the current generation number. See setGenerationNumber and
// IGraphicBufferProducer::setGenerationNumber for more information.
uint32_t mGenerationNumber;
};
}; // namespace android

View File

@ -94,6 +94,11 @@ public:
Rect getBounds() const { return Rect(width, height); }
uint64_t getId() const { return mId; }
uint32_t getGenerationNumber() const { return mGenerationNumber; }
void setGenerationNumber(uint32_t generation) {
mGenerationNumber = generation;
}
status_t reallocate(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inUsage);
@ -166,6 +171,11 @@ private:
sp<ANativeWindowBuffer> mWrappedBuffer;
uint64_t mId;
// Stores the generation number of this buffer. If this number does not
// match the BufferQueue's internal generation number (set through
// IGBP::setGenerationNumber), attempts to attach the buffer will fail.
uint32_t mGenerationNumber;
};
}; // namespace android

View File

@ -248,6 +248,13 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot,
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()) {

View File

@ -71,7 +71,8 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
mIsAllocating(false),
mIsAllocatingCondition(),
mAllowAllocation(true),
mBufferAge(0)
mBufferAge(0),
mGenerationNumber(0)
{
if (allocator == NULL) {
sp<ISurfaceComposer> composer(ComposerService::getComposerService());

View File

@ -383,6 +383,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
return NO_INIT;
}
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
} // Autolock scope
}
@ -498,6 +499,13 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot,
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 @@ status_t BufferQueueProducer::allowAllocation(bool allow) {
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

View File

@ -47,6 +47,7 @@ enum {
SET_SIDEBAND_STREAM,
ALLOCATE_BUFFERS,
ALLOW_ALLOCATION,
SET_GENERATION_NUMBER,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@ -284,6 +285,17 @@ public:
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 @@ status_t BnGraphicBufferProducer::onTransact(
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);
}

View File

@ -42,7 +42,8 @@ namespace android {
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 @@ void Surface::allocateBuffers() {
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 @@ int Surface::attachBuffer(ANativeWindowBuffer* buffer)
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;

View File

@ -402,4 +402,46 @@ TEST_F(BufferQueueTest, TestDisallowingAllocation) {
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

View File

@ -177,4 +177,37 @@ TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) {
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());
}
}

View File

@ -278,7 +278,7 @@ status_t GraphicBuffer::unlockAsync(int *fenceFd)
}
size_t GraphicBuffer::getFlattenedSize() const {
return static_cast<size_t>(10 + (handle ? handle->numInts : 0)) * sizeof(int);
return static_cast<size_t>(11 + (handle ? handle->numInts : 0)) * sizeof(int);
}
size_t GraphicBuffer::getFdCount() const {
@ -301,15 +301,16 @@ status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t&
buf[5] = usage;
buf[6] = static_cast<int32_t>(mId >> 32);
buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
buf[8] = 0;
buf[8] = static_cast<int32_t>(mGenerationNumber);
buf[9] = 0;
buf[10] = 0;
if (handle) {
buf[8] = handle->numFds;
buf[9] = handle->numInts;
buf[9] = handle->numFds;
buf[10] = handle->numInts;
memcpy(fds, handle->data,
static_cast<size_t>(handle->numFds) * sizeof(int));
memcpy(&buf[10], handle->data + handle->numFds,
memcpy(&buf[11], handle->data + handle->numFds,
static_cast<size_t>(handle->numInts) * sizeof(int));
}
@ -325,20 +326,20 @@ status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t&
status_t GraphicBuffer::unflatten(
void const*& buffer, size_t& size, int const*& fds, size_t& count) {
if (size < 8*sizeof(int)) return NO_MEMORY;
if (size < 11 * sizeof(int)) return NO_MEMORY;
int const* buf = static_cast<int const*>(buffer);
if (buf[0] != 'GBFR') return BAD_TYPE;
const size_t numFds = static_cast<size_t>(buf[8]);
const size_t numInts = static_cast<size_t>(buf[9]);
const size_t numFds = static_cast<size_t>(buf[9]);
const size_t numInts = static_cast<size_t>(buf[10]);
// Limit the maxNumber to be relatively small. The number of fds or ints
// should not come close to this number, and the number itself was simply
// chosen to be high enough to not cause issues and low enough to prevent
// overflow problems.
const size_t maxNumber = 4096;
if (numFds >= maxNumber || numInts >= (maxNumber - 10)) {
if (numFds >= maxNumber || numInts >= (maxNumber - 11)) {
width = height = stride = format = usage = 0;
handle = NULL;
ALOGE("unflatten: numFds or numInts is too large: %zd, %zd",
@ -346,7 +347,7 @@ status_t GraphicBuffer::unflatten(
return BAD_VALUE;
}
const size_t sizeNeeded = (10 + numInts) * sizeof(int);
const size_t sizeNeeded = (11 + numInts) * sizeof(int);
if (size < sizeNeeded) return NO_MEMORY;
size_t fdCountNeeded = numFds;
@ -372,7 +373,7 @@ status_t GraphicBuffer::unflatten(
return NO_MEMORY;
}
memcpy(h->data, fds, numFds * sizeof(int));
memcpy(h->data + numFds, &buf[10], numInts * sizeof(int));
memcpy(h->data + numFds, &buf[11], numInts * sizeof(int));
handle = h;
} else {
width = height = stride = format = usage = 0;
@ -382,6 +383,8 @@ status_t GraphicBuffer::unflatten(
mId = static_cast<uint64_t>(buf[6]) << 32;
mId |= static_cast<uint32_t>(buf[7]);
mGenerationNumber = static_cast<uint32_t>(buf[8]);
mOwner = ownHandle;
if (handle != 0) {

View File

@ -530,6 +530,11 @@ status_t VirtualDisplaySurface::allowAllocation(bool /* allow */) {
return INVALID_OPERATION;
}
status_t VirtualDisplaySurface::setGenerationNumber(uint32_t /* generation */) {
ALOGE("setGenerationNumber not supported on VirtualDisplaySurface");
return INVALID_OPERATION;
}
void VirtualDisplaySurface::updateQueueBufferOutput(
const QueueBufferOutput& qbo) {
uint32_t w, h, transformHint, numPendingBuffers;

View File

@ -116,6 +116,7 @@ private:
virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
PixelFormat format, uint32_t usage);
virtual status_t allowAllocation(bool allow);
virtual status_t setGenerationNumber(uint32_t generationNumber);
//
// Utility methods

View File

@ -114,6 +114,10 @@ status_t MonitoredProducer::allowAllocation(bool allow) {
return mProducer->allowAllocation(allow);
}
status_t MonitoredProducer::setGenerationNumber(uint32_t generationNumber) {
return mProducer->setGenerationNumber(generationNumber);
}
IBinder* MonitoredProducer::onAsBinder() {
return IInterface::asBinder(mProducer).get();
}

View File

@ -54,6 +54,7 @@ public:
virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
PixelFormat format, uint32_t usage);
virtual status_t allowAllocation(bool allow);
virtual status_t setGenerationNumber(uint32_t generationNumber);
virtual IBinder* onAsBinder();
private: