summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2010-07-28 15:48:59 -0700
committerJeff Brown <jeffbrown@google.com>2010-07-29 12:54:27 -0700
commit6ec402b5ae33c8927694d8522b4cc6a5c8ba974e (patch)
tree5d4b19eda9ade71d7e34635479426f1dd484e8c2 /services
parent6dea6f4e71b53e421564d783c227cbe0a2469183 (diff)
downloadframeworks_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.java72
-rw-r--r--services/java/com/android/server/WindowManagerService.java46
-rw-r--r--services/jni/com_android_server_InputManager.cpp55
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;
}