am 4f0dfaa7: am 9be7caf3: Merge "Include stack traces for certain native processes in bugreport." into jb-dev
* commit '4f0dfaa7c338870eb58d96fe40234505734db728':
Include stack traces for certain native processes in bugreport.
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 3b28b22..218ff07 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -88,6 +88,7 @@
dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
+ dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources");
dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
run_command("PROCESSES", 10, "ps", "-P", NULL);
@@ -312,6 +313,14 @@
int use_socket = 0;
int do_fb = 0;
+ if (getuid() != 0) {
+ // Old versions of the adb client would call the
+ // dumpstate command directly. Newer clients
+ // call /system/bin/bugreport instead. If we detect
+ // we're being called incorrectly, then exec the
+ // correct program.
+ return execl("/system/bin/bugreport", "/system/bin/bugreport", NULL);
+ }
ALOGI("begin\n");
signal(SIGPIPE, SIG_IGN);
@@ -356,44 +365,42 @@
fclose(cmdline);
}
- if (getuid() == 0) {
- if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
- return -1;
- }
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
+ return -1;
+ }
- /* switch to non-root user and group */
- gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
- AID_MOUNT, AID_INET, AID_NET_BW_STATS };
- if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
- ALOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
- return -1;
- }
- if (setgid(AID_SHELL) != 0) {
- ALOGE("Unable to setgid, aborting: %s\n", strerror(errno));
- return -1;
- }
- if (setuid(AID_SHELL) != 0) {
- ALOGE("Unable to setuid, aborting: %s\n", strerror(errno));
- return -1;
- }
+ /* switch to non-root user and group */
+ gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
+ AID_MOUNT, AID_INET, AID_NET_BW_STATS };
+ if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
+ ALOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
+ return -1;
+ }
+ if (setgid(AID_SHELL) != 0) {
+ ALOGE("Unable to setgid, aborting: %s\n", strerror(errno));
+ return -1;
+ }
+ if (setuid(AID_SHELL) != 0) {
+ ALOGE("Unable to setuid, aborting: %s\n", strerror(errno));
+ return -1;
+ }
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- capheader.pid = 0;
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
- capdata[0].inheritable = 0;
- capdata[1].inheritable = 0;
+ capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
+ capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
+ capdata[0].inheritable = 0;
+ capdata[1].inheritable = 0;
- if (capset(&capheader, &capdata[0]) < 0) {
- ALOGE("capset failed: %s\n", strerror(errno));
- return -1;
- }
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ return -1;
}
char path[PATH_MAX], tmp_path[PATH_MAX];
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 1c80d0c..0539a1b 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -209,6 +209,11 @@
// releaseBuffer releases a buffer slot from the consumer back to the
// BufferQueue pending a fence sync.
//
+ // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
+ // any references to the just-released buffer that it might have, as if it
+ // had received a onBuffersReleased() call with a mask set for the released
+ // buffer.
+ //
// Note that the dependencies on EGL will be removed once we switch to using
// the Android HW Sync HAL.
status_t releaseBuffer(int buf, EGLDisplay display, EGLSyncKHR fence);
diff --git a/include/gui/CpuConsumer.h b/include/gui/CpuConsumer.h
new file mode 100644
index 0000000..a50a1de
--- /dev/null
+++ b/include/gui/CpuConsumer.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ANDROID_GUI_CPUCONSUMER_H
+#define ANDROID_GUI_CPUCONSUMER_H
+
+#include <gui/BufferQueue.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+#define ANDROID_GRAPHICS_CPUCONSUMER_JNI_ID "mCpuConsumer"
+
+namespace android {
+
+/**
+ * CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU
+ * access to the underlying gralloc buffers provided by BufferQueue. Multiple
+ * buffers may be acquired by it at once, to be used concurrently by the
+ * CpuConsumer owner. Sets gralloc usage flags to be software-read-only.
+ * This queue is synchronous by default.
+ */
+
+class CpuConsumer: public virtual RefBase,
+ protected BufferQueue::ConsumerListener
+{
+ public:
+ struct FrameAvailableListener : public virtual RefBase {
+ // onFrameAvailable() is called each time an additional frame becomes
+ // available for consumption. A new frame queued will always trigger the
+ // callback, whether the queue is empty or not.
+ //
+ // This is called without any lock held and can be called concurrently
+ // by multiple threads.
+ virtual void onFrameAvailable() = 0;
+ };
+
+ struct LockedBuffer {
+ uint8_t *data;
+ uint32_t width;
+ uint32_t height;
+ PixelFormat format;
+ uint32_t stride;
+ Rect crop;
+ uint32_t transform;
+ uint32_t scalingMode;
+ int64_t timestamp;
+ uint64_t frameNumber;
+ };
+
+ // Create a new CPU consumer. The maxLockedBuffers parameter specifies
+ // how many buffers can be locked for user access at the same time.
+ CpuConsumer(uint32_t maxLockedBuffers);
+
+ virtual ~CpuConsumer();
+
+ // set the name of the CpuConsumer that will be used to identify it in
+ // log messages.
+ void setName(const String8& name);
+
+ // Gets the next graphics buffer from the producer and locks it for CPU use,
+ // filling out the passed-in locked buffer structure with the native pointer
+ // and metadata. Returns BAD_VALUE if no new buffer is available, and
+ // INVALID_OPERATION if the maximum number of buffers is already locked.
+ //
+ // Only a fixed number of buffers can be locked at a time, determined by the
+ // construction-time maxLockedBuffers parameter. If INVALID_OPERATION is
+ // returned by lockNextBuffer, then old buffers must be returned to the queue
+ // by calling unlockBuffer before more buffers can be acquired.
+ status_t lockNextBuffer(LockedBuffer *nativeBuffer);
+
+ // Returns a locked buffer to the queue, allowing it to be reused. Since
+ // only a fixed number of buffers may be locked at a time, old buffers must
+ // be released by calling unlockBuffer to ensure new buffers can be acquired by
+ // lockNextBuffer.
+ status_t unlockBuffer(const LockedBuffer &nativeBuffer);
+
+ // setFrameAvailableListener sets the listener object that will be notified
+ // when a new frame becomes available.
+ void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
+
+ sp<ISurfaceTexture> getProducerInterface() const { return mBufferQueue; }
+ protected:
+
+ // Implementation of the BufferQueue::ConsumerListener interface. These
+ // calls are used to notify the CpuConsumer of asynchronous events in the
+ // BufferQueue.
+ virtual void onFrameAvailable();
+ virtual void onBuffersReleased();
+
+ private:
+ // Free local buffer state
+ status_t freeBufferLocked(int buf);
+
+ // Maximum number of buffers that can be locked at a time
+ uint32_t mMaxLockedBuffers;
+
+ // mName is a string used to identify the SurfaceTexture in log messages.
+ // It can be set by the setName method.
+ String8 mName;
+
+ // mFrameAvailableListener is the listener object that will be called when a
+ // new frame becomes available. If it is not NULL it will be called from
+ // queueBuffer.
+ sp<FrameAvailableListener> mFrameAvailableListener;
+
+ // Underlying buffer queue
+ sp<BufferQueue> mBufferQueue;
+
+ // Array for caching buffers from the buffer queue
+ sp<GraphicBuffer> mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS];
+ // Array for tracking pointers passed to the consumer, matching the
+ // mBufferSlot indexing
+ void *mBufferPointers[BufferQueue::NUM_BUFFER_SLOTS];
+ // Count of currently locked buffers
+ uint32_t mCurrentLockedBuffers;
+
+ // mMutex is the mutex used to prevent concurrent access to the member
+ // variables of CpuConsumer objects. It must be locked whenever the
+ // member variables are accessed.
+ mutable Mutex mMutex;
+};
+
+} // namespace android
+
+#endif // ANDROID_GUI_CPUCONSUMER_H
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index 7320e4d..e4e8aa7 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -139,6 +139,12 @@
/* return an IDisplayEventConnection */
virtual sp<IDisplayEventConnection> createDisplayEventConnection() = 0;
+
+ /* triggers screen off and waits for it to complete */
+ virtual void blank() = 0;
+
+ /* triggers screen on and waits for it to complete */
+ virtual void unblank() = 0;
};
// ----------------------------------------------------------------------------
@@ -160,6 +166,8 @@
TURN_ELECTRON_BEAM_ON,
AUTHENTICATE_SURFACE,
CREATE_DISPLAY_EVENT_CONNECTION,
+ BLANK,
+ UNBLANK,
};
virtual status_t onTransact( uint32_t code,
diff --git a/include/utils/Trace.h b/include/utils/Trace.h
index 4219206..e5cc7ec 100644
--- a/include/utils/Trace.h
+++ b/include/utils/Trace.h
@@ -51,7 +51,8 @@
#define ATRACE_TAG_SYNC_MANAGER (1<<7)
#define ATRACE_TAG_AUDIO (1<<8)
#define ATRACE_TAG_VIDEO (1<<9)
-#define ATRACE_TAG_LAST ATRACE_TAG_VIDEO
+#define ATRACE_TAG_CAMERA (1<<10)
+#define ATRACE_TAG_LAST ATRACE_TAG_CAMERA
#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index 8224847..8fc96cf 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -21,7 +21,8 @@
LayerState.cpp \
Surface.cpp \
SurfaceComposerClient.cpp \
- DummyConsumer.cpp
+ DummyConsumer.cpp \
+ CpuConsumer.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
new file mode 100644
index 0000000..48a54c7
--- /dev/null
+++ b/libs/gui/CpuConsumer.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2012 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_NDEBUG 0
+#define LOG_TAG "CpuConsumer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Log.h>
+
+#include <gui/CpuConsumer.h>
+
+#define CC_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define CC_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define CC_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define CC_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define CC_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
+
+namespace android {
+
+// Get an ID that's unique within this process.
+static int32_t createProcessUniqueId() {
+ static volatile int32_t globalCounter = 0;
+ return android_atomic_inc(&globalCounter);
+}
+
+CpuConsumer::CpuConsumer(uint32_t maxLockedBuffers) :
+ mMaxLockedBuffers(maxLockedBuffers),
+ mCurrentLockedBuffers(0)
+{
+ mName = String8::format("cc-unnamed-%d-%d", getpid(),
+ createProcessUniqueId());
+
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ mBufferPointers[i] = NULL;
+ }
+
+ mBufferQueue = new BufferQueue(true);
+
+ wp<BufferQueue::ConsumerListener> listener;
+ sp<BufferQueue::ConsumerListener> proxy;
+ listener = static_cast<BufferQueue::ConsumerListener*>(this);
+ proxy = new BufferQueue::ProxyConsumerListener(listener);
+
+ status_t err = mBufferQueue->consumerConnect(proxy);
+ if (err != NO_ERROR) {
+ ALOGE("CpuConsumer: error connecting to BufferQueue: %s (%d)",
+ strerror(-err), err);
+ } else {
+ mBufferQueue->setSynchronousMode(true);
+ mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN);
+ mBufferQueue->setConsumerName(mName);
+ }
+}
+
+CpuConsumer::~CpuConsumer()
+{
+ Mutex::Autolock _l(mMutex);
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ freeBufferLocked(i);
+ }
+ mBufferQueue->consumerDisconnect();
+ mBufferQueue.clear();
+}
+
+void CpuConsumer::setName(const String8& name) {
+ Mutex::Autolock _l(mMutex);
+ mName = name;
+ mBufferQueue->setConsumerName(name);
+}
+
+status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) {
+ status_t err;
+
+ if (!nativeBuffer) return BAD_VALUE;
+ if (mCurrentLockedBuffers == mMaxLockedBuffers) {
+ return INVALID_OPERATION;
+ }
+
+ BufferQueue::BufferItem b;
+
+ Mutex::Autolock _l(mMutex);
+
+ err = mBufferQueue->acquireBuffer(&b);
+ if (err != OK) {
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ return BAD_VALUE;
+ } else {
+ CC_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
+ return err;
+ }
+ }
+
+ int buf = b.mBuf;
+
+ if (b.mGraphicBuffer != NULL) {
+ if (mBufferPointers[buf] != NULL) {
+ CC_LOGE("Reallocation of buffer %d while in consumer use!", buf);
+ mBufferQueue->releaseBuffer(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+ return BAD_VALUE;
+ }
+ mBufferSlot[buf] = b.mGraphicBuffer;
+ }
+
+ err = mBufferSlot[buf]->lock(
+ GraphicBuffer::USAGE_SW_READ_OFTEN,
+ b.mCrop,
+ &mBufferPointers[buf]);
+
+ if (mBufferPointers[buf] != NULL && err != OK) {
+ CC_LOGE("Unable to lock buffer for CPU reading: %s (%d)", strerror(-err),
+ err);
+ return err;
+ }
+
+ nativeBuffer->data = reinterpret_cast<uint8_t*>(mBufferPointers[buf]);
+ nativeBuffer->width = mBufferSlot[buf]->getWidth();
+ nativeBuffer->height = mBufferSlot[buf]->getHeight();
+ nativeBuffer->format = mBufferSlot[buf]->getPixelFormat();
+ nativeBuffer->stride = mBufferSlot[buf]->getStride();
+
+ nativeBuffer->crop = b.mCrop;
+ nativeBuffer->transform = b.mTransform;
+ nativeBuffer->scalingMode = b.mScalingMode;
+ nativeBuffer->timestamp = b.mTimestamp;
+ nativeBuffer->frameNumber = b.mFrameNumber;
+
+ mCurrentLockedBuffers++;
+
+ return OK;
+}
+
+status_t CpuConsumer::unlockBuffer(const LockedBuffer &nativeBuffer) {
+ Mutex::Autolock _l(mMutex);
+ int buf = 0;
+ status_t err;
+
+ void *bufPtr = reinterpret_cast<void *>(nativeBuffer.data);
+ for (; buf < BufferQueue::NUM_BUFFER_SLOTS; buf++) {
+ if (bufPtr == mBufferPointers[buf]) break;
+ }
+ if (buf == BufferQueue::NUM_BUFFER_SLOTS) {
+ CC_LOGE("%s: Can't find buffer to free", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ mBufferPointers[buf] = NULL;
+ err = mBufferSlot[buf]->unlock();
+ if (err != OK) {
+ CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__, buf);
+ return err;
+ }
+ err = mBufferQueue->releaseBuffer(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+ if (err == BufferQueue::STALE_BUFFER_SLOT) {
+ freeBufferLocked(buf);
+ } else if (err != OK) {
+ CC_LOGE("%s: Unable to release graphic buffer %d to queue", __FUNCTION__,
+ buf);
+ return err;
+ }
+
+ mCurrentLockedBuffers--;
+
+ return OK;
+}
+
+void CpuConsumer::setFrameAvailableListener(
+ const sp<FrameAvailableListener>& listener) {
+ CC_LOGV("setFrameAvailableListener");
+ Mutex::Autolock lock(mMutex);
+ mFrameAvailableListener = listener;
+}
+
+
+void CpuConsumer::onFrameAvailable() {
+ CC_LOGV("onFrameAvailable");
+ sp<FrameAvailableListener> listener;
+ { // scope for the lock
+ Mutex::Autolock _l(mMutex);
+ listener = mFrameAvailableListener;
+ }
+
+ if (listener != NULL) {
+ CC_LOGV("actually calling onFrameAvailable");
+ listener->onFrameAvailable();
+ }
+}
+
+void CpuConsumer::onBuffersReleased() {
+ CC_LOGV("onBuffersReleased");
+
+ Mutex::Autolock lock(mMutex);
+
+ uint32_t mask = 0;
+ mBufferQueue->getReleasedBuffers(&mask);
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ if (mask & (1 << i)) {
+ freeBufferLocked(i);
+ }
+ }
+
+}
+
+status_t CpuConsumer::freeBufferLocked(int buf) {
+ status_t err = OK;
+
+ if (mBufferPointers[buf] != NULL) {
+ CC_LOGW("Buffer %d freed while locked by consumer", buf);
+ mBufferPointers[buf] = NULL;
+ err = mBufferSlot[buf]->unlock();
+ if (err != OK) {
+ CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__, buf);
+ }
+ mCurrentLockedBuffers--;
+ }
+ mBufferSlot[buf] = NULL;
+ return err;
+}
+
+} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 1f1794c..8177e4d 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -193,6 +193,20 @@
result = interface_cast<IDisplayEventConnection>(reply.readStrongBinder());
return result;
}
+
+ virtual void blank()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ remote()->transact(BnSurfaceComposer::BLANK, data, &reply);
+ }
+
+ virtual void unblank()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ remote()->transact(BnSurfaceComposer::UNBLANK, data, &reply);
+ }
};
IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer");
@@ -279,6 +293,14 @@
reply->writeStrongBinder(connection->asBinder());
return NO_ERROR;
} break;
+ case BLANK: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ blank();
+ } break;
+ case UNBLANK: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ unblank();
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk
index 741534b..3a8e356 100644
--- a/libs/gui/tests/Android.mk
+++ b/libs/gui/tests/Android.mk
@@ -31,6 +31,35 @@
# to integrate with auto-test framework.
include $(BUILD_NATIVE_TEST)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CpuConsumer_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ CpuConsumer_test.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libEGL \
+ libGLESv2 \
+ libbinder \
+ libcutils \
+ libgui \
+ libstlport \
+ libui \
+ libutils \
+
+LOCAL_C_INCLUDES := \
+ bionic \
+ bionic/libstdc++/include \
+ external/gtest/include \
+ external/stlport/stlport \
+
+# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+# to integrate with auto-test framework.
+include $(BUILD_NATIVE_TEST)
+
# Include subdirectory makefiles
# ============================================================
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
new file mode 100644
index 0000000..7ad60e8
--- /dev/null
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2012 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 "CpuConsumer_test"
+//#define LOG_NDEBUG 0
+//#define LOG_NNDEBUG 0
+
+#ifdef LOG_NNDEBUG
+#define ALOGVV(...) ALOGV(__VA_ARGS__)
+#else
+#define ALOGVV(...) ((void)0)
+#endif
+
+#include <gtest/gtest.h>
+#include <gui/CpuConsumer.h>
+#include <gui/SurfaceTextureClient.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/String8.h>
+#include <utils/Thread.h>
+#include <utils/Mutex.h>
+#include <utils/Condition.h>
+
+#include <ui/FramebufferNativeWindow.h>
+
+namespace android {
+
+struct CpuConsumerTestParams {
+ uint32_t width;
+ uint32_t height;
+ int maxLockedBuffers;
+ PixelFormat format;
+};
+
+::std::ostream& operator<<(::std::ostream& os, const CpuConsumerTestParams& p) {
+ return os << "[ (" << p.width << ", " << p.height << "), B:"
+ << p.maxLockedBuffers << ", F:0x"
+ << ::std::hex << p.format << "]";
+}
+
+class CpuConsumerTest : public ::testing::TestWithParam<CpuConsumerTestParams> {
+protected:
+
+ virtual void SetUp() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ CpuConsumerTestParams params = GetParam();
+ ALOGV("** Starting test %s (%d x %d, %d, 0x%x)",
+ test_info->name(),
+ params.width, params.height,
+ params.maxLockedBuffers, params.format);
+ mCC = new CpuConsumer(params.maxLockedBuffers);
+ String8 name("CpuConsumer_Under_Test");
+ mCC->setName(name);
+ mSTC = new SurfaceTextureClient(mCC->getProducerInterface());
+ mANW = mSTC;
+ }
+
+ virtual void TearDown() {
+ mANW.clear();
+ mSTC.clear();
+ mCC.clear();
+ }
+
+ class FrameWaiter : public CpuConsumer::FrameAvailableListener {
+ public:
+ FrameWaiter():
+ mPendingFrames(0) {
+ }
+
+ void waitForFrame() {
+ Mutex::Autolock lock(mMutex);
+ while (mPendingFrames == 0) {
+ mCondition.wait(mMutex);
+ }
+ mPendingFrames--;
+ }
+
+ virtual void onFrameAvailable() {
+ Mutex::Autolock lock(mMutex);
+ mPendingFrames++;
+ mCondition.signal();
+ }
+
+ int mPendingFrames;
+ Mutex mMutex;
+ Condition mCondition;
+ };
+
+ // Note that SurfaceTexture will lose the notifications
+ // onBuffersReleased and onFrameAvailable as there is currently
+ // no way to forward the events. This DisconnectWaiter will not let the
+ // disconnect finish until finishDisconnect() is called. It will
+ // also block until a disconnect is called
+ class DisconnectWaiter : public BufferQueue::ConsumerListener {
+ public:
+ DisconnectWaiter () :
+ mWaitForDisconnect(false),
+ mPendingFrames(0) {
+ }
+
+ void waitForFrame() {
+ Mutex::Autolock lock(mMutex);
+ while (mPendingFrames == 0) {
+ mFrameCondition.wait(mMutex);
+ }
+ mPendingFrames--;
+ }
+
+ virtual void onFrameAvailable() {
+ Mutex::Autolock lock(mMutex);
+ mPendingFrames++;
+ mFrameCondition.signal();
+ }
+
+ virtual void onBuffersReleased() {
+ Mutex::Autolock lock(mMutex);
+ while (!mWaitForDisconnect) {
+ mDisconnectCondition.wait(mMutex);
+ }
+ }
+
+ void finishDisconnect() {
+ Mutex::Autolock lock(mMutex);
+ mWaitForDisconnect = true;
+ mDisconnectCondition.signal();
+ }
+
+ private:
+ Mutex mMutex;
+
+ bool mWaitForDisconnect;
+ Condition mDisconnectCondition;
+
+ int mPendingFrames;
+ Condition mFrameCondition;
+ };
+
+ sp<CpuConsumer> mCC;
+ sp<SurfaceTextureClient> mSTC;
+ sp<ANativeWindow> mANW;
+};
+
+#define ASSERT_NO_ERROR(err, msg) \
+ ASSERT_EQ(NO_ERROR, err) << msg << strerror(-err)
+
+void checkPixel(const CpuConsumer::LockedBuffer &buf,
+ uint32_t x, uint32_t y, uint32_t r, uint32_t g, uint32_t b) {
+ // Ignores components that don't exist for given pixel
+ switch(buf.format) {
+ case HAL_PIXEL_FORMAT_RAW_SENSOR: {
+ String8 msg;
+ uint16_t *bPtr = (uint16_t*)buf.data;
+ bPtr += y * buf.stride + x;
+ // GRBG Bayer mosaic; only check the matching channel
+ switch( ((y & 1) << 1) | (x & 1) ) {
+ case 0: // G
+ case 3: // G
+ EXPECT_EQ(g, *bPtr);
+ break;
+ case 1: // R
+ EXPECT_EQ(r, *bPtr);
+ break;
+ case 2: // B
+ EXPECT_EQ(b, *bPtr);
+ break;
+ }
+ break;
+ }
+ default: {
+ ADD_FAILURE() << "Unknown format for check:" << buf.format;
+ break;
+ }
+ }
+}
+
+// Fill a YV12 buffer with a multi-colored checkerboard pattern
+void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) {
+ const int blockWidth = w > 16 ? w / 16 : 1;
+ const int blockHeight = h > 16 ? h / 16 : 1;
+ const int yuvTexOffsetY = 0;
+ int yuvTexStrideY = stride;
+ int yuvTexOffsetV = yuvTexStrideY * h;
+ int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
+ int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
+ int yuvTexStrideU = yuvTexStrideV;
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ int parityX = (x / blockWidth) & 1;
+ int parityY = (y / blockHeight) & 1;
+ unsigned char intensity = (parityX ^ parityY) ? 63 : 191;
+ buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity;
+ if (x < w / 2 && y < h / 2) {
+ buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity;
+ if (x * 2 < w / 2 && y * 2 < h / 2) {
+ buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] =
+ buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] =
+ buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] =
+ buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] =
+ intensity;
+ }
+ }
+ }
+ }
+}
+
+// Fill a RAW sensor buffer with a multi-colored checkerboard pattern.
+// Assumes GRBG mosaic ordering. Result should be a grid in a 2x2 pattern
+// of [ R, B; G, W]
+void fillBayerRawBuffer(uint8_t* buf, int w, int h, int stride) {
+ ALOGVV("fillBayerRawBuffer: %p with %d x %d, stride %d", buf, w, h ,stride);
+ // Blocks need to be even-width/height, aim for 8-wide otherwise
+ const int blockWidth = (w > 16 ? w / 8 : 2) & ~0x1;
+ const int blockHeight = (h > 16 ? h / 8 : 2) & ~0x1;
+ for (int y = 0; y < h; y+=2) {
+ uint16_t *bPtr1 = ((uint16_t*)buf) + stride*y;
+ uint16_t *bPtr2 = bPtr1 + stride;
+ for (int x = 0; x < w; x+=2) {
+ int blockX = (x / blockWidth ) & 1;
+ int blockY = (y / blockHeight) & 1;
+ unsigned short r = (blockX == blockY) ? 1000 : 200;
+ unsigned short g = blockY ? 1000: 200;
+ unsigned short b = blockX ? 1000: 200;
+ // GR row
+ *bPtr1++ = g;
+ *bPtr1++ = r;
+ // BG row
+ *bPtr2++ = b;
+ *bPtr2++ = g;
+ }
+ }
+
+}
+
+void checkBayerRawBuffer(const CpuConsumer::LockedBuffer &buf) {
+ uint32_t w = buf.width;
+ uint32_t h = buf.height;
+ const int blockWidth = (w > 16 ? w / 8 : 2) & ~0x1;
+ const int blockHeight = (h > 16 ? h / 8 : 2) & ~0x1;
+ const int blockRows = h / blockHeight;
+ const int blockCols = w / blockWidth;
+
+ // Top-left square is red
+ checkPixel(buf, 0, 0, 1000, 200, 200);
+ checkPixel(buf, 1, 0, 1000, 200, 200);
+ checkPixel(buf, 0, 1, 1000, 200, 200);
+ checkPixel(buf, 1, 1, 1000, 200, 200);
+
+ // One-right square is blue
+ checkPixel(buf, blockWidth, 0, 200, 200, 1000);
+ checkPixel(buf, blockWidth + 1, 0, 200, 200, 1000);
+ checkPixel(buf, blockWidth, 1, 200, 200, 1000);
+ checkPixel(buf, blockWidth + 1, 1, 200, 200, 1000);
+
+ // One-down square is green
+ checkPixel(buf, 0, blockHeight, 200, 1000, 200);
+ checkPixel(buf, 1, blockHeight, 200, 1000, 200);
+ checkPixel(buf, 0, blockHeight + 1, 200, 1000, 200);
+ checkPixel(buf, 1, blockHeight + 1, 200, 1000, 200);
+
+ // One-diag square is white
+ checkPixel(buf, blockWidth, blockHeight, 1000, 1000, 1000);
+ checkPixel(buf, blockWidth + 1, blockHeight, 1000, 1000, 1000);
+ checkPixel(buf, blockWidth, blockHeight + 1, 1000, 1000, 1000);
+ checkPixel(buf, blockWidth + 1, blockHeight + 1, 1000, 1000, 1000);
+
+ // Test bottom-right pixel
+ const int maxBlockX = ((w-1) / blockWidth) & 0x1;
+ const int maxBlockY = ((w-1) / blockHeight) & 0x1;
+ unsigned short maxR = (maxBlockX == maxBlockY) ? 1000 : 200;
+ unsigned short maxG = maxBlockY ? 1000: 200;
+ unsigned short maxB = maxBlockX ? 1000: 200;
+ checkPixel(buf, w-1, h-1, maxR, maxG, maxB);
+}
+
+// Fill a YV12 buffer with red outside a given rectangle and green inside it.
+void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride,
+ const android_native_rect_t& rect) {
+ const int yuvTexOffsetY = 0;
+ int yuvTexStrideY = stride;
+ int yuvTexOffsetV = yuvTexStrideY * h;
+ int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
+ int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
+ int yuvTexStrideU = yuvTexStrideV;
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ bool inside = rect.left <= x && x < rect.right &&
+ rect.top <= y && y < rect.bottom;
+ buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64;
+ if (x < w / 2 && y < h / 2) {
+ bool inside = rect.left <= 2*x && 2*x < rect.right &&
+ rect.top <= 2*y && 2*y < rect.bottom;
+ buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16;
+ buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] =
+ inside ? 16 : 255;
+ }
+ }
+ }
+}
+
+void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) {
+ const size_t PIXEL_SIZE = 4;
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ off_t offset = (y * stride + x) * PIXEL_SIZE;
+ for (int c = 0; c < 4; c++) {
+ int parityX = (x / (1 << (c+2))) & 1;
+ int parityY = (y / (1 << (c+2))) & 1;
+ buf[offset + c] = (parityX ^ parityY) ? 231 : 35;
+ }
+ }
+ }
+}
+
+void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, uint8_t r,
+ uint8_t g, uint8_t b, uint8_t a) {
+ const size_t PIXEL_SIZE = 4;
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < h; x++) {
+ off_t offset = (y * stride + x) * PIXEL_SIZE;
+ buf[offset + 0] = r;
+ buf[offset + 1] = g;
+ buf[offset + 2] = b;
+ buf[offset + 3] = a;
+ }
+ }
+}
+
+// Configures the ANativeWindow producer-side interface based on test parameters
+void configureANW(const sp<ANativeWindow>& anw,
+ const CpuConsumerTestParams& params,
+ int maxBufferSlack) {
+ status_t err;
+ err = native_window_set_buffers_geometry(anw.get(),
+ params.width, params.height, params.format);
+ ASSERT_NO_ERROR(err, "set_buffers_geometry error: ");
+
+ err = native_window_set_usage(anw.get(),
+ GRALLOC_USAGE_SW_WRITE_OFTEN);
+ ASSERT_NO_ERROR(err, "set_usage error: ");
+
+ int minUndequeuedBuffers;
+ err = anw.get()->query(anw.get(),
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers);
+ ASSERT_NO_ERROR(err, "query error: ");
+
+ ALOGVV("Setting buffer count to %d",
+ maxBufferSlack + 1 + minUndequeuedBuffers);
+ err = native_window_set_buffer_count(anw.get(),
+ maxBufferSlack + 1 + minUndequeuedBuffers);
+ ASSERT_NO_ERROR(err, "set_buffer_count error: ");
+
+}
+
+// Produce one frame of image data; assumes format and resolution configuration
+// is already done.
+void produceOneFrame(const sp<ANativeWindow>& anw,
+ const CpuConsumerTestParams& params,
+ int64_t timestamp, uint32_t *stride) {
+ status_t err;
+ ANativeWindowBuffer* anb;
+ ALOGVV("Dequeue buffer from %p", anw.get());
+ err = anw->dequeueBuffer(anw.get(), &anb);
+ ASSERT_NO_ERROR(err, "dequeueBuffer error: ");
+
+ ASSERT_TRUE(anb != NULL);
+
+ sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+
+ ALOGVV("Lock buffer from %p", anw.get());
+ err = anw->lockBuffer(anw.get(), buf->getNativeBuffer());
+ ASSERT_NO_ERROR(err, "lockBuffer error: ");
+
+ *stride = buf->getStride();
+ uint8_t* img = NULL;
+
+ ALOGVV("Lock buffer from %p for write", anw.get());
+ err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+ ASSERT_NO_ERROR(err, "lock error: ");
+
+ switch (params.format) {
+ case HAL_PIXEL_FORMAT_YV12:
+ fillYV12Buffer(img, params.width, params.height, *stride);
+ break;
+ case HAL_PIXEL_FORMAT_RAW_SENSOR:
+ fillBayerRawBuffer(img, params.width, params.height, buf->getStride());
+ break;
+ default:
+ FAIL() << "Unknown pixel format under test!";
+ break;
+ }
+ ALOGVV("Unlock buffer from %p", anw.get());
+ err = buf->unlock();
+ ASSERT_NO_ERROR(err, "unlock error: ");
+
+ ALOGVV("Set timestamp to %p", anw.get());
+ err = native_window_set_buffers_timestamp(anw.get(), timestamp);
+ ASSERT_NO_ERROR(err, "set_buffers_timestamp error: ");
+
+ ALOGVV("Queue buffer to %p", anw.get());
+ err = anw->queueBuffer(anw.get(), buf->getNativeBuffer());
+ ASSERT_NO_ERROR(err, "queueBuffer error:");
+};
+
+TEST_P(CpuConsumerTest, FromCpuSingle) {
+ status_t err;
+ CpuConsumerTestParams params = GetParam();
+
+ // Set up
+
+ ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, 1));
+
+ // Produce
+
+ const int64_t time = 12345678L;
+ uint32_t stride;
+ ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time,
+ &stride));
+
+ // Consume
+
+ CpuConsumer::LockedBuffer b;
+ err = mCC->lockNextBuffer(&b);
+ ASSERT_NO_ERROR(err, "getNextBuffer error: ");
+
+ ASSERT_TRUE(b.data != NULL);
+ EXPECT_EQ(params.width, b.width);
+ EXPECT_EQ(params.height, b.height);
+ EXPECT_EQ(params.format, b.format);
+ EXPECT_EQ(stride, b.stride);
+ EXPECT_EQ(time, b.timestamp);
+
+ checkBayerRawBuffer(b);
+ mCC->unlockBuffer(b);
+}
+
+TEST_P(CpuConsumerTest, FromCpuManyInQueue) {
+ status_t err;
+ CpuConsumerTestParams params = GetParam();
+
+ const int numInQueue = 5;
+ // Set up
+
+ ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, numInQueue));
+
+ // Produce
+
+ const int64_t time[numInQueue] = { 1L, 2L, 3L, 4L, 5L};
+ uint32_t stride[numInQueue];
+
+ for (int i = 0; i < numInQueue; i++) {
+ ALOGV("Producing frame %d", i);
+ ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time[i],
+ &stride[i]));
+ }
+
+ // Consume
+
+ for (int i = 0; i < numInQueue; i++) {
+ ALOGV("Consuming frame %d", i);
+ CpuConsumer::LockedBuffer b;
+ err = mCC->lockNextBuffer(&b);
+ ASSERT_NO_ERROR(err, "getNextBuffer error: ");
+
+ ASSERT_TRUE(b.data != NULL);
+ EXPECT_EQ(params.width, b.width);
+ EXPECT_EQ(params.height, b.height);
+ EXPECT_EQ(params.format, b.format);
+ EXPECT_EQ(stride[i], b.stride);
+ EXPECT_EQ(time[i], b.timestamp);
+
+ checkBayerRawBuffer(b);
+
+ mCC->unlockBuffer(b);
+ }
+}
+
+TEST_P(CpuConsumerTest, FromCpuLockMax) {
+ status_t err;
+ CpuConsumerTestParams params = GetParam();
+
+ // Set up
+
+ ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, params.maxLockedBuffers + 1));
+
+ // Produce
+
+ const int64_t time = 1234L;
+ uint32_t stride;
+
+ for (int i = 0; i < params.maxLockedBuffers + 1; i++) {
+ ALOGV("Producing frame %d", i);
+ ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time,
+ &stride));
+ }
+
+ // Consume
+
+ CpuConsumer::LockedBuffer *b = new CpuConsumer::LockedBuffer[params.maxLockedBuffers];
+ for (int i = 0; i < params.maxLockedBuffers; i++) {
+ ALOGV("Locking frame %d", i);
+ err = mCC->lockNextBuffer(&b[i]);
+ ASSERT_NO_ERROR(err, "getNextBuffer error: ");
+
+ ASSERT_TRUE(b[i].data != NULL);
+ EXPECT_EQ(params.width, b[i].width);
+ EXPECT_EQ(params.height, b[i].height);
+ EXPECT_EQ(params.format, b[i].format);
+ EXPECT_EQ(stride, b[i].stride);
+ EXPECT_EQ(time, b[i].timestamp);
+
+ checkBayerRawBuffer(b[i]);
+ }
+
+ ALOGV("Locking frame %d (too many)", params.maxLockedBuffers);
+ CpuConsumer::LockedBuffer bTooMuch;
+ err = mCC->lockNextBuffer(&bTooMuch);
+ ASSERT_TRUE(err == INVALID_OPERATION) << "Allowing too many locks";
+
+ ALOGV("Unlocking frame 0");
+ err = mCC->unlockBuffer(b[0]);
+ ASSERT_NO_ERROR(err, "Could not unlock buffer 0: ");
+
+ ALOGV("Locking frame %d (should work now)", params.maxLockedBuffers);
+ err = mCC->lockNextBuffer(&bTooMuch);
+ ASSERT_NO_ERROR(err, "Did not allow new lock after unlock");
+
+ ASSERT_TRUE(bTooMuch.data != NULL);
+ EXPECT_EQ(params.width, bTooMuch.width);
+ EXPECT_EQ(params.height, bTooMuch.height);
+ EXPECT_EQ(params.format, bTooMuch.format);
+ EXPECT_EQ(stride, bTooMuch.stride);
+ EXPECT_EQ(time, bTooMuch.timestamp);
+
+ checkBayerRawBuffer(bTooMuch);
+
+ ALOGV("Unlocking extra buffer");
+ err = mCC->unlockBuffer(bTooMuch);
+ ASSERT_NO_ERROR(err, "Could not unlock extra buffer: ");
+
+ ALOGV("Locking frame %d (no more available)", params.maxLockedBuffers + 1);
+ err = mCC->lockNextBuffer(&b[0]);
+ ASSERT_EQ(BAD_VALUE, err) << "Not out of buffers somehow";
+
+ for (int i = 1; i < params.maxLockedBuffers; i++) {
+ mCC->unlockBuffer(b[i]);
+ }
+
+ delete[] b;
+
+}
+
+CpuConsumerTestParams rawTestSets[] = {
+ { 512, 512, 1, HAL_PIXEL_FORMAT_RAW_SENSOR},
+ { 512, 512, 3, HAL_PIXEL_FORMAT_RAW_SENSOR},
+ { 2608, 1960, 1, HAL_PIXEL_FORMAT_RAW_SENSOR},
+ { 2608, 1960, 3, HAL_PIXEL_FORMAT_RAW_SENSOR},
+ { 100, 100, 1, HAL_PIXEL_FORMAT_RAW_SENSOR},
+ { 100, 100, 3, HAL_PIXEL_FORMAT_RAW_SENSOR}
+};
+
+INSTANTIATE_TEST_CASE_P(RawTests,
+ CpuConsumerTest,
+ ::testing::ValuesIn(rawTestSets));
+
+} // namespace android
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index cad7720..db17546 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -118,7 +118,7 @@
*/
int ZipFileRO::entryToIndex(const ZipEntryRO entry) const
{
- long ent = ((long) entry) - kZipEntryAdj;
+ long ent = ((intptr_t) entry) - kZipEntryAdj;
if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) {
ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent);
return -1;
@@ -459,7 +459,7 @@
for (int ent = 0; ent < mHashTableSize; ent++) {
if (mHashTable[ent].name != NULL) {
if (idx-- == 0)
- return (ZipEntryRO) (ent + kZipEntryAdj);
+ return (ZipEntryRO) (intptr_t)(ent + kZipEntryAdj);
}
}
diff --git a/opengl/specs/EGL_ANDROID_fence_sync.txt b/opengl/specs/EGL_ANDROID_fence_sync.txt
new file mode 100644
index 0000000..bb618c9
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_fence_sync.txt
@@ -0,0 +1,213 @@
+Name
+
+ ANDROID_fence_sync
+
+Name Strings
+
+ EGL_ANDROID_fence_sync
+
+Contributors
+
+ Jamie Gennis
+
+Contact
+
+ Jamie Gennis, Google Inc. (jgennis 'at' google.com)
+
+Status
+
+ Draft.
+
+Version
+
+ Version 1, May 29, 2012
+
+Number
+
+ EGL Extension #XXX
+
+Dependencies
+
+ Requires EGL 1.1
+
+ This extension is written against the wording of the EGL 1.2 Specification
+
+ EGL_KHR_fence_sync is required.
+
+Overview
+
+ This extension enables the creation of EGL fence sync objects that are
+ associated with an Android Sync HAL fence object. These EGL fence sync
+ objects have nearly identical semantics to those defined by the
+ KHR_fence_sync extension, except that they have an additional attribute
+ storing the file descriptor referring to the native Android fence object.
+
+New Types
+
+ None.
+
+New Procedures and Functions
+
+ None.
+
+New Tokens
+
+ Accepted by the <type> parameter of eglCreateSyncKHR, and returned
+ in <value> when eglGetSyncAttribKHR is called with <attribute>
+ EGL_SYNC_TYPE_KHR:
+
+ EGL_SYNC_ANDROID_FENCE_ANDROID 0x3144
+
+ Accepted by the <attribute> parameter of eglGetSyncAttribKHR:
+
+ EGL_SYNC_FENCE_FD_ANDROID 0x3145
+
+ Returned in <value> when eglGetSyncAttribKHR is called with <attribute>
+ EGL_SYNC_CONDITION_KHR:
+
+ EGL_SYNC_ANDROID_FENCE_SIGNALED_ANDROID 0x3146
+
+Changes to Chapter 3 of the EGL 1.2 Specification (EGL Functions and Errors)
+
+ Add the following after the sixth paragraph of Section 3.8.1 (Sync
+ Objects), added by KHR_fence_sync
+
+ "If <type> is EGL_SYNC_ANDROID_FENCE_ANDROID, an EGL Android fence sync
+ object is created. In this case the EGL_SYNC_FENCE_FD_ANDROID attribute may
+ optionally be specified. If this attribute is specified, it must be set to
+ a file descriptor that refers to a native Android fence object.
+
+ The default values for the EGL Android fence sync object attributes are as
+ follows:
+
+ Attribute Name Initial Attribute Value(s)
+ --------------- --------------------------
+ EGL_SYNC_TYPE_KHR EGL_SYNC_ANDROID_FENCE_ANDROID
+ EGL_SYNC_STATUS_KHR EGL_UNSIGNALED_KHR
+ EGL_SYNC_CONDITION_KHR EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR
+
+ Upon creation of an EGL Android fence sync object, the
+ EGL_SYNC_FENCE_FD_ANDROID attribute is set to a newly generated file
+ descriptor that refers to a native Android fence object. If the
+ EGL_SYNC_FENCE_FD_ANDROID attribute is specified in the eglCreateSyncKHR
+ call then the generated file descriptor refers to the same native Android
+ fence object as the file descriptor passed to eglCreateSyncKHR. Note,
+ however, that the value of the sync object attribute is *not* the same file
+ descriptor as the one passed to eglCreateSyncKHR - it simply refers to the
+ same underlying native Android fence object. Additionally, if the
+ EGL_SYNC_FENCE_FD_ANDROID attribute is specified then the
+ EGL_SYNC_CONDITION_KHR attribute is set to
+ EGL_SYNC_ANDROID_FENCE_SIGNALED_ANDROID and the EGL_SYNC_STATUS_KHR
+ attribute is set to reflect the signal status of the native Android fence
+ object."
+
+ Modify Section 3.8.1 (Sync Objects), added by KHR_fence_sync, starting at
+ the seventh paragraph
+
+ "When a fence sync object is created or when an EGL Android fence sync
+ object is created without specifying the EGL_SYNC_FENCE_FD_ANDROID
+ attribute, eglCreateSyncKHR also inserts a fence command into the command
+ stream of the bound client API's current context (i.e., the context
+ returned by eglGetCurrentContext), and associates it with the newly created
+ sync object.
+
+ When the condition of the sync object is satisfied by the fence command,
+ the sync is signaled by the associated client API context, causing any
+ eglClientWaitSyncKHR commands (see below) blocking on <sync> to unblock. If
+ the sync object is an EGL Android fence sync object then the native Android
+ fence object is also signaled when the condition is satisfied. The
+ EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR condition is satisfied by completion
+ of the fence command corresponding to the sync object and all preceding
+ commands in the associated client API context's command stream. The sync
+ object will not be signaled until all effects from these commands on the
+ client API's internal and framebuffer state are fully realized. No other
+ state is affected by execution of the fence command.
+
+ The EGL_SYNC_ANDROID_FENCE_SIGNALED_ANDROID condition is satisfied by the
+ signaling of the native Android fence object. When this happens any
+ eglClientWaitSyncKHR commands blocking on <sync> unblock."
+
+ Modify the list of eglCreateSyncKHR errors in Section 3.8.1 (Sync Objects),
+ added by KHR_fence_sync
+
+ "Errors
+ ------
+
+ * If <dpy> is not the name of a valid, initialized EGLDisplay,
+ EGL_NO_SYNC_KHR is returned and an EGL_BAD_DISPLAY error is
+ generated.
+ * If <type> is EGL_SYNC_FENCE_KHR and <attrib_list> is neither NULL nor
+ empty (containing only EGL_NONE), EGL_NO_SYNC_KHR is returned and an
+ EGL_BAD_ATTRIBUTE error is generated.
+ * If <type> is EGL_SYNC_ANDROID_FENCE_ANDROID and <attrib_list> contains
+ an attribute other than EGL_SYNC_FENCE_FD_ANDROID, EGL_NO_SYNC_KHR is
+ returned and an EGL_BAD_ATTRIBUTE error is generated.
+ * If <type> is not a supported type of sync object,
+ EGL_NO_SYNC_KHR is returned and an EGL_BAD_ATTRIBUTE error is
+ generated.
+ * If <type> is EGL_SYNC_FENCE_KHR or EGL_SYNC_ANDROID_FENCE_ANDROID and
+ no context is current for the bound API (i.e., eglGetCurrentContext
+ returns EGL_NO_CONTEXT), EGL_NO_SYNC_KHR is returned and an
+ EGL_BAD_MATCH error is generated.
+ * If <type> is EGL_SYNC_FENCE_KHR or EGL_SYNC_ANDROID_FENCE_ANDROID and
+ <dpy> does not match the EGLDisplay of the currently bound context for
+ the currently bound client API (the EGLDisplay returned by
+ eglGetCurrentDisplay()) then EGL_NO_SYNC_KHR is returned and an
+ EGL_BAD_MATCH error is generated.
+ * If <type> is EGL_SYNC_FENCE_KHR or EGL_SYNC_ANDROID_FENCE_ANDROID and
+ the currently bound client API does not support the client API
+ extension indicating it can place fence commands, then EGL_NO_SYNC_KHR
+ is returned and an EGL_BAD_MATCH error is generated."
+
+ Modify table 3.cc in Section 3.8.1 (Sync Objects), added by KHR_fence_sync
+
+ "
+ Attribute Description Supported Sync Objects
+ ----------------- ----------------------- ----------------------
+ EGL_SYNC_TYPE_KHR Type of the sync object All
+ EGL_SYNC_STATUS_KHR Status of the sync object All
+ EGL_SYNC_CONDITION_KHR Signaling condition EGL_SYNC_FENCE_KHR and
+ EGL_SYNC_ANDROID_FENCE_ANDROID only
+ EGL_SYNC_FENCE_FD_ANDROID Native Android fence EGL_SYNC_ANDROID_FENCE_ANDROID only
+ object file descriptor
+ "
+
+ Modify the second paragraph description of eglDestroySyncKHR in Section
+ 3.8.1 (Sync Objects), added by KHR_fence_sync
+
+ "If no errors are generated, EGL_TRUE is returned, and <sync> will no
+ longer be the handle of a valid sync object. Additionally, if <sync> is an
+ EGL Android fence sync object then the file descriptor stored in the
+ EGL_SYNC_FENCE_FD_ANDROID attribute is closed and is no longer a valid file
+ descriptor."
+
+Issues
+
+ 1. Should EGLSyncKHR objects that wrap Android fence objects use the
+ EGL_SYNC_FENCE_KHR type?
+
+ RESOLVED: A new sync object type will be added.
+
+ We don't want to require all EGL fence sync objects to wrap Android fence
+ objects, so we need some way to tell the EGL implementation at sync object
+ creation whether the sync object should support querying the Android fence
+ FD attribute. We could do this with either a new sync object type or with a
+ boolean attribute. It might be nice to pick up future signaling conditions
+ that might be added for fence sync objects, but there may be things that
+ get added that don't make sense in the context of Android fence objects.
+
+ 2. Who is responsible for dup'ing the Android fence file descriptors?
+
+ RESOLVED: The recipient of a file descriptor is responsible for dup'ing it
+ if it wishes to maintain a reference to the Android fence object.
+
+ This means that when eglCreateSyncKHR is called with an existing file
+ descriptor, the EGL implementation must dup it. On the other hand, when
+ eglGetSyncAttribKHR is called to query the file descriptor, it is the
+ responsibility of the caller to dup the file descriptor if it wishes to
+ maintain a reference that will outlast the EGLSyncKHR object.
+
+Revision History
+
+#1 (Jamie Gennis, May 29, 2012)
+ - Initial draft.
diff --git a/opengl/specs/README b/opengl/specs/README
index 16b278f..af3f165 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -10,4 +10,7 @@
0x3141 (unused)
0x3142 EGL_ANDROID_recordable
0x3143 EGL_VERSION_HW_ANDROID (internal use)
-0x3144 - 0x314F (unused)
+0x3144 EGL_SYNC_ANDROID_FENCE_ANDROID
+0x3145 EGL_SYNC_FENCE_FD_ANDROID
+0x3146 EGL_SYNC_ANDROID_FENCE_SIGNALED_ANDROID
+0x3147 - 0x314F (unused)
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
index d3a8bde..e161c44 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
@@ -30,91 +30,13 @@
// ----------------------------------------------------------------------------
namespace android {
-static char const * const kSleepFileName = "/sys/power/wait_for_fb_sleep";
-static char const * const kWakeFileName = "/sys/power/wait_for_fb_wake";
-
-// ----------------------------------------------------------------------------
-
-DisplayHardwareBase::DisplayEventThread::DisplayEventThread(
- const sp<SurfaceFlinger>& flinger)
- : Thread(false), mFlinger(flinger) {
-}
-
-DisplayHardwareBase::DisplayEventThread::~DisplayEventThread() {
-}
-
-status_t DisplayHardwareBase::DisplayEventThread::initCheck() const {
- return ((access(kSleepFileName, R_OK) == 0 &&
- access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT;
-}
-
-bool DisplayHardwareBase::DisplayEventThread::threadLoop() {
-
- if (waitForFbSleep() == NO_ERROR) {
- sp<SurfaceFlinger> flinger = mFlinger.promote();
- ALOGD("About to give-up screen, flinger = %p", flinger.get());
- if (flinger != 0) {
- flinger->screenReleased();
- }
- if (waitForFbWake() == NO_ERROR) {
- ALOGD("Screen about to return, flinger = %p", flinger.get());
- if (flinger != 0) {
- flinger->screenAcquired();
- }
- return true;
- }
- }
-
- // error, exit the thread
- return false;
-}
-
-status_t DisplayHardwareBase::DisplayEventThread::waitForFbSleep() {
- int err = 0;
- char buf;
- int fd = open(kSleepFileName, O_RDONLY, 0);
- // if the file doesn't exist, the error will be caught in read() below
- do {
- err = read(fd, &buf, 1);
- } while (err < 0 && errno == EINTR);
- close(fd);
- ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
- return err < 0 ? -errno : int(NO_ERROR);
-}
-
-status_t DisplayHardwareBase::DisplayEventThread::waitForFbWake() {
- int err = 0;
- char buf;
- int fd = open(kWakeFileName, O_RDONLY, 0);
- // if the file doesn't exist, the error will be caught in read() below
- do {
- err = read(fd, &buf, 1);
- } while (err < 0 && errno == EINTR);
- close(fd);
- ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
- return err < 0 ? -errno : int(NO_ERROR);
-}
-
-// ----------------------------------------------------------------------------
-
DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
uint32_t displayIndex)
{
mScreenAcquired = true;
- mDisplayEventThread = new DisplayEventThread(flinger);
-}
-
-void DisplayHardwareBase::startSleepManagement() const {
- if (mDisplayEventThread->initCheck() == NO_ERROR) {
- mDisplayEventThread->run("DisplayEventThread", PRIORITY_URGENT_DISPLAY);
- } else {
- ALOGW("/sys/power/wait_for_fb_{wake|sleep} don't exist");
- }
}
DisplayHardwareBase::~DisplayHardwareBase() {
- // request exit
- mDisplayEventThread->requestExitAndWait();
}
bool DisplayHardwareBase::canDraw() const {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 25e80d7..61b5f71 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -297,7 +297,6 @@
// start the EventThread
mEventThread = new EventThread(this);
mEventQueue.setEventThread(mEventThread);
- hw.startSleepManagement();
/*
* We're now ready to accept clients...
@@ -1363,6 +1362,7 @@
// ---------------------------------------------------------------------------
void SurfaceFlinger::onScreenAcquired() {
+ ALOGD("Screen about to return, flinger = %p", this);
const DisplayHardware& hw(graphicPlane(0).displayHardware());
hw.acquireScreen();
mEventThread->onScreenAcquired();
@@ -1374,6 +1374,7 @@
}
void SurfaceFlinger::onScreenReleased() {
+ ALOGD("About to give-up screen, flinger = %p", this);
const DisplayHardware& hw(graphicPlane(0).displayHardware());
if (hw.isScreenAcquired()) {
mEventThread->onScreenReleased();
@@ -1382,7 +1383,7 @@
}
}
-void SurfaceFlinger::screenAcquired() {
+void SurfaceFlinger::unblank() {
class MessageScreenAcquired : public MessageBase {
SurfaceFlinger* flinger;
public:
@@ -1396,7 +1397,7 @@
postMessageSync(msg);
}
-void SurfaceFlinger::screenReleased() {
+void SurfaceFlinger::blank() {
class MessageScreenReleased : public MessageBase {
SurfaceFlinger* flinger;
public:
@@ -1654,6 +1655,8 @@
case BOOT_FINISHED:
case TURN_ELECTRON_BEAM_OFF:
case TURN_ELECTRON_BEAM_ON:
+ case BLANK:
+ case UNBLANK:
{
// codes that require permission check
IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d9c2033..f0e955b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -182,11 +182,10 @@
virtual status_t turnElectronBeamOff(int32_t mode);
virtual status_t turnElectronBeamOn(int32_t mode);
-
// called when screen needs to turn off
- void screenReleased();
+ virtual void blank();
// called when screen is turning back on
- void screenAcquired();
+ virtual void unblank();
// called on the main thread in response to screenReleased()
void onScreenReleased();