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