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/Vector.h>
     31 #include <utils/threads.h>
     32 #include <androidfw/InputTransport.h>
     33 #include "android_os_MessageQueue.h"
     34 #include "android_view_InputChannel.h"
     35 #include "android_view_KeyEvent.h"
     36 #include "android_view_MotionEvent.h"
     37 
     38 #include <ScopedLocalRef.h>
     39 
     40 namespace android {
     41 
     42 static struct {
     43     jclass clazz;
     44 
     45     jmethodID dispatchInputEvent;
     46     jmethodID dispatchBatchedInputEventPending;
     47 } gInputEventReceiverClassInfo;
     48 
     49 
     50 class NativeInputEventReceiver : public LooperCallback {
     51 public:
     52     NativeInputEventReceiver(JNIEnv* env,
     53             jobject receiverWeak, const sp<InputChannel>& inputChannel,
     54             const sp<MessageQueue>& messageQueue);
     55 
     56     status_t initialize();
     57     void dispose();
     58     status_t finishInputEvent(uint32_t seq, bool handled);
     59     status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime);
     60 
     61 protected:
     62     virtual ~NativeInputEventReceiver();
     63 
     64 private:
     65     struct Finish {
     66         uint32_t seq;
     67         bool handled;
     68     };
     69 
     70     jobject mReceiverWeakGlobal;
     71     InputConsumer mInputConsumer;
     72     sp<MessageQueue> mMessageQueue;
     73     PreallocatedInputEventFactory mInputEventFactory;
     74     bool mBatchedInputEventPending;
     75     int mFdEvents;
     76     Vector<Finish> mFinishQueue;
     77 
     78     void setFdEvents(int events);
     79 
     80     const char* getInputChannelName() {
     81         return mInputConsumer.getChannel()->getName().string();
     82     }
     83 
     84     virtual int handleEvent(int receiveFd, int events, void* data);
     85 };
     86 
     87 
     88 NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
     89         jobject receiverWeak, const sp<InputChannel>& inputChannel,
     90         const sp<MessageQueue>& messageQueue) :
     91         mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
     92         mInputConsumer(inputChannel), mMessageQueue(messageQueue),
     93         mBatchedInputEventPending(false), mFdEvents(0) {
     94 #if DEBUG_DISPATCH_CYCLE
     95     ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName());
     96 #endif
     97 }
     98 
     99 NativeInputEventReceiver::~NativeInputEventReceiver() {
    100     JNIEnv* env = AndroidRuntime::getJNIEnv();
    101     env->DeleteGlobalRef(mReceiverWeakGlobal);
    102 }
    103 
    104 status_t NativeInputEventReceiver::initialize() {
    105     setFdEvents(ALOOPER_EVENT_INPUT);
    106     return OK;
    107 }
    108 
    109 void NativeInputEventReceiver::dispose() {
    110 #if DEBUG_DISPATCH_CYCLE
    111     ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName());
    112 #endif
    113 
    114     setFdEvents(0);
    115 }
    116 
    117 status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
    118 #if DEBUG_DISPATCH_CYCLE
    119     ALOGD("channel '%s' ~ Finished input event.", getInputChannelName());
    120 #endif
    121 
    122     status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
    123     if (status) {
    124         if (status == WOULD_BLOCK) {
    125 #if DEBUG_DISPATCH_CYCLE
    126             ALOGD("channel '%s' ~ Could not send finished signal immediately.  "
    127                     "Enqueued for later.", getInputChannelName());
    128 #endif
    129             Finish finish;
    130             finish.seq = seq;
    131             finish.handled = handled;
    132             mFinishQueue.add(finish);
    133             if (mFinishQueue.size() == 1) {
    134                 setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
    135             }
    136             return OK;
    137         }
    138         ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
    139                 getInputChannelName(), status);
    140     }
    141     return status;
    142 }
    143 
    144 void NativeInputEventReceiver::setFdEvents(int events) {
    145     if (mFdEvents != events) {
    146         mFdEvents = events;
    147         int fd = mInputConsumer.getChannel()->getFd();
    148         if (events) {
    149             mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
    150         } else {
    151             mMessageQueue->getLooper()->removeFd(fd);
    152         }
    153     }
    154 }
    155 
    156 int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    157     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
    158 #if DEBUG_DISPATCH_CYCLE
    159         // This error typically occurs when the publisher has closed the input channel
    160         // as part of removing a window or finishing an IME session, in which case
    161         // the consumer will soon be disposed as well.
    162         ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  "
    163                 "events=0x%x", getInputChannelName(), events);
    164 #endif
    165         return 0; // remove the callback
    166     }
    167 
    168     if (events & ALOOPER_EVENT_INPUT) {
    169         JNIEnv* env = AndroidRuntime::getJNIEnv();
    170         status_t status = consumeEvents(env, false /*consumeBatches*/, -1);
    171         mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
    172         return status == OK || status == NO_MEMORY ? 1 : 0;
    173     }
    174 
    175     if (events & ALOOPER_EVENT_OUTPUT) {
    176         for (size_t i = 0; i < mFinishQueue.size(); i++) {
    177             const Finish& finish = mFinishQueue.itemAt(i);
    178             status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
    179             if (status) {
    180                 mFinishQueue.removeItemsAt(0, i);
    181 
    182                 if (status == WOULD_BLOCK) {
    183 #if DEBUG_DISPATCH_CYCLE
    184                     ALOGD("channel '%s' ~ Sent %u queued finish events; %u left.",
    185                             getInputChannelName(), i, mFinishQueue.size());
    186 #endif
    187                     return 1; // keep the callback, try again later
    188                 }
    189 
    190                 ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
    191                         getInputChannelName(), status);
    192                 if (status != DEAD_OBJECT) {
    193                     JNIEnv* env = AndroidRuntime::getJNIEnv();
    194                     String8 message;
    195                     message.appendFormat("Failed to finish input event.  status=%d", status);
    196                     jniThrowRuntimeException(env, message.string());
    197                     mMessageQueue->raiseAndClearException(env, "finishInputEvent");
    198                 }
    199                 return 0; // remove the callback
    200             }
    201         }
    202 #if DEBUG_DISPATCH_CYCLE
    203         ALOGD("channel '%s' ~ Sent %u queued finish events; none left.",
    204                 getInputChannelName(), mFinishQueue.size());
    205 #endif
    206         mFinishQueue.clear();
    207         setFdEvents(ALOOPER_EVENT_INPUT);
    208         return 1;
    209     }
    210 
    211     ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
    212             "events=0x%x", getInputChannelName(), events);
    213     return 1;
    214 }
    215 
    216 status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
    217         bool consumeBatches, nsecs_t frameTime) {
    218 #if DEBUG_DISPATCH_CYCLE
    219     ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.",
    220             getInputChannelName(), consumeBatches ? "true" : "false", frameTime);
    221 #endif
    222 
    223     if (consumeBatches) {
    224         mBatchedInputEventPending = false;
    225     }
    226 
    227     ScopedLocalRef<jobject> receiverObj(env, NULL);
    228     bool skipCallbacks = false;
    229     for (;;) {
    230         uint32_t seq;
    231         InputEvent* inputEvent;
    232         status_t status = mInputConsumer.consume(&mInputEventFactory,
    233                 consumeBatches, frameTime, &seq, &inputEvent);
    234         if (status) {
    235             if (status == WOULD_BLOCK) {
    236                 if (!skipCallbacks && !mBatchedInputEventPending
    237                         && mInputConsumer.hasPendingBatch()) {
    238                     // There is a pending batch.  Come back later.
    239                     if (!receiverObj.get()) {
    240                         receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
    241                         if (!receiverObj.get()) {
    242                             ALOGW("channel '%s' ~ Receiver object was finalized "
    243                                     "without being disposed.", getInputChannelName());
    244                             return DEAD_OBJECT;
    245                         }
    246                     }
    247 
    248                     mBatchedInputEventPending = true;
    249 #if DEBUG_DISPATCH_CYCLE
    250                     ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
    251                             getInputChannelName());
    252 #endif
    253                     env->CallVoidMethod(receiverObj.get(),
    254                             gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
    255                     if (env->ExceptionCheck()) {
    256                         ALOGE("Exception dispatching batched input events.");
    257                         mBatchedInputEventPending = false; // try again later
    258                     }
    259                 }
    260                 return OK;
    261             }
    262             ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
    263                     getInputChannelName(), status);
    264             return status;
    265         }
    266         assert(inputEvent);
    267 
    268         if (!skipCallbacks) {
    269             if (!receiverObj.get()) {
    270                 receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
    271                 if (!receiverObj.get()) {
    272                     ALOGW("channel '%s' ~ Receiver object was finalized "
    273                             "without being disposed.", getInputChannelName());
    274                     return DEAD_OBJECT;
    275                 }
    276             }
    277 
    278             jobject inputEventObj;
    279             switch (inputEvent->getType()) {
    280             case AINPUT_EVENT_TYPE_KEY:
    281 #if DEBUG_DISPATCH_CYCLE
    282                 ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
    283 #endif
    284                 inputEventObj = android_view_KeyEvent_fromNative(env,
    285                         static_cast<KeyEvent*>(inputEvent));
    286                 break;
    287 
    288             case AINPUT_EVENT_TYPE_MOTION:
    289 #if DEBUG_DISPATCH_CYCLE
    290                 ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
    291 #endif
    292                 inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
    293                         static_cast<MotionEvent*>(inputEvent));
    294                 break;
    295 
    296             default:
    297                 assert(false); // InputConsumer should prevent this from ever happening
    298                 inputEventObj = NULL;
    299             }
    300 
    301             if (inputEventObj) {
    302 #if DEBUG_DISPATCH_CYCLE
    303                 ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
    304 #endif
    305                 env->CallVoidMethod(receiverObj.get(),
    306                         gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
    307                 if (env->ExceptionCheck()) {
    308                     ALOGE("Exception dispatching input event.");
    309                     skipCallbacks = true;
    310                 }
    311                 env->DeleteLocalRef(inputEventObj);
    312             } else {
    313                 ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
    314                 skipCallbacks = true;
    315             }
    316         }
    317 
    318         if (skipCallbacks) {
    319             mInputConsumer.sendFinishedSignal(seq, false);
    320         }
    321     }
    322 }
    323 
    324 
    325 static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
    326         jobject inputChannelObj, jobject messageQueueObj) {
    327     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
    328             inputChannelObj);
    329     if (inputChannel == NULL) {
    330         jniThrowRuntimeException(env, "InputChannel is not initialized.");
    331         return 0;
    332     }
    333 
    334     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    335     if (messageQueue == NULL) {
    336         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
    337         return 0;
    338     }
    339 
    340     sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
    341             receiverWeak, inputChannel, messageQueue);
    342     status_t status = receiver->initialize();
    343     if (status) {
    344         String8 message;
    345         message.appendFormat("Failed to initialize input event receiver.  status=%d", status);
    346         jniThrowRuntimeException(env, message.string());
    347         return 0;
    348     }
    349 
    350     receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    351     return reinterpret_cast<jint>(receiver.get());
    352 }
    353 
    354 static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) {
    355     sp<NativeInputEventReceiver> receiver =
    356             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    357     receiver->dispose();
    358     receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
    359 }
    360 
    361 static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr,
    362         jint seq, jboolean handled) {
    363     sp<NativeInputEventReceiver> receiver =
    364             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    365     status_t status = receiver->finishInputEvent(seq, handled);
    366     if (status && status != DEAD_OBJECT) {
    367         String8 message;
    368         message.appendFormat("Failed to finish input event.  status=%d", status);
    369         jniThrowRuntimeException(env, message.string());
    370     }
    371 }
    372 
    373 static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr,
    374         jlong frameTimeNanos) {
    375     sp<NativeInputEventReceiver> receiver =
    376             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    377     status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos);
    378     if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) {
    379         String8 message;
    380         message.appendFormat("Failed to consume batched input event.  status=%d", status);
    381         jniThrowRuntimeException(env, message.string());
    382     }
    383 }
    384 
    385 
    386 static JNINativeMethod gMethods[] = {
    387     /* name, signature, funcPtr */
    388     { "nativeInit",
    389             "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
    390             (void*)nativeInit },
    391     { "nativeDispose", "(I)V",
    392             (void*)nativeDispose },
    393     { "nativeFinishInputEvent", "(IIZ)V",
    394             (void*)nativeFinishInputEvent },
    395     { "nativeConsumeBatchedInputEvents", "(IJ)V",
    396             (void*)nativeConsumeBatchedInputEvents },
    397 };
    398 
    399 #define FIND_CLASS(var, className) \
    400         var = env->FindClass(className); \
    401         LOG_FATAL_IF(! var, "Unable to find class " className); \
    402         var = jclass(env->NewGlobalRef(var));
    403 
    404 #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
    405         var = env->GetMethodID(clazz, methodName, methodDescriptor); \
    406         LOG_FATAL_IF(! var, "Unable to find method " methodName);
    407 
    408 int register_android_view_InputEventReceiver(JNIEnv* env) {
    409     int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver",
    410             gMethods, NELEM(gMethods));
    411     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
    412 
    413     FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver");
    414 
    415     GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent,
    416             gInputEventReceiverClassInfo.clazz,
    417             "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
    418     GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchBatchedInputEventPending,
    419             gInputEventReceiverClassInfo.clazz,
    420             "dispatchBatchedInputEventPending", "()V");
    421     return 0;
    422 }
    423 
    424 } // namespace android
    425