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