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