summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/gui/Android.mk1
-rw-r--r--libs/gui/ISurface.cpp47
-rw-r--r--libs/gui/ISurfaceComposer.cpp25
-rw-r--r--libs/gui/ISurfaceComposerClient.cpp56
-rw-r--r--libs/gui/SharedBufferStack.cpp714
-rw-r--r--libs/gui/Surface.cpp634
-rw-r--r--libs/gui/SurfaceComposerClient.cpp17
-rw-r--r--libs/gui/SurfaceTexture.cpp12
-rw-r--r--libs/gui/SurfaceTextureClient.cpp6
-rw-r--r--libs/gui/tests/SurfaceTextureClient_test.cpp114
-rw-r--r--libs/gui/tests/SurfaceTexture_test.cpp283
-rw-r--r--libs/utils/Android.mk1
-rw-r--r--libs/utils/BlobCache.cpp232
-rw-r--r--libs/utils/RefBase.cpp34
-rw-r--r--libs/utils/Threads.cpp13
-rw-r--r--libs/utils/tests/Android.mk1
-rw-r--r--libs/utils/tests/BlobCache_test.cpp257
17 files changed, 1005 insertions, 1442 deletions
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index b5737ff..4070eba 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -16,7 +16,6 @@ LOCAL_SRC_FILES:= \
ISurfaceComposerClient.cpp \
IGraphicBufferAlloc.cpp \
LayerState.cpp \
- SharedBufferStack.cpp \
Surface.cpp \
SurfaceComposerClient.cpp \
diff --git a/libs/gui/ISurface.cpp b/libs/gui/ISurface.cpp
index 23b90af..96155d7 100644
--- a/libs/gui/ISurface.cpp
+++ b/libs/gui/ISurface.cpp
@@ -22,9 +22,7 @@
#include <binder/Parcel.h>
-#include <ui/GraphicBuffer.h>
-
-#include <surfaceflinger/Surface.h>
+#include <gui/ISurfaceTexture.h>
#include <surfaceflinger/ISurface.h>
namespace android {
@@ -39,30 +37,11 @@ public:
{
}
- virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
- uint32_t w, uint32_t h, uint32_t format, uint32_t usage)
- {
+ virtual sp<ISurfaceTexture> getSurfaceTexture() const {
Parcel data, reply;
data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
- data.writeInt32(bufferIdx);
- data.writeInt32(w);
- data.writeInt32(h);
- data.writeInt32(format);
- data.writeInt32(usage);
- remote()->transact(REQUEST_BUFFER, data, &reply);
- sp<GraphicBuffer> buffer = new GraphicBuffer();
- reply.read(*buffer);
- return buffer;
- }
-
- virtual status_t setBufferCount(int bufferCount)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
- data.writeInt32(bufferCount);
- remote()->transact(SET_BUFFER_COUNT, data, &reply);
- status_t err = reply.readInt32();
- return err;
+ remote()->transact(GET_SURFACE_TEXTURE, data, &reply);
+ return interface_cast<ISurfaceTexture>(reply.readStrongBinder());
}
};
@@ -74,23 +53,9 @@ status_t BnSurface::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
- case REQUEST_BUFFER: {
- CHECK_INTERFACE(ISurface, data, reply);
- int bufferIdx = data.readInt32();
- uint32_t w = data.readInt32();
- uint32_t h = data.readInt32();
- uint32_t format = data.readInt32();
- uint32_t usage = data.readInt32();
- sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format, usage));
- if (buffer == NULL)
- return BAD_VALUE;
- return reply->write(*buffer);
- }
- case SET_BUFFER_COUNT: {
+ case GET_SURFACE_TEXTURE: {
CHECK_INTERFACE(ISurface, data, reply);
- int bufferCount = data.readInt32();
- status_t err = setBufferCount(bufferCount);
- reply->writeInt32(err);
+ reply->writeStrongBinder( getSurfaceTexture()->asBinder() );
return NO_ERROR;
}
default:
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 8951c3f..40450a3 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -57,15 +57,6 @@ public:
return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
- virtual sp<ISurfaceComposerClient> createClientConnection()
- {
- uint32_t n;
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- remote()->transact(BnSurfaceComposer::CREATE_CLIENT_CONNECTION, data, &reply);
- return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
- }
-
virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc()
{
uint32_t n;
@@ -174,13 +165,6 @@ public:
return reply.readInt32();
}
- virtual void signal() const
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- remote()->transact(BnSurfaceComposer::SIGNAL, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
virtual bool authenticateSurface(const sp<ISurface>& surface) const
{
Parcel data, reply;
@@ -229,11 +213,6 @@ status_t BnSurfaceComposer::onTransact(
sp<IBinder> b = createConnection()->asBinder();
reply->writeStrongBinder(b);
} break;
- case CREATE_CLIENT_CONNECTION: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> b = createClientConnection()->asBinder();
- reply->writeStrongBinder(b);
- } break;
case CREATE_GRAPHIC_BUFFER_ALLOC: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> b = createGraphicBufferAlloc()->asBinder();
@@ -270,10 +249,6 @@ status_t BnSurfaceComposer::onTransact(
CHECK_INTERFACE(ISurfaceComposer, data, reply);
bootFinished();
} break;
- case SIGNAL: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- signal();
- } break;
case GET_CBLK: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> b = getCblk()->asBinder();
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index ea38e08..8d83392 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -50,9 +50,7 @@
namespace android {
enum {
- GET_CBLK = IBinder::FIRST_CALL_TRANSACTION,
- GET_TOKEN,
- CREATE_SURFACE,
+ CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
DESTROY_SURFACE,
SET_STATE
};
@@ -65,23 +63,6 @@ public:
{
}
- virtual sp<IMemoryHeap> getControlBlock() const
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- remote()->transact(GET_CBLK, data, &reply);
- return interface_cast<IMemoryHeap>(reply.readStrongBinder());
- }
-
- virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- data.writeStrongBinder(sur->asBinder());
- remote()->transact(GET_TOKEN, data, &reply);
- return reply.readInt32();
- }
-
virtual sp<ISurface> createSurface( surface_data_t* params,
const String8& name,
DisplayID display,
@@ -131,41 +112,6 @@ IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClie
status_t BnSurfaceComposerClient::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
- // codes that don't require permission check
-
- switch(code) {
- case GET_CBLK: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- sp<IMemoryHeap> ctl(getControlBlock());
- reply->writeStrongBinder(ctl->asBinder());
- return NO_ERROR;
- } break;
- case GET_TOKEN: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- sp<ISurface> sur = interface_cast<ISurface>(data.readStrongBinder());
- ssize_t token = getTokenForSurface(sur);
- reply->writeInt32(token);
- return NO_ERROR;
- } break;
- }
-
- // these must be checked
-
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- const int self_pid = getpid();
- if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) {
- // we're called from a different process, do the real check
- if (!checkCallingPermission(
- String16("android.permission.ACCESS_SURFACE_FLINGER")))
- {
- LOGE("Permission Denial: "
- "can't openGlobalTransaction pid=%d, uid=%d", pid, uid);
- return PERMISSION_DENIED;
- }
- }
-
switch(code) {
case CREATE_SURFACE: {
CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
diff --git a/libs/gui/SharedBufferStack.cpp b/libs/gui/SharedBufferStack.cpp
deleted file mode 100644
index 7505d53..0000000
--- a/libs/gui/SharedBufferStack.cpp
+++ /dev/null
@@ -1,714 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SharedBufferStack"
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Debug.h>
-#include <utils/Log.h>
-#include <utils/threads.h>
-
-#include <private/surfaceflinger/SharedBufferStack.h>
-
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
-#define DEBUG_ATOMICS 0
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-SharedClient::SharedClient()
- : lock(Mutex::SHARED), cv(Condition::SHARED)
-{
-}
-
-SharedClient::~SharedClient() {
-}
-
-
-// these functions are used by the clients
-status_t SharedClient::validate(size_t i) const {
- if (uint32_t(i) >= uint32_t(SharedBufferStack::NUM_LAYERS_MAX))
- return BAD_INDEX;
- return surfaces[i].status;
-}
-
-// ----------------------------------------------------------------------------
-
-
-SharedBufferStack::SharedBufferStack()
-{
-}
-
-void SharedBufferStack::init(int32_t i)
-{
- status = NO_ERROR;
- identity = i;
-}
-
-status_t SharedBufferStack::setCrop(int buffer, const Rect& crop)
-{
- if (uint32_t(buffer) >= NUM_BUFFER_MAX)
- return BAD_INDEX;
-
- buffers[buffer].crop.l = uint16_t(crop.left);
- buffers[buffer].crop.t = uint16_t(crop.top);
- buffers[buffer].crop.r = uint16_t(crop.right);
- buffers[buffer].crop.b = uint16_t(crop.bottom);
- return NO_ERROR;
-}
-
-status_t SharedBufferStack::setTransform(int buffer, uint8_t transform)
-{
- if (uint32_t(buffer) >= NUM_BUFFER_MAX)
- return BAD_INDEX;
- buffers[buffer].transform = transform;
- return NO_ERROR;
-}
-
-status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty)
-{
- if (uint32_t(buffer) >= NUM_BUFFER_MAX)
- return BAD_INDEX;
-
- FlatRegion& reg(buffers[buffer].dirtyRegion);
- if (dirty.isEmpty()) {
- reg.count = 0;
- return NO_ERROR;
- }
-
- size_t count;
- Rect const* r = dirty.getArray(&count);
- if (count > FlatRegion::NUM_RECT_MAX) {
- const Rect bounds(dirty.getBounds());
- reg.count = 1;
- reg.rects[0].l = uint16_t(bounds.left);
- reg.rects[0].t = uint16_t(bounds.top);
- reg.rects[0].r = uint16_t(bounds.right);
- reg.rects[0].b = uint16_t(bounds.bottom);
- } else {
- reg.count = count;
- for (size_t i=0 ; i<count ; i++) {
- reg.rects[i].l = uint16_t(r[i].left);
- reg.rects[i].t = uint16_t(r[i].top);
- reg.rects[i].r = uint16_t(r[i].right);
- reg.rects[i].b = uint16_t(r[i].bottom);
- }
- }
- return NO_ERROR;
-}
-
-Region SharedBufferStack::getDirtyRegion(int buffer) const
-{
- Region res;
- if (uint32_t(buffer) >= NUM_BUFFER_MAX)
- return res;
-
- const FlatRegion& reg(buffers[buffer].dirtyRegion);
- if (reg.count > FlatRegion::NUM_RECT_MAX)
- return res;
-
- if (reg.count == 1) {
- const Rect r(
- reg.rects[0].l,
- reg.rects[0].t,
- reg.rects[0].r,
- reg.rects[0].b);
- res.set(r);
- } else {
- for (size_t i=0 ; i<reg.count ; i++) {
- const Rect r(
- reg.rects[i].l,
- reg.rects[i].t,
- reg.rects[i].r,
- reg.rects[i].b);
- res.orSelf(r);
- }
- }
- return res;
-}
-
-Rect SharedBufferStack::getCrop(int buffer) const
-{
- Rect res(-1, -1);
- if (uint32_t(buffer) >= NUM_BUFFER_MAX)
- return res;
- res.left = buffers[buffer].crop.l;
- res.top = buffers[buffer].crop.t;
- res.right = buffers[buffer].crop.r;
- res.bottom = buffers[buffer].crop.b;
- return res;
-}
-
-uint32_t SharedBufferStack::getTransform(int buffer) const
-{
- if (uint32_t(buffer) >= NUM_BUFFER_MAX)
- return 0;
- return buffers[buffer].transform;
-}
-
-
-// ----------------------------------------------------------------------------
-
-SharedBufferBase::SharedBufferBase(SharedClient* sharedClient,
- int surface, int32_t identity)
- : mSharedClient(sharedClient),
- mSharedStack(sharedClient->surfaces + surface),
- mIdentity(identity)
-{
-}
-
-SharedBufferBase::~SharedBufferBase()
-{
-}
-
-status_t SharedBufferBase::getStatus() const
-{
- SharedBufferStack& stack( *mSharedStack );
- return stack.status;
-}
-
-int32_t SharedBufferBase::getIdentity() const
-{
- SharedBufferStack& stack( *mSharedStack );
- return stack.identity;
-}
-
-String8 SharedBufferBase::dump(char const* prefix) const
-{
- const size_t SIZE = 1024;
- char buffer[SIZE];
- String8 result;
- SharedBufferStack& stack( *mSharedStack );
- snprintf(buffer, SIZE,
- "%s[ head=%2d, available=%2d, queued=%2d ] "
- "reallocMask=%08x, identity=%d, status=%d",
- prefix, stack.head, stack.available, stack.queued,
- stack.reallocMask, stack.identity, stack.status);
- result.append(buffer);
- result.append("\n");
- return result;
-}
-
-status_t SharedBufferBase::waitForCondition(const ConditionBase& condition)
-{
- const SharedBufferStack& stack( *mSharedStack );
- SharedClient& client( *mSharedClient );
- const nsecs_t TIMEOUT = s2ns(1);
- const int identity = mIdentity;
-
- Mutex::Autolock _l(client.lock);
- while ((condition()==false) &&
- (stack.identity == identity) &&
- (stack.status == NO_ERROR))
- {
- status_t err = client.cv.waitRelative(client.lock, TIMEOUT);
- // handle errors and timeouts
- if (CC_UNLIKELY(err != NO_ERROR)) {
- if (err == TIMED_OUT) {
- if (condition()) {
- LOGE("waitForCondition(%s) timed out (identity=%d), "
- "but condition is true! We recovered but it "
- "shouldn't happen." , condition.name(), stack.identity);
- break;
- } else {
- LOGW("waitForCondition(%s) timed out "
- "(identity=%d, status=%d). "
- "CPU may be pegged. trying again.", condition.name(),
- stack.identity, stack.status);
- }
- } else {
- LOGE("waitForCondition(%s) error (%s) ",
- condition.name(), strerror(-err));
- return err;
- }
- }
- }
- return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status;
-}
-// ============================================================================
-// conditions and updates
-// ============================================================================
-
-SharedBufferClient::DequeueCondition::DequeueCondition(
- SharedBufferClient* sbc) : ConditionBase(sbc) {
-}
-bool SharedBufferClient::DequeueCondition::operator()() const {
- return stack.available > 0;
-}
-
-SharedBufferClient::LockCondition::LockCondition(
- SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) {
-}
-bool SharedBufferClient::LockCondition::operator()() const {
- // NOTE: if stack.head is messed up, we could crash the client
- // or cause some drawing artifacts. This is okay, as long as it is
- // limited to the client.
- return (buf != stack.index[stack.head]);
-}
-
-SharedBufferServer::BuffersAvailableCondition::BuffersAvailableCondition(
- SharedBufferServer* sbs, int numBuffers) : ConditionBase(sbs),
- mNumBuffers(numBuffers) {
-}
-bool SharedBufferServer::BuffersAvailableCondition::operator()() const {
- return stack.available == mNumBuffers;
-}
-
-// ----------------------------------------------------------------------------
-
-SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb)
- : UpdateBase(sbb) {
-}
-ssize_t SharedBufferClient::QueueUpdate::operator()() {
- android_atomic_inc(&stack.queued);
- return NO_ERROR;
-}
-
-SharedBufferClient::DequeueUpdate::DequeueUpdate(SharedBufferBase* sbb)
- : UpdateBase(sbb) {
-}
-ssize_t SharedBufferClient::DequeueUpdate::operator()() {
- if (android_atomic_dec(&stack.available) == 0) {
- LOGW("dequeue probably called from multiple threads!");
- }
- return NO_ERROR;
-}
-
-SharedBufferClient::CancelUpdate::CancelUpdate(SharedBufferBase* sbb,
- int tail, int buf)
- : UpdateBase(sbb), tail(tail), buf(buf) {
-}
-ssize_t SharedBufferClient::CancelUpdate::operator()() {
- stack.index[tail] = buf;
- android_atomic_inc(&stack.available);
- return NO_ERROR;
-}
-
-SharedBufferServer::RetireUpdate::RetireUpdate(
- SharedBufferBase* sbb, int numBuffers)
- : UpdateBase(sbb), numBuffers(numBuffers) {
-}
-ssize_t SharedBufferServer::RetireUpdate::operator()() {
- int32_t head = stack.head;
- if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX)
- return BAD_VALUE;
-
- // Decrement the number of queued buffers
- int32_t queued;
- do {
- queued = stack.queued;
- if (queued == 0) {
- return NOT_ENOUGH_DATA;
- }
- } while (android_atomic_cmpxchg(queued, queued-1, &stack.queued));
-
- // lock the buffer before advancing head, which automatically unlocks
- // the buffer we preventively locked upon entering this function
-
- head = (head + 1) % numBuffers;
- const int8_t headBuf = stack.index[head];
- stack.headBuf = headBuf;
-
- // head is only modified here, so we don't need to use cmpxchg
- android_atomic_write(head, &stack.head);
-
- // now that head has moved, we can increment the number of available buffers
- android_atomic_inc(&stack.available);
- return head;
-}
-
-SharedBufferServer::StatusUpdate::StatusUpdate(
- SharedBufferBase* sbb, status_t status)
- : UpdateBase(sbb), status(status) {
-}
-
-ssize_t SharedBufferServer::StatusUpdate::operator()() {
- android_atomic_write(status, &stack.status);
- return NO_ERROR;
-}
-
-// ============================================================================
-
-SharedBufferClient::SharedBufferClient(SharedClient* sharedClient,
- int surface, int num, int32_t identity)
- : SharedBufferBase(sharedClient, surface, identity),
- mNumBuffers(num), tail(0)
-{
- SharedBufferStack& stack( *mSharedStack );
- tail = computeTail();
- queued_head = stack.head;
-}
-
-int32_t SharedBufferClient::computeTail() const
-{
- SharedBufferStack& stack( *mSharedStack );
- return (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers;
-}
-
-ssize_t SharedBufferClient::dequeue()
-{
- SharedBufferStack& stack( *mSharedStack );
-
- RWLock::AutoRLock _rd(mLock);
-
- const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD);
-
- //LOGD("[%d] about to dequeue a buffer",
- // mSharedStack->identity);
- DequeueCondition condition(this);
- status_t err = waitForCondition(condition);
- if (err != NO_ERROR)
- return ssize_t(err);
-
- DequeueUpdate update(this);
- updateCondition( update );
-
- int dequeued = stack.index[tail];
- tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1);
- LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail++=%d, %s",
- dequeued, tail, dump("").string());
-
- mDequeueTime[dequeued] = dequeueTime;
-
- return dequeued;
-}
-
-status_t SharedBufferClient::undoDequeue(int buf)
-{
- return cancel(buf);
-}
-
-status_t SharedBufferClient::cancel(int buf)
-{
- RWLock::AutoRLock _rd(mLock);
-
- // calculate the new position of the tail index (essentially tail--)
- int localTail = (tail + mNumBuffers - 1) % mNumBuffers;
- CancelUpdate update(this, localTail, buf);
- status_t err = updateCondition( update );
- if (err == NO_ERROR) {
- tail = localTail;
- }
- return err;
-}
-
-status_t SharedBufferClient::lock(int buf)
-{
- RWLock::AutoRLock _rd(mLock);
-
- SharedBufferStack& stack( *mSharedStack );
- LockCondition condition(this, buf);
- status_t err = waitForCondition(condition);
- return err;
-}
-
-status_t SharedBufferClient::queue(int buf)
-{
- RWLock::AutoRLock _rd(mLock);
-
- SharedBufferStack& stack( *mSharedStack );
-
- queued_head = (queued_head + 1) % mNumBuffers;
- stack.index[queued_head] = buf;
-
- QueueUpdate update(this);
- status_t err = updateCondition( update );
- LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string());
-
- const nsecs_t now = systemTime(SYSTEM_TIME_THREAD);
- stack.stats.totalTime = ns2us(now - mDequeueTime[buf]);
-
- return err;
-}
-
-bool SharedBufferClient::needNewBuffer(int buf) const
-{
- SharedBufferStack& stack( *mSharedStack );
- const uint32_t mask = 1<<(31-buf);
- return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0;
-}
-
-status_t SharedBufferClient::setCrop(int buf, const Rect& crop)
-{
- SharedBufferStack& stack( *mSharedStack );
- return stack.setCrop(buf, crop);
-}
-
-status_t SharedBufferClient::setTransform(int buf, uint32_t transform)
-{
- SharedBufferStack& stack( *mSharedStack );
- return stack.setTransform(buf, uint8_t(transform));
-}
-
-status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg)
-{
- SharedBufferStack& stack( *mSharedStack );
- return stack.setDirtyRegion(buf, reg);
-}
-
-status_t SharedBufferClient::setBufferCount(
- int bufferCount, const SetBufferCountCallback& ipc)
-{
- SharedBufferStack& stack( *mSharedStack );
- if (uint32_t(bufferCount) >= SharedBufferStack::NUM_BUFFER_MAX)
- return BAD_VALUE;
-
- if (uint32_t(bufferCount) < SharedBufferStack::NUM_BUFFER_MIN)
- return BAD_VALUE;
-
- RWLock::AutoWLock _wr(mLock);
-
- status_t err = ipc(bufferCount);
- if (err == NO_ERROR) {
- mNumBuffers = bufferCount;
- queued_head = (stack.head + stack.queued) % mNumBuffers;
- tail = computeTail();
- }
- return err;
-}
-
-// ----------------------------------------------------------------------------
-
-SharedBufferServer::SharedBufferServer(SharedClient* sharedClient,
- int surface, int num, int32_t identity)
- : SharedBufferBase(sharedClient, surface, identity),
- mNumBuffers(num)
-{
- mSharedStack->init(identity);
- mSharedStack->token = surface;
- mSharedStack->head = num-1;
- mSharedStack->available = num;
- mSharedStack->queued = 0;
- mSharedStack->reallocMask = 0;
- memset(mSharedStack->buffers, 0, sizeof(mSharedStack->buffers));
- for (int i=0 ; i<num ; i++) {
- mBufferList.add(i);
- mSharedStack->index[i] = i;
- }
-}
-
-SharedBufferServer::~SharedBufferServer()
-{
-}
-
-ssize_t SharedBufferServer::retireAndLock()
-{
- RWLock::AutoRLock _l(mLock);
-
- RetireUpdate update(this, mNumBuffers);
- ssize_t buf = updateCondition( update );
- if (buf >= 0) {
- if (uint32_t(buf) >= SharedBufferStack::NUM_BUFFER_MAX)
- return BAD_VALUE;
- SharedBufferStack& stack( *mSharedStack );
- buf = stack.index[buf];
- LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s",
- int(buf), dump("").string());
- }
- return buf;
-}
-
-void SharedBufferServer::setStatus(status_t status)
-{
- if (status < NO_ERROR) {
- StatusUpdate update(this, status);
- updateCondition( update );
- }
-}
-
-status_t SharedBufferServer::reallocateAll()
-{
- RWLock::AutoRLock _l(mLock);
-
- SharedBufferStack& stack( *mSharedStack );
- uint32_t mask = mBufferList.getMask();
- android_atomic_or(mask, &stack.reallocMask);
- return NO_ERROR;
-}
-
-status_t SharedBufferServer::reallocateAllExcept(int buffer)
-{
- RWLock::AutoRLock _l(mLock);
-
- SharedBufferStack& stack( *mSharedStack );
- BufferList temp(mBufferList);
- temp.remove(buffer);
- uint32_t mask = temp.getMask();
- android_atomic_or(mask, &stack.reallocMask);
- return NO_ERROR;
-}
-
-int32_t SharedBufferServer::getQueuedCount() const
-{
- SharedBufferStack& stack( *mSharedStack );
- return stack.queued;
-}
-
-Region SharedBufferServer::getDirtyRegion(int buf) const
-{
- SharedBufferStack& stack( *mSharedStack );
- return stack.getDirtyRegion(buf);
-}
-
-Rect SharedBufferServer::getCrop(int buf) const
-{
- SharedBufferStack& stack( *mSharedStack );
- return stack.getCrop(buf);
-}
-
-uint32_t SharedBufferServer::getTransform(int buf) const
-{
- SharedBufferStack& stack( *mSharedStack );
- return stack.getTransform(buf);
-}
-
-/*
- * NOTE: this is not thread-safe on the server-side, meaning
- * 'head' cannot move during this operation. The client-side
- * can safely operate an usual.
- *
- */
-status_t SharedBufferServer::resize(int newNumBuffers)
-{
- if ((unsigned int)(newNumBuffers) < SharedBufferStack::NUM_BUFFER_MIN ||
- (unsigned int)(newNumBuffers) > SharedBufferStack::NUM_BUFFER_MAX) {
- return BAD_VALUE;
- }
-
- RWLock::AutoWLock _l(mLock);
-
- if (newNumBuffers < mNumBuffers) {
- return shrink(newNumBuffers);
- } else {
- return grow(newNumBuffers);
- }
-}
-
-status_t SharedBufferServer::grow(int newNumBuffers)
-{
- SharedBufferStack& stack( *mSharedStack );
- const int numBuffers = mNumBuffers;
- const int extra = newNumBuffers - numBuffers;
-
- // read the head, make sure it's valid
- int32_t head = stack.head;
- if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX)
- return BAD_VALUE;
-
- int base = numBuffers;
- int32_t avail = stack.available;
- int tail = head - avail + 1;
-
- if (tail >= 0) {
- int8_t* const index = const_cast<int8_t*>(stack.index);
- const int nb = numBuffers - head;
- memmove(&index[head + extra], &index[head], nb);
- base = head;
- // move head 'extra' ahead, this doesn't impact stack.index[head];
- stack.head = head + extra;
- }
- stack.available += extra;
-
- // fill the new free space with unused buffers
- BufferList::const_iterator curr(mBufferList.free_begin());
- for (int i=0 ; i<extra ; i++) {
- stack.index[base+i] = *curr;
- mBufferList.add(*curr);
- ++curr;
- }
-
- mNumBuffers = newNumBuffers;
- return NO_ERROR;
-}
-
-status_t SharedBufferServer::shrink(int newNumBuffers)
-{
- SharedBufferStack& stack( *mSharedStack );
-
- // Shrinking is only supported if there are no buffers currently dequeued.
- int32_t avail = stack.available;
- int32_t queued = stack.queued;
- if (avail + queued != mNumBuffers) {
- return INVALID_OPERATION;
- }
-
- // Wait for any queued buffers to be displayed.
- BuffersAvailableCondition condition(this, mNumBuffers);
- status_t err = waitForCondition(condition);
- if (err < 0) {
- return err;
- }
-
- // Reset head to index 0 and make it refer to buffer 0. The same renaming
- // (head -> 0) is done in the BufferManager.
- int32_t head = stack.head;
- int8_t* index = const_cast<int8_t*>(stack.index);
- for (int8_t i = 0; i < newNumBuffers; i++) {
- index[i] = i;
- }
- stack.head = 0;
- stack.headBuf = 0;
-
- // Free the buffers from the end of the list that are no longer needed.
- for (int i = newNumBuffers; i < mNumBuffers; i++) {
- mBufferList.remove(i);
- }
-
- // Tell the client to reallocate all the buffers.
- reallocateAll();
-
- mNumBuffers = newNumBuffers;
- stack.available = newNumBuffers;
-
- return NO_ERROR;
-}
-
-SharedBufferStack::Statistics SharedBufferServer::getStats() const
-{
- SharedBufferStack& stack( *mSharedStack );
- return stack.stats;
-}
-
-// ---------------------------------------------------------------------------
-status_t SharedBufferServer::BufferList::add(int value)
-{
- if (uint32_t(value) >= mCapacity)
- return BAD_VALUE;
- uint32_t mask = 1<<(31-value);
- if (mList & mask)
- return ALREADY_EXISTS;
- mList |= mask;
- return NO_ERROR;
-}
-
-status_t SharedBufferServer::BufferList::remove(int value)
-{
- if (uint32_t(value) >= mCapacity)
- return BAD_VALUE;
- uint32_t mask = 1<<(31-value);
- if (!(mList & mask))
- return NAME_NOT_FOUND;
- mList &= ~mask;
- return NO_ERROR;
-}
-
-
-// ---------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 0c5767b..4d1d923 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -21,13 +21,15 @@
#include <sys/types.h>
#include <sys/stat.h>
-#include <utils/Errors.h>
-#include <utils/threads.h>
#include <utils/CallStack.h>
+#include <utils/Errors.h>
#include <utils/Log.h>
+#include <utils/threads.h>
-#include <binder/IPCThreadState.h>
#include <binder/IMemory.h>
+#include <binder/IPCThreadState.h>
+
+#include <gui/SurfaceTextureClient.h>
#include <ui/DisplayInfo.h>
#include <ui/GraphicBuffer.h>
@@ -35,12 +37,11 @@
#include <ui/GraphicLog.h>
#include <ui/Rect.h>
-#include <surfaceflinger/Surface.h>
#include <surfaceflinger/ISurface.h>
#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/Surface.h>
#include <surfaceflinger/SurfaceComposerClient.h>
-#include <private/surfaceflinger/SharedBufferStack.h>
#include <private/surfaceflinger/LayerState.h>
namespace android {
@@ -273,58 +274,10 @@ sp<Surface> SurfaceControl::getSurface() const
// Surface
// ============================================================================
-class SurfaceClient : public Singleton<SurfaceClient>
-{
- // all these attributes are constants
- sp<ISurfaceComposer> mComposerService;
- sp<ISurfaceComposerClient> mClient;
- status_t mStatus;
- SharedClient* mControl;
- sp<IMemoryHeap> mControlMemory;
-
- SurfaceClient()
- : Singleton<SurfaceClient>(), mStatus(NO_INIT)
- {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- mComposerService = sf;
- mClient = sf->createClientConnection();
- if (mClient != NULL) {
- mControlMemory = mClient->getControlBlock();
- if (mControlMemory != NULL) {
- mControl = static_cast<SharedClient *>(
- mControlMemory->getBase());
- if (mControl) {
- mStatus = NO_ERROR;
- }
- }
- }
- }
- friend class Singleton<SurfaceClient>;
-public:
- status_t initCheck() const {
- return mStatus;
- }
- SharedClient* getSharedClient() const {
- return mControl;
- }
- ssize_t getTokenForSurface(const sp<ISurface>& sur) const {
- // TODO: we could cache a few tokens here to avoid an IPC
- return mClient->getTokenForSurface(sur);
- }
- void signalServer() const {
- mComposerService->signal();
- }
-};
-
-ANDROID_SINGLETON_STATIC_INSTANCE(SurfaceClient);
-
// ---------------------------------------------------------------------------
Surface::Surface(const sp<SurfaceControl>& surface)
- : mBufferMapper(GraphicBufferMapper::get()),
- mClient(SurfaceClient::getInstance()),
- mSharedBufferClient(NULL),
- mInitCheck(NO_INIT),
+ : mInitCheck(NO_INIT),
mSurface(surface->mSurface),
mIdentity(surface->mIdentity),
mFormat(surface->mFormat), mFlags(surface->mFlags),
@@ -334,10 +287,7 @@ Surface::Surface(const sp<SurfaceControl>& surface)
}
Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
- : mBufferMapper(GraphicBufferMapper::get()),
- mClient(SurfaceClient::getInstance()),
- mSharedBufferClient(NULL),
- mInitCheck(NO_INIT)
+ : mInitCheck(NO_INIT)
{
mSurface = interface_cast<ISurface>(ref);
mIdentity = parcel.readInt32();
@@ -382,7 +332,6 @@ status_t Surface::writeToParcel(
}
-
Mutex Surface::sCachedSurfacesLock;
DefaultKeyedVector<wp<IBinder>, wp<Surface> > Surface::sCachedSurfaces;
@@ -422,32 +371,29 @@ void Surface::init()
ANativeWindow::query = query;
ANativeWindow::perform = perform;
- DisplayInfo dinfo;
- SurfaceComposerClient::getDisplayInfo(0, &dinfo);
- const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi;
- const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi;
- // FIXME: set real values here
- const_cast<int&>(ANativeWindow::minSwapInterval) = 1;
- const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
- const_cast<uint32_t&>(ANativeWindow::flags) = 0;
-
- mNextBufferTransform = 0;
- mConnected = 0;
- mSwapRectangle.makeInvalid();
- mNextBufferCrop = Rect(0,0);
- // two buffers by default
- mBuffers.setCapacity(2);
- mBuffers.insertAt(0, 2);
-
- if (mSurface != 0 && mClient.initCheck() == NO_ERROR) {
- int32_t token = mClient.getTokenForSurface(mSurface);
- if (token >= 0) {
- mSharedBufferClient = new SharedBufferClient(
- mClient.getSharedClient(), token, 2, mIdentity);
- mInitCheck = mClient.getSharedClient()->validate(token);
- } else {
- LOGW("Not initializing the shared buffer client because token = %d",
- token);
+ if (mSurface != NULL) {
+ sp<ISurfaceTexture> surfaceTexture(mSurface->getSurfaceTexture());
+ LOGE_IF(surfaceTexture==0, "got a NULL ISurfaceTexture from ISurface");
+ if (surfaceTexture != NULL) {
+ mSurfaceTextureClient = new SurfaceTextureClient(surfaceTexture);
+ mSurfaceTextureClient->setUsage(GraphicBuffer::USAGE_HW_RENDER);
+ }
+
+ DisplayInfo dinfo;
+ SurfaceComposerClient::getDisplayInfo(0, &dinfo);
+ const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi;
+ const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi;
+
+ const_cast<int&>(ANativeWindow::minSwapInterval) =
+ mSurfaceTextureClient->minSwapInterval;
+
+ const_cast<int&>(ANativeWindow::maxSwapInterval) =
+ mSurfaceTextureClient->maxSwapInterval;
+
+ const_cast<uint32_t&>(ANativeWindow::flags) = 0;
+
+ if (mSurfaceTextureClient != 0) {
+ mInitCheck = NO_ERROR;
}
}
}
@@ -456,9 +402,8 @@ Surface::~Surface()
{
// clear all references and trigger an IPC now, to make sure things
// happen without delay, since these resources are quite heavy.
- mBuffers.clear();
+ mSurfaceTextureClient.clear();
mSurface.clear();
- delete mSharedBufferClient;
IPCThreadState::self()->flushCommands();
}
@@ -473,32 +418,6 @@ status_t Surface::validate(bool inCancelBuffer) const
LOGE("invalid token (identity=%u)", mIdentity);
return mInitCheck;
}
-
- // verify the identity of this surface
- uint32_t identity = mSharedBufferClient->getIdentity();
- if (mIdentity != identity) {
- LOGE("[Surface] using an invalid surface, "
- "identity=%u should be %d",
- mIdentity, identity);
- CallStack stack;
- stack.update();
- stack.dump("Surface");
- return BAD_INDEX;
- }
-
- // check the surface didn't become invalid
- status_t err = mSharedBufferClient->getStatus();
- if (err != NO_ERROR) {
- if (!inCancelBuffer) {
- LOGE("surface (identity=%u) is invalid, err=%d (%s)",
- mIdentity, err, strerror(-err));
- CallStack stack;
- stack.update();
- stack.dump("Surface");
- }
- return err;
- }
-
return NO_ERROR;
}
@@ -509,7 +428,8 @@ sp<IBinder> Surface::asBinder() const {
// ----------------------------------------------------------------------------
int Surface::setSwapInterval(ANativeWindow* window, int interval) {
- return 0;
+ Surface* self = getSelf(window);
+ return self->setSwapInterval(interval);
}
int Surface::dequeueBuffer(ANativeWindow* window,
@@ -554,383 +474,52 @@ int Surface::perform(ANativeWindow* window,
// ----------------------------------------------------------------------------
-bool Surface::needNewBuffer(int bufIdx,
- uint32_t *pWidth, uint32_t *pHeight,
- uint32_t *pFormat, uint32_t *pUsage) const
-{
- Mutex::Autolock _l(mSurfaceLock);
-
- // Always call needNewBuffer(), since it clears the needed buffers flags
- bool needNewBuffer = mSharedBufferClient->needNewBuffer(bufIdx);
- bool validBuffer = mBufferInfo.validateBuffer(mBuffers[bufIdx]);
- bool newNeewBuffer = needNewBuffer || !validBuffer;
- if (newNeewBuffer) {
- mBufferInfo.get(pWidth, pHeight, pFormat, pUsage);
- }
- return newNeewBuffer;
+int Surface::setSwapInterval(int interval) {
+ return mSurfaceTextureClient->setSwapInterval(interval);
}
-int Surface::dequeueBuffer(ANativeWindowBuffer** buffer)
-{
- status_t err = validate();
- if (err != NO_ERROR)
- return err;
-
- GraphicLog& logger(GraphicLog::getInstance());
- logger.log(GraphicLog::SF_APP_DEQUEUE_BEFORE, mIdentity, -1);
-
- ssize_t bufIdx = mSharedBufferClient->dequeue();
-
- logger.log(GraphicLog::SF_APP_DEQUEUE_AFTER, mIdentity, bufIdx);
-
- if (bufIdx < 0) {
- LOGE("error dequeuing a buffer (%s)", strerror(bufIdx));
- return bufIdx;
- }
-
- // grow the buffer array if needed
- const size_t size = mBuffers.size();
- const size_t needed = bufIdx+1;
- if (size < needed) {
- mBuffers.insertAt(size, needed-size);
- }
-
- uint32_t w, h, format, usage;
- if (needNewBuffer(bufIdx, &w, &h, &format, &usage)) {
- err = getBufferLocked(bufIdx, w, h, format, usage);
- LOGE_IF(err, "getBufferLocked(%ld, %u, %u, %u, %08x) failed (%s)",
- bufIdx, w, h, format, usage, strerror(-err));
- if (err == NO_ERROR) {
- // reset the width/height with the what we get from the buffer
- const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]);
- mWidth = uint32_t(backBuffer->width);
- mHeight = uint32_t(backBuffer->height);
- }
- }
-
- // if we still don't have a buffer here, we probably ran out of memory
- const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]);
- if (!err && backBuffer==0) {
- err = NO_MEMORY;
- }
-
+int Surface::dequeueBuffer(ANativeWindowBuffer** buffer) {
+ status_t err = mSurfaceTextureClient->dequeueBuffer(buffer);
if (err == NO_ERROR) {
- mDirtyRegion.set(backBuffer->width, backBuffer->height);
- *buffer = backBuffer.get();
- } else {
- mSharedBufferClient->undoDequeue(bufIdx);
+ mDirtyRegion.set(buffer[0]->width, buffer[0]->height);
}
-
return err;
}
-int Surface::cancelBuffer(ANativeWindowBuffer* buffer)
-{
- status_t err = validate(true);
- switch (err) {
- case NO_ERROR:
- // no error, common case
- break;
- case BAD_INDEX:
- // legitimate errors here
- return err;
- default:
- // other errors happen because the surface is now invalid,
- // for instance because it has been destroyed. In this case,
- // we just fail silently (canceling a buffer is not technically
- // an error at this point)
- return NO_ERROR;
- }
-
- int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
-
- err = mSharedBufferClient->cancel(bufIdx);
-
- LOGE_IF(err, "error canceling buffer %d (%s)", bufIdx, strerror(-err));
- return err;
+int Surface::cancelBuffer(ANativeWindowBuffer* buffer) {
+ return mSurfaceTextureClient->cancelBuffer(buffer);
}
-
-int Surface::lockBuffer(ANativeWindowBuffer* buffer)
-{
- status_t err = validate();
- if (err != NO_ERROR)
- return err;
-
- int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
-
- GraphicLog& logger(GraphicLog::getInstance());
- logger.log(GraphicLog::SF_APP_LOCK_BEFORE, mIdentity, bufIdx);
-
- err = mSharedBufferClient->lock(bufIdx);
-
- logger.log(GraphicLog::SF_APP_LOCK_AFTER, mIdentity, bufIdx);
-
- LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err));
- return err;
+int Surface::lockBuffer(ANativeWindowBuffer* buffer) {
+ return mSurfaceTextureClient->lockBuffer(buffer);
}
-int Surface::queueBuffer(ANativeWindowBuffer* buffer)
-{
- status_t err = validate();
- if (err != NO_ERROR)
- return err;
-
- if (mSwapRectangle.isValid()) {
- mDirtyRegion.set(mSwapRectangle);
- }
-
- int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
-
- GraphicLog::getInstance().log(GraphicLog::SF_APP_QUEUE, mIdentity, bufIdx);
-
- mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform);
- mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop);
- mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion);
- err = mSharedBufferClient->queue(bufIdx);
- LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err));
-
- if (err == NO_ERROR) {
- // TODO: can we avoid this IPC if we know there is one pending?
- mClient.signalServer();
- }
- return err;
+int Surface::queueBuffer(ANativeWindowBuffer* buffer) {
+ return mSurfaceTextureClient->queueBuffer(buffer);
}
-int Surface::query(int what, int* value) const
-{
+int Surface::query(int what, int* value) const {
switch (what) {
- case NATIVE_WINDOW_WIDTH:
- *value = int(mWidth);
- return NO_ERROR;
- case NATIVE_WINDOW_HEIGHT:
- *value = int(mHeight);
- return NO_ERROR;
- case NATIVE_WINDOW_FORMAT:
- *value = int(mFormat);
+ case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
+ // TODO: this is not needed anymore
+ *value = 1;
return NO_ERROR;
- case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
- *value = MIN_UNDEQUEUED_BUFFERS;
- return NO_ERROR;
- case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- *value = sf->authenticateSurface(mSurface) ? 1 : 0;
- return NO_ERROR;
- }
case NATIVE_WINDOW_CONCRETE_TYPE:
+ // TODO: this is not needed anymore
*value = NATIVE_WINDOW_SURFACE;
return NO_ERROR;
}
- return BAD_VALUE;
-}
-
-int Surface::perform(int operation, va_list args)
-{
- status_t err = validate();
- if (err != NO_ERROR)
- return err;
-
- int res = NO_ERROR;
- switch (operation) {
- case NATIVE_WINDOW_SET_USAGE:
- dispatch_setUsage( args );
- break;
- case NATIVE_WINDOW_CONNECT:
- res = dispatch_connect( args );
- break;
- case NATIVE_WINDOW_DISCONNECT:
- res = dispatch_disconnect( args );
- break;
- case NATIVE_WINDOW_SET_CROP:
- res = dispatch_crop( args );
- break;
- case NATIVE_WINDOW_SET_BUFFER_COUNT:
- res = dispatch_set_buffer_count( args );
- break;
- case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
- res = dispatch_set_buffers_geometry( args );
- break;
- case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
- res = dispatch_set_buffers_transform( args );
- break;
- case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
- res = dispatch_set_buffers_timestamp( args );
- break;
- default:
- res = NAME_NOT_FOUND;
- break;
- }
- return res;
-}
-
-void Surface::dispatch_setUsage(va_list args) {
- int usage = va_arg(args, int);
- setUsage( usage );
-}
-int Surface::dispatch_connect(va_list args) {
- int api = va_arg(args, int);
- return connect( api );
-}
-int Surface::dispatch_disconnect(va_list args) {
- int api = va_arg(args, int);
- return disconnect( api );
-}
-int Surface::dispatch_crop(va_list args) {
- android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
- return crop( reinterpret_cast<Rect const*>(rect) );
-}
-int Surface::dispatch_set_buffer_count(va_list args) {
- size_t bufferCount = va_arg(args, size_t);
- return setBufferCount(bufferCount);
-}
-int Surface::dispatch_set_buffers_geometry(va_list args) {
- int w = va_arg(args, int);
- int h = va_arg(args, int);
- int f = va_arg(args, int);
- return setBuffersGeometry(w, h, f);
-}
-
-int Surface::dispatch_set_buffers_transform(va_list args) {
- int transform = va_arg(args, int);
- return setBuffersTransform(transform);
-}
-
-int Surface::dispatch_set_buffers_timestamp(va_list args) {
- int64_t timestamp = va_arg(args, int64_t);
- return setBuffersTimestamp(timestamp);
-}
-
-void Surface::setUsage(uint32_t reqUsage)
-{
- Mutex::Autolock _l(mSurfaceLock);
- mBufferInfo.set(reqUsage);
-}
-
-int Surface::connect(int api)
-{
- Mutex::Autolock _l(mSurfaceLock);
- int err = NO_ERROR;
- switch (api) {
- case NATIVE_WINDOW_API_EGL:
- if (mConnected) {
- err = -EINVAL;
- } else {
- mConnected = api;
- }
- break;
- default:
- err = -EINVAL;
- break;
- }
- return err;
-}
-
-int Surface::disconnect(int api)
-{
- Mutex::Autolock _l(mSurfaceLock);
- int err = NO_ERROR;
- switch (api) {
- case NATIVE_WINDOW_API_EGL:
- if (mConnected == api) {
- mConnected = 0;
- } else {
- err = -EINVAL;
- }
- break;
- default:
- err = -EINVAL;
- break;
- }
- return err;
-}
-
-int Surface::crop(Rect const* rect)
-{
- Mutex::Autolock _l(mSurfaceLock);
- // TODO: validate rect size
-
- if (rect == NULL || rect->isEmpty()) {
- mNextBufferCrop = Rect(0,0);
- } else {
- mNextBufferCrop = *rect;
- }
-
- return NO_ERROR;
+ return mSurfaceTextureClient->query(what, value);
}
-int Surface::setBufferCount(int bufferCount)
-{
- sp<ISurface> s(mSurface);
- if (s == 0) return NO_INIT;
-
- class SetBufferCountIPC : public SharedBufferClient::SetBufferCountCallback {
- sp<ISurface> surface;
- virtual status_t operator()(int bufferCount) const {
- return surface->setBufferCount(bufferCount);
- }
- public:
- SetBufferCountIPC(const sp<ISurface>& surface) : surface(surface) { }
- } ipc(s);
-
- status_t err = mSharedBufferClient->setBufferCount(bufferCount, ipc);
- LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s",
- bufferCount, strerror(-err));
-
- if (err == NO_ERROR) {
- // Clear out any references to the old buffers.
- mBuffers.clear();
- }
-
- return err;
-}
-
-int Surface::setBuffersGeometry(int w, int h, int format)
-{
- if (w<0 || h<0 || format<0)
- return BAD_VALUE;
-
- if ((w && !h) || (!w && h))
- return BAD_VALUE;
-
- Mutex::Autolock _l(mSurfaceLock);
- if (mConnected == NATIVE_WINDOW_API_EGL) {
- return INVALID_OPERATION;
- }
-
- mBufferInfo.set(w, h, format);
- if (format != 0) {
- // we update the format of the surface as reported by query().
- // this is to allow applications to change the format of a surface's
- // buffer, and have it reflected in EGL; which is needed for
- // EGLConfig validation.
- mFormat = format;
- }
-
- mNextBufferCrop = Rect(0,0);
-
- return NO_ERROR;
-}
-
-int Surface::setBuffersTransform(int transform)
-{
- Mutex::Autolock _l(mSurfaceLock);
- mNextBufferTransform = transform;
- return NO_ERROR;
-}
-
-int Surface::setBuffersTimestamp(int64_t timestamp)
-{
- // Surface doesn't really have anything meaningful to do with timestamps
- // so they'll just be dropped here.
- return NO_ERROR;
+int Surface::perform(int operation, va_list args) {
+ return mSurfaceTextureClient->perform(operation, args);
}
// ----------------------------------------------------------------------------
-int Surface::getConnectedApi() const
-{
- Mutex::Autolock _l(mSurfaceLock);
- return mConnected;
+int Surface::getConnectedApi() const {
+ return mSurfaceTextureClient->getConnectedApi();
}
// ----------------------------------------------------------------------------
@@ -967,16 +556,17 @@ status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking)
}
// we're intending to do software rendering from this point
- setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+ mSurfaceTextureClient->setUsage(
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
ANativeWindowBuffer* out;
- status_t err = dequeueBuffer(&out);
+ status_t err = mSurfaceTextureClient->dequeueBuffer(&out);
LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
if (err == NO_ERROR) {
sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
- err = lockBuffer(backBuffer.get());
- LOGE_IF(err, "lockBuffer (idx=%d) failed (%s)",
- getBufferIndex(backBuffer), strerror(-err));
+ err = mSurfaceTextureClient->lockBuffer(backBuffer.get());
+ LOGE_IF(err, "lockBuffer (handle=%p) failed (%s)",
+ backBuffer->handle, strerror(-err));
if (err == NO_ERROR) {
const Rect bounds(backBuffer->width, backBuffer->height);
const Region boundsRegion(bounds);
@@ -1043,110 +633,14 @@ status_t Surface::unlockAndPost()
status_t err = mLockedBuffer->unlock();
LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
- err = queueBuffer(mLockedBuffer.get());
- LOGE_IF(err, "queueBuffer (idx=%d) failed (%s)",
- getBufferIndex(mLockedBuffer), strerror(-err));
+ err = mSurfaceTextureClient->queueBuffer(mLockedBuffer.get());
+ LOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
+ mLockedBuffer->handle, strerror(-err));
mPostedBuffer = mLockedBuffer;
mLockedBuffer = 0;
return err;
}
-void Surface::setSwapRectangle(const Rect& r) {
- Mutex::Autolock _l(mSurfaceLock);
- mSwapRectangle = r;
-}
-
-int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const
-{
- int idx = buffer->getIndex();
- if (idx < 0) {
- // The buffer doesn't have an index set. See if the handle the same as
- // one of the buffers for which we do know the index. This can happen
- // e.g. if GraphicBuffer is used to wrap an ANativeWindowBuffer that
- // was dequeued from an ANativeWindow.
- for (size_t i = 0; i < mBuffers.size(); i++) {
- if (mBuffers[i] != 0 && buffer->handle == mBuffers[i]->handle) {
- idx = mBuffers[i]->getIndex();
- break;
- }
- }
- }
- return idx;
-}
-
-status_t Surface::getBufferLocked(int index,
- uint32_t w, uint32_t h, uint32_t format, uint32_t usage)
-{
- sp<ISurface> s(mSurface);
- if (s == 0) return NO_INIT;
-
- status_t err = NO_MEMORY;
-
- // free the current buffer
- sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index));
- if (currentBuffer != 0) {
- currentBuffer.clear();
- }
-
- sp<GraphicBuffer> buffer = s->requestBuffer(index, w, h, format, usage);
- LOGE_IF(buffer==0,
- "ISurface::getBuffer(%d, %08x) returned NULL",
- index, usage);
- if (buffer != 0) { // this should always happen by construction
- LOGE_IF(buffer->handle == NULL,
- "Surface (identity=%d) requestBuffer(%d, %u, %u, %u, %08x) "
- "returned a buffer with a null handle",
- mIdentity, index, w, h, format, usage);
- err = mSharedBufferClient->getStatus();
- LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err);
- if (!err && buffer->handle != NULL) {
- currentBuffer = buffer;
- currentBuffer->setIndex(index);
- } else {
- err = err<0 ? err : status_t(NO_MEMORY);
- }
- }
- return err;
-}
-
-// ----------------------------------------------------------------------------
-Surface::BufferInfo::BufferInfo()
- : mWidth(0), mHeight(0), mFormat(0),
- mUsage(GRALLOC_USAGE_HW_RENDER), mDirty(0)
-{
-}
-
-void Surface::BufferInfo::set(uint32_t w, uint32_t h, uint32_t format) {
- if ((mWidth != w) || (mHeight != h) || (mFormat != format)) {
- mWidth = w;
- mHeight = h;
- mFormat = format;
- mDirty |= GEOMETRY;
- }
-}
-
-void Surface::BufferInfo::set(uint32_t usage) {
- mUsage = usage;
-}
-
-void Surface::BufferInfo::get(uint32_t *pWidth, uint32_t *pHeight,
- uint32_t *pFormat, uint32_t *pUsage) const {
- *pWidth = mWidth;
- *pHeight = mHeight;
- *pFormat = mFormat;
- *pUsage = mUsage;
-}
-
-bool Surface::BufferInfo::validateBuffer(const sp<GraphicBuffer>& buffer) const {
- // make sure we AT LEAST have the usage flags we want
- if (mDirty || buffer==0 ||
- ((buffer->usage & mUsage) != mUsage)) {
- mDirty = 0;
- return false;
- }
- return true;
-}
-
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index a1ff2c1..1678711 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -20,19 +20,20 @@
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/threads.h>
-#include <utils/SortedVector.h>
#include <utils/Log.h>
#include <utils/Singleton.h>
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
-#include <binder/IServiceManager.h>
#include <binder/IMemory.h>
+#include <binder/IServiceManager.h>
#include <ui/DisplayInfo.h>
+#include <surfaceflinger/ISurface.h>
#include <surfaceflinger/ISurfaceComposer.h>
#include <surfaceflinger/ISurfaceComposerClient.h>
-#include <surfaceflinger/ISurface.h>
#include <surfaceflinger/SurfaceComposerClient.h>
#include <private/surfaceflinger/LayerState.h>
@@ -217,7 +218,7 @@ void SurfaceComposerClient::dispose()
status_t SurfaceComposerClient::getDisplayInfo(
DisplayID dpy, DisplayInfo* info)
{
- if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX)
+ if (uint32_t(dpy)>=NUM_DISPLAY_MAX)
return BAD_VALUE;
volatile surface_flinger_cblk_t const * cblk = get_cblk();
@@ -235,7 +236,7 @@ status_t SurfaceComposerClient::getDisplayInfo(
ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy)
{
- if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX)
+ if (uint32_t(dpy)>=NUM_DISPLAY_MAX)
return BAD_VALUE;
volatile surface_flinger_cblk_t const * cblk = get_cblk();
volatile display_cblk_t const * dcblk = cblk->displays + dpy;
@@ -244,7 +245,7 @@ ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy)
ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy)
{
- if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX)
+ if (uint32_t(dpy)>=NUM_DISPLAY_MAX)
return BAD_VALUE;
volatile surface_flinger_cblk_t const * cblk = get_cblk();
volatile display_cblk_t const * dcblk = cblk->displays + dpy;
@@ -253,7 +254,7 @@ ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy)
ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy)
{
- if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX)
+ if (uint32_t(dpy)>=NUM_DISPLAY_MAX)
return BAD_VALUE;
volatile surface_flinger_cblk_t const * cblk = get_cblk();
volatile display_cblk_t const * dcblk = cblk->displays + dpy;
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index ee97dcf..3cecdb4 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -96,6 +96,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex) :
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
mNextCrop.makeInvalid();
+ memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix));
}
SurfaceTexture::~SurfaceTexture() {
@@ -270,7 +271,7 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
if (state == BufferSlot::DEQUEUED) {
dequeuedCount++;
}
- if (state == BufferSlot::FREE || i == mCurrentTexture) {
+ if (state == BufferSlot::FREE /*|| i == mCurrentTexture*/) {
foundSync = i;
if (i != mCurrentTexture) {
found = i;
@@ -547,6 +548,7 @@ status_t SurfaceTexture::updateTexImage() {
mCurrentCrop = mSlots[buf].mCrop;
mCurrentTransform = mSlots[buf].mTransform;
mCurrentTimestamp = mSlots[buf].mTimestamp;
+ computeCurrentTransformMatrix();
mDequeueCondition.signal();
} else {
// We always bind the texture even if we don't update its contents.
@@ -596,8 +598,12 @@ GLenum SurfaceTexture::getCurrentTextureTarget() const {
}
void SurfaceTexture::getTransformMatrix(float mtx[16]) {
- LOGV("SurfaceTexture::getTransformMatrix");
Mutex::Autolock lock(mMutex);
+ memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+}
+
+void SurfaceTexture::computeCurrentTransformMatrix() {
+ LOGV("SurfaceTexture::computeCurrentTransformMatrix");
float xform[16];
for (int i = 0; i < 16; i++) {
@@ -684,7 +690,7 @@ void SurfaceTexture::getTransformMatrix(float mtx[16]) {
// coordinate of 0, so SurfaceTexture must behave the same way. We don't
// want to expose this to applications, however, so we must add an
// additional vertical flip to the transform after all the other transforms.
- mtxMul(mtx, mtxFlipV, mtxBeforeFlipV);
+ mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV);
}
nsecs_t SurfaceTexture::getTimestamp() {
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index c20fcf2..22b0852 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -181,6 +181,12 @@ int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) {
int SurfaceTextureClient::query(int what, int* value) const {
LOGV("SurfaceTextureClient::query");
switch (what) {
+ case NATIVE_WINDOW_FORMAT:
+ if (mReqFormat) {
+ *value = mReqFormat;
+ return NO_ERROR;
+ }
+ break;
case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
// TODO: this is not needed anymore
*value = 0;
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 2f704c8..e1a85f3 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -505,13 +505,121 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) {
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ thread->run();
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
- thread->run();
- ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
- ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+ //ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+ //ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
thread->bufferDequeued();
thread->requestExitAndWait();
}
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixReturnsVerticalFlip) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ float mtx[16] = {};
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, st->updateTexImage());
+ st->getTransformMatrix(mtx);
+
+ EXPECT_EQ(1.f, mtx[0]);
+ EXPECT_EQ(0.f, mtx[1]);
+ EXPECT_EQ(0.f, mtx[2]);
+ EXPECT_EQ(0.f, mtx[3]);
+
+ EXPECT_EQ(0.f, mtx[4]);
+ EXPECT_EQ(-1.f, mtx[5]);
+ EXPECT_EQ(0.f, mtx[6]);
+ EXPECT_EQ(0.f, mtx[7]);
+
+ EXPECT_EQ(0.f, mtx[8]);
+ EXPECT_EQ(0.f, mtx[9]);
+ EXPECT_EQ(1.f, mtx[10]);
+ EXPECT_EQ(0.f, mtx[11]);
+
+ EXPECT_EQ(0.f, mtx[12]);
+ EXPECT_EQ(1.f, mtx[13]);
+ EXPECT_EQ(0.f, mtx[14]);
+ EXPECT_EQ(1.f, mtx[15]);
+}
+
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ float mtx[16] = {};
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, st->updateTexImage());
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers
+ st->getTransformMatrix(mtx);
+
+ EXPECT_EQ(1.f, mtx[0]);
+ EXPECT_EQ(0.f, mtx[1]);
+ EXPECT_EQ(0.f, mtx[2]);
+ EXPECT_EQ(0.f, mtx[3]);
+
+ EXPECT_EQ(0.f, mtx[4]);
+ EXPECT_EQ(-1.f, mtx[5]);
+ EXPECT_EQ(0.f, mtx[6]);
+ EXPECT_EQ(0.f, mtx[7]);
+
+ EXPECT_EQ(0.f, mtx[8]);
+ EXPECT_EQ(0.f, mtx[9]);
+ EXPECT_EQ(1.f, mtx[10]);
+ EXPECT_EQ(0.f, mtx[11]);
+
+ EXPECT_EQ(0.f, mtx[12]);
+ EXPECT_EQ(1.f, mtx[13]);
+ EXPECT_EQ(0.f, mtx[14]);
+ EXPECT_EQ(1.f, mtx[15]);
+}
+
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ float mtx[16] = {};
+ android_native_rect_t crop;
+ crop.left = 0;
+ crop.top = 0;
+ crop.right = 5;
+ crop.bottom = 5;
+
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 8, 8, 0));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, native_window_set_crop(anw.get(), &crop));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, st->updateTexImage());
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers
+ st->getTransformMatrix(mtx);
+
+ // This accounts for the 1 texel shrink for each edge that's included in the
+ // transform matrix to avoid texturing outside the crop region.
+ EXPECT_EQ(.5f, mtx[0]);
+ EXPECT_EQ(0.f, mtx[1]);
+ EXPECT_EQ(0.f, mtx[2]);
+ EXPECT_EQ(0.f, mtx[3]);
+
+ EXPECT_EQ(0.f, mtx[4]);
+ EXPECT_EQ(-.5f, mtx[5]);
+ EXPECT_EQ(0.f, mtx[6]);
+ EXPECT_EQ(0.f, mtx[7]);
+
+ EXPECT_EQ(0.f, mtx[8]);
+ EXPECT_EQ(0.f, mtx[9]);
+ EXPECT_EQ(1.f, mtx[10]);
+ EXPECT_EQ(0.f, mtx[11]);
+
+ EXPECT_EQ(0.f, mtx[12]);
+ EXPECT_EQ(.5f, mtx[13]);
+ EXPECT_EQ(0.f, mtx[14]);
+ EXPECT_EQ(1.f, mtx[15]);
}
+
+} // namespace android
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index 8747ba5..56c1702 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -14,11 +14,14 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+
#include <gtest/gtest.h>
#include <gui/SurfaceTexture.h>
#include <gui/SurfaceTextureClient.h>
#include <ui/GraphicBuffer.h>
#include <utils/String8.h>
+#include <utils/threads.h>
#include <surfaceflinger/ISurfaceComposer.h>
#include <surfaceflinger/Surface.h>
@@ -270,8 +273,12 @@ protected:
*outPgm = program;
}
+ static int abs(int value) {
+ return value > 0 ? value : -value;
+ }
+
::testing::AssertionResult checkPixel(int x, int y, int r,
- int g, int b, int a) {
+ int g, int b, int a, int tolerance=2) {
GLubyte pixel[4];
String8 msg;
glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
@@ -285,22 +292,22 @@ protected:
return ::testing::AssertionFailure(
::testing::Message(msg.string()));
}
- if (r >= 0 && GLubyte(r) != pixel[0]) {
+ if (r >= 0 && abs(r - int(pixel[0])) > tolerance) {
msg += String8::format("r(%d isn't %d)", pixel[0], r);
}
- if (g >= 0 && GLubyte(g) != pixel[1]) {
+ if (g >= 0 && abs(g - int(pixel[1])) > tolerance) {
if (!msg.isEmpty()) {
msg += " ";
}
msg += String8::format("g(%d isn't %d)", pixel[1], g);
}
- if (b >= 0 && GLubyte(b) != pixel[2]) {
+ if (b >= 0 && abs(b - int(pixel[2])) > tolerance) {
if (!msg.isEmpty()) {
msg += " ";
}
msg += String8::format("b(%d isn't %d)", pixel[2], b);
}
- if (a >= 0 && GLubyte(a) != pixel[3]) {
+ if (a >= 0 && abs(a - int(pixel[3])) > tolerance) {
if (!msg.isEmpty()) {
msg += " ";
}
@@ -618,4 +625,270 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {
}
}
+/*
+ * This test is for testing GL -> GL texture streaming via SurfaceTexture. It
+ * contains functionality to create a producer thread that will perform GL
+ * rendering to an ANativeWindow that feeds frames to a SurfaceTexture.
+ * Additionally it supports interlocking the producer and consumer threads so
+ * that a specific sequence of calls can be deterministically created by the
+ * test.
+ *
+ * The intended usage is as follows:
+ *
+ * TEST_F(...) {
+ * class PT : public ProducerThread {
+ * virtual void render() {
+ * ...
+ * swapBuffers();
+ * }
+ * };
+ *
+ * runProducerThread(new PT());
+ *
+ * // The order of these calls will vary from test to test and may include
+ * // multiple frames and additional operations (e.g. GL rendering from the
+ * // texture).
+ * fc->waitForFrame();
+ * mST->updateTexImage();
+ * fc->finishFrame();
+ * }
+ *
+ */
+class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest {
+protected:
+
+ // ProducerThread is an abstract base class to simplify the creation of
+ // OpenGL ES frame producer threads.
+ class ProducerThread : public Thread {
+ public:
+ virtual ~ProducerThread() {
+ }
+
+ void setEglObjects(EGLDisplay producerEglDisplay,
+ EGLSurface producerEglSurface,
+ EGLContext producerEglContext) {
+ mProducerEglDisplay = producerEglDisplay;
+ mProducerEglSurface = producerEglSurface;
+ mProducerEglContext = producerEglContext;
+ }
+
+ virtual bool threadLoop() {
+ eglMakeCurrent(mProducerEglDisplay, mProducerEglSurface,
+ mProducerEglSurface, mProducerEglContext);
+ render();
+ eglMakeCurrent(mProducerEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ return false;
+ }
+
+ protected:
+ virtual void render() = 0;
+
+ void swapBuffers() {
+ eglSwapBuffers(mProducerEglDisplay, mProducerEglSurface);
+ }
+
+ EGLDisplay mProducerEglDisplay;
+ EGLSurface mProducerEglSurface;
+ EGLContext mProducerEglContext;
+ };
+
+ // FrameCondition is a utility class for interlocking between the producer
+ // and consumer threads. The FrameCondition object should be created and
+ // destroyed in the consumer thread only. The consumer thread should set
+ // the FrameCondition as the FrameAvailableListener of the SurfaceTexture,
+ // and should call both waitForFrame and finishFrame once for each expected
+ // frame.
+ //
+ // This interlocking relies on the fact that onFrameAvailable gets called
+ // synchronously from SurfaceTexture::queueBuffer.
+ class FrameCondition : public SurfaceTexture::FrameAvailableListener {
+ public:
+ // waitForFrame waits for the next frame to arrive. This should be
+ // called from the consumer thread once for every frame expected by the
+ // test.
+ void waitForFrame() {
+ LOGV("+waitForFrame");
+ Mutex::Autolock lock(mMutex);
+ status_t result = mFrameAvailableCondition.wait(mMutex);
+ LOGV("-waitForFrame");
+ }
+
+ // Allow the producer to return from its swapBuffers call and continue
+ // on to produce the next frame. This should be called by the consumer
+ // thread once for every frame expected by the test.
+ void finishFrame() {
+ LOGV("+finishFrame");
+ Mutex::Autolock lock(mMutex);
+ mFrameFinishCondition.signal();
+ LOGV("-finishFrame");
+ }
+
+ // This should be called by SurfaceTexture on the producer thread.
+ virtual void onFrameAvailable() {
+ LOGV("+onFrameAvailable");
+ Mutex::Autolock lock(mMutex);
+ mFrameAvailableCondition.signal();
+ mFrameFinishCondition.wait(mMutex);
+ LOGV("-onFrameAvailable");
+ }
+
+ protected:
+ Mutex mMutex;
+ Condition mFrameAvailableCondition;
+ Condition mFrameFinishCondition;
+ };
+
+ SurfaceTextureGLToGLTest():
+ mProducerEglSurface(EGL_NO_SURFACE),
+ mProducerEglContext(EGL_NO_CONTEXT) {
+ }
+
+ virtual void SetUp() {
+ SurfaceTextureGLTest::SetUp();
+
+ EGLConfig myConfig = {0};
+ EGLint numConfigs = 0;
+ EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
+ 1, &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
+ mANW.get(), NULL);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);
+
+ mProducerEglContext = eglCreateContext(mEglDisplay, myConfig,
+ EGL_NO_CONTEXT, getContextAttribs());
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext);
+
+ mFC = new FrameCondition();
+ mST->setFrameAvailableListener(mFC);
+ }
+
+ virtual void TearDown() {
+ if (mProducerThread != NULL) {
+ mProducerThread->requestExitAndWait();
+ }
+ if (mProducerEglContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEglDisplay, mProducerEglContext);
+ }
+ if (mProducerEglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, mProducerEglSurface);
+ }
+ mProducerThread.clear();
+ mFC.clear();
+ }
+
+ void runProducerThread(const sp<ProducerThread> producerThread) {
+ ASSERT_TRUE(mProducerThread == NULL);
+ mProducerThread = producerThread;
+ producerThread->setEglObjects(mEglDisplay, mProducerEglSurface,
+ mProducerEglContext);
+ producerThread->run();
+ }
+
+ EGLSurface mProducerEglSurface;
+ EGLContext mProducerEglContext;
+ sp<ProducerThread> mProducerThread;
+ sp<FrameCondition> mFC;
+};
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_UpdateTexImageBeforeFrameFinishedWorks) {
+ class PT : public ProducerThread {
+ virtual void render() {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ swapBuffers();
+ }
+ };
+
+ runProducerThread(new PT());
+
+ mFC->waitForFrame();
+ mST->updateTexImage();
+ mFC->finishFrame();
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+}
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_UpdateTexImageAfterFrameFinishedWorks) {
+ class PT : public ProducerThread {
+ virtual void render() {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ swapBuffers();
+ }
+ };
+
+ runProducerThread(new PT());
+
+ mFC->waitForFrame();
+ mFC->finishFrame();
+ mST->updateTexImage();
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+}
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageBeforeFrameFinishedWorks) {
+ enum { NUM_ITERATIONS = 1024 };
+
+ class PT : public ProducerThread {
+ virtual void render() {
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ LOGV("+swapBuffers");
+ swapBuffers();
+ LOGV("-swapBuffers");
+ }
+ }
+ };
+
+ runProducerThread(new PT());
+
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ mFC->waitForFrame();
+ LOGV("+updateTexImage");
+ mST->updateTexImage();
+ LOGV("-updateTexImage");
+ mFC->finishFrame();
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+ }
}
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageAfterFrameFinishedWorks) {
+ enum { NUM_ITERATIONS = 1024 };
+
+ class PT : public ProducerThread {
+ virtual void render() {
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ LOGV("+swapBuffers");
+ swapBuffers();
+ LOGV("-swapBuffers");
+ }
+ }
+ };
+
+ runProducerThread(new PT());
+
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ mFC->waitForFrame();
+ mFC->finishFrame();
+ LOGV("+updateTexImage");
+ mST->updateTexImage();
+ LOGV("-updateTexImage");
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+ }
+}
+
+} // namespace android
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index e8d40ba..093189c 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -21,6 +21,7 @@ commonSources:= \
Asset.cpp \
AssetDir.cpp \
AssetManager.cpp \
+ BlobCache.cpp \
BufferedTextOutput.cpp \
CallStack.cpp \
Debug.cpp \
diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp
new file mode 100644
index 0000000..1298fa7
--- /dev/null
+++ b/libs/utils/BlobCache.cpp
@@ -0,0 +1,232 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#define LOG_TAG "BlobCache"
+//#define LOG_NDEBUG 0
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/BlobCache.h>
+#include <utils/Log.h>
+
+namespace android {
+
+BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
+ mMaxKeySize(maxKeySize),
+ mMaxValueSize(maxValueSize),
+ mMaxTotalSize(maxTotalSize),
+ mTotalSize(0) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ mRandState[0] = (now >> 0) & 0xFFFF;
+ mRandState[1] = (now >> 16) & 0xFFFF;
+ mRandState[2] = (now >> 32) & 0xFFFF;
+ LOGV("initializing random seed using %lld", now);
+}
+
+void BlobCache::set(const void* key, size_t keySize, const void* value,
+ size_t valueSize) {
+ if (mMaxKeySize < keySize) {
+ LOGV("set: not caching because the key is too large: %d (limit: %d)",
+ keySize, mMaxKeySize);
+ return;
+ }
+ if (mMaxValueSize < valueSize) {
+ LOGV("set: not caching because the value is too large: %d (limit: %d)",
+ valueSize, mMaxValueSize);
+ return;
+ }
+ if (mMaxTotalSize < keySize + valueSize) {
+ LOGV("set: not caching because the combined key/value size is too "
+ "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize);
+ return;
+ }
+ if (keySize == 0) {
+ LOGW("set: not caching because keySize is 0");
+ return;
+ }
+ if (valueSize <= 0) {
+ LOGW("set: not caching because valueSize is 0");
+ return;
+ }
+
+ Mutex::Autolock lock(mMutex);
+ sp<Blob> dummyKey(new Blob(key, keySize, false));
+ CacheEntry dummyEntry(dummyKey, NULL);
+
+ while (true) {
+
+ ssize_t index = mCacheEntries.indexOf(dummyEntry);
+ if (index < 0) {
+ // Create a new cache entry.
+ sp<Blob> keyBlob(new Blob(key, keySize, true));
+ sp<Blob> valueBlob(new Blob(value, valueSize, true));
+ size_t newTotalSize = mTotalSize + keySize + valueSize;
+ if (mMaxTotalSize < newTotalSize) {
+ if (isCleanable()) {
+ // Clean the cache and try again.
+ clean();
+ continue;
+ } else {
+ LOGV("set: not caching new key/value pair because the "
+ "total cache size limit would be exceeded: %d "
+ "(limit: %d)",
+ keySize + valueSize, mMaxTotalSize);
+ break;
+ }
+ }
+ mCacheEntries.add(CacheEntry(keyBlob, valueBlob));
+ mTotalSize = newTotalSize;
+ LOGV("set: created new cache entry with %d byte key and %d byte value",
+ keySize, valueSize);
+ } else {
+ // Update the existing cache entry.
+ sp<Blob> valueBlob(new Blob(value, valueSize, true));
+ sp<Blob> oldValueBlob(mCacheEntries[index].getValue());
+ size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize();
+ if (mMaxTotalSize < newTotalSize) {
+ if (isCleanable()) {
+ // Clean the cache and try again.
+ clean();
+ continue;
+ } else {
+ LOGV("set: not caching new value because the total cache "
+ "size limit would be exceeded: %d (limit: %d)",
+ keySize + valueSize, mMaxTotalSize);
+ break;
+ }
+ }
+ mCacheEntries.editItemAt(index).setValue(valueBlob);
+ mTotalSize = newTotalSize;
+ LOGV("set: updated existing cache entry with %d byte key and %d byte "
+ "value", keySize, valueSize);
+ }
+ break;
+ }
+}
+
+size_t BlobCache::get(const void* key, size_t keySize, void* value,
+ size_t valueSize) {
+ if (mMaxKeySize < keySize) {
+ LOGV("get: not searching because the key is too large: %d (limit %d)",
+ keySize, mMaxKeySize);
+ return 0;
+ }
+ Mutex::Autolock lock(mMutex);
+ sp<Blob> dummyKey(new Blob(key, keySize, false));
+ CacheEntry dummyEntry(dummyKey, NULL);
+ ssize_t index = mCacheEntries.indexOf(dummyEntry);
+ if (index < 0) {
+ LOGV("get: no cache entry found for key of size %d", keySize);
+ return 0;
+ }
+
+ // The key was found. Return the value if the caller's buffer is large
+ // enough.
+ sp<Blob> valueBlob(mCacheEntries[index].getValue());
+ size_t valueBlobSize = valueBlob->getSize();
+ if (valueBlobSize <= valueSize) {
+ LOGV("get: copying %d bytes to caller's buffer", valueBlobSize);
+ memcpy(value, valueBlob->getData(), valueBlobSize);
+ } else {
+ LOGV("get: caller's buffer is too small for value: %d (needs %d)",
+ valueSize, valueBlobSize);
+ }
+ return valueBlobSize;
+}
+
+void BlobCache::clean() {
+ // Remove a random cache entry until the total cache size gets below half
+ // the maximum total cache size.
+ while (mTotalSize > mMaxTotalSize / 2) {
+ size_t i = size_t(nrand48(mRandState) % (mCacheEntries.size()));
+ const CacheEntry& entry(mCacheEntries[i]);
+ mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize();
+ mCacheEntries.removeAt(i);
+ }
+}
+
+bool BlobCache::isCleanable() const {
+ return mTotalSize > mMaxTotalSize / 2;
+}
+
+BlobCache::Blob::Blob(const void* data, size_t size, bool copyData):
+ mData(copyData ? malloc(size) : data),
+ mSize(size),
+ mOwnsData(copyData) {
+ if (copyData) {
+ memcpy(const_cast<void*>(mData), data, size);
+ }
+}
+
+BlobCache::Blob::~Blob() {
+ if (mOwnsData) {
+ free(const_cast<void*>(mData));
+ }
+}
+
+bool BlobCache::Blob::operator<(const Blob& rhs) const {
+ if (mSize == rhs.mSize) {
+ return memcmp(mData, rhs.mData, mSize) < 0;
+ } else {
+ return mSize < rhs.mSize;
+ }
+}
+
+const void* BlobCache::Blob::getData() const {
+ return mData;
+}
+
+size_t BlobCache::Blob::getSize() const {
+ return mSize;
+}
+
+BlobCache::CacheEntry::CacheEntry() {
+}
+
+BlobCache::CacheEntry::CacheEntry(const sp<Blob>& key, const sp<Blob>& value):
+ mKey(key),
+ mValue(value) {
+}
+
+BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
+ mKey(ce.mKey),
+ mValue(ce.mValue) {
+}
+
+bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
+ return *mKey < *rhs.mKey;
+}
+
+const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) {
+ mKey = rhs.mKey;
+ mValue = rhs.mValue;
+ return *this;
+}
+
+sp<BlobCache::Blob> BlobCache::CacheEntry::getKey() const {
+ return mKey;
+}
+
+sp<BlobCache::Blob> BlobCache::CacheEntry::getValue() const {
+ return mValue;
+}
+
+void BlobCache::CacheEntry::setValue(const sp<Blob>& value) {
+ mValue = value;
+}
+
+} // namespace android
diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp
index 58e0811..8db2009 100644
--- a/libs/utils/RefBase.cpp
+++ b/libs/utils/RefBase.cpp
@@ -49,6 +49,11 @@ namespace android {
// ---------------------------------------------------------------------------
+RefBase::Destroyer::~Destroyer() {
+}
+
+// ---------------------------------------------------------------------------
+
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
@@ -56,7 +61,7 @@ public:
volatile int32_t mWeak;
RefBase* const mBase;
volatile int32_t mFlags;
-
+ Destroyer* mDestroyer;
#if !DEBUG_REFS
@@ -65,6 +70,7 @@ public:
, mWeak(0)
, mBase(base)
, mFlags(0)
+ , mDestroyer(0)
{
}
@@ -345,10 +351,6 @@ void RefBase::incStrong(const void* id) const
const_cast<RefBase*>(this)->onFirstRef();
}
-void RefBase::destroy() const {
- delete this;
-}
-
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
@@ -361,7 +363,11 @@ void RefBase::decStrong(const void* id) const
if (c == 1) {
const_cast<RefBase*>(this)->onLastStrongRef(id);
if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
- destroy();
+ if (refs->mDestroyer) {
+ refs->mDestroyer->destroy(this);
+ } else {
+ delete this;
+ }
}
}
refs->decWeak(id);
@@ -394,7 +400,9 @@ int32_t RefBase::getStrongCount() const
return mRefs->mStrong;
}
-
+void RefBase::setDestroyer(RefBase::Destroyer* destroyer) {
+ mRefs->mDestroyer = destroyer;
+}
RefBase* RefBase::weakref_type::refBase() const
{
@@ -420,7 +428,11 @@ void RefBase::weakref_type::decWeak(const void* id)
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
if (impl->mStrong == INITIAL_STRONG_VALUE) {
if (impl->mBase) {
- impl->mBase->destroy();
+ if (impl->mDestroyer) {
+ impl->mDestroyer->destroy(impl->mBase);
+ } else {
+ delete impl->mBase;
+ }
}
} else {
// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
@@ -430,7 +442,11 @@ void RefBase::weakref_type::decWeak(const void* id)
impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
if (impl->mBase) {
- impl->mBase->destroy();
+ if (impl->mDestroyer) {
+ impl->mDestroyer->destroy(impl->mBase);
+ } else {
+ delete impl->mBase;
+ }
}
}
}
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 8b5da0e..c748228 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -168,6 +168,9 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
return 0;
}
+ // Note that *threadID is directly available to the parent only, as it is
+ // assigned after the child starts. Use memory barrier / lock if the child
+ // or other threads also need access.
if (threadId != NULL) {
*threadId = (android_thread_id_t)thread; // XXX: this is not portable
}
@@ -718,7 +721,6 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack)
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
}
- // The new thread wakes up at _threadLoop, but immediately blocks on mLock
if (res == false) {
mStatus = UNKNOWN_ERROR; // something happened!
@@ -742,11 +744,6 @@ int Thread::_threadLoop(void* user)
{
Thread* const self = static_cast<Thread*>(user);
- // force a memory barrier before reading any fields, in particular mHoldSelf
- {
- Mutex::Autolock _l(self->mLock);
- }
-
sp<Thread> strong(self->mHoldSelf);
wp<Thread> weak(strong);
self->mHoldSelf.clear();
@@ -816,6 +813,7 @@ void Thread::requestExit()
status_t Thread::requestExitAndWait()
{
+ Mutex::Autolock _l(mLock);
if (mThread == getThreadId()) {
LOGW(
"Thread (this=%p): don't call waitForExit() from this "
@@ -825,9 +823,8 @@ status_t Thread::requestExitAndWait()
return WOULD_BLOCK;
}
- requestExit();
+ mExitPending = true;
- Mutex::Autolock _l(mLock);
while (mRunning == true) {
mThreadExitedCondition.wait(mLock);
}
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index 72d4876..87ad98e 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -6,6 +6,7 @@ ifneq ($(TARGET_SIMULATOR),true)
# Build the unit tests.
test_src_files := \
+ BlobCache_test.cpp \
ObbFile_test.cpp \
Looper_test.cpp \
String8_test.cpp \
diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp
new file mode 100644
index 0000000..653ea5e
--- /dev/null
+++ b/libs/utils/tests/BlobCache_test.cpp
@@ -0,0 +1,257 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <utils/BlobCache.h>
+
+namespace android {
+
+class BlobCacheTest : public ::testing::Test {
+protected:
+ enum {
+ MAX_KEY_SIZE = 6,
+ MAX_VALUE_SIZE = 8,
+ MAX_TOTAL_SIZE = 13,
+ };
+
+ virtual void SetUp() {
+ mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
+ }
+
+ virtual void TearDown() {
+ mBC.clear();
+ }
+
+ sp<BlobCache> mBC;
+};
+
+TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+ ASSERT_EQ('e', buf[0]);
+ ASSERT_EQ('f', buf[1]);
+ ASSERT_EQ('g', buf[2]);
+ ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
+ char buf[2] = { 0xee, 0xee };
+ mBC->set("ab", 2, "cd", 2);
+ mBC->set("ef", 2, "gh", 2);
+ ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
+ ASSERT_EQ('c', buf[0]);
+ ASSERT_EQ('d', buf[1]);
+ ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2));
+ ASSERT_EQ('g', buf[0]);
+ ASSERT_EQ('h', buf[1]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
+ char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
+ ASSERT_EQ(0xee, buf[0]);
+ ASSERT_EQ('e', buf[1]);
+ ASSERT_EQ('f', buf[2]);
+ ASSERT_EQ('g', buf[3]);
+ ASSERT_EQ('h', buf[4]);
+ ASSERT_EQ(0xee, buf[5]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
+ char buf[3] = { 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
+ ASSERT_EQ(0xee, buf[0]);
+ ASSERT_EQ(0xee, buf[1]);
+ ASSERT_EQ(0xee, buf[2]);
+}
+
+TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ mBC->set("abcd", 4, "ijkl", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+ ASSERT_EQ('i', buf[0]);
+ ASSERT_EQ('j', buf[1]);
+ ASSERT_EQ('k', buf[2]);
+ ASSERT_EQ('l', buf[3]);
+}
+
+TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
+ char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+ ASSERT_EQ('e', buf[0]);
+ ASSERT_EQ('f', buf[1]);
+ ASSERT_EQ('g', buf[2]);
+ ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
+ char key[MAX_KEY_SIZE+1];
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
+ key[i] = 'a';
+ }
+ mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
+ ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
+ ASSERT_EQ(0xee, buf[0]);
+ ASSERT_EQ(0xee, buf[1]);
+ ASSERT_EQ(0xee, buf[2]);
+ ASSERT_EQ(0xee, buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
+ char buf[MAX_VALUE_SIZE+1];
+ for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ buf[i] = 'b';
+ }
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+ for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ buf[i] = 0xee;
+ }
+ ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
+ for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ SCOPED_TRACE(i);
+ ASSERT_EQ(0xee, buf[i]);
+ }
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) {
+ // Check a testing assumptions
+ ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE);
+ ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+ enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 };
+
+ char key[MAX_KEY_SIZE];
+ char buf[bufSize];
+ for (int i = 0; i < MAX_KEY_SIZE; i++) {
+ key[i] = 'a';
+ }
+ for (int i = 0; i < bufSize; i++) {
+ buf[i] = 'b';
+ }
+
+ mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
+ ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
+ char key[MAX_KEY_SIZE];
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ for (int i = 0; i < MAX_KEY_SIZE; i++) {
+ key[i] = 'a';
+ }
+ mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
+ ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
+ ASSERT_EQ('w', buf[0]);
+ ASSERT_EQ('x', buf[1]);
+ ASSERT_EQ('y', buf[2]);
+ ASSERT_EQ('z', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) {
+ char buf[MAX_VALUE_SIZE];
+ for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+ buf[i] = 'b';
+ }
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
+ for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+ buf[i] = 0xee;
+ }
+ ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
+ MAX_VALUE_SIZE));
+ for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+ SCOPED_TRACE(i);
+ ASSERT_EQ('b', buf[i]);
+ }
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) {
+ // Check a testing assumption
+ ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+ enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
+
+ char key[MAX_KEY_SIZE];
+ char buf[bufSize];
+ for (int i = 0; i < MAX_KEY_SIZE; i++) {
+ key[i] = 'a';
+ }
+ for (int i = 0; i < bufSize; i++) {
+ buf[i] = 'b';
+ }
+
+ mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
+ ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
+ char buf[1] = { 0xee };
+ mBC->set("x", 1, "y", 1);
+ ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
+ ASSERT_EQ('y', buf[0]);
+}
+
+TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) {
+ for (int i = 0; i < 256; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, "x", 1);
+ }
+ int numCached = 0;
+ for (int i = 0; i < 256; i++) {
+ uint8_t k = i;
+ if (mBC->get(&k, 1, NULL, 0) == 1) {
+ numCached++;
+ }
+ }
+ ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
+}
+
+TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
+ // Fill up the entire cache with 1 char key/value pairs.
+ const int maxEntries = MAX_TOTAL_SIZE / 2;
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, "x", 1);
+ }
+ // Insert one more entry, causing a cache overflow.
+ {
+ uint8_t k = maxEntries;
+ mBC->set(&k, 1, "x", 1);
+ }
+ // Count the number of entries in the cache.
+ int numCached = 0;
+ for (int i = 0; i < maxEntries+1; i++) {
+ uint8_t k = i;
+ if (mBC->get(&k, 1, NULL, 0) == 1) {
+ numCached++;
+ }
+ }
+ ASSERT_EQ(maxEntries/2 + 1, numCached);
+}
+
+} // namespace android