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 31 namespace 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. 36 static const size_t EVENT_BUFFER_SIZE = 100; 37 38 static struct { 39 jclass clazz; 40 41 jmethodID dispatchVsync; 42 jmethodID dispatchHotplug; 43 } gDisplayEventReceiverClassInfo; 44 45 46 class NativeDisplayEventReceiver : public LooperCallback { 47 public: 48 NativeDisplayEventReceiver(JNIEnv* env, 49 jobject receiverObj, const sp<MessageQueue>& messageQueue); 50 51 status_t initialize(); 52 void dispose(); 53 status_t scheduleVsync(); 54 55 protected: 56 virtual ~NativeDisplayEventReceiver(); 57 58 private: 59 jobject mReceiverObjGlobal; 60 sp<MessageQueue> mMessageQueue; 61 DisplayEventReceiver mReceiver; 62 bool mWaitingForVsync; 63 64 virtual int handleEvent(int receiveFd, int events, void* data); 65 bool processPendingEvents(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount); 66 void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count); 67 void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected); 68 }; 69 70 71 NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, 72 jobject receiverObj, const sp<MessageQueue>& messageQueue) : 73 mReceiverObjGlobal(env->NewGlobalRef(receiverObj)), 74 mMessageQueue(messageQueue), mWaitingForVsync(false) { 75 ALOGV("receiver %p ~ Initializing input event receiver.", this); 76 } 77 78 NativeDisplayEventReceiver::~NativeDisplayEventReceiver() { 79 JNIEnv* env = AndroidRuntime::getJNIEnv(); 80 env->DeleteGlobalRef(mReceiverObjGlobal); 81 } 82 83 status_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 = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT, 91 this, NULL); 92 if (rc < 0) { 93 return UNKNOWN_ERROR; 94 } 95 return OK; 96 } 97 98 void NativeDisplayEventReceiver::dispose() { 99 ALOGV("receiver %p ~ Disposing display event receiver.", this); 100 101 if (!mReceiver.initCheck()) { 102 mMessageQueue->getLooper()->removeFd(mReceiver.getFd()); 103 } 104 } 105 106 status_t NativeDisplayEventReceiver::scheduleVsync() { 107 if (!mWaitingForVsync) { 108 ALOGV("receiver %p ~ Scheduling vsync.", this); 109 110 // Drain all pending events. 111 nsecs_t vsyncTimestamp; 112 int32_t vsyncDisplayId; 113 uint32_t vsyncCount; 114 processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount); 115 116 status_t status = mReceiver.requestNextVsync(); 117 if (status) { 118 ALOGW("Failed to request next vsync, status=%d", status); 119 return status; 120 } 121 122 mWaitingForVsync = true; 123 } 124 return OK; 125 } 126 127 int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) { 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; 142 int32_t vsyncDisplayId; 143 uint32_t vsyncCount; 144 if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { 145 ALOGV("receiver %p ~ Vsync pulse: timestamp=%lld, id=%d, count=%d", 146 this, vsyncTimestamp, vsyncDisplayId, vsyncCount); 147 mWaitingForVsync = false; 148 dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); 149 } 150 151 return 1; // keep the callback 152 } 153 154 bool NativeDisplayEventReceiver::processPendingEvents( 155 nsecs_t* outTimestamp, int32_t* outId, uint32_t* outCount) { 156 bool gotVsync = false; 157 DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; 158 ssize_t n; 159 while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { 160 ALOGV("receiver %p ~ Read %d events.", this, int(n)); 161 for (ssize_t i = 0; i < n; i++) { 162 const DisplayEventReceiver::Event& ev = buf[i]; 163 switch (ev.header.type) { 164 case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: 165 // Later vsync events will just overwrite the info from earlier 166 // ones. That's fine, we only care about the most recent. 167 gotVsync = true; 168 *outTimestamp = ev.header.timestamp; 169 *outId = ev.header.id; 170 *outCount = ev.vsync.count; 171 break; 172 case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: 173 dispatchHotplug(ev.header.timestamp, ev.header.id, ev.hotplug.connected); 174 break; 175 default: 176 ALOGW("receiver %p ~ ignoring unknown event type %#x", this, ev.header.type); 177 break; 178 } 179 } 180 } 181 if (n < 0) { 182 ALOGW("Failed to get events from display event receiver, status=%d", status_t(n)); 183 } 184 return gotVsync; 185 } 186 187 void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) { 188 JNIEnv* env = AndroidRuntime::getJNIEnv(); 189 190 ALOGV("receiver %p ~ Invoking vsync handler.", this); 191 env->CallVoidMethod(mReceiverObjGlobal, 192 gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count); 193 ALOGV("receiver %p ~ Returned from vsync handler.", this); 194 195 mMessageQueue->raiseAndClearException(env, "dispatchVsync"); 196 } 197 198 void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected) { 199 JNIEnv* env = AndroidRuntime::getJNIEnv(); 200 201 ALOGV("receiver %p ~ Invoking hotplug handler.", this); 202 env->CallVoidMethod(mReceiverObjGlobal, 203 gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, id, connected); 204 ALOGV("receiver %p ~ Returned from hotplug handler.", this); 205 206 mMessageQueue->raiseAndClearException(env, "dispatchHotplug"); 207 } 208 209 210 static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj, 211 jobject messageQueueObj) { 212 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); 213 if (messageQueue == NULL) { 214 jniThrowRuntimeException(env, "MessageQueue is not initialized."); 215 return 0; 216 } 217 218 sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env, 219 receiverObj, messageQueue); 220 status_t status = receiver->initialize(); 221 if (status) { 222 String8 message; 223 message.appendFormat("Failed to initialize display event receiver. status=%d", status); 224 jniThrowRuntimeException(env, message.string()); 225 return 0; 226 } 227 228 receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object 229 return reinterpret_cast<jint>(receiver.get()); 230 } 231 232 static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) { 233 sp<NativeDisplayEventReceiver> receiver = 234 reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr); 235 receiver->dispose(); 236 receiver->decStrong(gDisplayEventReceiverClassInfo.clazz); // drop reference held by the object 237 } 238 239 static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jint receiverPtr) { 240 sp<NativeDisplayEventReceiver> receiver = 241 reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr); 242 status_t status = receiver->scheduleVsync(); 243 if (status) { 244 String8 message; 245 message.appendFormat("Failed to schedule next vertical sync pulse. status=%d", status); 246 jniThrowRuntimeException(env, message.string()); 247 } 248 } 249 250 251 static JNINativeMethod gMethods[] = { 252 /* name, signature, funcPtr */ 253 { "nativeInit", 254 "(Landroid/view/DisplayEventReceiver;Landroid/os/MessageQueue;)I", 255 (void*)nativeInit }, 256 { "nativeDispose", 257 "(I)V", 258 (void*)nativeDispose }, 259 { "nativeScheduleVsync", "(I)V", 260 (void*)nativeScheduleVsync } 261 }; 262 263 #define FIND_CLASS(var, className) \ 264 var = env->FindClass(className); \ 265 LOG_FATAL_IF(! var, "Unable to find class " className); \ 266 var = jclass(env->NewGlobalRef(var)); 267 268 #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 269 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 270 LOG_FATAL_IF(! var, "Unable to find method " methodName); 271 272 int register_android_view_DisplayEventReceiver(JNIEnv* env) { 273 int res = jniRegisterNativeMethods(env, "android/view/DisplayEventReceiver", 274 gMethods, NELEM(gMethods)); 275 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 276 277 FIND_CLASS(gDisplayEventReceiverClassInfo.clazz, "android/view/DisplayEventReceiver"); 278 279 GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchVsync, 280 gDisplayEventReceiverClassInfo.clazz, 281 "dispatchVsync", "(JII)V"); 282 GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchHotplug, 283 gDisplayEventReceiverClassInfo.clazz, 284 "dispatchHotplug", "(JIZ)V"); 285 return 0; 286 } 287 288 } // namespace android 289