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
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.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 @@
             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 @@
     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 @@
     /** 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 @@
     }
     
     /**
-     * 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 @@
         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.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 @@
 
     /**
      * 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 @@
         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 @@
 
     /**
      * 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 @@
             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 @@
 
     /**
      * 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 @@
             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);