android_view_DisplayEventReceiver.cpp revision 603b44589682db3ff33ade172facb0c5e309f1be
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<MessageQueue>& messageQueue);
49
50    status_t initialize();
51    status_t scheduleVsync();
52
53protected:
54    virtual ~NativeDisplayEventReceiver();
55
56private:
57    jobject mReceiverObjGlobal;
58    sp<MessageQueue> mMessageQueue;
59    DisplayEventReceiver mReceiver;
60    bool mWaitingForVsync;
61
62    static int handleReceiveCallback(int receiveFd, int events, void* data);
63    bool readLastVsyncMessage(nsecs_t* outTimestamp, uint32_t* outCount);
64};
65
66
67NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
68        jobject receiverObj, const sp<MessageQueue>& messageQueue) :
69        mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
70        mMessageQueue(messageQueue), mWaitingForVsync(false) {
71    ALOGV("receiver %p ~ Initializing input event receiver.", this);
72}
73
74NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
75    ALOGV("receiver %p ~ Disposing display event receiver.", this);
76
77    if (!mReceiver.initCheck()) {
78        mMessageQueue->getLooper()->removeFd(mReceiver.getFd());
79    }
80
81    JNIEnv* env = AndroidRuntime::getJNIEnv();
82    env->DeleteGlobalRef(mReceiverObjGlobal);
83}
84
85status_t NativeDisplayEventReceiver::initialize() {
86    status_t result = mReceiver.initCheck();
87    if (result) {
88        ALOGW("Failed to initialize display event receiver, status=%d", result);
89        return result;
90    }
91
92    int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT,
93            handleReceiveCallback, this);
94    if (rc < 0) {
95        return UNKNOWN_ERROR;
96    }
97    return OK;
98}
99
100status_t NativeDisplayEventReceiver::scheduleVsync() {
101    if (!mWaitingForVsync) {
102        ALOGV("receiver %p ~ Scheduling vsync.", this);
103
104        // Drain all pending events.
105        nsecs_t vsyncTimestamp;
106        uint32_t vsyncCount;
107        readLastVsyncMessage(&vsyncTimestamp, &vsyncCount);
108
109        status_t status = mReceiver.requestNextVsync();
110        if (status) {
111            ALOGW("Failed to request next vsync, status=%d", status);
112            return status;
113        }
114
115        mWaitingForVsync = true;
116    }
117    return OK;
118}
119
120int NativeDisplayEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) {
121    sp<NativeDisplayEventReceiver> r = static_cast<NativeDisplayEventReceiver*>(data);
122
123    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
124        ALOGE("Display event receiver pipe was closed or an error occurred.  "
125                "events=0x%x", events);
126        return 0; // remove the callback
127    }
128
129    if (!(events & ALOOPER_EVENT_INPUT)) {
130        ALOGW("Received spurious callback for unhandled poll event.  "
131                "events=0x%x", events);
132        return 1; // keep the callback
133    }
134
135    // Drain all pending events, keep the last vsync.
136    nsecs_t vsyncTimestamp;
137    uint32_t vsyncCount;
138    if (!r->readLastVsyncMessage(&vsyncTimestamp, &vsyncCount)) {
139        ALOGV("receiver %p ~ Woke up but there was no vsync pulse!", data);
140        return 1; // keep the callback, did not obtain a vsync pulse
141    }
142
143    ALOGV("receiver %p ~ Vsync pulse: timestamp=%lld, count=%d",
144            data, vsyncTimestamp, vsyncCount);
145    r->mWaitingForVsync = false;
146
147    JNIEnv* env = AndroidRuntime::getJNIEnv();
148
149    ALOGV("receiver %p ~ Invoking vsync handler.", data);
150    env->CallVoidMethod(r->mReceiverObjGlobal,
151            gDisplayEventReceiverClassInfo.dispatchVsync, vsyncTimestamp, vsyncCount);
152    ALOGV("receiver %p ~ Returned from vsync handler.", data);
153
154    r->mMessageQueue->raiseAndClearException(env, "dispatchVsync");
155    return 1; // keep the callback
156}
157
158bool NativeDisplayEventReceiver::readLastVsyncMessage(
159        nsecs_t* outTimestamp, uint32_t* outCount) {
160    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
161    ssize_t n;
162    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
163        ALOGV("receiver %p ~ Read %d events.", this, int(n));
164        while (n-- > 0) {
165            if (buf[n].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
166                *outTimestamp = buf[n].header.timestamp;
167                *outCount = buf[n].vsync.count;
168                return true; // stop at last vsync in the buffer
169            }
170        }
171    }
172    if (n < 0) {
173        ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
174    }
175    return false;
176}
177
178
179static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
180        jobject messageQueueObj) {
181    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
182    if (messageQueue == NULL) {
183        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
184        return 0;
185    }
186
187    sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
188            receiverObj, messageQueue);
189    status_t status = receiver->initialize();
190    if (status) {
191        String8 message;
192        message.appendFormat("Failed to initialize display event receiver.  status=%d", status);
193        jniThrowRuntimeException(env, message.string());
194        return 0;
195    }
196
197    receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
198    return reinterpret_cast<jint>(receiver.get());
199}
200
201static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) {
202    sp<NativeDisplayEventReceiver> receiver =
203            reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
204    receiver->decStrong(gDisplayEventReceiverClassInfo.clazz); // drop reference held by the object
205}
206
207static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jint receiverPtr) {
208    sp<NativeDisplayEventReceiver> receiver =
209            reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
210    status_t status = receiver->scheduleVsync();
211    if (status) {
212        String8 message;
213        message.appendFormat("Failed to schedule next vertical sync pulse.  status=%d", status);
214        jniThrowRuntimeException(env, message.string());
215    }
216}
217
218
219static JNINativeMethod gMethods[] = {
220    /* name, signature, funcPtr */
221    { "nativeInit",
222            "(Landroid/view/DisplayEventReceiver;Landroid/os/MessageQueue;)I",
223            (void*)nativeInit },
224    { "nativeDispose",
225            "(I)V",
226            (void*)nativeDispose },
227    { "nativeScheduleVsync", "(I)V",
228            (void*)nativeScheduleVsync }
229};
230
231#define FIND_CLASS(var, className) \
232        var = env->FindClass(className); \
233        LOG_FATAL_IF(! var, "Unable to find class " className); \
234        var = jclass(env->NewGlobalRef(var));
235
236#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
237        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
238        LOG_FATAL_IF(! var, "Unable to find method " methodName);
239
240int register_android_view_DisplayEventReceiver(JNIEnv* env) {
241    int res = jniRegisterNativeMethods(env, "android/view/DisplayEventReceiver",
242            gMethods, NELEM(gMethods));
243    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
244
245    FIND_CLASS(gDisplayEventReceiverClassInfo.clazz, "android/view/DisplayEventReceiver");
246
247    GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchVsync,
248            gDisplayEventReceiverClassInfo.clazz,
249            "dispatchVsync", "(JI)V");
250    return 0;
251}
252
253} // namespace android
254