android_view_DisplayEventReceiver.cpp revision 58aedbc9bea13415e2d42cf7c9fe8a7efd243e66
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "DisplayEventReceiver" 18 19//#define LOG_NDEBUG 0 20 21 22#include "JNIHelp.h" 23 24#include <android_runtime/AndroidRuntime.h> 25#include <utils/Log.h> 26#include <utils/Looper.h> 27#include <utils/threads.h> 28#include <gui/DisplayEventReceiver.h> 29#include "android_os_MessageQueue.h" 30 31namespace android { 32 33// Number of events to read at a time from the DisplayEventReceiver pipe. 34// The value should be large enough that we can quickly drain the pipe 35// using just a few large reads. 36static const size_t EVENT_BUFFER_SIZE = 100; 37 38static struct { 39 jclass clazz; 40 41 jmethodID dispatchVsync; 42} gDisplayEventReceiverClassInfo; 43 44 45class NativeDisplayEventReceiver : public RefBase { 46public: 47 NativeDisplayEventReceiver(JNIEnv* env, 48 jobject receiverObj, const sp<Looper>& looper); 49 50 status_t initialize(); 51 status_t scheduleVsync(); 52 static int handleReceiveCallback(int receiveFd, int events, void* data); 53 54protected: 55 virtual ~NativeDisplayEventReceiver(); 56 57private: 58 jobject mReceiverObjGlobal; 59 sp<Looper> mLooper; 60 DisplayEventReceiver mReceiver; 61 bool mWaitingForVsync; 62}; 63 64 65NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, 66 jobject receiverObj, const sp<Looper>& looper) : 67 mReceiverObjGlobal(env->NewGlobalRef(receiverObj)), 68 mLooper(looper), mWaitingForVsync(false) { 69 ALOGV("receiver %p ~ Initializing input event receiver.", this); 70} 71 72NativeDisplayEventReceiver::~NativeDisplayEventReceiver() { 73 ALOGV("receiver %p ~ Disposing display event receiver.", this); 74 75 if (!mReceiver.initCheck()) { 76 mLooper->removeFd(mReceiver.getFd()); 77 } 78 79 JNIEnv* env = AndroidRuntime::getJNIEnv(); 80 env->DeleteGlobalRef(mReceiverObjGlobal); 81} 82 83status_t NativeDisplayEventReceiver::initialize() { 84 status_t result = mReceiver.initCheck(); 85 if (result) { 86 ALOGW("Failed to initialize display event receiver, status=%d", result); 87 return result; 88 } 89 90 int rc = mLooper->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT, 91 handleReceiveCallback, this); 92 if (rc < 0) { 93 return UNKNOWN_ERROR; 94 } 95 return OK; 96} 97 98status_t NativeDisplayEventReceiver::scheduleVsync() { 99 if (!mWaitingForVsync) { 100 ALOGV("receiver %p ~ Scheduling vsync.", this); 101 102 // Drain all pending events. 103 DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; 104 ssize_t n; 105 while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { 106 ALOGV("receiver %p ~ Drained %d events.", this, int(n)); 107 } 108 109 if (n < 0) { 110 ALOGW("Failed to drain events from display event receiver, status=%d", status_t(n)); 111 return status_t(n); 112 } 113 114 status_t status = mReceiver.requestNextVsync(); 115 if (status) { 116 ALOGW("Failed to request next vsync, status=%d", status); 117 return status; 118 } 119 120 mWaitingForVsync = true; 121 } 122 return OK; 123} 124 125int NativeDisplayEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) { 126 sp<NativeDisplayEventReceiver> r = static_cast<NativeDisplayEventReceiver*>(data); 127 128 if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { 129 ALOGE("Display event receiver pipe was closed or an error occurred. " 130 "events=0x%x", events); 131 return 0; // remove the callback 132 } 133 134 if (!(events & ALOOPER_EVENT_INPUT)) { 135 ALOGW("Received spurious callback for unhandled poll event. " 136 "events=0x%x", events); 137 return 1; // keep the callback 138 } 139 140 // Drain all pending events, keep the last vsync. 141 nsecs_t vsyncTimestamp = -1; 142 uint32_t vsyncCount = 0; 143 144 DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; 145 ssize_t n; 146 while ((n = r->mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { 147 ALOGV("receiver %p ~ Read %d events.", data, int(n)); 148 while (n-- > 0) { 149 if (buf[n].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { 150 vsyncTimestamp = buf[n].header.timestamp; 151 vsyncCount = buf[n].vsync.count; 152 break; // stop at last vsync in the buffer 153 } 154 } 155 } 156 157 if (vsyncTimestamp < 0) { 158 ALOGV("receiver %p ~ Woke up but there was no vsync pulse!", data); 159 return 1; // keep the callback, did not obtain a vsync pulse 160 } 161 162 ALOGV("receiver %p ~ Vsync pulse: timestamp=%lld, count=%d", 163 data, vsyncTimestamp, vsyncCount); 164 r->mWaitingForVsync = false; 165 166 JNIEnv* env = AndroidRuntime::getJNIEnv(); 167 168 ALOGV("receiver %p ~ Invoking vsync handler.", data); 169 env->CallVoidMethod(r->mReceiverObjGlobal, 170 gDisplayEventReceiverClassInfo.dispatchVsync, vsyncTimestamp, vsyncCount); 171 ALOGV("receiver %p ~ Returned from vsync handler.", data); 172 173 if (env->ExceptionCheck()) { 174 ALOGE("An exception occurred while dispatching a vsync event."); 175 LOGE_EX(env); 176 env->ExceptionClear(); 177 } 178 179 return 1; // keep the callback 180} 181 182 183static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj, 184 jobject messageQueueObj) { 185 sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); 186 if (looper == NULL) { 187 jniThrowRuntimeException(env, "MessageQueue is not initialized."); 188 return 0; 189 } 190 191 sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env, 192 receiverObj, looper); 193 status_t status = receiver->initialize(); 194 if (status) { 195 String8 message; 196 message.appendFormat("Failed to initialize display event receiver. status=%d", status); 197 jniThrowRuntimeException(env, message.string()); 198 return 0; 199 } 200 201 receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object 202 return reinterpret_cast<jint>(receiver.get()); 203} 204 205static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) { 206 sp<NativeDisplayEventReceiver> receiver = 207 reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr); 208 receiver->decStrong(gDisplayEventReceiverClassInfo.clazz); // drop reference held by the object 209} 210 211static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jint receiverPtr) { 212 sp<NativeDisplayEventReceiver> receiver = 213 reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr); 214 status_t status = receiver->scheduleVsync(); 215 if (status) { 216 String8 message; 217 message.appendFormat("Failed to schedule next vertical sync pulse. status=%d", status); 218 jniThrowRuntimeException(env, message.string()); 219 } 220} 221 222 223static JNINativeMethod gMethods[] = { 224 /* name, signature, funcPtr */ 225 { "nativeInit", 226 "(Landroid/view/DisplayEventReceiver;Landroid/os/MessageQueue;)I", 227 (void*)nativeInit }, 228 { "nativeDispose", 229 "(I)V", 230 (void*)nativeDispose }, 231 { "nativeScheduleVsync", "(I)V", 232 (void*)nativeScheduleVsync } 233}; 234 235#define FIND_CLASS(var, className) \ 236 var = env->FindClass(className); \ 237 LOG_FATAL_IF(! var, "Unable to find class " className); \ 238 var = jclass(env->NewGlobalRef(var)); 239 240#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 241 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 242 LOG_FATAL_IF(! var, "Unable to find method " methodName); 243 244int register_android_view_DisplayEventReceiver(JNIEnv* env) { 245 int res = jniRegisterNativeMethods(env, "android/view/DisplayEventReceiver", 246 gMethods, NELEM(gMethods)); 247 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 248 249 FIND_CLASS(gDisplayEventReceiverClassInfo.clazz, "android/view/DisplayEventReceiver"); 250 251 GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchVsync, 252 gDisplayEventReceiverClassInfo.clazz, 253 "dispatchVsync", "(JI)V"); 254 return 0; 255} 256 257} // namespace android 258