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
This commit is contained in:
Dan Stoza 2015-06-02 15:45:22 -07:00
parent a8c2454d52
commit 812ed0644f
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 // buffer as the number of frames that have elapsed since it was last queued
uint64_t mBufferAge; 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 }; // class BufferQueueCore
} // namespace android } // namespace android

View File

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

View File

@ -110,7 +110,8 @@ public:
// will be deallocated as stale. // will be deallocated as stale.
// //
// Return of a value other than NO_ERROR means an error has occurred: // 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 // * INVALID_OPERATION - cannot attach the buffer because it would cause too
// many buffers to be acquired. // many buffers to be acquired.
// * NO_MEMORY - no free slots available // * NO_MEMORY - no free slots available

View File

@ -218,8 +218,9 @@ public:
// //
// Return of a negative value means an error has occurred: // Return of a negative value means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned. // * NO_INIT - the buffer queue has been abandoned.
// * BAD_VALUE - outSlot or buffer were NULL or invalid combination of // * BAD_VALUE - outSlot or buffer were NULL, invalid combination of
// async mode and buffer count override. // 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 // * INVALID_OPERATION - cannot attach the buffer because it would cause
// too many buffers to be dequeued, either because // too many buffers to be dequeued, either because
// the producer already has a single buffer dequeued // the producer already has a single buffer dequeued
@ -470,6 +471,15 @@ public:
// eligible slot is available, dequeueBuffer will block or return an error // eligible slot is available, dequeueBuffer will block or return an error
// as usual. // as usual.
virtual status_t allowAllocation(bool allow) = 0; 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(); 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: protected:
virtual ~Surface(); virtual ~Surface();
@ -305,6 +310,10 @@ private:
// When a non-CPU producer is attached, this reflects the surface damage // When a non-CPU producer is attached, this reflects the surface damage
// (the change since the previous frame) passed in by the producer. // (the change since the previous frame) passed in by the producer.
Region mDirtyRegion; Region mDirtyRegion;
// Stores the current generation number. See setGenerationNumber and
// IGraphicBufferProducer::setGenerationNumber for more information.
uint32_t mGenerationNumber;
}; };
}; // namespace android }; // namespace android

View File

@ -94,6 +94,11 @@ public:
Rect getBounds() const { return Rect(width, height); } Rect getBounds() const { return Rect(width, height); }
uint64_t getId() const { return mId; } 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, status_t reallocate(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inUsage); PixelFormat inFormat, uint32_t inUsage);
@ -166,6 +171,11 @@ private:
sp<ANativeWindowBuffer> mWrappedBuffer; sp<ANativeWindowBuffer> mWrappedBuffer;
uint64_t mId; 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 }; // namespace android

View File

@ -248,6 +248,13 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot,
return INVALID_OPERATION; 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 // Find a free slot to put the buffer into
int found = BufferQueueCore::INVALID_BUFFER_SLOT; int found = BufferQueueCore::INVALID_BUFFER_SLOT;
if (!mCore->mFreeSlots.empty()) { if (!mCore->mFreeSlots.empty()) {

View File

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

View File

@ -383,6 +383,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
return NO_INIT; return NO_INIT;
} }
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer; mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
} // Autolock scope } // Autolock scope
} }
@ -498,6 +499,13 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot,
Mutex::Autolock lock(mCore->mMutex); Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked(); 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; status_t returnFlags = NO_ERROR;
int found; int found;
// TODO: Should we provide an async flag to attachBuffer? It seems // TODO: Should we provide an async flag to attachBuffer? It seems
@ -1072,6 +1080,15 @@ status_t BufferQueueProducer::allowAllocation(bool allow) {
return NO_ERROR; 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 */) { void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
// If we're here, it means that a producer we were connected to died. // 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 // We're guaranteed that we are still connected to it because we remove

View File

@ -47,6 +47,7 @@ enum {
SET_SIDEBAND_STREAM, SET_SIDEBAND_STREAM,
ALLOCATE_BUFFERS, ALLOCATE_BUFFERS,
ALLOW_ALLOCATION, ALLOW_ALLOCATION,
SET_GENERATION_NUMBER,
}; };
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@ -284,6 +285,17 @@ public:
result = reply.readInt32(); result = reply.readInt32();
return result; 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 // Out-of-line virtual method definition to trigger vtable emission in this
@ -448,6 +460,13 @@ status_t BnGraphicBufferProducer::onTransact(
reply->writeInt32(result); reply->writeInt32(result);
return NO_ERROR; 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); return BBinder::onTransact(code, data, reply, flags);
} }

View File

