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/api/current.xml b/api/current.xml
index 1b7254f..f1a383f 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -174217,6 +174217,17 @@
 >
 <implements name="android.os.Parcelable">
 </implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getDevice"
  return="android.view.InputDevice"
  abstract="false"
@@ -174250,6 +174261,16 @@
  visibility="public"
 >
 </method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="mDeviceId"
  type="int"
  transient="false"
@@ -174893,17 +174914,6 @@
 <parameter name="newFlags" type="int">
 </parameter>
 </method>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="dispatch"
  return="boolean"
  abstract="false"
@@ -178103,17 +178113,6 @@
 <parameter name="metaState" type="int">
 </parameter>
 </method>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="findPointerIndex"
  return="int"
  abstract="false"
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9b7b2f40..d6b9212 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -26,6 +26,7 @@
 import android.view.IRotationWatcher;
 import android.view.IWindowSession;
 import android.view.KeyEvent;
+import android.view.InputEvent;
 import android.view.MotionEvent;
 
 /**
@@ -50,10 +51,13 @@
     boolean inputMethodClientHasFocus(IInputMethodClient client);
     
     // These can only be called when injecting events to your own window,
-    // or by holding the INJECT_EVENTS permission.
+    // or by holding the INJECT_EVENTS permission.  These methods may block
+    // until pending input events are finished being dispatched even when 'sync' is false.
+    // Avoid calling these methods on your UI thread or use the 'NoWait' version instead.
     boolean injectKeyEvent(in KeyEvent ev, boolean sync);
     boolean injectPointerEvent(in MotionEvent ev, boolean sync);
     boolean injectTrackballEvent(in MotionEvent ev, boolean sync);
+    boolean injectInputEventNoWait(in InputEvent ev);
     
     // These can only be called when holding the MANAGE_APP_TOKENS permission.
     void pauseKeyDispatching(IBinder token);
diff --git a/core/java/android/view/InputEvent.aidl b/core/java/android/view/InputEvent.aidl
new file mode 100644
index 0000000..61acaaf
--- /dev/null
+++ b/core/java/android/view/InputEvent.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android.view.InputEvent.aidl
+**
+** Copyright 2007, 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.
+*/
+
+package android.view;
+
+parcelable InputEvent;
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 78b73fe..9afd16e 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
@@ -25,6 +26,11 @@
     protected int mDeviceId;
     protected int mSource;
     
+    /** @hide */
+    protected static final int PARCEL_TOKEN_MOTION_EVENT = 1;
+    /** @hide */
+    protected static final int PARCEL_TOKEN_KEY_EVENT = 2;
+    
     /*package*/ InputEvent() {
     }
 
@@ -69,4 +75,38 @@
     public final void setSource(int source) {
         mSource = source;
     }
+    
+    public final int describeContents() {
+        return 0;
+    }
+    
+    /** @hide */
+    protected final void readBaseFromParcel(Parcel in) {
+        mDeviceId = in.readInt();
+        mSource = in.readInt();
+    }
+    
+    /** @hide */
+    protected final void writeBaseToParcel(Parcel out) {
+        out.writeInt(mDeviceId);
+        out.writeInt(mSource);
+    }
+    
+    public static final Parcelable.Creator<InputEvent> CREATOR
+            = new Parcelable.Creator<InputEvent>() {
+        public InputEvent createFromParcel(Parcel in) {
+            int token = in.readInt();
+            if (token == PARCEL_TOKEN_KEY_EVENT) {
+                return KeyEvent.createFromParcelBody(in);
+            } else if (token == PARCEL_TOKEN_MOTION_EVENT) {
+                return MotionEvent.createFromParcelBody(in);
+            } else {
+                throw new IllegalStateException("Unexpected input event type token in parcel.");
+            }
+        }
+        
+        public InputEvent[] newArray(int size) {
+            return new InputEvent[size];
+        }
+    };
 }
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index dd0d21a3..9223e17 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1211,44 +1211,48 @@
     public static final Parcelable.Creator<KeyEvent> CREATOR
             = new Parcelable.Creator<KeyEvent>() {
         public KeyEvent createFromParcel(Parcel in) {
-            return new KeyEvent(in);
+            in.readInt(); // skip token, we already know this is a KeyEvent
+            return KeyEvent.createFromParcelBody(in);
         }
 
         public KeyEvent[] newArray(int size) {
             return new KeyEvent[size];
         }
     };
