From a44dd26a75e24cc021802288fb81f4761e47be6b Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Wed, 10 Apr 2013 21:12:00 -0700 Subject: Rewrite input handling for native applications Bug: 8473020 Change-Id: Ic4353d8924ab877bec21aff8c2dba9fe725bf906 --- core/java/android/app/NativeActivity.java | 46 +-- core/java/android/view/InputQueue.java | 130 ++++++- core/java/android/view/KeyEvent.java | 6 + core/java/android/view/ViewRootImpl.java | 64 ++- core/jni/Android.mk | 1 + core/jni/AndroidRuntime.cpp | 2 + core/jni/android_app_NativeActivity.cpp | 428 +-------------------- core/jni/android_view_InputQueue.cpp | 282 ++++++++++++++ .../android_runtime/android_app_NativeActivity.h | 103 ----- include/android_runtime/android_view_InputQueue.h | 85 ++++ include/androidfw/Input.h | 3 + native/android/input.cpp | 27 +- 12 files changed, 585 insertions(+), 592 deletions(-) create mode 100644 core/jni/android_view_InputQueue.cpp create mode 100644 include/android_runtime/android_view_InputQueue.h diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java index 7d8a36e..63c6acd 100644 --- a/core/java/android/app/NativeActivity.java +++ b/core/java/android/app/NativeActivity.java @@ -27,7 +27,6 @@ import android.os.Environment; import android.os.Looper; import android.os.MessageQueue; import android.util.AttributeSet; -import android.view.InputChannel; import android.view.InputQueue; import android.view.KeyEvent; import android.view.Surface; @@ -111,11 +110,9 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, int format, int width, int height); private native void onSurfaceRedrawNeededNative(int handle, Surface surface); private native void onSurfaceDestroyedNative(int handle); - private native void onInputChannelCreatedNative(int handle, InputChannel channel); - private native void onInputChannelDestroyedNative(int handle, InputChannel channel); + private native void onInputQueueCreatedNative(int handle, int queuePtr); + private native void onInputQueueDestroyedNative(int handle, int queuePtr); private native void onContentRectChangedNative(int handle, int x, int y, int w, int h); - private native void dispatchKeyEventNative(int handle, KeyEvent event); - private native void finishPreDispatchKeyEventNative(int handle, int seq, boolean handled); static class NativeContentView extends View { NativeActivity mActivity; @@ -197,7 +194,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, mCurSurfaceHolder = null; } if (mCurInputQueue != null) { - onInputChannelDestroyedNative(mNativeHandle, mCurInputQueue.getInputChannel()); + onInputQueueDestroyedNative(mNativeHandle, mCurInputQueue.getNativePtr()); mCurInputQueue = null; } unloadNativeCode(mNativeHandle); @@ -261,18 +258,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, } } - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - if (mDispatchingUnhandledKey) { - return super.dispatchKeyEvent(event); - } else { - // Key events from the IME do not go through the input channel; - // we need to intercept them here to hand to the application. - dispatchKeyEventNative(mNativeHandle, event); - return true; - } - } - public void surfaceCreated(SurfaceHolder holder) { if (!mDestroyed) { mCurSurfaceHolder = holder; @@ -304,14 +289,14 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, public void onInputQueueCreated(InputQueue queue) { if (!mDestroyed) { mCurInputQueue = queue; - onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel()); + onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr()); } } public void onInputQueueDestroyed(InputQueue queue) { - mCurInputQueue = null; if (!mDestroyed) { - onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel()); + onInputQueueDestroyedNative(mNativeHandle, queue.getNativePtr()); + mCurInputQueue = null; } } @@ -332,25 +317,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, } } - boolean dispatchUnhandledKeyEvent(KeyEvent event) { - try { - mDispatchingUnhandledKey = true; - View decor = getWindow().getDecorView(); - if (decor != null) { - return decor.dispatchKeyEvent(event); - } else { - return false; - } - } finally { - mDispatchingUnhandledKey = false; - } - } - - void preDispatchKeyEvent(KeyEvent event, int seq) { - // FIXME: Input dispatch should be redirected back through ViewRootImpl again. - finishPreDispatchKeyEventNative(mNativeHandle, seq, false); - } - void setWindowFlags(int flags, int mask) { getWindow().setFlags(flags, mask); } diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java index 909a3b2..e3de89d 100644 --- a/core/java/android/view/InputQueue.java +++ b/core/java/android/view/InputQueue.java @@ -16,11 +16,127 @@ package android.view; +import dalvik.system.CloseGuard; + +import android.os.Handler; +import android.os.Looper; +import android.os.MessageQueue; +import android.util.Pools.Pool; +import android.util.Pools.SimplePool; +import android.util.SparseArray; + +import java.lang.ref.WeakReference; + /** * An input queue provides a mechanism for an application to receive incoming * input events. Currently only usable from native code. */ public final class InputQueue { + private final SparseArray mActiveEventArray = + new SparseArray(20); + private final Pool mActiveInputEventPool = + new SimplePool(20); + + private final CloseGuard mCloseGuard = CloseGuard.get(); + + private int mPtr; + + private static native int nativeInit(WeakReference weakQueue, + MessageQueue messageQueue); + private static native int nativeSendKeyEvent(int ptr, KeyEvent e, boolean preDispatch); + private static native int nativeSendMotionEvent(int ptr, MotionEvent e); + private static native void nativeDispose(int ptr); + + /** @hide */ + public InputQueue() { + mPtr = nativeInit(new WeakReference(this), Looper.myQueue()); + + mCloseGuard.open("dispose"); + } + + @Override + protected void finalize() throws Throwable { + try { + dispose(true); + } finally { + super.finalize(); + } + } + + /** @hide */ + public void dispose() { + dispose(false); + } + + /** @hide */ + public void dispose(boolean finalized) { + if (mCloseGuard != null) { + if (finalized) { + mCloseGuard.warnIfOpen(); + } + mCloseGuard.close(); + } + + if (mPtr != 0) { + nativeDispose(mPtr); + mPtr = 0; + } + } + + /** @hide */ + public int getNativePtr() { + return mPtr; + } + + /** @hide */ + public void sendInputEvent(InputEvent e, Object token, boolean predispatch, + FinishedInputEventCallback callback) { + ActiveInputEvent event = obtainActiveInputEvent(token, callback); + int id; + if (e instanceof KeyEvent) { + id = nativeSendKeyEvent(mPtr, (KeyEvent) e, predispatch); + } else { + id = nativeSendMotionEvent(mPtr, (MotionEvent) e); + } + mActiveEventArray.put(id, event); + } + + private void finishInputEvent(int id, boolean handled) { + int index = mActiveEventArray.indexOfKey(id); + if (index >= 0) { + ActiveInputEvent e = mActiveEventArray.valueAt(index); + mActiveEventArray.removeAt(index); + e.mCallback.onFinishedInputEvent(e.mToken, handled); + recycleActiveInputEvent(e); + } + } + + private ActiveInputEvent obtainActiveInputEvent(Object token, + FinishedInputEventCallback callback) { + ActiveInputEvent e = mActiveInputEventPool.acquire(); + if (e == null) { + e = new ActiveInputEvent(); + } + e.mToken = token; + e.mCallback = callback; + return e; + } + + private void recycleActiveInputEvent(ActiveInputEvent e) { + e.recycle(); + mActiveInputEventPool.release(e); + } + + private final class ActiveInputEvent { + public Object mToken; + public FinishedInputEventCallback mCallback; + + public void recycle() { + mToken = null; + mCallback = null; + } + } + /** * Interface to receive notification of when an InputQueue is associated * and dissociated with a thread. @@ -31,7 +147,7 @@ public final class InputQueue { * thread making this call, so it can start receiving events from it. */ void onInputQueueCreated(InputQueue queue); - + /** * Called when the given InputQueue is no longer associated with * the thread and thus not dispatching events. @@ -39,15 +155,9 @@ public final class InputQueue { void onInputQueueDestroyed(InputQueue queue); } - final InputChannel mChannel; - - /** @hide */ - public InputQueue(InputChannel channel) { - mChannel = channel; - } - /** @hide */ - public InputChannel getInputChannel() { - return mChannel; + public static interface FinishedInputEventCallback { + void onFinishedInputEvent(Object token, boolean handled); } + } diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index bb533bf..7d9f30a 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1233,6 +1233,12 @@ public class KeyEvent extends InputEvent implements Parcelable { public static final int FLAG_FALLBACK = 0x400; /** + * Signifies that the key is being predispatched. + * @hide + */ + public static final int FLAG_PREDISPATCH = 0x20000000; + + /** * Private control to determine when an app is tracking a key sequence. * @hide */ diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f03c077..7ecb52e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -597,12 +597,11 @@ public final class ViewRootImpl implements ViewParent, } if (mInputChannel != null) { if (mInputQueueCallback != null) { - mInputQueue = new InputQueue(mInputChannel); + mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); - } else { - mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, - Looper.myLooper()); } + mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, + Looper.myLooper()); } view.assignParent(this); @@ -2822,9 +2821,11 @@ public final class ViewRootImpl implements ViewParent, if (mInputQueueCallback != null && mInputQueue != null) { mInputQueueCallback.onInputQueueDestroyed(mInputQueue); + mInputQueue.dispose(); mInputQueueCallback = null; mInputQueue = null; - } else if (mInputEventReceiver != null) { + } + if (mInputEventReceiver != null) { mInputEventReceiver.dispose(); mInputEventReceiver = null; } @@ -3347,6 +3348,15 @@ public final class ViewRootImpl implements ViewParent, if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) { forward(q); } else if (mView == null || !mAdded) { + Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent); + finish(q, false); + } else if (!mAttachInfo.mHasWindowFocus && + !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) && + !isTerminalInputEvent(q.mEvent)) { + // If this is a focused event and the window doesn't currently have input focus, + // then drop this event. This could be an event that came back from the previous + // stage but the window has lost focus in the meantime. + Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent); finish(q, false); } else { apply(q, onProcess(q)); @@ -3547,15 +3557,30 @@ public final class ViewRootImpl implements ViewParent, * Delivers pre-ime input events to a native activity. * Does not support pointer events. */ - final class NativePreImeInputStage extends AsyncInputStage { + final class NativePreImeInputStage extends AsyncInputStage + implements InputQueue.FinishedInputEventCallback { public NativePreImeInputStage(InputStage next, String traceCounter) { super(next, traceCounter); } @Override protected int onProcess(QueuedInputEvent q) { + if (mInputQueue != null && q.mEvent instanceof KeyEvent) { + mInputQueue.sendInputEvent(q.mEvent, q, true, this); + return DEFER; + } return FORWARD; } + + @Override + public void onFinishedInputEvent(Object token, boolean handled) { + QueuedInputEvent q = (QueuedInputEvent)token; + if (handled) { + finish(q, true); + return; + } + forward(q); + } } /** @@ -3621,16 +3646,6 @@ public final class ViewRootImpl implements ViewParent, finish(q, true); return; } - - // If the window doesn't currently have input focus, then drop - // this event. This could be an event that came back from the - // IME dispatch but the window has lost focus in the meantime. - if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) { - Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent); - finish(q, false); - return; - } - forward(q); } } @@ -3702,15 +3717,30 @@ public final class ViewRootImpl implements ViewParent, /** * Delivers post-ime input events to a native activity. */ - final class NativePostImeInputStage extends AsyncInputStage { + final class NativePostImeInputStage extends AsyncInputStage + implements InputQueue.FinishedInputEventCallback { public NativePostImeInputStage(InputStage next, String traceCounter) { super(next, traceCounter); } @Override protected int onProcess(QueuedInputEvent q) { + if (mInputQueue != null) { + mInputQueue.sendInputEvent(q.mEvent, q, false, this); + return DEFER; + } return FORWARD; } + + @Override + public void onFinishedInputEvent(Object token, boolean handled) { + QueuedInputEvent q = (QueuedInputEvent)token; + if (handled) { + finish(q, true); + return; + } + forward(q); + } } /** 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& 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(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; igetName().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; igetName().string(), res); - } - } - mPooledInputEventFactory.recycle(static_cast(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(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= 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 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(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(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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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, + 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(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(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 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(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) { + 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 = android_os_MessageQueue_getMessageQueue(env, jMsgQueue); + if (messageQueue == NULL) { + jniThrowRuntimeException(env, "MessageQueue is not initialized."); + return 0; + } + sp queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper()); + if (!queue.get()) { + jniThrowRuntimeException(env, "InputQueue failed to initialize"); + return 0; + } + queue->incStrong(&gInputQueueClassInfo); + return reinterpret_cast(queue.get()); +} + +static void nativeDispose(JNIEnv* env, jobject clazz, jint ptr) { + sp queue = reinterpret_cast(ptr); + queue->detachLooper(); + queue->decStrong(&gInputQueueClassInfo); +} + +static jint nativeSendKeyEvent(JNIEnv* env, jobject clazz, jint ptr, jobject eventObj, + jboolean predispatch) { + InputQueue* queue = reinterpret_cast(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(event); +} + +static jint nativeSendMotionEvent(JNIEnv* env, jobject clazz, jint ptr, jobject eventObj) { + sp queue = reinterpret_cast(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(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 diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h index 7977c23..e096e91 100644 --- a/include/android_runtime/android_app_NativeActivity.h +++ b/include/android_runtime/android_app_NativeActivity.h @@ -17,7 +17,6 @@ #ifndef _ANDROID_APP_NATIVEACTIVITY_H #define _ANDROID_APP_NATIVEACTIVITY_H -#include #include #include @@ -43,106 +42,4 @@ extern void android_NativeActivity_hideSoftInput( } // namespace android - -/* - * NDK input queue API. - * - * Here is the event flow: - * 1. Event arrives in input consumer, and is returned by getEvent(). - * 2. Application calls preDispatchEvent(): - * a. Event is assigned a sequence ID and enqueued in mPreDispatchingKeys. - * b. Main thread picks up event, hands to input method. - * c. Input method eventually returns sequence # and whether it was handled. - * d. finishPreDispatch() is called to enqueue the information. - * e. next getEvent() call will: - * - finish any pre-dispatch events that the input method handled - * - return the next pre-dispatched event that the input method didn't handle. - * f. (A preDispatchEvent() call on this event will now return false). - * 3. Application calls finishEvent() with whether it was handled. - * - If handled is true, the event is finished. - * - If handled is false, the event is put on mUnhandledKeys, and: - * a. Main thread receives event from consumeUnhandledEvent(). - * b. Java sends event through default key handler. - * c. event is finished. - */ -struct AInputQueue { -public: - /* Creates a consumer associated with an input channel. */ - explicit AInputQueue(const android::sp& channel, int workWrite); - - /* Destroys the consumer and releases its input channel. */ - ~AInputQueue(); - - void attachLooper(ALooper* looper, int ident, ALooper_callbackFunc callback, void* data); - - void detachLooper(); - - int32_t hasEvents(); - - int32_t getEvent(AInputEvent** outEvent); - - bool preDispatchEvent(AInputEvent* event); - - void finishEvent(AInputEvent* event, bool handled, bool didDefaultHandling); - - // ---------------------------------------------------------- - - inline android::InputConsumer& getConsumer() { return mConsumer; } - - void dispatchEvent(android::KeyEvent* event); - - void finishPreDispatch(int seq, bool handled); - - android::KeyEvent* consumeUnhandledEvent(); - android::KeyEvent* consumePreDispatchingEvent(int* outSeq); - - android::KeyEvent* createKeyEvent(); - - int mWorkWrite; - -private: - void doUnhandledKey(android::KeyEvent* keyEvent); - bool preDispatchKey(android::KeyEvent* keyEvent); - void wakeupDispatchLocked(); - - android::PooledInputEventFactory mPooledInputEventFactory; - android::InputConsumer mConsumer; - android::sp mLooper; - - int mDispatchKeyRead; - int mDispatchKeyWrite; - - struct in_flight_event { - android::InputEvent* event; - int seq; // internal sequence number for synthetic pre-dispatch events - uint32_t finishSeq; // sequence number for sendFinishedSignal, or 0 if finish not required - }; - - struct finish_pre_dispatch { - int seq; - bool handled; - }; - - android::Mutex mLock; - - int mSeq; - - // All input events that are actively being processed. - android::Vector mInFlightEvents; - - // Key events that the app didn't handle, and are pending for - // delivery to the activity's default key handling. - android::Vector mUnhandledKeys; - - // Keys that arrived in the Java framework and need to be - // dispatched to the app. - android::Vector mDispatchingKeys; - - // Key events that are pending to be pre-dispatched to the IME. - android::Vector mPreDispatchingKeys; - - // Event sequence numbers that we have finished pre-dispatching. - android::Vector mFinishPreDispatches; -}; - #endif // _ANDROID_APP_NATIVEACTIVITY_H diff --git a/include/android_runtime/android_view_InputQueue.h b/include/android_runtime/android_view_InputQueue.h new file mode 100644 index 0000000..ba2d02d --- /dev/null +++ b/include/android_runtime/android_view_InputQueue.h @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#ifndef _ANDROID_VIEW_INPUTQUEUE_H +#define _ANDROID_VIEW_INPUTQUEUE_H + +#include +#include +#include +#include + +#include "JNIHelp.h" + +/* + * Declare a concrete type for the NDK's AInputQueue forward declaration + */ +struct AInputQueue{ +}; + +namespace android { + +class InputQueue : public AInputQueue, public MessageHandler { +public: + virtual ~InputQueue(); + + void attachLooper(Looper* looper, int ident, ALooper_callbackFunc callback, void* data); + + void detachLooper(); + + bool hasEvents(); + + status_t getEvent(InputEvent** outEvent); + + bool preDispatchEvent(InputEvent* event); + + void finishEvent(InputEvent* event, bool handled); + + KeyEvent* createKeyEvent(); + + MotionEvent* createMotionEvent(); + + void recycleInputEvent(InputEvent* event); + + void enqueueEvent(InputEvent* event); + + static InputQueue* createQueue(jobject inputQueueObj, const sp& looper); + +protected: + virtual void handleMessage(const Message& message); + +private: + InputQueue(jobject inputQueueObj, const sp& looper, + int readDispatchFd, int writeDispatchFd); + + void detachLooperLocked(); + + jobject mInputQueueWeakGlobal; + int mDispatchReadFd; + int mDispatchWriteFd; + Vector mAppLoopers; + sp mDispatchLooper; + sp mHandler; + PooledInputEventFactory mPooledInputEventFactory; + // Guards the pending and finished event vectors + mutable Mutex mLock; + Vector mPendingEvents; + Vector > mFinishedEvents; +}; + +} // namespace android + +#endif diff --git a/include/androidfw/Input.h b/include/androidfw/Input.h index e88835e..37ab279 100644 --- a/include/androidfw/Input.h +++ b/include/androidfw/Input.h @@ -36,6 +36,9 @@ class SkMatrix; * Additional private constants not defined in ndk/ui/input.h. */ enum { + /* Signifies that the key is being predispatched */ + AKEY_EVENT_FLAG_PREDISPATCH = 0x20000000, + /* Private control to determine when an app is tracking a key sequence. */ AKEY_EVENT_FLAG_START_TRACKING = 0x40000000, diff --git a/native/android/input.cpp b/native/android/input.cpp index accec64..7ac73c7 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -25,12 +25,15 @@ #include #include +#include #include #include using android::InputEvent; +using android::InputQueue; using android::KeyEvent; +using android::Looper; using android::MotionEvent; using android::sp; using android::Vector; @@ -269,25 +272,37 @@ float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, int ident, ALooper_callbackFunc callback, void* data) { - queue->attachLooper(looper, ident, callback, data); + InputQueue* iq = static_cast(queue); + Looper* l = static_cast(looper); + iq->attachLooper(l, ident, callback, data); } void AInputQueue_detachLooper(AInputQueue* queue) { - queue->detachLooper(); + InputQueue* iq = static_cast(queue); + iq->detachLooper(); } int32_t AInputQueue_hasEvents(AInputQueue* queue) { - return queue->hasEvents(); + InputQueue* iq = static_cast(queue); + return iq->hasEvents(); } int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent) { - return queue->getEvent(outEvent); + InputQueue* iq = static_cast(queue); + InputEvent* event; + int32_t res = iq->getEvent(&event); + *outEvent = event; + return res; } int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event) { - return queue->preDispatchEvent(event) ? 1 : 0; + InputQueue* iq = static_cast(queue); + InputEvent* e = static_cast(event); + return iq->preDispatchEvent(e) ? 1 : 0; } void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled) { - queue->finishEvent(event, handled != 0, false); + InputQueue* iq = static_cast(queue); + InputEvent* e = static_cast(event); + iq->finishEvent(e, handled != 0); } -- cgit v1.1