diff options
author | Michael Wright <michaelwr@google.com> | 2013-04-10 21:12:00 -0700 |
---|---|---|
committer | Michael Wright <michaelwr@google.com> | 2013-04-22 17:01:51 -0700 |
commit | a44dd26a75e24cc021802288fb81f4761e47be6b (patch) | |
tree | ce61491d8a35cc7a61af5597f6709b3c92504a54 /core/jni | |
parent | c3d0a81a4a1809446bc1fa9abc9b5b74b01e676e (diff) | |
download | frameworks_base-a44dd26a75e24cc021802288fb81f4761e47be6b.zip frameworks_base-a44dd26a75e24cc021802288fb81f4761e47be6b.tar.gz frameworks_base-a44dd26a75e24cc021802288fb81f4761e47be6b.tar.bz2 |
Rewrite input handling for native applications
Bug: 8473020
Change-Id: Ic4353d8924ab877bec21aff8c2dba9fe725bf906
Diffstat (limited to 'core/jni')
-rw-r--r-- | core/jni/Android.mk | 1 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
-rw-r--r-- | core/jni/android_app_NativeActivity.cpp | 428 | ||||
-rw-r--r-- | core/jni/android_view_InputQueue.cpp | 282 |
4 files changed, 297 insertions, 416 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index efb59ef..edc0baf 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -46,6 +46,7 @@ LOCAL_SRC_FILES:= \ android_view_InputDevice.cpp \ android_view_InputEventReceiver.cpp \ android_view_InputEventSender.cpp \ + android_view_InputQueue.cpp \ android_view_KeyEvent.cpp \ android_view_KeyCharacterMap.cpp \ android_view_HardwareRenderer.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 1300d01..3b6b160 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -164,6 +164,7 @@ extern int register_android_view_InputChannel(JNIEnv* env); extern int register_android_view_InputDevice(JNIEnv* env); extern int register_android_view_InputEventReceiver(JNIEnv* env); extern int register_android_view_InputEventSender(JNIEnv* env); +extern int register_android_view_InputQueue(JNIEnv* env); extern int register_android_view_KeyCharacterMap(JNIEnv *env); extern int register_android_view_KeyEvent(JNIEnv* env); extern int register_android_view_MotionEvent(JNIEnv* env); @@ -1197,6 +1198,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_InputChannel), REG_JNI(register_android_view_InputEventReceiver), REG_JNI(register_android_view_InputEventSender), + REG_JNI(register_android_view_InputQueue), REG_JNI(register_android_view_KeyEvent), REG_JNI(register_android_view_MotionEvent), REG_JNI(register_android_view_PointerIcon), diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 21162f4..9fc01e1 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -45,8 +45,6 @@ namespace android { static struct { - jmethodID dispatchUnhandledKeyEvent; - jmethodID preDispatchKeyEvent; jmethodID finish; jmethodID setWindowFlags; jmethodID setWindowFormat; @@ -63,8 +61,7 @@ struct ActivityWork { }; enum { - CMD_DEF_KEY = 1, - CMD_FINISH, + CMD_FINISH = 1, CMD_SET_WINDOW_FORMAT, CMD_SET_WINDOW_FLAGS, CMD_SHOW_SOFT_INPUT, @@ -101,299 +98,6 @@ static bool read_work(int fd, ActivityWork* outWork) { return false; } -// ------------------------------------------------------------------------ - -} // namespace android - -using namespace android; - -AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) : - mWorkWrite(workWrite), mConsumer(channel), mSeq(0) { - int msgpipe[2]; - if (pipe(msgpipe)) { - ALOGW("could not create pipe: %s", strerror(errno)); - mDispatchKeyRead = mDispatchKeyWrite = -1; - } else { - mDispatchKeyRead = msgpipe[0]; - mDispatchKeyWrite = msgpipe[1]; - int result = fcntl(mDispatchKeyRead, F_SETFL, O_NONBLOCK); - SLOGW_IF(result != 0, "Could not make AInputQueue read pipe " - "non-blocking: %s", strerror(errno)); - result = fcntl(mDispatchKeyWrite, F_SETFL, O_NONBLOCK); - SLOGW_IF(result != 0, "Could not make AInputQueue write pipe " - "non-blocking: %s", strerror(errno)); - } -} - -AInputQueue::~AInputQueue() { - close(mDispatchKeyRead); - close(mDispatchKeyWrite); -} - -void AInputQueue::attachLooper(ALooper* looper, int ident, - ALooper_callbackFunc callback, void* data) { - mLooper = static_cast<android::Looper*>(looper); - mLooper->addFd(mConsumer.getChannel()->getFd(), - ident, ALOOPER_EVENT_INPUT, callback, data); - mLooper->addFd(mDispatchKeyRead, - ident, ALOOPER_EVENT_INPUT, callback, data); -} - -void AInputQueue::detachLooper() { - mLooper->removeFd(mConsumer.getChannel()->getFd()); - mLooper->removeFd(mDispatchKeyRead); -} - -int32_t AInputQueue::hasEvents() { - struct pollfd pfd[2]; - - pfd[0].fd = mConsumer.getChannel()->getFd(); - pfd[0].events = POLLIN; - pfd[0].revents = 0; - pfd[1].fd = mDispatchKeyRead; - pfd[1].events = POLLIN; - pfd[1].revents = 0; - - int nfd = poll(pfd, 2, 0); - if (nfd <= 0) return 0; - return ((pfd[0].revents & POLLIN) || (pfd[1].revents & POLLIN)) ? 1 : -1; -} - -int32_t AInputQueue::getEvent(AInputEvent** outEvent) { - *outEvent = NULL; - - char byteread; - ssize_t nRead = read(mDispatchKeyRead, &byteread, 1); - - Mutex::Autolock _l(mLock); - - if (nRead == 1) { - if (mDispatchingKeys.size() > 0) { - KeyEvent* kevent = mDispatchingKeys[0]; - *outEvent = kevent; - mDispatchingKeys.removeAt(0); - in_flight_event inflight; - inflight.event = kevent; - inflight.seq = -1; - inflight.finishSeq = 0; - mInFlightEvents.push(inflight); - } - - bool finishNow = false; - if (mFinishPreDispatches.size() > 0) { - finish_pre_dispatch finish(mFinishPreDispatches[0]); - mFinishPreDispatches.removeAt(0); - const size_t N = mInFlightEvents.size(); - for (size_t i=0; i<N; i++) { - const in_flight_event& inflight(mInFlightEvents[i]); - if (inflight.seq == finish.seq) { - *outEvent = inflight.event; - finishNow = finish.handled; - } - } - if (*outEvent == NULL) { - ALOGW("getEvent couldn't find inflight for seq %d", finish.seq); - } - } - - if (finishNow) { - finishEvent(*outEvent, true, false); - *outEvent = NULL; - return -1; - } else if (*outEvent != NULL) { - return 0; - } - } - - uint32_t consumerSeq; - InputEvent* myEvent = NULL; - status_t res = mConsumer.consume(&mPooledInputEventFactory, true /*consumeBatches*/, -1, - &consumerSeq, &myEvent); - if (res != android::OK) { - if (res != android::WOULD_BLOCK) { - ALOGW("channel '%s' ~ Failed to consume input event. status=%d", - mConsumer.getChannel()->getName().string(), res); - } - return -1; - } - - if (mConsumer.hasDeferredEvent()) { - wakeupDispatchLocked(); - } - - in_flight_event inflight; - inflight.event = myEvent; - inflight.seq = -1; - inflight.finishSeq = consumerSeq; - mInFlightEvents.push(inflight); - - *outEvent = myEvent; - return 0; -} - -bool AInputQueue::preDispatchEvent(AInputEvent* event) { - if (((InputEvent*)event)->getType() != AINPUT_EVENT_TYPE_KEY) { - // The IME only cares about key events. - return false; - } - - // For now we only send system keys to the IME... this avoids having - // critical keys like DPAD go through this path. We really need to have - // the IME report which keys it wants. - if (!((KeyEvent*)event)->isSystemKey()) { - return false; - } - - return preDispatchKey((KeyEvent*)event); -} - -void AInputQueue::finishEvent(AInputEvent* event, bool handled, bool didDefaultHandling) { - LOG_TRACE("finishEvent: %p handled=%d, didDefaultHandling=%d", event, - handled ? 1 : 0, didDefaultHandling ? 1 : 0); - - if (!handled && !didDefaultHandling - && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY - && ((KeyEvent*)event)->hasDefaultAction()) { - // The app didn't handle this, but it may have a default action - // associated with it. We need to hand this back to Java to be - // executed. - doUnhandledKey((KeyEvent*)event); - return; - } - - Mutex::Autolock _l(mLock); - - const size_t N = mInFlightEvents.size(); - for (size_t i=0; i<N; i++) { - const in_flight_event& inflight(mInFlightEvents[i]); - if (inflight.event == event) { - if (inflight.finishSeq) { - status_t res = mConsumer.sendFinishedSignal(inflight.finishSeq, handled); - if (res != android::OK) { - ALOGW("Failed to send finished signal on channel '%s'. status=%d", - mConsumer.getChannel()->getName().string(), res); - } - } - mPooledInputEventFactory.recycle(static_cast<InputEvent*>(event)); - mInFlightEvents.removeAt(i); - return; - } - } - - ALOGW("finishEvent called for unknown event: %p", event); -} - -void AInputQueue::dispatchEvent(android::KeyEvent* event) { - Mutex::Autolock _l(mLock); - - LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(), - mDispatchKeyWrite); - mDispatchingKeys.add(event); - wakeupDispatchLocked(); -} - -void AInputQueue::finishPreDispatch(int seq, bool handled) { - Mutex::Autolock _l(mLock); - - LOG_TRACE("finishPreDispatch: seq=%d handled=%d\n", seq, handled ? 1 : 0); - finish_pre_dispatch finish; - finish.seq = seq; - finish.handled = handled; - mFinishPreDispatches.add(finish); - wakeupDispatchLocked(); -} - -KeyEvent* AInputQueue::consumeUnhandledEvent() { - Mutex::Autolock _l(mLock); - - KeyEvent* event = NULL; - if (mUnhandledKeys.size() > 0) { - event = mUnhandledKeys[0]; - mUnhandledKeys.removeAt(0); - } - - LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event); - return event; -} - -KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) { - Mutex::Autolock _l(mLock); - - KeyEvent* event = NULL; - if (mPreDispatchingKeys.size() > 0) { - const in_flight_event& inflight(mPreDispatchingKeys[0]); - event = static_cast<KeyEvent*>(inflight.event); - *outSeq = inflight.seq; - mPreDispatchingKeys.removeAt(0); - } - - LOG_TRACE("consumePreDispatchingEvent: KeyEvent=%p", event); - return event; -} - -KeyEvent* AInputQueue::createKeyEvent() { - Mutex::Autolock _l(mLock); - - return mPooledInputEventFactory.createKeyEvent(); -} - -void AInputQueue::doUnhandledKey(KeyEvent* keyEvent) { - Mutex::Autolock _l(mLock); - - LOG_TRACE("Unhandled key: pending=%d write=%d\n", mUnhandledKeys.size(), mWorkWrite); - if (mUnhandledKeys.size() <= 0 && mWorkWrite >= 0) { - write_work(mWorkWrite, CMD_DEF_KEY); - } - mUnhandledKeys.add(keyEvent); -} - -bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) { - Mutex::Autolock _l(mLock); - - LOG_TRACE("preDispatch key: pending=%d write=%d\n", mPreDispatchingKeys.size(), mWorkWrite); - const size_t N = mInFlightEvents.size(); - for (size_t i=0; i<N; i++) { - in_flight_event& inflight(mInFlightEvents.editItemAt(i)); - if (inflight.event == keyEvent) { - if (inflight.seq >= 0) { - // This event has already been pre-dispatched! - LOG_TRACE("Event already pre-dispatched!"); - return false; - } - mSeq++; - if (mSeq < 0) mSeq = 1; - inflight.seq = mSeq; - - if (mPreDispatchingKeys.size() <= 0 && mWorkWrite >= 0) { - write_work(mWorkWrite, CMD_DEF_KEY); - } - mPreDispatchingKeys.add(inflight); - return true; - } - } - - ALOGW("preDispatchKey called for unknown event: %p", keyEvent); - return false; -} - -void AInputQueue::wakeupDispatchLocked() { -restart: - char dummy = 0; - int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy)); - if (res < 0 && errno == EINTR) { - goto restart; - } - - if (res == sizeof(dummy)) return; - - if (res < 0) ALOGW("Failed writing to dispatch fd: %s", strerror(errno)); - else ALOGW("Truncated writing to dispatch fd: %d", res); -} - -namespace android { - -// ------------------------------------------------------------------------ - /* * Native state for interacting with the NativeActivity class. */ @@ -404,8 +108,6 @@ struct NativeCode : public ANativeActivity { dlhandle = _dlhandle; createActivityFunc = _createFunc; nativeWindow = NULL; - inputChannel = NULL; - nativeInputQueue = NULL; mainWorkRead = mainWorkWrite = -1; } @@ -419,11 +121,7 @@ struct NativeCode : public ANativeActivity { if (messageQueue != NULL && mainWorkRead >= 0) { messageQueue->getLooper()->removeFd(mainWorkRead); } - if (nativeInputQueue != NULL) { - nativeInputQueue->mWorkWrite = -1; - } setSurface(NULL); - setInputChannel(NULL); if (mainWorkRead >= 0) close(mainWorkRead); if (mainWorkWrite >= 0) close(mainWorkWrite); if (dlhandle != NULL) { @@ -442,26 +140,6 @@ struct NativeCode : public ANativeActivity { } } - status_t setInputChannel(jobject _channel) { - if (inputChannel != NULL) { - delete nativeInputQueue; - env->DeleteGlobalRef(inputChannel); - } - inputChannel = NULL; - nativeInputQueue = NULL; - if (_channel != NULL) { - inputChannel = env->NewGlobalRef(_channel); - sp<InputChannel> ic = - android_view_InputChannel_getInputChannel(env, _channel); - if (ic != NULL) { - nativeInputQueue = new AInputQueue(ic, mainWorkWrite); - } else { - return UNKNOWN_ERROR; - } - } - return OK; - } - ANativeActivityCallbacks callbacks; void* dlhandle; @@ -475,9 +153,6 @@ struct NativeCode : public ANativeActivity { int32_t lastWindowWidth; int32_t lastWindowHeight; - jobject inputChannel; - struct AInputQueue* nativeInputQueue; - // These are used to wake up the main thread to process work. int mainWorkRead; int mainWorkWrite; @@ -532,38 +207,6 @@ static int mainWorkCallback(int fd, int events, void* data) { LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd); switch (work.cmd) { - case CMD_DEF_KEY: { - KeyEvent* keyEvent; - while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) { - jobject inputEventObj = android_view_KeyEvent_fromNative( - code->env, keyEvent); - jboolean handled; - if (inputEventObj) { - handled = code->env->CallBooleanMethod(code->clazz, - gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj); - code->messageQueue->raiseAndClearException( - code->env, "dispatchUnhandledKeyEvent"); - code->env->DeleteLocalRef(inputEventObj); - } else { - ALOGE("Failed to obtain key event for dispatchUnhandledKeyEvent."); - handled = false; - } - code->nativeInputQueue->finishEvent(keyEvent, handled, true); - } - int seq; - while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) { - jobject inputEventObj = android_view_KeyEvent_fromNative( - code->env, keyEvent); - if (inputEventObj) { - code->env->CallVoidMethod(code->clazz, - gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq); - code->messageQueue->raiseAndClearException(code->env, "preDispatchKeyEvent"); - code->env->DeleteLocalRef(inputEventObj); - } else { - ALOGE("Failed to obtain key event for preDispatchKeyEvent."); - } - } - } break; case CMD_FINISH: { code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish); code->messageQueue->raiseAndClearException(code->env, "finish"); @@ -903,36 +546,28 @@ onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surfa } static void -onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel) +onInputQueueCreated_native(JNIEnv* env, jobject clazz, jint handle, jint queuePtr) { LOG_TRACE("onInputChannelCreated_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; - status_t err = code->setInputChannel(channel); - if (err != OK) { - jniThrowException(env, "java/lang/IllegalStateException", - "Error setting input channel"); - return; - } if (code->callbacks.onInputQueueCreated != NULL) { - code->callbacks.onInputQueueCreated(code, - code->nativeInputQueue); + AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr); + code->callbacks.onInputQueueCreated(code, queue); } } } static void -onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel) +onInputQueueDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jint queuePtr) { LOG_TRACE("onInputChannelDestroyed_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; - if (code->nativeInputQueue != NULL - && code->callbacks.onInputQueueDestroyed != NULL) { - code->callbacks.onInputQueueDestroyed(code, - code->nativeInputQueue); + if (code->callbacks.onInputQueueDestroyed != NULL) { + AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr); + code->callbacks.onInputQueueDestroyed(code, queue); } - code->setInputChannel(NULL); } } @@ -954,38 +589,6 @@ onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle, } } -static void -dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventObj) -{ - LOG_TRACE("dispatchKeyEvent_native"); - if (handle != 0) { - NativeCode* code = (NativeCode*)handle; - if (code->nativeInputQueue != NULL) { - KeyEvent* event = code->nativeInputQueue->createKeyEvent(); - status_t status = android_view_KeyEvent_toNative(env, eventObj, event); - if (status) { - delete event; - jniThrowRuntimeException(env, "Could not read contents of KeyEvent object."); - return; - } - code->nativeInputQueue->dispatchEvent(event); - } - } -} - -static void -finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, - jint seq, jboolean handled) -{ - LOG_TRACE("finishPreDispatchKeyEvent_native"); - if (handle != 0) { - NativeCode* code = (NativeCode*)handle; - if (code->nativeInputQueue != NULL) { - code->nativeInputQueue->finishPreDispatch(seq, handled ? true : false); - } - } -} - static const JNINativeMethod g_methods[] = { { "loadNativeCode", "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)I", (void*)loadNativeCode_native }, @@ -1002,11 +605,11 @@ static const JNINativeMethod g_methods[] = { { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native }, { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native }, { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native }, - { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native }, - { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native }, + { "onInputQueueCreatedNative", "(II)V", + (void*)onInputQueueCreated_native }, + { "onInputQueueDestroyedNative", "(II)V", + (void*)onInputQueueDestroyed_native }, { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native }, - { "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native }, - { "finishPreDispatchKeyEventNative", "(IIZ)V", (void*)finishPreDispatchKeyEvent_native }, }; static const char* const kNativeActivityPathName = "android/app/NativeActivity"; @@ -1025,13 +628,6 @@ int register_android_app_NativeActivity(JNIEnv* env) jclass clazz; FIND_CLASS(clazz, kNativeActivityPathName); - GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent, - clazz, - "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)Z"); - GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent, - clazz, - "preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V"); - GET_METHOD_ID(gNativeActivityClassInfo.finish, clazz, "finish", "()V"); diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp new file mode 100644 index 0000000..ec56afa --- /dev/null +++ b/core/jni/android_view_InputQueue.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputQueue" + +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#include <android/input.h> +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/android_view_InputQueue.h> +#include <androidfw/Input.h> +#include <utils/Looper.h> +#include <utils/TypeHelpers.h> +#include <ScopedLocalRef.h> + +#include "JNIHelp.h" +#include "android_os_MessageQueue.h" +#include "android_view_KeyEvent.h" +#include "android_view_MotionEvent.h" + +namespace android { + +static struct { + jmethodID finishInputEvent; +} gInputQueueClassInfo; + +enum { + MSG_FINISH_INPUT = 1, +}; + +InputQueue::InputQueue(jobject inputQueueObj, const sp<Looper>& looper, + int dispatchReadFd, int dispatchWriteFd) : + mDispatchReadFd(dispatchReadFd), mDispatchWriteFd(dispatchWriteFd), + mDispatchLooper(looper), mHandler(new WeakMessageHandler(this)) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + mInputQueueWeakGlobal = env->NewGlobalRef(inputQueueObj); +} + +InputQueue::~InputQueue() { + mDispatchLooper->removeMessages(mHandler); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mInputQueueWeakGlobal); + close(mDispatchReadFd); + close(mDispatchWriteFd); +} + +void InputQueue::attachLooper(Looper* looper, int ident, + ALooper_callbackFunc callback, void* data) { + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mAppLoopers.size(); i++) { + if (looper == mAppLoopers[i]) { + return; + } + } + mAppLoopers.push(looper); + looper->addFd(mDispatchReadFd, ident, ALOOPER_EVENT_INPUT, callback, data); +} + +void InputQueue::detachLooper() { + Mutex::Autolock _l(mLock); + detachLooperLocked(); +} + +void InputQueue::detachLooperLocked() { + for (size_t i = 0; i < mAppLoopers.size(); i++) { + mAppLoopers[i]->removeFd(mDispatchReadFd); + } + mAppLoopers.clear(); +} + +bool InputQueue::hasEvents() { + Mutex::Autolock _l(mLock); + return mPendingEvents.size() > 0; +} + +status_t InputQueue::getEvent(InputEvent** outEvent) { + Mutex::Autolock _l(mLock); + *outEvent = NULL; + if (!mPendingEvents.isEmpty()) { + *outEvent = mPendingEvents[0]; + mPendingEvents.removeAt(0); + } + + if (mPendingEvents.isEmpty()) { + char byteread[16]; + ssize_t nRead; + do { + nRead = TEMP_FAILURE_RETRY(read(mDispatchReadFd, &byteread, sizeof(byteread))); + if (nRead < 0 && errno != EAGAIN) { + ALOGW("Failed to read from native dispatch pipe: %s", strerror(errno)); + } + } while (nRead > 0); + } + + return *outEvent != NULL ? OK : WOULD_BLOCK; +} + +bool InputQueue::preDispatchEvent(InputEvent* e) { + if (e->getType() == AINPUT_EVENT_TYPE_KEY) { + KeyEvent* keyEvent = static_cast<KeyEvent*>(e); + if (keyEvent->getFlags() & AKEY_EVENT_FLAG_PREDISPATCH) { + finishEvent(e, false); + return true; + } + } + return false; +} + +void InputQueue::finishEvent(InputEvent* event, bool handled) { + Mutex::Autolock _l(mLock); + mFinishedEvents.push(key_value_pair_t<InputEvent*, bool>(event, handled)); + if (mFinishedEvents.size() == 1) { + mDispatchLooper->sendMessage(this, Message(MSG_FINISH_INPUT)); + } +} + +void InputQueue::handleMessage(const Message& message) { + switch(message.what) { + case MSG_FINISH_INPUT: + JNIEnv* env = AndroidRuntime::getJNIEnv(); + ScopedLocalRef<jobject> inputQueueObj(env, jniGetReferent(env, mInputQueueWeakGlobal)); + if (!inputQueueObj.get()) { + ALOGW("InputQueue was finalized without being disposed"); + return; + } + while (true) { + InputEvent* event; + bool handled; + { + Mutex::Autolock _l(mLock); + if (mFinishedEvents.isEmpty()) { + break; + } + event = mFinishedEvents[0].getKey(); + handled = mFinishedEvents[0].getValue(); + mFinishedEvents.removeAt(0); + } + env->CallVoidMethod(inputQueueObj.get(), gInputQueueClassInfo.finishInputEvent, + reinterpret_cast<jint>(event), handled); + recycleInputEvent(event); + } + break; + } +} + +void InputQueue::recycleInputEvent(InputEvent* event) { + mPooledInputEventFactory.recycle(event); +} + +KeyEvent* InputQueue::createKeyEvent() { + return mPooledInputEventFactory.createKeyEvent(); +} + +MotionEvent* InputQueue::createMotionEvent() { + return mPooledInputEventFactory.createMotionEvent(); +} + +void InputQueue::enqueueEvent(InputEvent* event) { + Mutex::Autolock _l(mLock); + mPendingEvents.push(event); + if (mPendingEvents.size() == 1) { + char dummy = 0; + int res = TEMP_FAILURE_RETRY(write(mDispatchWriteFd, &dummy, sizeof(dummy))); + if (res < 0 && errno != EAGAIN) { + ALOGW("Failed writing to dispatch fd: %s", strerror(errno)); + } + } +} + +InputQueue* InputQueue::createQueue(jobject inputQueueObj, const sp<Looper>& looper) { + int pipeFds[2]; + if (pipe(pipeFds)) { + ALOGW("Could not create native input dispatching pipe: %s", strerror(errno)); + return NULL; + } + fcntl(pipeFds[0], F_SETFL, O_NONBLOCK); + fcntl(pipeFds[1], F_SETFL, O_NONBLOCK); + return new InputQueue(inputQueueObj, looper, pipeFds[0], pipeFds[1]); +} + +static jint nativeInit(JNIEnv* env, jobject clazz, jobject queueWeak, jobject jMsgQueue) { + sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, jMsgQueue); + if (messageQueue == NULL) { + jniThrowRuntimeException(env, "MessageQueue is not initialized."); + return 0; + } + sp<InputQueue> queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper()); + if (!queue.get()) { + jniThrowRuntimeException(env, "InputQueue failed to initialize"); + return 0; + } + queue->incStrong(&gInputQueueClassInfo); + return reinterpret_cast<jint>(queue.get()); +} + +static void nativeDispose(JNIEnv* env, jobject clazz, jint ptr) { + sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr); + queue->detachLooper(); + queue->decStrong(&gInputQueueClassInfo); +} + +static jint nativeSendKeyEvent(JNIEnv* env, jobject clazz, jint ptr, jobject eventObj, + jboolean predispatch) { + InputQueue* queue = reinterpret_cast<InputQueue*>(ptr); + KeyEvent* event = queue->createKeyEvent(); + status_t status = android_view_KeyEvent_toNative(env, eventObj, event); + if (status) { + queue->recycleInputEvent(event); + jniThrowRuntimeException(env, "Could not read contents of KeyEvent object."); + return -1; + } + + if (predispatch) { + event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH); + } + + queue->enqueueEvent(event); + return reinterpret_cast<jint>(event); +} + +static jint nativeSendMotionEvent(JNIEnv* env, jobject clazz, jint ptr, jobject eventObj) { + sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr); + MotionEvent* originalEvent = android_view_MotionEvent_getNativePtr(env, eventObj); + if (!originalEvent) { + jniThrowRuntimeException(env, "Could not obtain MotionEvent pointer."); + return -1; + } + MotionEvent* event = queue->createMotionEvent(); + event->copyFrom(originalEvent, true /* keepHistory */); + queue->enqueueEvent(event); + return reinterpret_cast<jint>(event); +} + +static const JNINativeMethod g_methods[] = { + { "nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)I", + (void*) nativeInit }, + { "nativeDispose", "(I)V", (void*) nativeDispose }, + { "nativeSendKeyEvent", "(ILandroid/view/KeyEvent;Z)I", (void*) nativeSendKeyEvent }, + { "nativeSendMotionEvent", "(ILandroid/view/MotionEvent;)I", (void*) nativeSendMotionEvent }, +}; + +static const char* const kInputQueuePathName = "android/view/InputQueue"; + +#define FIND_CLASS(var, className) \ + do { \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class %s", className); \ + } while(0) + +#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + do { \ + var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method" methodName); \ + } while(0) + +int register_android_view_InputQueue(JNIEnv* env) +{ + jclass clazz; + FIND_CLASS(clazz, kInputQueuePathName); + GET_METHOD_ID(gInputQueueClassInfo.finishInputEvent, clazz, "finishInputEvent", "(IZ)V"); + + return AndroidRuntime::registerNativeMethods( + env, kInputQueuePathName, + g_methods, NELEM(g_methods)); +} + +} // namespace android |