-
-    public int describeContents() {
-        return 0;
+    
+    /** @hide */
+    public static KeyEvent createFromParcelBody(Parcel in) {
+        return new KeyEvent(in);
+    }
+    
+    private KeyEvent(Parcel in) {
+        readBaseFromParcel(in);
+        
+        mAction = in.readInt();
+        mKeyCode = in.readInt();
+        mRepeatCount = in.readInt();
+        mMetaState = in.readInt();
+        mScanCode = in.readInt();
+        mFlags = in.readInt();
+        mDownTime = in.readLong();
+        mEventTime = in.readLong();
     }
 
     public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(PARCEL_TOKEN_KEY_EVENT);
+        
+        writeBaseToParcel(out);
+        
         out.writeInt(mAction);
         out.writeInt(mKeyCode);
         out.writeInt(mRepeatCount);
         out.writeInt(mMetaState);
-        out.writeInt(mDeviceId);
-        out.writeInt(mSource);
         out.writeInt(mScanCode);
         out.writeInt(mFlags);
         out.writeLong(mDownTime);
         out.writeLong(mEventTime);
     }
 
-    private KeyEvent(Parcel in) {
-        mAction = in.readInt();
-        mKeyCode = in.readInt();
-        mRepeatCount = in.readInt();
-        mMetaState = in.readInt();
-        mDeviceId = in.readInt();
-        mSource = in.readInt();
-        mScanCode = in.readInt();
-        mFlags = in.readInt();
-        mDownTime = in.readLong();
-        mEventTime = in.readLong();
-    }
-    
     private native boolean native_isSystemKey(int keyCode);
     private native boolean native_hasDefaultAction(int keyCode);
 }
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 0e78570d..98fe73f 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1453,43 +1453,8 @@
     public static final Parcelable.Creator<MotionEvent> CREATOR
             = new Parcelable.Creator<MotionEvent>() {
         public MotionEvent createFromParcel(Parcel in) {
-            final int NP = in.readInt();
-            final int NS = in.readInt();
-            final int NI = NP * NS * NUM_SAMPLE_DATA;
-            
-            MotionEvent ev = obtain(NP, NS);
-            ev.mNumPointers = NP;
-            ev.mNumSamples = NS;
-            
-            ev.mDownTimeNano = in.readLong();
-            ev.mAction = in.readInt();
-            ev.mXOffset = in.readFloat();
-            ev.mYOffset = in.readFloat();
-            ev.mXPrecision = in.readFloat();
-            ev.mYPrecision = in.readFloat();
-            ev.mDeviceId = in.readInt();
-            ev.mSource = in.readInt();
-            ev.mEdgeFlags = in.readInt();
-            ev.mMetaState = in.readInt();
-            
-            final int[] pointerIdentifiers = ev.mPointerIdentifiers;
-            for (int i = 0; i < NP; i++) {
-                pointerIdentifiers[i] = in.readInt();
-            }
-            
-            final long[] eventTimeNanoSamples = ev.mEventTimeNanoSamples;
-            for (int i = 0; i < NS; i++) {
-                eventTimeNanoSamples[i] = in.readLong();
-            }
-
-            final float[] dataSamples = ev.mDataSamples;
-            for (int i = 0; i < NI; i++) {
-                dataSamples[i] = in.readFloat();
-            }
-            
-            ev.mLastEventTimeNanoSampleIndex = NS - 1;
-            ev.mLastDataSampleIndex = (NS - 1) * NP * NUM_SAMPLE_DATA;
-            return ev;
+            in.readInt(); // skip token, we already know this is a MotionEvent
+            return MotionEvent.createFromParcelBody(in);
         }
 
         public MotionEvent[] newArray(int size) {
@@ -1497,11 +1462,50 @@
         }
     };
 
