Home | History | Annotate | Download | only in jni
      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 
     31 namespace 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.
     36 static const size_t EVENT_BUFFER_SIZE = 100;
     37 
     38 static struct {
     39     jclass clazz;
     40 
     41     jmethodID dispatchVsync;
     42 } gDisplayEventReceiverClassInfo;
     43 
     44 
     45 class NativeDisplayEventReceiver : public LooperCallback {
     46 public:
     47     NativeDisplayEventReceiver(JNIEnv* env,
     48             jobject receiverObj, const sp<MessageQueue>& messageQueue);
     49 
     50     status_t initialize();
     51     void dispose();
     52     status_t scheduleVsync();
     53 
     54 protected:
     55     virtual ~NativeDisplayEventReceiver();
     56 
     57 private:
     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 
     68 NativeDisplayEventReceiver::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 
     75 NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
     76     JNIEnv* env = AndroidRuntime::getJNIEnv();
     77     env->DeleteGlobalRef(mReceiverObjGlobal);
     78 }
     79 
     80 status_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 
     95 void 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 
    103 status_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 
    123 int 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 
    159 bool 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 
    180 static 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 
    202 static 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 
    209 static 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 
    221 static 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 
    242 int 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