diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/view/DisplayEventReceiver.java | 115 | ||||
-rw-r--r-- | core/jni/Android.mk | 1 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
-rw-r--r-- | core/jni/android_view_DisplayEventReceiver.cpp | 263 | ||||
-rw-r--r-- | core/jni/android_view_InputEventReceiver.cpp | 1 |
5 files changed, 381 insertions, 1 deletions
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java new file mode 100644 index 0000000..d6711ee --- /dev/null +++ b/core/java/android/view/DisplayEventReceiver.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 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. + */ + +package android.view; + +import dalvik.system.CloseGuard; + +import android.os.Looper; +import android.os.MessageQueue; +import android.util.Log; + +/** + * Provides a low-level mechanism for an application to receive display events + * such as vertical sync. + * @hide + */ +public abstract class DisplayEventReceiver { + private static final String TAG = "DisplayEventReceiver"; + + private final CloseGuard mCloseGuard = CloseGuard.get(); + + private int mReceiverPtr; + + // We keep a reference message queue object here so that it is not + // GC'd while the native peer of the receiver is using them. + private MessageQueue mMessageQueue; + + private static native int nativeInit(DisplayEventReceiver receiver, + MessageQueue messageQueue); + private static native void nativeDispose(int receiverPtr); + private static native void nativeScheduleVsync(int receiverPtr); + + /** + * Creates a display event receiver. + * + * @param looper The looper to use when invoking callbacks. + */ + public DisplayEventReceiver(Looper looper) { + if (looper == null) { + throw new IllegalArgumentException("looper must not be null"); + } + + mMessageQueue = looper.getQueue(); + mReceiverPtr = nativeInit(this, mMessageQueue); + + mCloseGuard.open("dispose"); + } + + @Override + protected void finalize() throws Throwable { + try { + dispose(); + } finally { + super.finalize(); + } + } + + /** + * Disposes the receiver. + */ + public void dispose() { + if (mCloseGuard != null) { + mCloseGuard.close(); + } + if (mReceiverPtr != 0) { + nativeDispose(mReceiverPtr); + mReceiverPtr = 0; + } + mMessageQueue = null; + } + + /** + * Called when a vertical sync pulse is received. + * The recipient should render a frame and then call {@link #scheduleVsync} + * to schedule the next vertical sync pulse. + * + * @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()} + * timebase. + * @param frame The frame number. Increases by one for each vertical sync interval. + */ + public void onVsync(long timestampNanos, int frame) { + } + + /** + * Schedules a single vertical sync pulse to be delivered when the next + * display frame begins. + */ + public void scheduleVsync() { + if (mReceiverPtr == 0) { + Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event " + + "receiver has already been disposed."); + } else { + nativeScheduleVsync(mReceiverPtr); + } + } + + // Called from native code. + @SuppressWarnings("unused") + private void dispatchVsync(long timestampNanos, int frame) { + onVsync(timestampNanos, frame); + } +} diff --git a/core/jni/Android.mk b/core/jni/Android.mk index f20fbbb..bafee0e 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -47,6 +47,7 @@ LOCAL_SRC_FILES:= \ android_database_SQLiteStatement.cpp \ android_emoji_EmojiFactory.cpp \ android_view_Display.cpp \ + android_view_DisplayEventReceiver.cpp \ android_view_Surface.cpp \ android_view_TextureView.cpp \ android_view_InputChannel.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index c6447e1..8db7b24 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -115,6 +115,7 @@ extern int register_android_graphics_SurfaceTexture(JNIEnv* env); extern int register_android_graphics_Xfermode(JNIEnv* env); extern int register_android_graphics_PixelFormat(JNIEnv* env); extern int register_android_view_Display(JNIEnv* env); +extern int register_android_view_DisplayEventReceiver(JNIEnv* env); extern int register_android_view_GLES20Canvas(JNIEnv* env); extern int register_android_view_HardwareRenderer(JNIEnv* env); extern int register_android_view_Surface(JNIEnv* env); @@ -1098,6 +1099,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_SystemProperties), REG_JNI(register_android_os_Binder), REG_JNI(register_android_view_Display), + REG_JNI(register_android_view_DisplayEventReceiver), REG_JNI(register_android_nio_utils), REG_JNI(register_android_graphics_PixelFormat), REG_JNI(register_android_graphics_Graphics), diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp new file mode 100644 index 0000000..3b45ccd --- /dev/null +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 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 "DisplayEventReceiver" + +//#define LOG_NDEBUG 0 + + +#include "JNIHelp.h" + +#include <android_runtime/AndroidRuntime.h> +#include <utils/Log.h> +#include <utils/Looper.h> +#include <utils/threads.h> +#include <gui/DisplayEventReceiver.h> +#include "android_os_MessageQueue.h" + +namespace android { + +// Number of events to read at a time from the DisplayEventReceiver pipe. +// The value should be large enough that we can quickly drain the pipe +// using just a few large reads. +static const size_t EVENT_BUFFER_SIZE = 100; + +static struct { + jclass clazz; + + jmethodID dispatchVsync; +} gDisplayEventReceiverClassInfo; + + +class NativeDisplayEventReceiver : public RefBase { +public: + NativeDisplayEventReceiver(JNIEnv* env, + jobject receiverObj, const sp<Looper>& looper); + + status_t initialize(); + status_t scheduleVsync(); + static int handleReceiveCallback(int receiveFd, int events, void* data); + +protected: + virtual ~NativeDisplayEventReceiver(); + +private: + jobject mReceiverObjGlobal; + sp<Looper> mLooper; + DisplayEventReceiver mReceiver; + bool mWaitingForVsync; + bool mFdCallbackRegistered; +}; + + +NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, + jobject receiverObj, const sp<Looper>& looper) : + mReceiverObjGlobal(env->NewGlobalRef(receiverObj)), + mLooper(looper), mWaitingForVsync(false), mFdCallbackRegistered(false) { + ALOGV("receiver %p ~ Initializing input event receiver.", this); +} + +NativeDisplayEventReceiver::~NativeDisplayEventReceiver() { + ALOGV("receiver %p ~ Disposing display event receiver.", this); + + if (mFdCallbackRegistered) { + mLooper->removeFd(mReceiver.getFd()); + } + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mReceiverObjGlobal); +} + +status_t NativeDisplayEventReceiver::initialize() { + status_t result = mReceiver.initCheck(); + if (result) { + LOGW("Failed to initialize display event receiver, status=%d", result); + return result; + } + + return OK; +} + +status_t NativeDisplayEventReceiver::scheduleVsync() { + if (!mWaitingForVsync) { + ALOGV("receiver %p ~ Scheduling vsync.", this); + + // Drain all pending events. + DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; + ssize_t n; + while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + ALOGV("receiver %p ~ Drained %d events.", this, int(n)); + } + + if (n < 0) { + LOGW("Failed to drain events from display event receiver, status=%d", status_t(n)); + return status_t(n); + } + + if (!mFdCallbackRegistered) { + int rc = mLooper->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT, + handleReceiveCallback, this); + if (rc < 0) { + return UNKNOWN_ERROR; + } + mFdCallbackRegistered = true; + } + + mWaitingForVsync = true; + } + return OK; +} + +int NativeDisplayEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) { + sp<NativeDisplayEventReceiver> r = static_cast<NativeDisplayEventReceiver*>(data); + + if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { + LOGE("Display event receiver pipe was closed or an error occurred. " + "events=0x%x", events); + r->mFdCallbackRegistered = false; + return 0; // remove the callback + } + + if (!(events & ALOOPER_EVENT_INPUT)) { + LOGW("Received spurious callback for unhandled poll event. " + "events=0x%x", events); + return 1; // keep the callback + } + + // Drain all pending events, keep the last vsync. + nsecs_t vsyncTimestamp = -1; + uint32_t vsyncCount = 0; + + DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; + ssize_t n; + while ((n = r->mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + ALOGV("receiver %p ~ Read %d events.", this, int(n)); + while (n-- > 0) { + if (buf[n].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { + vsyncTimestamp = buf[n].header.timestamp; + vsyncCount = buf[n].vsync.count; + break; // stop at last vsync in the buffer + } + } + } + + if (vsyncTimestamp < 0) { + ALOGV("receiver %p ~ Woke up but there was no vsync pulse!", this); + return 1; // keep the callback, did not obtain a vsync pulse + } + + ALOGV("receiver %p ~ Vsync pulse: timestamp=%lld, count=%d", + this, vsyncTimestamp, vsyncCount); + r->mWaitingForVsync = false; + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + ALOGV("receiver %p ~ Invoking vsync handler.", this); + env->CallVoidMethod(r->mReceiverObjGlobal, + gDisplayEventReceiverClassInfo.dispatchVsync, vsyncTimestamp, vsyncCount); + ALOGV("receiver %p ~ Returned from vsync handler.", this); + + if (env->ExceptionCheck()) { + LOGE("An exception occurred while dispatching a vsync event."); + LOGE_EX(env); + env->ExceptionClear(); + } + + // Check whether dispatchVsync called scheduleVsync reentrantly and set mWaitingForVsync. + // If so, keep the callback, otherwise remove it. + if (r->mWaitingForVsync) { + return 1; // keep the callback + } + r->mFdCallbackRegistered = false; + return 0; // remove the callback +} + + +static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj, + jobject messageQueueObj) { + sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); + if (looper == NULL) { + jniThrowRuntimeException(env, "MessageQueue is not initialized."); + return 0; + } + + sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env, + receiverObj, looper); + status_t status = receiver->initialize(); + if (status) { + String8 message; + message.appendFormat("Failed to initialize display event receiver. status=%d", status); + jniThrowRuntimeException(env, message.string()); + return 0; + } + + receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object + return reinterpret_cast<jint>(receiver.get()); +} + +static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) { + sp<NativeDisplayEventReceiver> receiver = + reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr); + receiver->decStrong(gDisplayEventReceiverClassInfo.clazz); // drop reference held by the object +} + +static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jint receiverPtr) { + sp<NativeDisplayEventReceiver> receiver = + reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr); + status_t status = receiver->scheduleVsync(); + if (status) { + String8 message; + message.appendFormat("Failed to schedule next vertical sync pulse. status=%d", status); + jniThrowRuntimeException(env, message.string()); + } +} + + +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "nativeInit", + "(Landroid/view/DisplayEventReceiver;Landroid/os/MessageQueue;)I", + (void*)nativeInit }, + { "nativeDispose", + "(I)V", + (void*)nativeDispose }, + { "nativeScheduleVsync", "(I)V", + (void*)nativeScheduleVsync } +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + +#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method " methodName); + +int register_android_view_DisplayEventReceiver(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "android/view/DisplayEventReceiver", + gMethods, NELEM(gMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + FIND_CLASS(gDisplayEventReceiverClassInfo.clazz, "android/view/DisplayEventReceiver"); + + GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchVsync, + gDisplayEventReceiverClassInfo.clazz, + "dispatchVsync", "(JI)V"); + return 0; +} + +} // namespace android diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 9ae63dd..523baf1 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -27,7 +27,6 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> #include <utils/Looper.h> -#include <utils/KeyedVector.h> #include <utils/threads.h> #include <ui/InputTransport.h> #include "android_os_MessageQueue.h" |