Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2013 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 "InputQueue"
     18 
     19 #include <fcntl.h>
     20 #include <string.h>
     21 #include <unistd.h>
     22 
     23 #include <android/input.h>
     24 #include <android_runtime/AndroidRuntime.h>
     25 #include <android_runtime/android_view_InputQueue.h>
     26 #include <input/Input.h>
     27 #include <utils/Looper.h>
     28 #include <utils/TypeHelpers.h>
     29 #include <ScopedLocalRef.h>
     30 
     31 #include "JNIHelp.h"
     32 #include "android_os_MessageQueue.h"
     33 #include "android_view_KeyEvent.h"
     34 #include "android_view_MotionEvent.h"
     35 
     36 namespace android {
     37 
     38 static struct {
     39     jmethodID finishInputEvent;
     40 } gInputQueueClassInfo;
     41 
     42 enum {
     43     MSG_FINISH_INPUT = 1,
     44 };
     45 
     46 InputQueue::InputQueue(jobject inputQueueObj, const sp<Looper>& looper,
     47         int dispatchReadFd, int dispatchWriteFd) :
     48         mDispatchReadFd(dispatchReadFd), mDispatchWriteFd(dispatchWriteFd),
     49         mDispatchLooper(looper), mHandler(new WeakMessageHandler(this)) {
     50     JNIEnv* env = AndroidRuntime::getJNIEnv();
     51     mInputQueueWeakGlobal = env->NewGlobalRef(inputQueueObj);
     52 }
     53 
     54 InputQueue::~InputQueue() {
     55     mDispatchLooper->removeMessages(mHandler);
     56     JNIEnv* env = AndroidRuntime::getJNIEnv();
     57     env->DeleteGlobalRef(mInputQueueWeakGlobal);
     58     close(mDispatchReadFd);
     59     close(mDispatchWriteFd);
     60 }
     61 
     62 void InputQueue::attachLooper(Looper* looper, int ident,
     63         ALooper_callbackFunc callback, void* data) {
     64     Mutex::Autolock _l(mLock);
     65     for (size_t i = 0; i < mAppLoopers.size(); i++) {
     66         if (looper == mAppLoopers[i]) {
     67             return;
     68         }
     69     }
     70     mAppLoopers.push(looper);
     71     looper->addFd(mDispatchReadFd, ident, ALOOPER_EVENT_INPUT, callback, data);
     72 }
     73 
     74 void InputQueue::detachLooper() {
     75     Mutex::Autolock _l(mLock);
     76     detachLooperLocked();
     77 }
     78 
     79 void InputQueue::detachLooperLocked() {
     80     for (size_t i = 0; i < mAppLoopers.size(); i++) {
     81         mAppLoopers[i]->removeFd(mDispatchReadFd);
     82     }
     83     mAppLoopers.clear();
     84 }
     85 
     86 bool InputQueue::hasEvents() {
     87     Mutex::Autolock _l(mLock);
     88     return mPendingEvents.size() > 0;
     89 }
     90 
     91 status_t InputQueue::getEvent(InputEvent** outEvent) {
     92     Mutex::Autolock _l(mLock);
     93     *outEvent = NULL;
     94     if (!mPendingEvents.isEmpty()) {
     95         *outEvent = mPendingEvents[0];
     96         mPendingEvents.removeAt(0);
     97     }
     98 
     99     if (mPendingEvents.isEmpty()) {
    100         char byteread[16];
    101         ssize_t nRead;
    102         do {
    103             nRead = TEMP_FAILURE_RETRY(read(mDispatchReadFd, &byteread, sizeof(byteread)));
    104             if (nRead < 0 && errno != EAGAIN) {
    105                 ALOGW("Failed to read from native dispatch pipe: %s", strerror(errno));
    106             }
    107         } while (nRead > 0);
    108     }
    109 
    110     return *outEvent != NULL ? OK : WOULD_BLOCK;
    111 }
    112 
    113 bool InputQueue::preDispatchEvent(InputEvent* e) {
    114     if (e->getType() == AINPUT_EVENT_TYPE_KEY) {
    115         KeyEvent* keyEvent = static_cast<KeyEvent*>(e);
    116         if (keyEvent->getFlags() & AKEY_EVENT_FLAG_PREDISPATCH) {
    117             finishEvent(e, false);
    118             return true;
    119         }
    120     }
    121     return false;
    122 }
    123 
    124 void InputQueue::finishEvent(InputEvent* event, bool handled) {
    125     Mutex::Autolock _l(mLock);
    126     mFinishedEvents.push(key_value_pair_t<InputEvent*, bool>(event, handled));
    127     if (mFinishedEvents.size() == 1) {
    128         mDispatchLooper->sendMessage(this, Message(MSG_FINISH_INPUT));
    129     }
    130 }
    131 
    132 void InputQueue::handleMessage(const Message& message) {
    133     switch(message.what) {
    134     case MSG_FINISH_INPUT:
    135         JNIEnv* env = AndroidRuntime::getJNIEnv();
    136         ScopedLocalRef<jobject> inputQueueObj(env, jniGetReferent(env, mInputQueueWeakGlobal));
    137         if (!inputQueueObj.get()) {
    138             ALOGW("InputQueue was finalized without being disposed");
    139             return;
    140         }
    141         while (true) {
    142             InputEvent* event;
    143             bool handled;
    144             {
    145                 Mutex::Autolock _l(mLock);
    146                 if (mFinishedEvents.isEmpty()) {
    147                     break;
    148                 }
    149                 event = mFinishedEvents[0].getKey();
    150                 handled = mFinishedEvents[0].getValue();
    151                 mFinishedEvents.removeAt(0);
    152             }
    153             env->CallVoidMethod(inputQueueObj.get(), gInputQueueClassInfo.finishInputEvent,
    154                     reinterpret_cast<jint>(event), handled);
    155             recycleInputEvent(event);
    156         }
    157         break;
    158     }
    159 }
    160 
    161 void InputQueue::recycleInputEvent(InputEvent* event) {
    162     mPooledInputEventFactory.recycle(event);
    163 }
    164 
    165 KeyEvent* InputQueue::createKeyEvent() {
    166     return mPooledInputEventFactory.createKeyEvent();
    167 }
    168 
    169 MotionEvent* InputQueue::createMotionEvent() {
    170     return mPooledInputEventFactory.createMotionEvent();
    171 }
    172 
    173 void InputQueue::enqueueEvent(InputEvent* event) {
    174     Mutex::Autolock _l(mLock);
    175     mPendingEvents.push(event);
    176     if (mPendingEvents.size() == 1) {
    177         char dummy = 0;
    178         int res = TEMP_FAILURE_RETRY(write(mDispatchWriteFd, &dummy, sizeof(dummy)));
    179         if (res < 0 && errno != EAGAIN) {
    180             ALOGW("Failed writing to dispatch fd: %s", strerror(errno));
    181         }
    182     }
    183 }
    184 
    185 InputQueue* InputQueue::createQueue(jobject inputQueueObj, const sp<Looper>& looper) {
    186     int pipeFds[2];
    187     if (pipe(pipeFds)) {
    188         ALOGW("Could not create native input dispatching pipe: %s", strerror(errno));
    189         return NULL;
    190     }
    191     fcntl(pipeFds[0], F_SETFL, O_NONBLOCK);
    192     fcntl(pipeFds[1], F_SETFL, O_NONBLOCK);
    193     return new InputQueue(inputQueueObj, looper, pipeFds[0], pipeFds[1]);
    194 }
    195 
    196 static jint nativeInit(JNIEnv* env, jobject clazz, jobject queueWeak, jobject jMsgQueue) {
    197     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, jMsgQueue);
    198     if (messageQueue == NULL) {
    199         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
    200         return 0;
    201     }
    202     sp<InputQueue> queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper());
    203     if (!queue.get()) {
    204         jniThrowRuntimeException(env, "InputQueue failed to initialize");
    205         return 0;
    206     }
    207     queue->incStrong(&gInputQueueClassInfo);
    208     return reinterpret_cast<jint>(queue.get());
    209 }
    210 
    211 static void nativeDispose(JNIEnv* env, jobject clazz, jint ptr) {
    212     sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
    213     queue->detachLooper();
    214     queue->decStrong(&gInputQueueClassInfo);
    215 }
    216 
    217 static jint nativeSendKeyEvent(JNIEnv* env, jobject clazz, jint ptr, jobject eventObj,
    218         jboolean predispatch) {
    219     InputQueue* queue = reinterpret_cast<InputQueue*>(ptr);
    220     KeyEvent* event = queue->createKeyEvent();
    221     status_t status = android_view_KeyEvent_toNative(env, eventObj, event);
    222     if (status) {
    223         queue->recycleInputEvent(event);
    224         jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
    225         return -1;
    226     }
    227 
    228     if (predispatch) {
    229         event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH);
    230     }
    231 
    232     queue->enqueueEvent(event);
    233     return reinterpret_cast<jint>(event);
    234 }
    235 
    236 static jint nativeSendMotionEvent(JNIEnv* env, jobject clazz, jint ptr, jobject eventObj) {
    237     sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
    238     MotionEvent* originalEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
    239     if (!originalEvent) {
    240         jniThrowRuntimeException(env, "Could not obtain MotionEvent pointer.");
    241         return -1;
    242     }
    243     MotionEvent* event = queue->createMotionEvent();
    244     event->copyFrom(originalEvent, true /* keepHistory */);
    245     queue->enqueueEvent(event);
    246     return reinterpret_cast<jint>(event);
    247 }
    248 
    249 static const JNINativeMethod g_methods[] = {
    250     { "nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)I",
    251         (void*) nativeInit },
    252     { "nativeDispose", "(I)V", (void*) nativeDispose },
    253     { "nativeSendKeyEvent", "(ILandroid/view/KeyEvent;Z)I", (void*) nativeSendKeyEvent },
    254     { "nativeSendMotionEvent", "(ILandroid/view/MotionEvent;)I", (void*) nativeSendMotionEvent },
    255 };
    256 
    257 static const char* const kInputQueuePathName = "android/view/InputQueue";
    258 
    259 #define FIND_CLASS(var, className) \
    260         do { \
    261         var = env->FindClass(className); \
    262         LOG_FATAL_IF(! var, "Unable to find class %s", className); \
    263         } while(0)
    264 
    265 #define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
    266         do { \
    267         var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
    268         LOG_FATAL_IF(! var, "Unable to find method" methodName); \
    269         } while(0)
    270 
    271 int register_android_view_InputQueue(JNIEnv* env)
    272 {
    273     jclass clazz;
    274     FIND_CLASS(clazz, kInputQueuePathName);
    275     GET_METHOD_ID(gInputQueueClassInfo.finishInputEvent, clazz, "finishInputEvent", "(IZ)V");
    276 
    277     return AndroidRuntime::registerNativeMethods(
    278         env, kInputQueuePathName,
    279         g_methods, NELEM(g_methods));
    280 }
    281 
    282 } // namespace android
    283