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