-    public int describeContents() {
-        return 0;
-    }
+    /** @hide */
+    public static MotionEvent createFromParcelBody(Parcel in) {
+        final int NP = in.readInt();
+        final int NS = in.readInt();
+        final int NI = NP * NS * NUM_SAMPLE_DATA;
+        
+        MotionEvent ev = obtain(NP, NS);
+        ev.mNumPointers = NP;
+        ev.mNumSamples = NS;
+        
+        ev.readBaseFromParcel(in);
+        
+        ev.mDownTimeNano = in.readLong();
+        ev.mAction = in.readInt();
+        ev.mXOffset = in.readFloat();
+        ev.mYOffset = in.readFloat();
+        ev.mXPrecision = in.readFloat();
+        ev.mYPrecision = in.readFloat();
+        ev.mEdgeFlags = in.readInt();
+        ev.mMetaState = in.readInt();
+        
+        final int[] pointerIdentifiers = ev.mPointerIdentifiers;
+        for (int i = 0; i < NP; i++) {
+            pointerIdentifiers[i] = in.readInt();
+        }
+        
+        final long[] eventTimeNanoSamples = ev.mEventTimeNanoSamples;
+        for (int i = 0; i < NS; i++) {
+            eventTimeNanoSamples[i] = in.readLong();
+        }
 
+        final float[] dataSamples = ev.mDataSamples;
+        for (int i = 0; i < NI; i++) {
+            dataSamples[i] = in.readFloat();
+        }
+        
+        ev.mLastEventTimeNanoSampleIndex = NS - 1;
+        ev.mLastDataSampleIndex = (NS - 1) * NP * NUM_SAMPLE_DATA;
+        return ev;
+    }
+    
     public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(PARCEL_TOKEN_MOTION_EVENT);
+        
         final int NP = mNumPointers;
         final int NS = mNumSamples;
         final int NI = NP * NS * NUM_SAMPLE_DATA;
@@ -1509,14 +1513,14 @@
         out.writeInt(NP);
         out.writeInt(NS);
         
+        writeBaseToParcel(out);
+        
         out.writeLong(mDownTimeNano);
         out.writeInt(mAction);
         out.writeFloat(mXOffset);
         out.writeFloat(mYOffset);
         out.writeFloat(mXPrecision);
         out.writeFloat(mYPrecision);
-        out.writeInt(mDeviceId);
-        out.writeInt(mSource);
         out.writeInt(mEdgeFlags);
         out.writeInt(mMetaState);
         
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 674852a..d3495fe 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -55,6 +55,22 @@
     INPUT_EVENT_INJECTION_TIMED_OUT = 3
 };
 
