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 "InputEventReceiver"
     18 
     19 //#define LOG_NDEBUG 0
     20 
     21 // Log debug messages about the dispatch cycle.
     22 #define DEBUG_DISPATCH_CYCLE 0
     23 
     24 
     25 #include "JNIHelp.h"
     26 
     27 #include <android_runtime/AndroidRuntime.h>
     28 #include <utils/Log.h>
     29 #include <utils/Looper.h>
     30 #include <utils/threads.h>
     31 #include <androidfw/InputTransport.h>
     32 #include "android_os_MessageQueue.h"
     33 #include "android_view_InputChannel.h"
     34 #include "android_view_KeyEvent.h"
     35 #include "android_view_MotionEvent.h"
     36 
     37 namespace android {
     38 
     39 static struct {
     40     jclass clazz;
     41 
     42     jmethodID dispatchInputEvent;
     43     jmethodID dispatchBatchedInputEventPending;
     44 } gInputEventReceiverClassInfo;
     45 
     46 
     47 class NativeInputEventReceiver : public LooperCallback {
     48 public:
     49     NativeInputEventReceiver(JNIEnv* env,
     50             jobject receiverObj, const sp<InputChannel>& inputChannel,
     51             const sp<MessageQueue>& messageQueue);
     52 
     53     status_t initialize();
     54     void dispose();
     55     status_t finishInputEvent(uint32_t seq, bool handled);
     56     status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime);
     57 
     58 protected:
     59     virtual ~NativeInputEventReceiver();
     60 
     61 private:
     62     jobject mReceiverObjGlobal;
     63     InputConsumer mInputConsumer;
     64     sp<MessageQueue> mMessageQueue;
     65     PreallocatedInputEventFactory mInputEventFactory;
     66     bool mBatchedInputEventPending;
     67 
     68     const char* getInputChannelName() {
     69         return mInputConsumer.getChannel()->getName().string();
     70     }
     71 
     72     virtual int handleEvent(int receiveFd, int events, void* data);
     73 };
     74 
     75 
     76 NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
     77         jobject receiverObj, const sp<InputChannel>& inputChannel,
     78         const sp<MessageQueue>& messageQueue) :
     79         mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
     80         mInputConsumer(inputChannel), mMessageQueue(messageQueue),
     81         mBatchedInputEventPending(false) {
     82 #if DEBUG_DISPATCH_CYCLE
     83     ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName());
     84 #endif
     85 }
     86 
     87 NativeInputEventReceiver::~NativeInputEventReceiver() {
     88     JNIEnv* env = AndroidRuntime::getJNIEnv();
     89     env->DeleteGlobalRef(mReceiverObjGlobal);
     90 }
     91 
     92 status_t NativeInputEventReceiver::initialize() {
     93     int receiveFd = mInputConsumer.getChannel()->getFd();
     94     mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
     95     return OK;
     96 }
     97 
     98 void NativeInputEventReceiver::dispose() {
     99 #if DEBUG_DISPATCH_CYCLE
    100     ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName());
    101 #endif
    102 
    103     mMessageQueue->getLooper()->removeFd(mInputConsumer.getChannel()->getFd());
    104 }
    105 
    106 status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
    107 #if DEBUG_DISPATCH_CYCLE
    108     ALOGD("channel '%s' ~ Finished input event.", getInputChannelName());
    109 #endif
    110 
    111     status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
    112     if (status) {
    113         ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
    114                 getInputChannelName(), status);
    115     }
    116     return status;
    117 }
    118 
    119 int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    120     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
    121         ALOGE("channel '%s' ~ Publisher closed input channel or an error occurred.  "
    122                 "events=0x%x", getInputChannelName(), events);
    123         return 0; // remove the callback
    124     }
    125 
    126     if (!(events & ALOOPER_EVENT_INPUT)) {
    127         ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
    128                 "events=0x%x", getInputChannelName(), events);
    129         return 1;
    130     }
    131 
    132     JNIEnv* env = AndroidRuntime::getJNIEnv();
    133     status_t status = consumeEvents(env, false /*consumeBatches*/, -1);
    134     mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
    135     return status == OK || status == NO_MEMORY ? 1 : 0;
    136 }
    137 
    138 status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
    139         bool consumeBatches, nsecs_t frameTime) {
    140 #if DEBUG_DISPATCH_CYCLE
    141     ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.",
    142             getInputChannelName(), consumeBatches ? "true" : "false", frameTime);
    143 #endif
    144 
    145     if (consumeBatches) {
    146         mBatchedInputEventPending = false;
    147     }
    148 
    149     bool skipCallbacks = false;
    150     for (;;) {
    151         uint32_t seq;
    152         InputEvent* inputEvent;
    153         status_t status = mInputConsumer.consume(&mInputEventFactory,
    154                 consumeBatches, frameTime, &seq, &inputEvent);
    155         if (status) {
    156             if (status == WOULD_BLOCK) {
    157                 if (!skipCallbacks && !mBatchedInputEventPending
    158                         && mInputConsumer.hasPendingBatch()) {
    159                     // There is a pending batch.  Come back later.
    160                     mBatchedInputEventPending = true;
    161 #if DEBUG_DISPATCH_CYCLE
    162                     ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
    163                             getInputChannelName());
    164 #endif
    165                     env->CallVoidMethod(mReceiverObjGlobal,
    166                             gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
    167                     if (env->ExceptionCheck()) {
    168                         ALOGE("Exception dispatching batched input events.");
    169                         mBatchedInputEventPending = false; // try again later
    170                     }
    171                 }
    172                 return OK;
    173             }
    174             ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
    175                     getInputChannelName(), status);
    176             return status;
    177         }
    178         assert(inputEvent);
    179 
    180         if (!skipCallbacks) {
    181             jobject inputEventObj;
    182             switch (inputEvent->getType()) {
    183             case AINPUT_EVENT_TYPE_KEY:
    184 #if DEBUG_DISPATCH_CYCLE
    185                 ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
    186 #endif
    187                 inputEventObj = android_view_KeyEvent_fromNative(env,
    188                         static_cast<KeyEvent*>(inputEvent));
    189                 break;
    190 
    191             case AINPUT_EVENT_TYPE_MOTION:
    192 #if DEBUG_DISPATCH_CYCLE
    193                 ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
    194 #endif
    195                 inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
    196                         static_cast<MotionEvent*>(inputEvent));
    197                 break;
    198 
    199             default:
    200                 assert(false); // InputConsumer should prevent this from ever happening
    201                 inputEventObj = NULL;
    202             }
    203 
    204             if (inputEventObj) {
    205 #if DEBUG_DISPATCH_CYCLE
    206                 ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
    207 #endif
    208                 env->CallVoidMethod(mReceiverObjGlobal,
    209                         gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
    210                 if (env->ExceptionCheck()) {
    211                     ALOGE("Exception dispatching input event.");
    212                     skipCallbacks = true;
    213                 }
    214             } else {
    215                 ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
    216                 skipCallbacks = true;
    217             }
    218         }
    219 
    220         if (skipCallbacks) {
    221             mInputConsumer.sendFinishedSignal(seq, false);
    222         }
    223     }
    224 }
    225 
    226 
    227 static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
    228         jobject inputChannelObj, jobject messageQueueObj) {
    229     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
    230             inputChannelObj);
    231     if (inputChannel == NULL) {
    232         jniThrowRuntimeException(env, "InputChannel is not initialized.");
    233         return 0;
    234     }
    235 
    236     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    237     if (messageQueue == NULL) {
    238         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
    239         return 0;
    240     }
    241 
    242     sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
    243             receiverObj, inputChannel, messageQueue);
    244     status_t status = receiver->initialize();
    245     if (status) {
    246         String8 message;
    247         message.appendFormat("Failed to initialize input event receiver.  status=%d", status);
    248         jniThrowRuntimeException(env, message.string());
    249         return 0;
    250     }
    251 
    252     receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    253     return reinterpret_cast<jint>(receiver.get());
    254 }
    255 
    256 static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) {
    257     sp<NativeInputEventReceiver> receiver =
    258             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    259     receiver->dispose();
    260     receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
    261 }
    262 
    263 static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr,
    264         jint seq, jboolean handled) {
    265     sp<NativeInputEventReceiver> receiver =
    266             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    267     status_t status = receiver->finishInputEvent(seq, handled);
    268     if (status && status != DEAD_OBJECT) {
    269         String8 message;
    270         message.appendFormat("Failed to finish input event.  status=%d", status);
    271         jniThrowRuntimeException(env, message.string());
    272     }
    273 }
    274 
    275 static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr,
    276         jlong frameTimeNanos) {
    277     sp<NativeInputEventReceiver> receiver =
    278             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    279     status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos);
    280     if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) {
    281         String8 message;
    282         message.appendFormat("Failed to consume batched input event.  status=%d", status);
    283         jniThrowRuntimeException(env, message.string());
    284     }
    285 }
    286 
    287 
    288 static JNINativeMethod gMethods[] = {
    289     /* name, signature, funcPtr */
    290     { "nativeInit",
    291             "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
    292             (void*)nativeInit },
    293     { "nativeDispose", "(I)V",
    294             (void*)nativeDispose },
    295     { "nativeFinishInputEvent", "(IIZ)V",
    296             (void*)nativeFinishInputEvent },
    297     { "nativeConsumeBatchedInputEvents", "(IJ)V",
    298             (void*)nativeConsumeBatchedInputEvents },
    299 };
    300 
    301 #define FIND_CLASS(var, className) \
    302         var = env->FindClass(className); \
    303         LOG_FATAL_IF(! var, "Unable to find class " className); \
    304         var = jclass(env->NewGlobalRef(var));
    305 
    306 #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
    307         var = env->GetMethodID(clazz, methodName, methodDescriptor); \
    308         LOG_FATAL_IF(! var, "Unable to find method " methodName);
    309 
    310 int register_android_view_InputEventReceiver(JNIEnv* env) {
    311     int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver",
    312             gMethods, NELEM(gMethods));
    313     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
    314 
    315     FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver");
    316 
    317     GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent,
    318             gInputEventReceiverClassInfo.clazz,
    319             "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
    320     GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchBatchedInputEventPending,
    321             gInputEventReceiverClassInfo.clazz,
    322             "dispatchBatchedInputEventPending", "()V");
    323     return 0;
    324 }
    325 
    326 } // namespace android
    327