android_view_DisplayEventReceiver.cpp revision 82b007d7572dceb0981b269338bd1ac6c40496c5
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    jmethodID dispatchHotplug;
43} gDisplayEventReceiverClassInfo;
44
45
46class NativeDisplayEventReceiver : public LooperCallback {
47public:
48    NativeDisplayEventReceiver(JNIEnv* env,
49            jobject receiverObj, const sp<MessageQueue>& messageQueue);
50
51    status_t initialize();
52    void dispose();
53    status_t scheduleVsync();
54
55protected:
56    virtual ~NativeDisplayEventReceiver();
57
58private:
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
71NativeDisplayEventReceiver::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
78NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
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 = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
91            this, NULL);
92    if (rc < 0) {
93        return UNKNOWN_ERROR;
94    }
95    return OK;
96}
97
98void 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
106status_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
127int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) {
128    if (events & (Looper::EVENT_ERROR | Looper::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 & Looper::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
154bool 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
187void 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
198void 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
210static 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
232static 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
239static 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
251static 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
272int 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