• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
NativeDisplayEventReceiver(JNIEnv * env,jobject receiverObj,const sp<MessageQueue> & messageQueue)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 
~NativeDisplayEventReceiver()78 NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
79     JNIEnv* env = AndroidRuntime::getJNIEnv();
80     env->DeleteGlobalRef(mReceiverObjGlobal);
81 }
82 
initialize()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 
dispose()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 
scheduleVsync()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 
handleEvent(int receiveFd,int events,void * data)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 
processPendingEvents(nsecs_t * outTimestamp,int32_t * outId,uint32_t * outCount)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 
dispatchVsync(nsecs_t timestamp,int32_t id,uint32_t count)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 
dispatchHotplug(nsecs_t timestamp,int32_t id,bool connected)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 
nativeInit(JNIEnv * env,jclass clazz,jobject receiverObj,jobject messageQueueObj)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 
nativeDispose(JNIEnv * env,jclass clazz,jint receiverPtr)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 
nativeScheduleVsync(JNIEnv * env,jclass clazz,jint receiverPtr)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 
register_android_view_DisplayEventReceiver(JNIEnv * env)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