@ -42,7 +42,8 @@ namespace android {
Surface::Surface( Surface::Surface(
const sp<IGraphicBufferProducer>& bufferProducer, const sp<IGraphicBufferProducer>& bufferProducer,
bool controlledByApp) bool controlledByApp)
: mGraphicBufferProducer(bufferProducer) : mGraphicBufferProducer(bufferProducer),
mGenerationNumber(0)
{ {
// Initialize the ANativeWindow function pointers. // Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval; ANativeWindow::setSwapInterval = hook_setSwapInterval;
@ -102,6 +103,14 @@ void Surface::allocateBuffers() {
reqHeight, mReqFormat, mReqUsage); 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) { int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
Surface* c = getSelf(window); Surface* c = getSelf(window);
return c->setSwapInterval(interval); return c->setSwapInterval(interval);
@ -698,11 +707,14 @@ int Surface::attachBuffer(ANativeWindowBuffer* buffer)
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer)); sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer));
uint32_t priorGeneration = graphicBuffer->mGenerationNumber;
graphicBuffer->mGenerationNumber = mGenerationNumber;
int32_t attachedSlot = -1; int32_t attachedSlot = -1;
status_t result = mGraphicBufferProducer->attachBuffer( status_t result = mGraphicBufferProducer->attachBuffer(
&attachedSlot, graphicBuffer); &attachedSlot, graphicBuffer);
if (result != NO_ERROR) { if (result != NO_ERROR) {
ALOGE("attachBuffer: IGraphicBufferProducer call failed (%d)", result); ALOGE("attachBuffer: IGraphicBufferProducer call failed (%d)", result);
graphicBuffer->mGenerationNumber = priorGeneration;
return result; return result;
} }
mSlots[attachedSlot].buffer = graphicBuffer; mSlots[attachedSlot].buffer = graphicBuffer;

View File

@ -402,4 +402,46 @@ TEST_F(BufferQueueTest, TestDisallowingAllocation) {
WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN)); 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 } // namespace android

View File

@ -177,4 +177,37 @@ TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) {
ASSERT_EQ(TEST_DATASPACE, dataSpace); 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 { 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 { 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[5] = usage;
buf[6] = static_cast<int32_t>(mId >> 32); buf[6] = static_cast<int32_t>(mId >> 32);
buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull); buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
buf[8] = 0; buf[8] = static_cast<int32_t>(mGenerationNumber);
buf[9] = 0; buf[9] = 0;
buf[10] = 0;
if (handle) { if (handle) {
buf[8] = handle->numFds; buf[9] = handle->numFds;
buf[9] = handle->numInts; buf[10] = handle->numInts;
memcpy(fds, handle->data, memcpy(fds, handle->data,
static_cast<size_t>(handle->numFds) * sizeof(int)); 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)); 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( status_t GraphicBuffer::unflatten(
void const*& buffer, size_t& size, int const*& fds, size_t& count) { 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); int const* buf = static_cast<int const*>(buffer);
if (buf[0] != 'GBFR') return BAD_TYPE; if (buf[0] != 'GBFR') return BAD_TYPE;
const size_t numFds = static_cast<size_t>(buf[8]); const size_t numFds = static_cast<size_t>(buf[9]);
const size_t numInts = 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 // 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 // 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 // chosen to be high enough to not cause issues and low enough to prevent
// overflow problems. // overflow problems.
const size_t maxNumber = 4096; const size_t maxNumber = 4096;
if (numFds >= maxNumber || numInts >= (maxNumber - 10)) { if (numFds >= maxNumber || numInts >= (maxNumber - 11)) {
width = height = stride = format = usage = 0; width = height = stride = format = usage = 0;
handle = NULL; handle = NULL;
ALOGE("unflatten: numFds or numInts is too large: %zd, %zd", ALOGE("unflatten: numFds or numInts is too large: %zd, %zd",
@ -346,7 +347,7 @@ status_t GraphicBuffer::unflatten(
return BAD_VALUE; 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; if (size < sizeNeeded) return NO_MEMORY;
size_t fdCountNeeded = numFds; size_t fdCountNeeded = numFds;
@ -372,7 +373,7 @@ status_t GraphicBuffer::unflatten(
return NO_MEMORY; return NO_MEMORY;
} }
memcpy(h->data, fds, numFds * sizeof(int)); 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; handle = h;
} else { } else {
width = height = stride = format = usage = 0; 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<uint64_t>(buf[6]) << 32;
mId |= static_cast<uint32_t>(buf[7]); mId |= static_cast<uint32_t>(buf[7]);
mGenerationNumber = static_cast<uint32_t>(buf[8]);
mOwner = ownHandle; mOwner = ownHandle;
if (handle != 0) { if (handle != 0) {

View File

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

View File

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

View File

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

View File

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