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 
36 namespace 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.
41 static const size_t EVENT_BUFFER_SIZE = 100;
42 
43 static struct {
44     jclass clazz;
45 
46     jmethodID dispatchVsync;
47     jmethodID dispatchHotplug;
48 } gDisplayEventReceiverClassInfo;
49 
50 
51 class NativeDisplayEventReceiver : public LooperCallback {
52 public:
53     NativeDisplayEventReceiver(JNIEnv* env,
54             jobject receiverWeak, const sp<MessageQueue>& messageQueue);
55 
56     status_t initialize();
57     void dispose();
58     status_t scheduleVsync();
59 
60 protected:
61     virtual ~NativeDisplayEventReceiver();
62 
63 private:
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 
NativeDisplayEventReceiver(JNIEnv * env,jobject receiverWeak,const sp<MessageQueue> & messageQueue)76 NativeDisplayEventReceiver::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 
~NativeDisplayEventReceiver()83 NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
84     JNIEnv* env = AndroidRuntime::getJNIEnv();
85     env->DeleteGlobalRef(mReceiverWeakGlobal);
86 }
87 
initialize()88 status_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 
dispose()103 void 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 
scheduleVsync()111 status_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 
handleEvent(int receiveFd,int events,void * data)132 int 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 
processPendingEvents(nsecs_t * outTimestamp,int32_t * outId,uint32_t * outCount)159 bool 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 
dispatchVsync(nsecs_t timestamp,int32_t id,uint32_t count)192 void 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 
dispatchHotplug(nsecs_t timestamp,int32_t id,bool connected)206 void 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 
nativeInit(JNIEnv * env,jclass clazz,jobject receiverWeak,jobject messageQueueObj)221 static 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 
nativeDispose(JNIEnv * env,jclass clazz,jlong receiverPtr)243 static 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 
nativeScheduleVsync(JNIEnv * env,jclass clazz,jlong receiverPtr)250 static 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 
262 static 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 
register_android_view_DisplayEventReceiver(JNIEnv * env)274 int 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