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