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