+/*
+ * Constants used to determine the input event injection synchronization mode.
+ */
+enum {
+    /* Injection is asynchronous and is assumed always to be successful. */
+    INPUT_EVENT_INJECTION_SYNC_NONE = 0,
+
+    /* Waits for previous events to be dispatched so that the input dispatcher can determine
+     * whether input event injection willbe permitted based on the current input focus.
+     * Does not wait for the input event to finish processing. */
+    INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,
+
+    /* Waits for the input event to be completely processed. */
+    INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
+};
+
 
 /*
  * An input target specifies how an input event is to be dispatched to a particular window
@@ -176,15 +192,14 @@
             float xPrecision, float yPrecision, nsecs_t downTime) = 0;
 
     /* Injects an input event and optionally waits for sync.
-     * 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.
+     * The synchronization mode determines whether the method blocks while waiting for
+     * input injection to proceed.
      * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
      *
      * This method may be called on any thread (usually by the input manager).
      */
     virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
 
     /* Preempts input dispatch in progress by making pending synchronous
      * dispatches asynchronous instead.  This method is generally called during a focus
@@ -241,7 +256,7 @@
             float xPrecision, float yPrecision, nsecs_t downTime);
 
     virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
 
     virtual void preemptInputDispatch();
 
@@ -267,11 +282,13 @@
         int32_t type;
         nsecs_t eventTime;
 
-        int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
-        int32_t injectorPid;     // -1 if not injected
-        int32_t injectorUid;     // -1 if not injected
+        int32_t injectionResult;  // initially INPUT_EVENT_INJECTION_PENDING
+        bool    injectionIsAsync; // set to true if injection is not waiting for the result
+        int32_t injectorPid;      // -1 if not injected
+        int32_t injectorUid;      // -1 if not injected
 
         bool dispatchInProgress; // initially false, set to true while dispatching
+        int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress
 
         inline bool isInjected() { return injectorPid >= 0; }
     };
@@ -340,6 +357,10 @@
         //   headMotionSample will be initialized to tailMotionSample and tailMotionSample
         //   will be set to NULL.
         MotionSample* tailMotionSample;
+
+        inline bool isSyncTarget() {
+            return targetFlags & InputTarget::FLAG_SYNC;
+        }
     };
 
     // A command entry captures state and behavior for an action to be performed in the
@@ -497,8 +518,7 @@
         // Since there can only ever be at most one such target at a time, if there is one,
         // it must be at the tail because nothing else can be enqueued after it.
         inline bool hasPendingSyncTarget() {
-            return ! outboundQueue.isEmpty()
-                    && (outboundQueue.tail.prev->targetFlags & InputTarget::FLAG_SYNC);
+            return ! outboundQueue.isEmpty() && outboundQueue.tail.prev->isSyncTarget();
         }
 
         // Gets the time since the current event was originally obtained from the input driver.
@@ -559,11 +579,12 @@
 
     // Event injection and synchronization.
     Condition mInjectionResultAvailableCondition;
-    Condition mFullySynchronizedCondition;
-    bool isFullySynchronizedLocked();
     EventEntry* createEntryFromInputEventLocked(const InputEvent* event);
     void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
 
+    Condition mInjectionSyncFinishedCondition;
+    void decrementPendingSyncDispatchesLocked(EventEntry* entry);
+
     // Key repeat tracking.
     // XXX Move this up to the input reader instead.
     struct KeyRepeatState {
diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h
index 7ebec10..4012c69 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -79,13 +79,12 @@
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
 
     /* Injects an input event and optionally waits for sync.
-     * 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.
+     * The synchronization mode determines whether the method blocks while waiting for
+     * input injection to proceed.
      * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
      */
     virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
 
     /* Preempts input dispatch in progress by making pending synchronous
      * dispatches asynchronous instead.  This method is generally called during a focus
@@ -142,7 +141,7 @@
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
     virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
 
     virtual void preemptInputDispatch();
 
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index a55864b..b53f140 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -184,11 +184,6 @@
 
         // Run any deferred commands.
         skipPoll |= runCommandsLockedInterruptible();
-
-        // Wake up synchronization waiters, if needed.
-        if (isFullySynchronizedLocked()) {
-            mFullySynchronizedCondition.broadcast();
-        }
     } // release lock
 
     // If we dispatched anything, don't poll just now.  Wait for the next iteration.
@@ -560,6 +555,10 @@
     dispatchEntry->headMotionSample = NULL;
     dispatchEntry->tailMotionSample = NULL;
 
+    if (dispatchEntry->isSyncTarget()) {
+        eventEntry->pendingSyncDispatches += 1;
+    }
+
     // Handle the case where we could not stream a new motion sample because the consumer has
     // already consumed the motion event (otherwise the corresponding dispatch entry would
     // still be in the outbound queue for this connection).  We set the head motion sample
@@ -789,6 +788,9 @@
             }
             // Finished.
             connection->outboundQueue.dequeueAtHead();
+            if (dispatchEntry->isSyncTarget()) {
+                decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+            }
             mAllocator.releaseDispatchEntry(dispatchEntry);
         } else {
             // If the head is not in progress, then we must have already dequeued the in
@@ -854,6 +856,9 @@
     if (! connection->outboundQueue.isEmpty()) {
         do {
             DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
+            if (dispatchEntry->isSyncTarget()) {
+                decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+            }
             mAllocator.releaseDispatchEntry(dispatchEntry);
         } while (! connection->outboundQueue.isEmpty());
 
@@ -1097,7 +1102,7 @@
                     Connection* connection = mActiveConnections.itemAt(i);
                     if (! connection->outboundQueue.isEmpty()) {
                         DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev;
-                        if (dispatchEntry->targetFlags & InputTarget::FLAG_SYNC) {
+                        if (dispatchEntry->isSyncTarget()) {
                             if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
                                 goto NoBatchingOrStreaming;
                             }
@@ -1148,11 +1153,11 @@
 }
 
 int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
-        int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) {
+        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
-            "sync=%d, timeoutMillis=%d",
-            event->getType(), injectorPid, injectorUid, sync, timeoutMillis);
+            "syncMode=%d, timeoutMillis=%d",
+            event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis);
 #endif
 
     nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
@@ -1167,6 +1172,10 @@
         injectedEntry->injectorPid = injectorPid;
         injectedEntry->injectorUid = injectorUid;
 
+        if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+            injectedEntry->injectionIsAsync = true;
+        }
+
         wasEmpty = mInboundQueue.isEmpty();
         mInboundQueue.enqueueAtTail(injectedEntry);
 
@@ -1180,37 +1189,59 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        for (;;) {
-            injectionResult = injectedEntry->injectionResult;
-            if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
-                break;
-            }
+        if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+            injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+        } else {
+            for (;;) {
+                injectionResult = injectedEntry->injectionResult;
+                if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
+                    break;
+                }
 
-            nsecs_t remainingTimeout = endTime - now();
-            if (remainingTimeout <= 0) {
-                injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
-                sync = false;
-                break;
-            }
-
-            mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
-        }
-
-        if (sync) {
-            while (! isFullySynchronizedLocked()) {
                 nsecs_t remainingTimeout = endTime - now();
                 if (remainingTimeout <= 0) {
+#if DEBUG_INJECTION
+                    LOGD("injectInputEvent - Timed out waiting for injection result "
+                            "to become available.");
+#endif
                     injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
                     break;
                 }
 
-                mFullySynchronizedCondition.waitRelative(mLock, remainingTimeout);
+                mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
+            }
+
+            if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
+                    && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
+                while (injectedEntry->pendingSyncDispatches != 0) {
+#if DEBUG_INJECTION
+                    LOGD("injectInputEvent - Waiting for %d pending synchronous dispatches.",
+                            injectedEntry->pendingSyncDispatches);
+#endif
+                    nsecs_t remainingTimeout = endTime - now();
+                    if (remainingTimeout <= 0) {
+#if DEBUG_INJECTION
+                    LOGD("injectInputEvent - Timed out waiting for pending synchronous "
+                            "dispatches to finish.");
+#endif
+                        injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+                        break;
+                    }
+
+                    mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
+                }
             }
         }
 
         mAllocator.releaseEventEntry(injectedEntry);
     } // release lock
 
+#if DEBUG_INJECTION
+    LOGD("injectInputEvent - Finished with result %d.  "
+            "injectorPid=%d, injectorUid=%d",
+            injectionResult, injectorPid, injectorUid);
+#endif
+
     return injectionResult;
 }
 
@@ -1222,13 +1253,35 @@
                  injectionResult, entry->injectorPid, entry->injectorUid);
 #endif
 
+        if (entry->injectionIsAsync) {
+            // Log the outcome since the injector did not wait for the injection result.
+            switch (injectionResult) {
+            case INPUT_EVENT_INJECTION_SUCCEEDED:
+                LOGV("Asynchronous input event injection succeeded.");
+                break;
+            case INPUT_EVENT_INJECTION_FAILED:
+                LOGW("Asynchronous input event injection failed.");
+                break;
+            case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+                LOGW("Asynchronous input event injection permission denied.");
+                break;
+            case INPUT_EVENT_INJECTION_TIMED_OUT:
+                LOGW("Asynchronous input event injection timed out.");
+                break;
+            }
+        }
+
         entry->injectionResult = injectionResult;
         mInjectionResultAvailableCondition.broadcast();
     }
 }
 
-bool InputDispatcher::isFullySynchronizedLocked() {
-    return mInboundQueue.isEmpty() && mActiveConnections.isEmpty();
+void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) {
+    entry->pendingSyncDispatches -= 1;
+
+    if (entry->isInjected() && entry->pendingSyncDispatches == 0) {
+        mInjectionSyncFinishedCondition.broadcast();
+    }
 }
 
 InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
@@ -1498,8 +1551,10 @@
     entry->dispatchInProgress = false;
     entry->eventTime = eventTime;
     entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    entry->injectionIsAsync = false;
     entry->injectorPid = -1;
     entry->injectorUid = -1;
+    entry->pendingSyncDispatches = 0;
 }
 
 InputDispatcher::ConfigurationChangedEntry*
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
index bf23479..ed4f07b 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -81,8 +81,8 @@
 }
 
 int32_t InputManager::injectInputEvent(const InputEvent* event,
-        int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) {
-    return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis);
+        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+    return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
 }
 
 void InputManager::preemptInputDispatch() {
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);
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 @@
     jfieldID token;
 } gInputApplicationClassInfo;
 
+static struct {
+    jclass clazz;
+} gKeyEventClassInfo;
+
+static struct {
+    jclass clazz;
+} gMotionEventClassInfo;
+
 // ----------------------------------------------------------------------------
 
 static inline nsecs_t now() {
@@ -2051,32 +2059,29 @@
     }
 }
 
-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 @@
             (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 @@
     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;
 }