diff options
author | Jeff Brown <jeffbrown@google.com> | 2010-07-28 15:48:59 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2010-07-29 12:54:27 -0700 |
commit | 6ec402b5ae33c8927694d8522b4cc6a5c8ba974e (patch) | |
tree | 5d4b19eda9ade71d7e34635479426f1dd484e8c2 /services | |
parent | 6dea6f4e71b53e421564d783c227cbe0a2469183 (diff) | |
download | frameworks_base-6ec402b5ae33c8927694d8522b4cc6a5c8ba974e.zip frameworks_base-6ec402b5ae33c8927694d8522b4cc6a5c8ba974e.tar.gz frameworks_base-6ec402b5ae33c8927694d8522b4cc6a5c8ba974e.tar.bz2 |
DO NOT MERGE: Fix input event injection ANRs on UI thread.
Added a new asynchronous injection mode and made the existing
synchronization mechanism more robust.
Change-Id: Ia4aa04fd9b75ea2461a844c5b7933c831c1027e6
Diffstat (limited to 'services')
-rw-r--r-- | services/java/com/android/server/InputManager.java | 72 | ||||
-rw-r--r-- | services/java/com/android/server/WindowManagerService.java | 46 | ||||
-rw-r--r-- | services/jni/com_android_server_InputManager.cpp | 55 |
3 files changed, 101 insertions, 72 deletions
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index 9a1d017..9195123 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -29,6 +29,7 @@ import android.os.PowerManager; import android.util.Slog; import android.util.Xml; import android.view.InputChannel; +import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; @@ -76,10 +77,8 @@ public class InputManager { int[] keyCodes, boolean[] keyExists); private static native void nativeRegisterInputChannel(InputChannel inputChannel); private static native void nativeUnregisterInputChannel(InputChannel inputChannel); - private static native int nativeInjectKeyEvent(KeyEvent event, - int injectorPid, int injectorUid, boolean sync, int timeoutMillis); - private static native int nativeInjectMotionEvent(MotionEvent event, - int injectorPid, int injectorUid, boolean sync, int timeoutMillis); + private static native int nativeInjectInputEvent(InputEvent event, + int injectorPid, int injectorUid, int syncMode, int timeoutMillis); private static native void nativeSetInputWindows(InputWindow[] windows); private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen); private static native void nativeSetFocusedApplication(InputApplication application); @@ -92,6 +91,11 @@ public class InputManager { static final int INPUT_EVENT_INJECTION_FAILED = 2; static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3; + // Input event injection synchronization modes defined in InputDispatcher.h + static final int INPUT_EVENT_INJECTION_SYNC_NONE = 0; + static final int INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1; + static final int INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH = 2; + // Key states (may be returned by queries about the current state of a // particular key code, scan code or switch). @@ -107,7 +111,6 @@ public class InputManager { /** The key is down but is a virtual key press that is being emulated by the system. */ public static final int KEY_STATE_VIRTUAL = 2; - public InputManager(Context context, WindowManagerService windowManagerService) { this.mContext = context; this.mWindowManagerService = windowManagerService; @@ -239,19 +242,30 @@ public class InputManager { } /** - * Injects a key event into the event system on behalf of an application. - * This method may block even if sync is false because it must wait for previous events - * to be dispatched before it can determine whether input event injection will be - * permitted based on the current input focus. + * Injects an input event into the event system on behalf of an application. + * The synchronization mode determines whether the method blocks while waiting for + * input injection to proceed. + * + * {@link #INPUT_EVENT_INJECTION_SYNC_NONE} never blocks. Injection is asynchronous and + * is assumed always to be successful. + * + * {@link #INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT} waits for previous events to be + * dispatched so that the input dispatcher can determine whether input event injection will + * be permitted based on the current input focus. Does not wait for the input event to + * finish processing. + * + * {@link #INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH} waits for the input event to + * be completely processed. + * * @param event The event to inject. * @param injectorPid The pid of the injecting application. * @param injectorUid The uid of the injecting application. - * @param sync If true, waits for the event to be completed before returning. + * @param syncMode The synchronization mode. * @param timeoutMillis The injection timeout in milliseconds. * @return One of the INPUT_EVENT_INJECTION_XXX constants. */ - public int injectKeyEvent(KeyEvent event, int injectorPid, int injectorUid, - boolean sync, int timeoutMillis) { + public int injectInputEvent(InputEvent event, int injectorPid, int injectorUid, + int syncMode, int timeoutMillis) { if (event == null) { throw new IllegalArgumentException("event must not be null"); } @@ -261,38 +275,8 @@ public class InputManager { if (timeoutMillis <= 0) { throw new IllegalArgumentException("timeoutMillis must be positive"); } - - return nativeInjectKeyEvent(event, injectorPid, injectorUid, - sync, timeoutMillis); - } - - /** - * Injects a motion event into the event system on behalf of an application. - * This method may block even if sync is false because it must wait for previous events - * to be dispatched before it can determine whether input event injection will be - * permitted based on the current input focus. - * @param event The event to inject. - * @param sync If true, waits for the event to be completed before returning. - * @param injectorPid The pid of the injecting application. - * @param injectorUid The uid of the injecting application. - * @param sync If true, waits for the event to be completed before returning. - * @param timeoutMillis The injection timeout in milliseconds. - * @return One of the INPUT_EVENT_INJECTION_XXX constants. - */ - public int injectMotionEvent(MotionEvent event, int injectorPid, int injectorUid, - boolean sync, int timeoutMillis) { - if (event == null) { - throw new IllegalArgumentException("event must not be null"); - } - if (injectorPid < 0 || injectorUid < 0) { - throw new IllegalArgumentException("injectorPid and injectorUid must not be negative."); - } - if (timeoutMillis <= 0) { - throw new IllegalArgumentException("timeoutMillis must be positive"); - } - - return nativeInjectMotionEvent(event, injectorPid, injectorUid, - sync, timeoutMillis); + + return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis); } public void setInputWindows(InputWindow[] windows) { diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index eb0f343..5615232 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -103,6 +103,7 @@ import android.view.IWindowManager; import android.view.IWindowSession; import android.view.InputChannel; import android.view.InputDevice; +import android.view.InputEvent; import android.view.InputQueue; import android.view.KeyEvent; import android.view.MotionEvent; @@ -5382,6 +5383,8 @@ public class WindowManagerService extends IWindowManager.Stub /** * Injects a keystroke event into the UI. + * Even when sync is false, this method may block while waiting for current + * input events to be dispatched. * * @param ev A motion event describing the keystroke action. (Be sure to use * {@link SystemClock#uptimeMillis()} as the timebase.) @@ -5414,8 +5417,10 @@ public class WindowManagerService extends IWindowManager.Stub final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); - final int result = mInputManager.injectKeyEvent(newEvent, - pid, uid, sync, INJECTION_TIMEOUT_MILLIS); + final int result = mInputManager.injectInputEvent(newEvent, pid, uid, + sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH + : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); return reportInjectionResult(result); @@ -5423,6 +5428,8 @@ public class WindowManagerService extends IWindowManager.Stub /** * Inject a pointer (touch) event into the UI. + * Even when sync is false, this method may block while waiting for current + * input events to be dispatched. * * @param ev A motion event describing the pointer (touch) action. (As noted in * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use @@ -5440,8 +5447,10 @@ public class WindowManagerService extends IWindowManager.Stub newEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN); } - final int result = mInputManager.injectMotionEvent(newEvent, - pid, uid, sync, INJECTION_TIMEOUT_MILLIS); + final int result = mInputManager.injectInputEvent(newEvent, pid, uid, + sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH + : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); return reportInjectionResult(result); @@ -5449,6 +5458,8 @@ public class WindowManagerService extends IWindowManager.Stub /** * Inject a trackball (navigation device) event into the UI. + * Even when sync is false, this method may block while waiting for current + * input events to be dispatched. * * @param ev A motion event describing the trackball action. (As noted in * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use @@ -5466,8 +5477,31 @@ public class WindowManagerService extends IWindowManager.Stub newEvent.setSource(InputDevice.SOURCE_TRACKBALL); } - final int result = mInputManager.injectMotionEvent(newEvent, - pid, uid, sync, INJECTION_TIMEOUT_MILLIS); + final int result = mInputManager.injectInputEvent(newEvent, pid, uid, + sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH + : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + INJECTION_TIMEOUT_MILLIS); + + Binder.restoreCallingIdentity(ident); + return reportInjectionResult(result); + } + + /** + * Inject an input event into the UI without waiting for dispatch to commence. + * This variant is useful for fire-and-forget input event injection. It does not + * block any longer than it takes to enqueue the input event. + * + * @param ev An input event. (Be sure to set the input source correctly.) + * @return Returns true if event was dispatched, false if it was dropped for any reason + */ + public boolean injectInputEventNoWait(InputEvent ev) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + + final int result = mInputManager.injectInputEvent(ev, pid, uid, + InputManager.INPUT_EVENT_INJECTION_SYNC_NONE, + INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); return reportInjectionResult(result); diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index a332376..0982b32 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -180,6 +180,14 @@ static struct { jfieldID token; } gInputApplicationClassInfo; +static struct { + jclass clazz; +} gKeyEventClassInfo; + +static struct { + jclass clazz; +} gMotionEventClassInfo; + // ---------------------------------------------------------------------------- static inline nsecs_t now() { @@ -2051,32 +2059,29 @@ static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env } } -static jint android_server_InputManager_nativeInjectKeyEvent(JNIEnv* env, jclass clazz, - jobject keyEventObj, jint injectorPid, jint injectorUid, - jboolean sync, jint timeoutMillis) { +static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jclass clazz, + jobject inputEventObj, jint injectorPid, jint injectorUid, + jint syncMode, jint timeoutMillis) { if (checkInputManagerUnitialized(env)) { return INPUT_EVENT_INJECTION_FAILED; } - KeyEvent keyEvent; - android_view_KeyEvent_toNative(env, keyEventObj, & keyEvent); + if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) { + KeyEvent keyEvent; + android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent); - return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent, - injectorPid, injectorUid, sync, timeoutMillis); -} + return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent, + injectorPid, injectorUid, syncMode, timeoutMillis); + } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) { + MotionEvent motionEvent; + android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent); -static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jclass clazz, - jobject motionEventObj, jint injectorPid, jint injectorUid, - jboolean sync, jint timeoutMillis) { - if (checkInputManagerUnitialized(env)) { + return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent, + injectorPid, injectorUid, syncMode, timeoutMillis); + } else { + jniThrowRuntimeException(env, "Invalid input event type."); return INPUT_EVENT_INJECTION_FAILED; } - - MotionEvent motionEvent; - android_view_MotionEvent_toNative(env, motionEventObj, & motionEvent); - - return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent, - injectorPid, injectorUid, sync, timeoutMillis); } static void android_server_InputManager_nativeSetInputWindows(JNIEnv* env, jclass clazz, @@ -2148,10 +2153,8 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) android_server_InputManager_nativeRegisterInputChannel }, { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V", (void*) android_server_InputManager_nativeUnregisterInputChannel }, - { "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIZI)I", - (void*) android_server_InputManager_nativeInjectKeyEvent }, - { "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIZI)I", - (void*) android_server_InputManager_nativeInjectMotionEvent }, + { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIII)I", + (void*) android_server_InputManager_nativeInjectInputEvent }, { "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V", (void*) android_server_InputManager_nativeSetInputWindows }, { "nativeSetFocusedApplication", "(Lcom/android/server/InputApplication;)V", @@ -2318,6 +2321,14 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gInputApplicationClassInfo.token, gInputApplicationClassInfo.clazz, "token", "Ljava/lang/Object;"); + // KeyEvent + + FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); + + // MotionEVent + + FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent"); + return 0; } |