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#include "JNIHelp.h" 22 23#include <inttypes.h> 24 25#include <android_runtime/AndroidRuntime.h> 26#include <utils/Log.h> 27#include <utils/Looper.h> 28#include <utils/threads.h> 29#include <gui/DisplayEventReceiver.h> 30#include "android_os_MessageQueue.h" 31 32#include <ScopedLocalRef.h> 33 34#include "core_jni_helpers.h" 35 36namespace android { 37 38// Number of events to read at a time from the DisplayEventReceiver pipe. 39// The value should be large enough that we can quickly drain the pipe 40// using just a few large reads. 41static const size_t EVENT_BUFFER_SIZE = 100; 42 43static struct { 44 jclass clazz; 45 46 jmethodID dispatchVsync; 47 jmethodID dispatchHotplug; 48} gDisplayEventReceiverClassInfo; 49 50 51class NativeDisplayEventReceiver : public LooperCallback { 52public: 53 NativeDisplayEventReceiver(JNIEnv* env, 54 jobject receiverWeak, const sp<MessageQueue>& messageQueue); 55 56 status_t initialize(); 57 void dispose(); 58 status_t scheduleVsync(); 59 60protected: 61 virtual ~NativeDisplayEventReceiver(); 62 63private: 64 jobject mReceiverWeakGlobal; 65 sp<MessageQueue> mMessageQueue; 66 DisplayEventReceiver mReceiver; 67 bool mWaitingForVsync; 68 69 virtual int handleEvent(int receiveFd, int events, void* data); 70 bool processPendingEvents(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount); 71 void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count); 72 void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected); 73}; 74 75 76NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, 77 jobject receiverWeak, const sp<MessageQueue>& messageQueue) : 78 mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), 79 mMessageQueue(messageQueue), mWaitingForVsync(false) { 80 ALOGV("receiver %p ~ Initializing display event receiver.", this); 81} 82 83NativeDisplayEventReceiver::~NativeDisplayEventReceiver() { 84 JNIEnv* env = AndroidRuntime::getJNIEnv(); 85 env->DeleteGlobalRef(mReceiverWeakGlobal); 86} 87 88status_t NativeDisplayEventReceiver::initialize() { 89 status_t result = mReceiver.initCheck(); 90 if (result) { 91 ALOGW("Failed to initialize display event receiver, status=%d", result); 92 return result; 93 } 94 95 int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, 96 this, NULL); 97 if (rc < 0) { 98 return UNKNOWN_ERROR; 99 } 100 return OK; 101} 102 103void NativeDisplayEventReceiver::dispose() { 104 ALOGV("receiver %p ~ Disposing display event receiver.", this); 105 106 if (!mReceiver.initCheck()) { 107 mMessageQueue->getLooper()->removeFd(mReceiver.getFd()); 108 } 109} 110 111status_t NativeDisplayEventReceiver::scheduleVsync() { 112 if (!mWaitingForVsync) { 113 ALOGV("receiver %p ~ Scheduling vsync.", this); 114 115 // Drain all pending events. 116 nsecs_t vsyncTimestamp; 117 int32_t vsyncDisplayId; 118 uint32_t vsyncCount; 119 processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount); 120 121 status_t status = mReceiver.requestNextVsync(); 122 if (status) { 123 ALOGW("Failed to request next vsync, status=%d", status); 124 return status; 125 } 126 127 mWaitingForVsync = true; 128 } 129 return OK; 130} 131 132int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) { 133 if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { 134 ALOGE("Display event receiver pipe was closed or an error occurred. " 135 "events=0x%x", events); 136 return 0; // remove the callback 137 } 138 139 if (!(events & Looper::EVENT_INPUT)) { 140 ALOGW("Received spurious callback for unhandled poll event. " 141 "events=0x%x", events); 142 return 1; // keep the callback 143 } 144 145 // Drain all pending events, keep the last vsync. 146 nsecs_t vsyncTimestamp; 147 int32_t vsyncDisplayId; 148 uint32_t vsyncCount; 149 if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { 150 ALOGV("receiver %p ~ Vsync pulse: timestamp=%" PRId64 ", id=%d, count=%d", 151 this, vsyncTimestamp, vsyncDisplayId, vsyncCount); 152 mWaitingForVsync = false; 153 dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); 154 } 155 156 return 1; // keep the callback 157} 158 159bool NativeDisplayEventReceiver::processPendingEvents( 160 nsecs_t* outTimestamp, int32_t* outId, uint32_t* outCount) { 161 bool gotVsync = false; 162 DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; 163 ssize_t n; 164 while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { 165 ALOGV("receiver %p ~ Read %d events.", this, int(n)); 166 for (ssize_t i = 0; i < n; i++) { 167 const DisplayEventReceiver::Event& ev = buf[i]; 168 switch (ev.header.type) { 169 case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: 170 // Later vsync events will just overwrite the info from earlier 171 // ones. That's fine, we only care about the most recent. 172 gotVsync = true; 173 *outTimestamp = ev.header.timestamp; 174 *outId = ev.header.id; 175 *outCount = ev.vsync.count; 176 break; 177 case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: 178 dispatchHotplug(ev.header.timestamp, ev.header.id, ev.hotplug.connected); 179 break; 180 default: 181 ALOGW("receiver %p ~ ignoring unknown event type %#x", this, ev.header.type); 182 break; 183 } 184 } 185 } 186 if (n < 0) { 187 ALOGW("Failed to get events from display event receiver, status=%d", status_t(n)); 188 } 189 return gotVsync; 190} 191 192void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) { 193 JNIEnv* env = AndroidRuntime::getJNIEnv(); 194 195 ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); 196 if (receiverObj.get()) { 197 ALOGV("receiver %p ~ Invoking vsync handler.", this); 198 env->CallVoidMethod(receiverObj.get(), 199 gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count); 200 ALOGV("receiver %p ~ Returned from vsync handler.", this); 201 } 202 203 mMessageQueue->raiseAndClearException(env, "dispatchVsync"); 204} 205 206void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected) { 207 JNIEnv* env = AndroidRuntime::getJNIEnv(); 208 209 ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); 210 if (receiverObj.get()) { 211 ALOGV("receiver %p ~ Invoking hotplug handler.", this); 212 env->CallVoidMethod(receiverObj.get(), 213 gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, id, connected); 214 ALOGV("receiver %p ~ Returned from hotplug handler.", this); 215 } 216 217 mMessageQueue->raiseAndClearException(env, "dispatchHotplug"); 218} 219 220 221static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, 222 jobject messageQueueObj) { 223 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); 224 if (messageQueue == NULL) { 225 jniThrowRuntimeException(env, "MessageQueue is not initialized."); 226 return 0; 227 } 228 229 sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env, 230 receiverWeak, messageQueue); 231 status_t status = receiver->initialize(); 232 if (status) { 233 String8 message; 234 message.appendFormat("Failed to initialize display event receiver. status=%d", status); 235 jniThrowRuntimeException(env, message.string()); 236 return 0; 237 } 238 239 receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object 240 return reinterpret_cast<jlong>(receiver.get()); 241} 242 243static void nativeDispose(JNIEnv* env, jclass clazz, jlong receiverPtr) { 244 sp<NativeDisplayEventReceiver> receiver = 245 reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr); 246 receiver->dispose(); 247 receiver->decStrong(gDisplayEventReceiverClassInfo.clazz); // drop reference held by the object 248} 249 250static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) { 251 sp<NativeDisplayEventReceiver> receiver = 252 reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr); 253 status_t status = receiver->scheduleVsync(); 254 if (status) { 255 String8 message; 256 message.appendFormat("Failed to schedule next vertical sync pulse. status=%d", status); 257 jniThrowRuntimeException(env, message.string()); 258 } 259} 260 261 262static JNINativeMethod gMethods[] = { 263 /* name, signature, funcPtr */ 264 { "nativeInit", 265 "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)J", 266 (void*)nativeInit }, 267 { "nativeDispose", 268 "(J)V", 269 (void*)nativeDispose }, 270 { "nativeScheduleVsync", "(J)V", 271 (void*)nativeScheduleVsync } 272}; 273 274int register_android_view_DisplayEventReceiver(JNIEnv* env) { 275 int res = RegisterMethodsOrDie(env, "android/view/DisplayEventReceiver", gMethods, 276 NELEM(gMethods)); 277 278 jclass clazz = FindClassOrDie(env, "android/view/DisplayEventReceiver"); 279 gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); 280 281 gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env, 282 gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JII)V"); 283 gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env, 284 gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JIZ)V"); 285 286 return res; 287} 288 289} // namespace android 290