Merge "Improve Launcher drag performance." into ics-mr1
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 24423c3..f7078ec 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -218,6 +218,7 @@
     boolean mNewSurfaceNeeded;
     boolean mHasHadWindowFocus;
     boolean mLastWasImTarget;
+    InputEventMessage mPendingInputEvents = null;
 
     boolean mWindowAttributesChanged = false;
     int mWindowAttributesChangesFlag = 0;
@@ -832,10 +833,24 @@
         }
     }
 
+    private void processInputEvents(boolean outOfOrder) {
+        while (mPendingInputEvents != null) {
+            handleMessage(mPendingInputEvents.mMessage);
+            InputEventMessage tmpMessage = mPendingInputEvents;
+            mPendingInputEvents = mPendingInputEvents.mNext;
+            tmpMessage.recycle();
+            if (outOfOrder) {
+                removeMessages(PROCESS_INPUT_EVENTS);
+            }
+        }
+    }
+
     private void performTraversals() {
         // cache mView since it is used so much below...
         final View host = mView;
 
+        processInputEvents(true);
+
         if (DBG) {
             System.out.println("======================================");
             System.out.println("performTraversals");
@@ -2336,6 +2351,7 @@
     public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1021;
     public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022;
     public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023;
+    public final static int PROCESS_INPUT_EVENTS = 1024;
 
     @Override
     public String getMessageName(Message message) {
@@ -2388,7 +2404,9 @@
                 return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
             case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT:
                 return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT";
-                                                                                                                                                                                                                                    
+            case PROCESS_INPUT_EVENTS:
+                return "PROCESS_INPUT_EVENTS";
+
         }
         return super.getMessageName(message);
     }
@@ -2447,6 +2465,9 @@
         case DISPATCH_GENERIC_MOTION:
             deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0);
             break;
+        case PROCESS_INPUT_EVENTS:
+            processInputEvents(false);
+            break;
         case DISPATCH_APP_VISIBILITY:
             handleAppVisibility(msg.arg1 != 0);
             break;
@@ -3744,7 +3765,7 @@
         msg.obj = ri;
         sendMessage(msg);
     }
-    
+
     private long mInputEventReceiveTimeNanos;
     private long mInputEventDeliverTimeNanos;
     private long mInputEventDeliverPostImeTimeNanos;
@@ -3762,6 +3783,78 @@
         }
     };
 
+    /**
+     * Utility class used to queue up input events which are then handled during
+     * performTraversals(). Doing it this way allows us to ensure that we are up to date with
+     * all input events just prior to drawing, instead of placing those events on the regular
+     * handler queue, potentially behind a drawing event.
+     */
+    static class InputEventMessage {
+        Message mMessage;
+        InputEventMessage mNext;
+
+        private static final Object sPoolSync = new Object();
+        private static InputEventMessage sPool;
+        private static int sPoolSize = 0;
+
+        private static final int MAX_POOL_SIZE = 10;
+
+        private InputEventMessage(Message m) {
+            mMessage = m;
+            mNext = null;
+        }
+
+        /**
+         * Return a new Message instance from the global pool. Allows us to
+         * avoid allocating new objects in many cases.
+         */
+        public static InputEventMessage obtain(Message msg) {
+            synchronized (sPoolSync) {
+                if (sPool != null) {
+                    InputEventMessage m = sPool;
+                    sPool = m.mNext;
+                    m.mNext = null;
+                    sPoolSize--;
+                    m.mMessage = msg;
+                    return m;
+                }
+            }
+            return new InputEventMessage(msg);
+        }
+
+        /**
+         * Return the message to the pool.
+         */
+        public void recycle() {
+            mMessage.recycle();
+            synchronized (sPoolSync) {
+                if (sPoolSize < MAX_POOL_SIZE) {
+                    mNext = sPool;
+                    sPool = this;
+                    sPoolSize++;
+                }
+            }
+
+        }
+    }
+
+    /**
+     * Place the input event message at the end of the current pending list
+     */
+    private void enqueueInputEvent(Message msg, long when) {
+        InputEventMessage inputMessage = InputEventMessage.obtain(msg);
+        if (mPendingInputEvents == null) {
+            mPendingInputEvents = inputMessage;
+        } else {
+            InputEventMessage currMessage = mPendingInputEvents;
+            while (currMessage.mNext != null) {
+                currMessage = currMessage.mNext;
+            }
+            currMessage.mNext = inputMessage;
+        }
+        sendEmptyMessageAtTime(PROCESS_INPUT_EVENTS, when);
+    }
+
     public void dispatchKey(KeyEvent event) {
         dispatchKey(event, false);
     }
@@ -3786,7 +3879,7 @@
         if (LOCAL_LOGV) Log.v(
             TAG, "sending key " + event + " to " + mView);
 
-        sendMessageAtTime(msg, event.getEventTime());
+        enqueueInputEvent(msg, event.getEventTime());
     }
     
     private void dispatchMotion(MotionEvent event, boolean sendDone) {
@@ -3804,21 +3897,21 @@
         Message msg = obtainMessage(DISPATCH_POINTER);
         msg.obj = event;
         msg.arg1 = sendDone ? 1 : 0;
-        sendMessageAtTime(msg, event.getEventTime());
+        enqueueInputEvent(msg, event.getEventTime());
     }
 
     private void dispatchTrackball(MotionEvent event, boolean sendDone) {
         Message msg = obtainMessage(DISPATCH_TRACKBALL);
         msg.obj = event;
         msg.arg1 = sendDone ? 1 : 0;
-        sendMessageAtTime(msg, event.getEventTime());
+        enqueueInputEvent(msg, event.getEventTime());
     }
 
     private void dispatchGenericMotion(MotionEvent event, boolean sendDone) {
         Message msg = obtainMessage(DISPATCH_GENERIC_MOTION);
         msg.obj = event;
         msg.arg1 = sendDone ? 1 : 0;
-        sendMessageAtTime(msg, event.getEventTime());
+        enqueueInputEvent(msg, event.getEventTime());
     }
 
     public void dispatchAppVisibility(boolean visible) {