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 <input/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             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 DEBUG_DISPATCH_CYCLE
     96     ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName());
     97 #endif
     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 DEBUG_DISPATCH_CYCLE
    112     ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName());
    113 #endif
    114 
    115     setFdEvents(0);
    116 }
    117 
    118 status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
    119 #if DEBUG_DISPATCH_CYCLE
    120     ALOGD("channel '%s' ~ Finished input event.", getInputChannelName());
    121 #endif
    122 
    123     status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
    124     if (status) {
    125         if (status == WOULD_BLOCK) {
    126 #if DEBUG_DISPATCH_CYCLE
    127             ALOGD("channel '%s' ~ Could not send finished signal immediately.  "
    128                     "Enqueued for later.", getInputChannelName());
    129 #endif
    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 #if DEBUG_DISPATCH_CYCLE
    160         // This error typically occurs when the publisher has closed the input channel
    161         // as part of removing a window or finishing an IME session, in which case
    162         // the consumer will soon be disposed as well.
    163         ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  "
    164                 "events=0x%x", getInputChannelName(), events);
    165 #endif
    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 DEBUG_DISPATCH_CYCLE
    185                     ALOGD("channel '%s' ~ Sent %u queued finish events; %u left.",
    186                             getInputChannelName(), i, mFinishQueue.size());
    187 #endif
    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 DEBUG_DISPATCH_CYCLE
    204         ALOGD("channel '%s' ~ Sent %u queued finish events; none left.",
    205                 getInputChannelName(), mFinishQueue.size());
    206 #endif
    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 DEBUG_DISPATCH_CYCLE
    220     ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.",
    221             getInputChannelName(), consumeBatches ? "true" : "false", frameTime);
    222 #endif
    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         status_t status = mInputConsumer.consume(&mInputEventFactory,
    237                 consumeBatches, frameTime, &seq, &inputEvent);
    238         if (status) {
    239             if (status == WOULD_BLOCK) {
    240                 if (!skipCallbacks && !mBatchedInputEventPending
    241                         && mInputConsumer.hasPendingBatch()) {
    242                     // There is a pending batch.  Come back later.
    243                     if (!receiverObj.get()) {
    244                         receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
    245                         if (!receiverObj.get()) {
    246                             ALOGW("channel '%s' ~ Receiver object was finalized "
    247                                     "without being disposed.", getInputChannelName());
    248                             return DEAD_OBJECT;
    249                         }
    250                     }
    251 
    252                     mBatchedInputEventPending = true;
    253 #if DEBUG_DISPATCH_CYCLE
    254                     ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
    255                             getInputChannelName());
    256 #endif
    257                     env->CallVoidMethod(receiverObj.get(),
    258                             gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
    259                     if (env->ExceptionCheck()) {
    260                         ALOGE("Exception dispatching batched input events.");
    261                         mBatchedInputEventPending = false; // try again later
    262                     }
    263                 }
    264                 return OK;
    265             }
    266             ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
    267                     getInputChannelName(), status);
    268             return status;
    269         }
    270         assert(inputEvent);
    271 
    272         if (!skipCallbacks) {
    273             if (!receiverObj.get()) {
    274                 receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
    275                 if (!receiverObj.get()) {
    276                     ALOGW("channel '%s' ~ Receiver object was finalized "
    277                             "without being disposed.", getInputChannelName());
    278                     return DEAD_OBJECT;
    279                 }
    280             }
    281 
    282             jobject inputEventObj;
    283             switch (inputEvent->getType()) {
    284             case AINPUT_EVENT_TYPE_KEY:
    285 #if DEBUG_DISPATCH_CYCLE
    286                 ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
    287 #endif
    288                 inputEventObj = android_view_KeyEvent_fromNative(env,
    289                         static_cast<KeyEvent*>(inputEvent));
    290                 break;
    291 
    292             case AINPUT_EVENT_TYPE_MOTION: {
    293 #if DEBUG_DISPATCH_CYCLE
    294                 ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
    295 #endif
    296                 MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
    297                 if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
    298                     *outConsumedBatch = true;
    299                 }
    300                 inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
    301                 break;
    302             }
    303 
    304             default:
    305                 assert(false); // InputConsumer should prevent this from ever happening
    306                 inputEventObj = NULL;
    307             }
    308 
    309             if (inputEventObj) {
    310 #if DEBUG_DISPATCH_CYCLE
    311                 ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
    312 #endif
    313                 env->CallVoidMethod(receiverObj.get(),
    314                         gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
    315                 if (env->ExceptionCheck()) {
    316                     ALOGE("Exception dispatching input event.");
    317                     skipCallbacks = true;
    318                 }
    319                 env->DeleteLocalRef(inputEventObj);
    320             } else {
    321                 ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
    322                 skipCallbacks = true;
    323             }
    324         }
    325 
    326         if (skipCallbacks) {
    327             mInputConsumer.sendFinishedSignal(seq, false);
    328         }
    329     }
    330 }
    331 
    332 
    333 static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
    334         jobject inputChannelObj, jobject messageQueueObj) {
    335     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
    336             inputChannelObj);
    337     if (inputChannel == NULL) {
    338         jniThrowRuntimeException(env, "InputChannel is not initialized.");
    339         return 0;
    340     }
    341 
    342     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    343     if (messageQueue == NULL) {
    344         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
    345         return 0;
    346     }
    347 
    348     sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
    349             receiverWeak, inputChannel, messageQueue);
    350     status_t status = receiver->initialize();
    351     if (status) {
    352         String8 message;
    353         message.appendFormat("Failed to initialize input event receiver.  status=%d", status);
    354         jniThrowRuntimeException(env, message.string());
    355         return 0;
    356     }
    357 
    358     receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    359     return reinterpret_cast<jint>(receiver.get());
    360 }
    361 
    362 static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) {
    363     sp<NativeInputEventReceiver> receiver =
    364             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    365     receiver->dispose();
    366     receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
    367 }
    368 
    369 static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr,
    370         jint seq, jboolean handled) {
    371     sp<NativeInputEventReceiver> receiver =
    372             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    373     status_t status = receiver->finishInputEvent(seq, handled);
    374     if (status && status != DEAD_OBJECT) {
    375         String8 message;
    376         message.appendFormat("Failed to finish input event.  status=%d", status);
    377         jniThrowRuntimeException(env, message.string());
    378     }
    379 }
    380 
    381 static bool nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr,
    382         jlong frameTimeNanos) {
    383     sp<NativeInputEventReceiver> receiver =
    384             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    385     bool consumedBatch;
    386     status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos,
    387             &consumedBatch);
    388     if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) {
    389         String8 message;
    390         message.appendFormat("Failed to consume batched input event.  status=%d", status);
    391         jniThrowRuntimeException(env, message.string());
    392         return false;
    393     }
    394     return consumedBatch;
    395 }
    396 
    397 
    398 static JNINativeMethod gMethods[] = {
    399     /* name, signature, funcPtr */
    400     { "nativeInit",
    401             "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
    402             (void*)nativeInit },
    403     { "nativeDispose", "(I)V",
    404             (void*)nativeDispose },
    405     { "nativeFinishInputEvent", "(IIZ)V",
    406             (void*)nativeFinishInputEvent },
    407     { "nativeConsumeBatchedInputEvents", "(IJ)Z",
    408             (void*)nativeConsumeBatchedInputEvents },
    409 };
    410 
    411 #define FIND_CLASS(var, className) \
    412         var = env->FindClass(className); \
    413         LOG_FATAL_IF(! var, "Unable to find class " className); \
    414         var = jclass(env->NewGlobalRef(var));
    415 
    416 #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
    417         var = env->GetMethodID(clazz, methodName, methodDescriptor); \
    418         LOG_FATAL_IF(! var, "Unable to find method " methodName);
    419 
    420 int register_android_view_InputEventReceiver(JNIEnv* env) {
    421     int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver",
    422             gMethods, NELEM(gMethods));
    423     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
    424 
    425     FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver");
    426 
    427     GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent,
    428             gInputEventReceiverClassInfo.clazz,
    429             "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
    430     GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchBatchedInputEventPending,
    431             gInputEventReceiverClassInfo.clazz,
    432             "dispatchBatchedInputEventPending", "()V");
    433     return 0;
    434 }
    435 
    436 } // namespace android
    437