Add suuport for splitting touch events across windows.

This feature is currently used to enable dragging the start and end
selection handles of a TextView at the same time.  Could be used for
other things later.

Deleted some dead code in ArrowKeyMovementMethod and CursorControllers.

Change-Id: I930accd97ca1ca1917aab8a807db2c950fc7b409
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 733b535..220e023 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -23,19 +23,12 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.TextView;
-import android.widget.TextView.CursorController;
 
 // XXX this doesn't extend MetaKeyKeyListener because the signatures
 // don't match.  Need to figure that out.  Meanwhile the meta keys
 // won't work in fields that don't take input.
 
 public class ArrowKeyMovementMethod implements MovementMethod {
-    /**
-     * An optional controller for the cursor.
-     * Use {@link #setCursorController(CursorController)} to set this field.
-     */
-    private CursorController mCursorController;
-
     private boolean isCap(Spannable buffer) {
         return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) ||
                 (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0));
@@ -192,21 +185,10 @@
     }
 
     public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) {
-        if (mCursorController != null) {
-            mCursorController.hide();
-        }
         return false;
     }
 
     public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
-        if (mCursorController != null) {
-            return onTouchEventCursor(widget, buffer, event);
-        } else {
-            return onTouchEventStandard(widget, buffer, event);
-        }
-    }
-
-    private boolean onTouchEventStandard(TextView widget, Spannable buffer, MotionEvent event) {
         int initialScrollX = -1, initialScrollY = -1;
         if (event.getAction() == MotionEvent.ACTION_UP) {
             initialScrollX = Touch.getInitialScrollX(widget, buffer);
@@ -278,49 +260,6 @@
         return handled;
     }
 
-    private boolean onTouchEventCursor(TextView widget, Spannable buffer, MotionEvent event) {
-        if (widget.isFocused() && !widget.didTouchFocusSelect()) {
-            switch (event.getActionMasked()) {
-                case MotionEvent.ACTION_MOVE:
-                    widget.cancelLongPress();
-
-                    // Offset the current touch position (from controller to cursor)
-                    final float x = event.getX() + mCursorController.getOffsetX();
-                    final float y = event.getY() + mCursorController.getOffsetY();
-                    mCursorController.updatePosition((int) x, (int) y);
-                    return true;
-
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_CANCEL:
-                    mCursorController = null;
-                    return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Defines the cursor controller.
-     *
-     * When set, this object can be used to handle touch events, that can be translated into cursor
-     * updates.
-     *
-     * {@link MotionEvent#ACTION_MOVE} events will call back the 
-     * {@link CursorController#updatePosition(int, int)} controller's method, passing the current
-     * finger coordinates (offset by {@link CursorController#getOffsetX()} and
-     * {@link CursorController#getOffsetY()}) as parameters. 
-     *
-     * When the gesture is finished (on a {@link MotionEvent#ACTION_UP} or
-     * {@link MotionEvent#ACTION_CANCEL} event), the controller is reset to null.
-     *
-     * @param cursorController A cursor controller implementation
-     *
-     * @hide
-     */
-    public void setCursorController(CursorController cursorController) {
-        mCursorController = cursorController;
-    }
-
     public boolean canSelectArbitrarily() {
         return true;
     }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index eebbc93..9c4aefe 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -583,6 +583,19 @@
          * also been set.
          */
         public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
+        
+        /** Window flag: when set the window will accept for touch events
+         * outside of its bounds to be sent to other windows that also
+         * support split touch.  When this flag is not set, the first pointer
+         * that goes down determines the window to which all subsequent touches
+         * go until all pointers go up.  When this flag is set, each pointer
+         * (not necessarily the first) that goes down determines the window
+         * to which all subsequent touches of that pointer will go until that
+         * pointer goes up thereby enabling touches with multiple pointers
+         * to be split across multiple windows.
+         * 
+         * {@hide} */
+        public static final int FLAG_SPLIT_TOUCH = 0x00800000;
 
         /** Window flag: *sigh* The lock screen wants to continue running its
          * animation while it is fading.  A kind-of hack to allow this.  Maybe
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index a10d647..7fe6190 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -85,6 +85,7 @@
     private boolean mTouchable = true;
     private boolean mOutsideTouchable = false;
     private boolean mClippingEnabled = true;
+    private boolean mSplitTouchEnabled;
 
     private OnTouchListener mTouchInterceptor;
     
@@ -565,6 +566,36 @@
     }
 
     /**
+     * <p>Indicates whether the popup window supports splitting touches.</p>
+     * 
+     * @return true if the touch splitting is enabled, false otherwise
+     * 
+     * @see #setSplitTouchEnabled(boolean)
+     * @hide
+     */
+    public boolean isSplitTouchEnabled() {
+        return mSplitTouchEnabled;
+    }
+
+    /**
+     * <p>Allows the popup window to split touches across other windows that also
+     * support split touch.  When this flag is not set, the first pointer
+     * that goes down determines the window to which all subsequent touches
+     * go until all pointers go up.  When this flag is set, each pointer
+     * (not necessarily the first) that goes down determines the window
+     * to which all subsequent touches of that pointer will go until that
+     * pointer goes up thereby enabling touches with multiple pointers
+     * to be split across multiple windows.</p>
+     *
+     * @param enabled true if the split touches should be enabled, false otherwise
+     * @see #isSplitTouchEnabled()
+     * @hide
+     */
+    public void setSplitTouchEnabled(boolean enabled) {
+        mSplitTouchEnabled = enabled;
+    }
+
+    /**
      * <p>Change the width and height measure specs that are given to the
      * window manager by the popup.  By default these are 0, meaning that
      * the current width or height is requested as an explicit size from
@@ -889,6 +920,9 @@
         if (!mClippingEnabled) {
             curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
         }
+        if (mSplitTouchEnabled) {
+            curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+        }
         return curFlags;
     }
     
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 034c617..6278192 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7601,16 +7601,10 @@
 
     /**
      * A CursorController instance can be used to control a cursor in the text.
-     *
-     * It can be passed to an {@link ArrowKeyMovementMethod} which can intercepts events
-     * and send them to this object instead of the cursor.
-     *
+     * It is not used outside of {@link TextView}.
      * @hide
      */
-    public interface CursorController {
-        /* Cursor fade-out animation duration, in milliseconds. */
-        static final int FADE_OUT_DURATION = 400;
-
+    private interface CursorController {
         /**
          * Makes the cursor controller visible on screen. Will be drawn by {@link #draw(Canvas)}.
          * See also {@link #hide()}.
@@ -7631,23 +7625,11 @@
         /**
          * Update the controller's position.
          */
-        public void updatePosition(int x, int y);
+        public void updatePosition(HandleView handle, int x, int y);
 
         public void updatePosition();
 
         /**
-         * The controller and the cursor's positions can be link by a fixed offset,
-         * computed when the controller is touched, and then maintained as it moves
-         * @return Horizontal offset between the controller and the cursor.
-         */
-        public float getOffsetX();
-
-        /**
-         * @return Vertical offset between the controller and the cursor.
-         */
-        public float getOffsetY();
-
-        /**
          * This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
          * a chance to become active and/or visible.
          * @param event The touch event
@@ -7670,6 +7652,7 @@
             mDrawable = handle;
             mContainer = new PopupWindow(TextView.this.mContext, null,
                     com.android.internal.R.attr.textSelectHandleWindowStyle);
+            mContainer.setSplitTouchEnabled(true);
         }
 
         @Override
@@ -7768,7 +7751,7 @@
                 TextView.this.getLocationOnScreen(coords);
                 final int x = (int) (rawX - coords[0] + 0.5f);
                 final int y = (int) (rawY - coords[1] + 0.5f);
-                mController.updatePosition(x, y);
+                mController.updatePosition(this, x, y);
                 break;
 
             case MotionEvent.ACTION_UP:
@@ -7802,13 +7785,11 @@
         }
     }
 
-    class InsertionPointCursorController implements CursorController {
+    private class InsertionPointCursorController implements CursorController {
         private static final int DELAY_BEFORE_FADE_OUT = 4100;
 
         // The cursor controller image
         private final HandleView mHandle;
-        // Offset between finger hot point on cursor controller and actual cursor
-        private float mOffsetX, mOffsetY;
 
         private final Runnable mHider = new Runnable() {
             public void run() {
@@ -7841,7 +7822,7 @@
             return mHandle.isShowing();
         }
 
-        public void updatePosition(int x, int y) {
+        public void updatePosition(HandleView handle, int x, int y) {
             final int previousOffset = getSelectionStart();
             int offset = getHysteresisOffset(x, y, previousOffset);
 
@@ -7865,24 +7846,14 @@
             mHandle.positionAtCursor(offset, true);
         }
 
-        public float getOffsetX() {
-            return mOffsetX;
-        }
-
-        public float getOffsetY() {
-            return mOffsetY;
-        }
-
         public boolean onTouchEvent(MotionEvent ev) {
             return false;
         }
     }
 
-    class SelectionModifierCursorController implements CursorController {
+    private class SelectionModifierCursorController implements CursorController {
         // The cursor controller images
         private HandleView mStartHandle, mEndHandle;
-        // Offset between finger hot point on active cursor controller and actual cursor
-        private float mOffsetX, mOffsetY;
         // The offsets of that last touch down event. Remembered to start selection there.
         private int mMinTouchOffset, mMaxTouchOffset;
         // Whether selection anchors are active
@@ -7916,15 +7887,15 @@
             hide();
         }
 
-        public void updatePosition(int x, int y) {
+        public void updatePosition(HandleView handle, int x, int y) {
             int selectionStart = getSelectionStart();
             int selectionEnd = getSelectionEnd();
 
-            final int previousOffset = mStartHandle.isDragging() ? selectionStart : selectionEnd;
+            final int previousOffset = handle == mStartHandle ? selectionStart : selectionEnd;
             int offset = getHysteresisOffset(x, y, previousOffset);
 
             // Handle the case where start and end are swapped, making sure start <= end
-            if (mStartHandle.isDragging()) {
+            if (handle == mStartHandle) {
                 if (offset <= selectionEnd) {
                     if (selectionStart == offset) {
                         return; // no change, no need to redraw;
@@ -8021,14 +7992,6 @@
             return mMaxTouchOffset;
         }
 
-        public float getOffsetX() {
-            return mOffsetX;
-        }
-
-        public float getOffsetY() {
-            return mOffsetY;
-        }
-
         /**
          * @return true iff this controller is currently used to move the selection start.
          */
diff --git a/include/ui/Input.h b/include/ui/Input.h
index b587e94..21baf32 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -40,10 +40,18 @@
 
 /*
  * Maximum number of pointers supported per motion event.
+ * Smallest number of pointers is 1.
  */
 #define MAX_POINTERS 10
 
 /*
+ * Maximum pointer id value supported in a motion event.
+ * Smallest pointer id is 0.
+ * (This is limited by our use of BitSet32 to track pointer assignments.)
+ */
+#define MAX_POINTER_ID 31
+
+/*
  * Declare a concrete type for the NDK's input event forward declaration.
  */
 struct AInputEvent {
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 96b4fae..cc160129 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -27,6 +27,7 @@
 #include <utils/String8.h>
 #include <utils/Looper.h>
 #include <utils/Pool.h>
+#include <utils/BitSet.h>
 
 #include <stddef.h>
 #include <unistd.h>
@@ -89,17 +90,13 @@
          * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
         FLAG_OUTSIDE = 0x02,
 
-        /* This flag indicates that a KeyEvent or MotionEvent is being canceled.
-         * In the case of a key event, it should be delivered with flag
-         * AKEY_EVENT_FLAG_CANCELED set.
-         * In the case of a motion event, it should be delivered with action
-         * AMOTION_EVENT_ACTION_CANCEL instead. */
-        FLAG_CANCEL = 0x04,
-
         /* This flag indicates that the target of a MotionEvent is partly or wholly
          * obscured by another visible window above it.  The motion event should be
          * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
-        FLAG_WINDOW_IS_OBSCURED = 0x08,
+        FLAG_WINDOW_IS_OBSCURED = 0x04,
+
+        /* This flag indicates that a motion event is being split across multiple windows. */
+        FLAG_SPLIT = 0x08,
     };
 
     // The input channel to be targeted.
@@ -111,6 +108,13 @@
     // The x and y offset to add to a MotionEvent as it is delivered.
     // (ignored for KeyEvents)
     float xOffset, yOffset;
+
+    // The window type of the input target.
+    int32_t windowType;
+
+    // The subset of pointer ids to include in motion events dispatched to this input target
+    // if FLAG_SPLIT is set.
+    BitSet32 pointerIds;
 };
 
 
@@ -143,7 +147,7 @@
         FLAG_SHOW_WALLPAPER = 0x00100000,
         FLAG_TURN_SCREEN_ON = 0x00200000,
         FLAG_DISMISS_KEYGUARD = 0x00400000,
-        FLAG_IMMERSIVE = 0x00800000,
+        FLAG_SPLIT_TOUCH = 0x00800000,
         FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
         FLAG_COMPATIBLE_WINDOW = 0x20000000,
         FLAG_SYSTEM_ERROR = 0x40000000,
@@ -276,7 +280,7 @@
             const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
 
     /* Poke user activity for an event dispatched to a window. */
-    virtual void pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType) = 0;
+    virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
 
     /* Checks whether a given application pid/uid has permission to inject input events
      * into other applications.
@@ -415,6 +419,16 @@
         T* prev;
     };
 
+    struct InjectionState {
+        mutable int32_t refCount;
+
+        int32_t injectorPid;
+        int32_t injectorUid;
+        int32_t injectionResult;  // initially INPUT_EVENT_INJECTION_PENDING
+        bool injectionIsAsync; // set to true if injection is not waiting for the result
+        int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
+    };
+
     struct EventEntry : Link<EventEntry> {
         enum {
             TYPE_SENTINEL,
@@ -423,21 +437,14 @@
             TYPE_MOTION
         };
 
-        int32_t refCount;
+        mutable int32_t refCount;
         int32_t type;
         nsecs_t eventTime;
-
-        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
+        InjectionState* injectionState;
 
         bool dispatchInProgress; // initially false, set to true while dispatching
-        int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
 
-        inline bool isInjected() { return injectorPid >= 0; }
-
-        void recycle();
+        inline bool isInjected() { return injectionState != NULL; }
     };
 
     struct ConfigurationChangedEntry : EventEntry {
@@ -463,8 +470,6 @@
             INTERCEPT_KEY_RESULT_CONTINUE,
         };
         InterceptKeyResult interceptKeyResult; // set based on the interception result
-
-        void recycle();
     };
 
     struct MotionSample {
@@ -521,6 +526,10 @@
         inline bool hasForegroundTarget() const {
             return targetFlags & InputTarget::FLAG_FOREGROUND;
         }
+
+        inline bool isSplit() const {
+            return targetFlags & InputTarget::FLAG_SPLIT;
+        }
     };
 
     // A command entry captures state and behavior for an action to be performed in the
@@ -555,7 +564,6 @@
         KeyEntry* keyEntry;
         sp<InputChannel> inputChannel;
         sp<InputApplicationHandle> inputApplicationHandle;
-        int32_t windowType;
         int32_t userActivityEventType;
     };
 
@@ -611,6 +619,7 @@
     public:
         Allocator();
 
+        InjectionState* obtainInjectionState(int32_t injectorPid, int32_t injectorUid);
         ConfigurationChangedEntry* obtainConfigurationChangedEntry(nsecs_t eventTime);
         KeyEntry* obtainKeyEntry(nsecs_t eventTime,
                 int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action,
@@ -626,6 +635,7 @@
                 int32_t targetFlags, float xOffset, float yOffset);
         CommandEntry* obtainCommandEntry(Command command);
 
+        void releaseInjectionState(InjectionState* injectionState);
         void releaseEventEntry(EventEntry* entry);
         void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry);
         void releaseKeyEntry(KeyEntry* entry);
@@ -633,10 +643,13 @@
         void releaseDispatchEntry(DispatchEntry* entry);
         void releaseCommandEntry(CommandEntry* entry);
 
+        void recycleKeyEntry(KeyEntry* entry);
+
         void appendMotionSample(MotionEntry* motionEntry,
                 nsecs_t eventTime, const PointerCoords* pointerCoords);
 
     private:
+        Pool<InjectionState> mInjectionStatePool;
         Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool;
         Pool<KeyEntry> mKeyEntryPool;
         Pool<MotionEntry> mMotionEntryPool;
@@ -645,6 +658,7 @@
         Pool<CommandEntry> mCommandEntryPool;
 
         void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime);
+        void releaseEventEntryInjectionState(EventEntry* entry);
     };
 
     /* Tracks dispatched key and motion event state so that cancelation events can be
@@ -823,6 +837,7 @@
     void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
 
     Condition mInjectionSyncFinishedCondition;
+    void incrementPendingForegroundDispatchesLocked(EventEntry* entry);
     void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
 
     // Throttling state.
@@ -858,23 +873,37 @@
     // Dispatch state.
     bool mDispatchEnabled;
     bool mDispatchFrozen;
+
     Vector<InputWindow> mWindows;
-    Vector<InputWindow*> mWallpaperWindows;
+
+    const InputWindow* getWindowLocked(const sp<InputChannel>& inputChannel);
 
     // Focus tracking for keys, trackball, etc.
-    InputWindow* mFocusedWindow;
+    const InputWindow* mFocusedWindow;
 
     // Focus tracking for touch.
-    bool mTouchDown;
-    InputWindow* mTouchedWindow;                   // primary target for current down
-    bool mTouchedWindowIsObscured;                 // true if other windows may obscure the target
-    Vector<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets
-    struct OutsideTarget {
-        InputWindow* window;
-        bool obscured;
+    struct TouchedWindow {
+        const InputWindow* window;
+        int32_t targetFlags;
+        BitSet32 pointerIds;
+        sp<InputChannel> channel;
     };
-    Vector<OutsideTarget> mTempTouchedOutsideTargets; // temporary outside touch targets
-    Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
+    struct TouchState {
+        bool down;
+        bool split;
+        Vector<TouchedWindow> windows;
+
+        TouchState();
+        ~TouchState();
+        void reset();
+        void copyFrom(const TouchState& other);
+        void addOrUpdateWindow(const InputWindow* window, int32_t targetFlags, BitSet32 pointerIds);
+        void removeOutsideTouchWindows();
+        const InputWindow* getFirstForegroundWindow();
+    };
+
+    TouchState mTouchState;
+    TouchState mTempTouchState;
 
     // Focused application.
     InputApplication* mFocusedApplication;
@@ -899,8 +928,6 @@
     // The input targets that were most recently identified for dispatch.
     bool mCurrentInputTargetsValid; // false while targets are being recomputed
     Vector<InputTarget> mCurrentInputTargets;
-    int32_t mCurrentInputWindowType;
-    sp<InputChannel> mCurrentInputChannel;
 
     enum InputTargetWaitCause {
         INPUT_TARGET_WAIT_CAUSE_NONE,
@@ -915,7 +942,7 @@
 
     // Finding targets for input events.
     void resetTargetsLocked();
-    void commitTargetsLocked(const InputWindow* window);
+    void commitTargetsLocked();
     int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
             const InputApplication* application, const InputWindow* window,
             nsecs_t* nextWakeupTime);
@@ -924,19 +951,19 @@
     nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
     void resetANRTimeoutsLocked();
 
-    int32_t findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
-            nsecs_t* nextWakeupTime, InputWindow** outWindow);
-    int32_t findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
-            nsecs_t* nextWakeupTime, InputWindow** outWindow);
+    int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
+            nsecs_t* nextWakeupTime);
+    int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
+            nsecs_t* nextWakeupTime);
 
-    void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags);
+    void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+            BitSet32 pointerIds);
     void addMonitoringTargetsLocked();
-    void pokeUserActivityLocked(nsecs_t eventTime, int32_t windowType, int32_t eventType);
-    bool checkInjectionPermission(const InputWindow* window,
-            int32_t injectorPid, int32_t injectorUid);
+    bool shouldPokeUserActivityForCurrentInputTargetsLocked();
+    void pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType);
+    bool checkInjectionPermission(const InputWindow* window, const InjectionState* injectionState);
     bool isWindowObscuredLocked(const InputWindow* window);
     bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window);
-    void releaseTouchedWindowLocked();
     String8 getApplicationWindowLabelLocked(const InputApplication* application,
             const InputWindow* window);
 
@@ -955,6 +982,9 @@
     void drainOutboundQueueLocked(Connection* connection);
     static int handleReceiveCallback(int receiveFd, int events, void* data);
 
+    // Splitting motion events across windows.
+    MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
+
     // Dump state.
     void dumpDispatchStateLocked(String8& dump);
     void logDispatchStateLocked();
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 903c3c4..e85735a 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -549,10 +549,6 @@
             const int32_t* keyCodes, uint8_t* outFlags);
 
 protected:
-    /* Maximum pointer id value supported.
-     * (This is limited by our use of BitSet32 to track pointer assignments.) */
-    static const uint32_t MAX_POINTER_ID = 31;
-
     Mutex mLock;
 
     struct VirtualKey {
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
index 19c8bf0..f5dbcd94 100644
--- a/include/utils/BitSet.h
+++ b/include/utils/BitSet.h
@@ -38,6 +38,9 @@
     // Clears the bit set.
     inline void clear() { value = 0; }
 
+    // Returns the number of marked bits in the set.
+    inline uint32_t count() const { return __builtin_popcount(value); }
+
     // Returns true if the bit set does not contain any marked bits.
     inline bool isEmpty() const { return ! value; }
 
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 1cf7592..5da1676 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -69,6 +69,65 @@
     return value ? "true" : "false";
 }
 
+static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
+    return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+            >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+}
+
+static bool isValidKeyAction(int32_t action) {
+    switch (action) {
+    case AKEY_EVENT_ACTION_DOWN:
+    case AKEY_EVENT_ACTION_UP:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static bool validateKeyEvent(int32_t action) {
+    if (! isValidKeyAction(action)) {
+        LOGE("Key event has invalid action code 0x%x", action);
+        return false;
+    }
+    return true;
+}
+
+static bool isValidMotionAction(int32_t action) {
+    switch (action & AMOTION_EVENT_ACTION_MASK) {
+    case AMOTION_EVENT_ACTION_DOWN:
+    case AMOTION_EVENT_ACTION_UP:
+    case AMOTION_EVENT_ACTION_CANCEL:
+    case AMOTION_EVENT_ACTION_MOVE:
+    case AMOTION_EVENT_ACTION_POINTER_DOWN:
+    case AMOTION_EVENT_ACTION_POINTER_UP:
+    case AMOTION_EVENT_ACTION_OUTSIDE:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static bool validateMotionEvent(int32_t action, size_t pointerCount,
+        const int32_t* pointerIds) {
+    if (! isValidMotionAction(action)) {
+        LOGE("Motion event has invalid action code 0x%x", action);
+        return false;
+    }
+    if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
+        LOGE("Motion event has invalid pointer count %d; value must be between 1 and %d.",
+                pointerCount, MAX_POINTERS);
+        return false;
+    }
+    for (size_t i = 0; i < pointerCount; i++) {
+        if (pointerIds[i] < 0 || pointerIds[i] > MAX_POINTER_ID) {
+            LOGE("Motion event has invalid pointer id %d; value must be between 0 and %d",
+                    pointerIds[i], MAX_POINTER_ID);
+            return false;
+        }
+    }
+    return true;
+}
+
 
 // --- InputWindow ---
 
@@ -91,7 +150,7 @@
     mPolicy(policy),
     mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX),
     mDispatchEnabled(true), mDispatchFrozen(false),
-    mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
+    mFocusedWindow(NULL),
     mFocusedApplication(NULL),
     mCurrentInputTargetsValid(false),
     mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
@@ -414,9 +473,10 @@
 }
 
 void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
-    if (entry->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
 #if DEBUG_DISPATCH_CYCLE
-        LOGD("Inbound event was dropped.  Setting injection result to failed.");
+        LOGD("Injected inbound event was dropped.");
 #endif
         setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
     }
@@ -424,10 +484,11 @@
 }
 
 bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) {
-    return ! entry->isInjected()
-            || entry->injectorUid == 0
+    InjectionState* injectionState = entry->injectionState;
+    return ! injectionState
+            || injectionState->injectorUid == 0
             || mPolicy->checkInjectEventsPermissionNonReentrant(
-                    entry->injectorPid, entry->injectorUid);
+                    injectionState->injectorPid, injectionState->injectorUid);
 }
 
 void InputDispatcher::resetKeyRepeatLocked() {
@@ -444,7 +505,7 @@
     // Reuse the repeated key entry if it is otherwise unreferenced.
     uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
     if (entry->refCount == 1) {
-        entry->recycle();
+        mAllocator.recycleKeyEntry(entry);
         entry->eventTime = currentTime;
         entry->policyFlags = policyFlags;
         entry->repeatCount += 1;
@@ -496,8 +557,7 @@
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
         bool trusted;
         if (! dropEvent && mFocusedWindow) {
-            trusted = checkInjectionPermission(mFocusedWindow,
-                    entry->injectorPid, entry->injectorUid);
+            trusted = checkInjectionPermission(mFocusedWindow, entry->injectionState);
         } else {
             trusted = isEventFromReliableSourceLocked(entry);
         }
@@ -559,9 +619,8 @@
 
     // Identify targets.
     if (! mCurrentInputTargetsValid) {
-        InputWindow* window = NULL;
-        int32_t injectionResult = findFocusedWindowLocked(currentTime,
-                entry, nextWakeupTime, & window);
+        int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
+                entry, nextWakeupTime);
         if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
             return false;
         }
@@ -572,14 +631,16 @@
         }
 
         addMonitoringTargetsLocked();
-        commitTargetsLocked(window);
+        commitTargetsLocked();
     }
 
     // Dispatch the key.
     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
 
     // Poke user activity.
-    pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT);
+    if (shouldPokeUserActivityForCurrentInputTargetsLocked()) {
+        pokeUserActivityLocked(entry->eventTime, POWER_MANAGER_BUTTON_EVENT);
+    }
     return true;
 }
 
@@ -616,16 +677,15 @@
 
     // Identify targets.
     if (! mCurrentInputTargetsValid) {
-        InputWindow* window = NULL;
         int32_t injectionResult;
         if (isPointerEvent) {
             // Pointer event.  (eg. touchscreen)
-            injectionResult = findTouchedWindowLocked(currentTime,
-                    entry, nextWakeupTime, & window);
+            injectionResult = findTouchedWindowTargetsLocked(currentTime,
+                    entry, nextWakeupTime);
         } else {
             // Non touch event.  (eg. trackball)
-            injectionResult = findFocusedWindowLocked(currentTime,
-                    entry, nextWakeupTime, & window);
+            injectionResult = findFocusedWindowTargetsLocked(currentTime,
+                    entry, nextWakeupTime);
         }
         if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
             return false;
@@ -637,34 +697,36 @@
         }
 
         addMonitoringTargetsLocked();
-        commitTargetsLocked(window);
+        commitTargetsLocked();
     }
 
     // Dispatch the motion.
     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
 
     // Poke user activity.
-    int32_t eventType;
-    if (isPointerEvent) {
-        switch (entry->action) {
-        case AMOTION_EVENT_ACTION_DOWN:
-            eventType = POWER_MANAGER_TOUCH_EVENT;
-            break;
-        case AMOTION_EVENT_ACTION_UP:
-            eventType = POWER_MANAGER_TOUCH_UP_EVENT;
-            break;
-        default:
-            if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) {
+    if (shouldPokeUserActivityForCurrentInputTargetsLocked()) {
+        int32_t eventType;
+        if (isPointerEvent) {
+            switch (entry->action) {
+            case AMOTION_EVENT_ACTION_DOWN:
                 eventType = POWER_MANAGER_TOUCH_EVENT;
-            } else {
-                eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+                break;
+            case AMOTION_EVENT_ACTION_UP:
+                eventType = POWER_MANAGER_TOUCH_UP_EVENT;
+                break;
+            default:
+                if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) {
+                    eventType = POWER_MANAGER_TOUCH_EVENT;
+                } else {
+                    eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+                }
+                break;
             }
-            break;
+        } else {
+            eventType = POWER_MANAGER_BUTTON_EVENT;
         }
-    } else {
-        eventType = POWER_MANAGER_BUTTON_EVENT;
+        pokeUserActivityLocked(entry->eventTime, eventType);
     }
-    pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType);
     return true;
 }
 
@@ -735,13 +797,10 @@
 void InputDispatcher::resetTargetsLocked() {
     mCurrentInputTargetsValid = false;
     mCurrentInputTargets.clear();
-    mCurrentInputChannel.clear();
     mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
 }
 
-void InputDispatcher::commitTargetsLocked(const InputWindow* window) {
-    mCurrentInputWindowType = window->layoutParamsType;
-    mCurrentInputChannel = window->inputChannel;
+void InputDispatcher::commitTargetsLocked() {
     mCurrentInputTargetsValid = true;
 }
 
@@ -803,8 +862,8 @@
         // Give up.
         mInputTargetWaitTimeoutExpired = true;
 
-        // Release the touch target.
-        releaseTouchedWindowLocked();
+        // Release the touch targets.
+        mTouchState.reset();
 
         // Input state will not be realistic.  Mark it out of sync.
         if (inputChannel.get()) {
@@ -834,9 +893,8 @@
     mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
 }
 
-int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
-        nsecs_t* nextWakeupTime, InputWindow** outWindow) {
-    *outWindow = NULL;
+int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
+        const EventEntry* entry, nsecs_t* nextWakeupTime) {
     mCurrentInputTargets.clear();
 
     int32_t injectionResult;
@@ -861,7 +919,7 @@
     }
 
     // Check permissions.
-    if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) {
+    if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) {
         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
         goto Failed;
     }
@@ -888,8 +946,7 @@
 
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-    *outWindow = mFocusedWindow;
-    addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND);
+    addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0));
 
     // Done.
 Failed:
@@ -905,15 +962,14 @@
     return injectionResult;
 }
 
-int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
-        nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
+        const MotionEntry* entry, nsecs_t* nextWakeupTime) {
     enum InjectionPermission {
         INJECTION_PERMISSION_UNKNOWN,
         INJECTION_PERMISSION_GRANTED,
         INJECTION_PERMISSION_DENIED
     };
 
-    *outWindow = NULL;
     mCurrentInputTargets.clear();
 
     nsecs_t startTime = now();
@@ -945,25 +1001,33 @@
     bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
 
     int32_t action = entry->action;
+    int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
 
     // Update the touch state as needed based on the properties of the touch event.
-    int32_t injectionResult;
-    InjectionPermission injectionPermission;
-    if (action == AMOTION_EVENT_ACTION_DOWN) {
-        /* Case 1: ACTION_DOWN */
+    int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+        mTempTouchState.reset();
+        mTempTouchState.down = true;
+    } else {
+        mTempTouchState.copyFrom(mTouchState);
+    }
 
-        InputWindow* newTouchedWindow = NULL;
-        mTempTouchedOutsideTargets.clear();
+    bool isSplit = mTempTouchState.split && mTempTouchState.down;
+    if (maskedAction == AMOTION_EVENT_ACTION_DOWN
+            || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
+        /* Case 1: New splittable pointer going down. */
 
-        int32_t x = int32_t(entry->firstSample.pointerCoords[0].x);
-        int32_t y = int32_t(entry->firstSample.pointerCoords[0].y);
-        InputWindow* topErrorWindow = NULL;
-        bool obscured = false;
+        int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+        int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].x);
+        int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].y);
+        const InputWindow* newTouchedWindow = NULL;
+        const InputWindow* topErrorWindow = NULL;
 
         // Traverse windows from front to back to find touched window and outside targets.
         size_t numWindows = mWindows.size();
         for (size_t i = 0; i < numWindows; i++) {
-            InputWindow* window = & mWindows.editItemAt(i);
+            const InputWindow* window = & mWindows.editItemAt(i);
             int32_t flags = window->layoutParamsFlags;
 
             if (flags & InputWindow::FLAG_SYSTEM_ERROR) {
@@ -979,17 +1043,15 @@
                     if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
                         if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) {
                             newTouchedWindow = window;
-                            obscured = isWindowObscuredLocked(window);
                         }
                         break; // found touched window, exit window loop
                     }
                 }
 
-                if (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH) {
-                    OutsideTarget outsideTarget;
-                    outsideTarget.window = window;
-                    outsideTarget.obscured = isWindowObscuredLocked(window);
-                    mTempTouchedOutsideTargets.push(outsideTarget);
+                if (maskedAction == AMOTION_EVENT_ACTION_DOWN
+                        && (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) {
+                    mTempTouchState.addOrUpdateWindow(window,
+                            InputTarget::FLAG_OUTSIDE, BitSet32(0));
                 }
             }
         }
@@ -1007,6 +1069,21 @@
             goto Unresponsive;
         }
 
+        // Figure out whether splitting will be allowed for this window.
+        if (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH) {
+            // New window supports splitting.
+            isSplit = true;
+        } else if (isSplit) {
+            // New window does not support splitting but we have already split events.
+            // Assign the pointer to the first foreground window we find.
+            // (May be NULL which is why we put this code block before the next check.)
+            newTouchedWindow = mTempTouchState.getFirstForegroundWindow();
+        }
+        int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
+        if (isSplit) {
+            targetFlags |= InputTarget::FLAG_SPLIT;
+        }
+
         // If we did not find a touched window then fail.
         if (! newTouchedWindow) {
             if (mFocusedApplication) {
@@ -1017,140 +1094,127 @@
 #endif
                 injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                         mFocusedApplication, NULL, nextWakeupTime);
-                injectionPermission = INJECTION_PERMISSION_UNKNOWN;
                 goto Unresponsive;
             }
 
             LOGI("Dropping event because there is no touched window or focused application.");
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            injectionPermission = INJECTION_PERMISSION_UNKNOWN;
             goto Failed;
         }
 
-        // Check permissions.
-        if (! checkInjectionPermission(newTouchedWindow, entry->injectorPid, entry->injectorUid)) {
-            injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-            injectionPermission = INJECTION_PERMISSION_DENIED;
-            goto Failed;
+        // Update the temporary touch state.
+        BitSet32 pointerIds;
+        if (isSplit) {
+            uint32_t pointerId = entry->pointerIds[pointerIndex];
+            pointerIds.markBit(pointerId);
         }
-
-        // If the touched window is paused then keep waiting.
-        if (newTouchedWindow->paused) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-            LOGD("Waiting because touched window is paused.");
-#endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, newTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
-        }
-
-        // If the touched window is still working on previous events then keep waiting.
-        if (! isWindowFinishedWithPreviousInputLocked(newTouchedWindow)) {
-#if DEBUG_FOCUS
-            LOGD("Waiting because touched window still processing previous input.");
-#endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, newTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
-        }
-
-        // Success!  Update the touch dispatch state for real.
-        releaseTouchedWindowLocked();
-
-        mTouchedWindow = newTouchedWindow;
-        mTouchedWindowIsObscured = obscured;
-
-        if (newTouchedWindow->hasWallpaper) {
-            mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
-        }
+        mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds);
     } else {
-        /* Case 2: Everything but ACTION_DOWN */
-
-        // Check permissions.
-        if (! checkInjectionPermission(mTouchedWindow, entry->injectorPid, entry->injectorUid)) {
-            injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-            injectionPermission = INJECTION_PERMISSION_DENIED;
-            goto Failed;
-        }
+        /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
 
         // If the pointer is not currently down, then ignore the event.
-        if (! mTouchDown) {
+        if (! mTempTouchState.down) {
             LOGI("Dropping event because the pointer is not down.");
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
             goto Failed;
         }
+    }
 
-        // If there is no currently touched window then fail.
-        if (! mTouchedWindow) {
+    // Check permission to inject into all touched foreground windows and ensure there
+    // is at least one touched foreground window.
+    {
+        bool haveForegroundWindow = false;
+        for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+            const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+            if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
+                haveForegroundWindow = true;
+                if (! checkInjectionPermission(touchedWindow.window, entry->injectionState)) {
+                    injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+                    injectionPermission = INJECTION_PERMISSION_DENIED;
+                    goto Failed;
+                }
+            }
+        }
+        if (! haveForegroundWindow) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
-            LOGD("Dropping event because there is no touched window to receive it.");
+            LOGD("Dropping event because there is no touched foreground window to receive it.");
 #endif
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
             goto Failed;
         }
 
-        // If the touched window is paused then keep waiting.
-        if (mTouchedWindow->paused) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-            LOGD("Waiting because touched window is paused.");
-#endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, mTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
-        }
+        // Permission granted to injection into all touched foreground windows.
+        injectionPermission = INJECTION_PERMISSION_GRANTED;
+    }
 
-        // If the touched window is still working on previous events then keep waiting.
-        if (! isWindowFinishedWithPreviousInputLocked(mTouchedWindow)) {
-#if DEBUG_FOCUS
-            LOGD("Waiting because touched window still processing previous input.");
+    // Ensure all touched foreground windows are ready for new input.
+    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+        const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+        if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
+            // If the touched window is paused then keep waiting.
+            if (touchedWindow.window->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+                LOGD("Waiting because touched window is paused.");
 #endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, mTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
+                injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                        NULL, touchedWindow.window, nextWakeupTime);
+                goto Unresponsive;
+            }
+
+            // If the touched window is still working on previous events then keep waiting.
+            if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.window)) {
+#if DEBUG_FOCUS
+                LOGD("Waiting because touched window still processing previous input.");
+#endif
+                injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                        NULL, touchedWindow.window, nextWakeupTime);
+                goto Unresponsive;
+            }
+        }
+    }
+
+    // If this is the first pointer going down and the touched window has a wallpaper
+    // then also add the touched wallpaper windows so they are locked in for the duration
+    // of the touch gesture.
+    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+        const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow();
+        if (foregroundWindow->hasWallpaper) {
+            for (size_t i = 0; i < mWindows.size(); i++) {
+                const InputWindow* window = & mWindows[i];
+                if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
+                    mTempTouchState.addOrUpdateWindow(window, 0, BitSet32(0));
+                }
+            }
+        }
+    }
+
+    // If a touched window has been obscured at any point during the touch gesture, set
+    // the appropriate flag so we remember it for the entire gesture.
+    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+        TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
+        if ((touchedWindow.targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) == 0) {
+            if (isWindowObscuredLocked(touchedWindow.window)) {
+                touchedWindow.targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+            }
         }
     }
 
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-    injectionPermission = INJECTION_PERMISSION_GRANTED;
 
-    {
-        size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
-        for (size_t i = 0; i < numWallpaperWindows; i++) {
-            addWindowTargetLocked(mTouchedWallpaperWindows[i],
-                    InputTarget::FLAG_WINDOW_IS_OBSCURED);
-        }
-
-        size_t numOutsideTargets = mTempTouchedOutsideTargets.size();
-        for (size_t i = 0; i < numOutsideTargets; i++) {
-            const OutsideTarget& outsideTarget = mTempTouchedOutsideTargets[i];
-            int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
-            if (outsideTarget.obscured) {
-                outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
-            }
-            addWindowTargetLocked(outsideTarget.window, outsideTargetFlags);
-        }
-        mTempTouchedOutsideTargets.clear();
-
-        int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
-        if (mTouchedWindowIsObscured) {
-            targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
-        }
-        addWindowTargetLocked(mTouchedWindow, targetFlags);
-        *outWindow = mTouchedWindow;
+    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+        const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
+        addWindowTargetLocked(touchedWindow.window, touchedWindow.targetFlags,
+                touchedWindow.pointerIds);
     }
 
+    // Drop the outside touch window since we will not care about them in the next iteration.
+    mTempTouchState.removeOutsideTouchWindows();
+
 Failed:
     // Check injection permission once and for all.
     if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
-        if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow,
-                entry->injectorPid, entry->injectorUid)) {
+        if (checkInjectionPermission(NULL, entry->injectionState)) {
             injectionPermission = INJECTION_PERMISSION_GRANTED;
         } else {
             injectionPermission = INJECTION_PERMISSION_DENIED;
@@ -1159,25 +1223,41 @@
 
     // Update final pieces of touch state if the injector had permission.
     if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
-        if (action == AMOTION_EVENT_ACTION_DOWN) {
-            if (mTouchDown) {
-                // This is weird.  We got a down but we thought it was already down!
+        if (maskedAction == AMOTION_EVENT_ACTION_UP
+                || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+            // All pointers up or canceled.
+            mTempTouchState.reset();
+        } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+            // First pointer went down.
+            if (mTouchState.down) {
                 LOGW("Pointer down received while already down.");
-            } else {
-                mTouchDown = true;
             }
+        } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+            // One pointer went up.
+            if (isSplit) {
+                int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+                uint32_t pointerId = entry->pointerIds[pointerIndex];
 
-            if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
-                // Since we failed to identify a target for this touch down, we may still
-                // be holding on to an earlier target from a previous touch down.  Release it.
-                releaseTouchedWindowLocked();
+                for (size_t i = 0; i < mTempTouchState.windows.size(); ) {
+                    TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
+                    if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
+                        touchedWindow.pointerIds.clearBit(pointerId);
+                        if (touchedWindow.pointerIds.isEmpty()) {
+                            mTempTouchState.windows.removeAt(i);
+                            continue;
+                        }
+                    }
+                    i += 1;
+                }
             }
-        } else if (action == AMOTION_EVENT_ACTION_UP) {
-            mTouchDown = false;
-            releaseTouchedWindowLocked();
         }
+
+        // Save changes to touch state.
+        mTouchState.copyFrom(mTempTouchState);
     } else {
-        LOGW("Not updating touch focus because injection was denied.");
+#if DEBUG_FOCUS
+        LOGD("Not updating touch focus because injection was denied.");
+#endif
     }
 
 Unresponsive:
@@ -1185,20 +1265,15 @@
     updateDispatchStatisticsLocked(currentTime, entry,
             injectionResult, timeSpentWaitingForApplication);
 #if DEBUG_FOCUS
-    LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d,"
-            "timeSpendWaitingForApplication=%0.1fms",
+    LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
+            "timeSpentWaitingForApplication=%0.1fms",
             injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
 #endif
     return injectionResult;
 }
 
-void InputDispatcher::releaseTouchedWindowLocked() {
-    mTouchedWindow = NULL;
-    mTouchedWindowIsObscured = false;
-    mTouchedWallpaperWindows.clear();
-}
-
-void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags) {
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+        BitSet32 pointerIds) {
     mCurrentInputTargets.push();
 
     InputTarget& target = mCurrentInputTargets.editTop();
@@ -1206,6 +1281,8 @@
     target.flags = targetFlags;
     target.xOffset = - window->frameLeft;
     target.yOffset = - window->frameTop;
+    target.windowType = window->layoutParamsType;
+    target.pointerIds = pointerIds;
 }
 
 void InputDispatcher::addMonitoringTargetsLocked() {
@@ -1217,22 +1294,27 @@
         target.flags = 0;
         target.xOffset = 0;
         target.yOffset = 0;
+        target.windowType = InputWindow::TYPE_SYSTEM_OVERLAY;
     }
 }
 
 bool InputDispatcher::checkInjectionPermission(const InputWindow* window,
-        int32_t injectorPid, int32_t injectorUid) {
-    if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
-        bool result = mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
+        const InjectionState* injectionState) {
+    if (injectionState
+            && injectionState->injectorUid > 0
+            && (window == NULL || window->ownerUid != injectionState->injectorUid)) {
+        bool result = mPolicy->checkInjectEventsPermissionNonReentrant(
+                injectionState->injectorPid, injectionState->injectorUid);
         if (! result) {
             if (window) {
                 LOGW("Permission denied: injecting event from pid %d uid %d to window "
                         "with input channel %s owned by uid %d",
-                        injectorPid, injectorUid, window->inputChannel->getName().string(),
+                        injectionState->injectorPid, injectionState->injectorUid,
+                        window->inputChannel->getName().string(),
                         window->ownerUid);
             } else {
                 LOGW("Permission denied: injecting event from pid %d uid %d",
-                        injectorPid, injectorUid);
+                        injectionState->injectorPid, injectionState->injectorUid);
             }
             return false;
         }
@@ -1282,12 +1364,19 @@
     }
 }
 
-void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
-        int32_t windowType, int32_t eventType) {
+bool InputDispatcher::shouldPokeUserActivityForCurrentInputTargetsLocked() {
+    for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+        if (mCurrentInputTargets[i].windowType == InputWindow::TYPE_KEYGUARD) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType) {
     CommandEntry* commandEntry = postCommandLocked(
             & InputDispatcher::doPokeUserActivityLockedInterruptible);
     commandEntry->eventTime = eventTime;
-    commandEntry->windowType = windowType;
     commandEntry->userActivityEventType = eventType;
 }
 
@@ -1296,12 +1385,19 @@
         bool resumeWithAppendedMotionSample) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
-            "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
+            "xOffset=%f, yOffset=%f, "
+            "windowType=%d, pointerIds=0x%x, "
+            "resumeWithAppendedMotionSample=%s",
             connection->getInputChannelName(), inputTarget->flags,
             inputTarget->xOffset, inputTarget->yOffset,
+            inputTarget->windowType, inputTarget->pointerIds.value,
             toString(resumeWithAppendedMotionSample));
 #endif
 
+    // Make sure we are never called for streaming when splitting across multiple windows.
+    bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT;
+    assert(! (resumeWithAppendedMotionSample && isSplit));
+
     // Skip this event if the connection status is not normal.
     // We don't want to enqueue additional outbound events if the connection is broken.
     if (connection->status != Connection::STATUS_NORMAL) {
@@ -1310,6 +1406,23 @@
         return;
     }
 
+    // Split a motion event if needed.
+    if (isSplit) {
+        assert(eventEntry->type == EventEntry::TYPE_MOTION);
+
+        MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
+        if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
+            MotionEntry* splitMotionEntry = splitMotionEvent(
+                    originalMotionEntry, inputTarget->pointerIds);
+#if DEBUG_FOCUS
+            LOGD("channel '%s' ~ Split motion event.",
+                    connection->getInputChannelName());
+            logOutboundMotionDetailsLocked("  ", splitMotionEntry);
+#endif
+            eventEntry = splitMotionEntry;
+        }
+    }
+
     // Resume the dispatch cycle with a freshly appended motion sample.
     // First we check that the last dispatch entry in the outbound queue is for the same
     // motion event to which we appended the motion sample.  If we find such a dispatch
@@ -1351,7 +1464,8 @@
             // The dispatch entry is in progress and is still potentially open for streaming.
             // Try to stream the new motion sample.  This might fail if the consumer has already
             // consumed the motion event (or if the channel is broken).
-            MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample;
+            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+            MotionSample* appendedMotionSample = motionEntry->lastSample;
             status_t status = connection->inputPublisher.appendMotionSample(
                     appendedMotionSample->eventTime, appendedMotionSample->pointerCoords);
             if (status == OK) {
@@ -1426,7 +1540,7 @@
     DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
             inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);
     if (dispatchEntry->hasForegroundTarget()) {
-        eventEntry->pendingForegroundDispatches += 1;
+        incrementPendingForegroundDispatchesLocked(eventEntry);
     }
 
     // Handle the case where we could not stream a new motion sample because the consumer has
@@ -1470,8 +1584,8 @@
     dispatchEntry->inProgress = true;
 
     // Update the connection's input state.
-    InputState::Consistency consistency = connection->inputState.trackEvent(
-            dispatchEntry->eventEntry);
+    EventEntry* eventEntry = dispatchEntry->eventEntry;
+    InputState::Consistency consistency = connection->inputState.trackEvent(eventEntry);
 
 #if FILTER_INPUT_EVENTS
     // Filter out inconsistent sequences of input events.
@@ -1497,16 +1611,13 @@
 
     // Publish the event.
     status_t status;
-    switch (dispatchEntry->eventEntry->type) {
+    switch (eventEntry->type) {
     case EventEntry::TYPE_KEY: {
-        KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+        KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
 
         // Apply target flags.
         int32_t action = keyEntry->action;
         int32_t flags = keyEntry->flags;
-        if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
-            flags |= AKEY_EVENT_FLAG_CANCELED;
-        }
 
         // Publish the key event.
         status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
@@ -1524,7 +1635,7 @@
     }
 
     case EventEntry::TYPE_MOTION: {
-        MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+        MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
 
         // Apply target flags.
         int32_t action = motionEntry->action;
@@ -1532,9 +1643,6 @@
         if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) {
             action = AMOTION_EVENT_ACTION_OUTSIDE;
         }
-        if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
-            action = AMOTION_EVENT_ACTION_CANCEL;
-        }
         if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
             flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
         }
@@ -1617,7 +1725,7 @@
     }
 
     // Record information about the newly started dispatch cycle.
-    connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
+    connection->lastEventTime = eventEntry->eventTime;
     connection->lastDispatchTime = currentTime;
 
     // Notify other system components.
@@ -1773,6 +1881,78 @@
     } // release lock
 }
 
+InputDispatcher::MotionEntry*
+InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) {
+    assert(pointerIds.value != 0);
+
+    uint32_t splitPointerIndexMap[MAX_POINTERS];
+    int32_t splitPointerIds[MAX_POINTERS];
+    PointerCoords splitPointerCoords[MAX_POINTERS];
+
+    uint32_t originalPointerCount = originalMotionEntry->pointerCount;
+    uint32_t splitPointerCount = 0;
+
+    for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
+            originalPointerIndex++) {
+        int32_t pointerId = uint32_t(originalMotionEntry->pointerIds[originalPointerIndex]);
+        if (pointerIds.hasBit(pointerId)) {
+            splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
+            splitPointerIds[splitPointerCount] = pointerId;
+            splitPointerCoords[splitPointerCount] =
+                    originalMotionEntry->firstSample.pointerCoords[originalPointerIndex];
+            splitPointerCount += 1;
+        }
+    }
+    assert(splitPointerCount == pointerIds.count());
+
+    int32_t action = originalMotionEntry->action;
+    int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+    if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
+            || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+        int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
+        int32_t pointerId = originalMotionEntry->pointerIds[originalPointerIndex];
+        if (pointerIds.hasBit(pointerId)) {
+            if (pointerIds.count() == 1) {
+                // The first/last pointer went down/up.
+                action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
+                        ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+            }
+        } else {
+            // An unrelated pointer changed.
+            action = AMOTION_EVENT_ACTION_MOVE;
+        }
+    }
+
+    MotionEntry* splitMotionEntry = mAllocator.obtainMotionEntry(
+            originalMotionEntry->eventTime,
+            originalMotionEntry->deviceId,
+            originalMotionEntry->source,
+            originalMotionEntry->policyFlags,
+            action,
+            originalMotionEntry->flags,
+            originalMotionEntry->metaState,
+            originalMotionEntry->edgeFlags,
+            originalMotionEntry->xPrecision,
+            originalMotionEntry->yPrecision,
+            originalMotionEntry->downTime,
+            splitPointerCount, splitPointerIds, splitPointerCoords);
+
+    for (MotionSample* originalMotionSample = originalMotionEntry->firstSample.next;
+            originalMotionSample != NULL; originalMotionSample = originalMotionSample->next) {
+        for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount;
+                splitPointerIndex++) {
+            uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex];
+            splitPointerCoords[splitPointerIndex] =
+                    originalMotionSample->pointerCoords[originalPointerIndex];
+        }
+
+        mAllocator.appendMotionSample(splitMotionEntry, originalMotionSample->eventTime,
+                splitPointerCoords);
+    }
+
+    return splitMotionEntry;
+}
+
 void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
@@ -1800,6 +1980,9 @@
             eventTime, deviceId, source, policyFlags, action, flags,
             keyCode, scanCode, metaState, downTime);
 #endif
+    if (! validateKeyEvent(action)) {
+        return;
+    }
 
     bool needWake;
     { // acquire lock
@@ -1839,6 +2022,9 @@
                 pointerCoords[i].orientation);
     }
 #endif
+    if (! validateMotionEvent(action, pointerCount, pointerIds)) {
+        return;
+    }
 
     bool needWake;
     { // acquire lock
@@ -1913,8 +2099,10 @@
 
                     DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
                     if (! dispatchEntry->inProgress
-                            || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
-                        // No motion event is being dispatched.
+                            || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION
+                            || dispatchEntry->isSplit()) {
+                        // No motion event is being dispatched, or it is being split across
+                        // windows in which case we cannot stream.
                         continue;
                     }
 
@@ -1972,24 +2160,24 @@
 
     nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
 
-    EventEntry* injectedEntry;
+    InjectionState* injectionState;
     bool needWake;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        injectedEntry = createEntryFromInjectedInputEventLocked(event);
+        EventEntry* injectedEntry = createEntryFromInjectedInputEventLocked(event);
         if (! injectedEntry) {
             return INPUT_EVENT_INJECTION_FAILED;
         }
 
-        injectedEntry->refCount += 1;
-        injectedEntry->injectorPid = injectorPid;
-        injectedEntry->injectorUid = injectorUid;
-
+        injectionState = mAllocator.obtainInjectionState(injectorPid, injectorUid);
         if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
-            injectedEntry->injectionIsAsync = true;
+            injectionState->injectionIsAsync = true;
         }
 
+        injectionState->refCount += 1;
+        injectedEntry->injectionState = injectionState;
+
         needWake = enqueueInboundEventLocked(injectedEntry);
     } // release lock
 
@@ -2005,7 +2193,7 @@
             injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
         } else {
             for (;;) {
-                injectionResult = injectedEntry->injectionResult;
+                injectionResult = injectionState->injectionResult;
                 if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
                     break;
                 }
@@ -2025,10 +2213,10 @@
 
             if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
                     && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
-                while (injectedEntry->pendingForegroundDispatches != 0) {
+                while (injectionState->pendingForegroundDispatches != 0) {
 #if DEBUG_INJECTION
                     LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
-                            injectedEntry->pendingForegroundDispatches);
+                            injectionState->pendingForegroundDispatches);
 #endif
                     nsecs_t remainingTimeout = endTime - now();
                     if (remainingTimeout <= 0) {
@@ -2045,7 +2233,7 @@
             }
         }
 
-        mAllocator.releaseEventEntry(injectedEntry);
+        mAllocator.releaseInjectionState(injectionState);
     } // release lock
 
 #if DEBUG_INJECTION
@@ -2058,14 +2246,15 @@
 }
 
 void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
-    if (entry->isInjected()) {
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState) {
 #if DEBUG_INJECTION
         LOGD("Setting input event injection result to %d.  "
                 "injectorPid=%d, injectorUid=%d",
-                 injectionResult, entry->injectorPid, entry->injectorUid);
+                 injectionResult, injectionState->injectorPid, injectionState->injectorUid);
 #endif
 
-        if (entry->injectionIsAsync) {
+        if (injectionState->injectionIsAsync) {
             // Log the outcome since the injector did not wait for the injection result.
             switch (injectionResult) {
             case INPUT_EVENT_INJECTION_SUCCEEDED:
@@ -2083,41 +2272,26 @@
             }
         }
 
-        entry->injectionResult = injectionResult;
+        injectionState->injectionResult = injectionResult;
         mInjectionResultAvailableCondition.broadcast();
     }
 }
 
+void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState) {
+        injectionState->pendingForegroundDispatches += 1;
+    }
+}
+
 void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
-    entry->pendingForegroundDispatches -= 1;
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState) {
+        injectionState->pendingForegroundDispatches -= 1;
 
-    if (entry->isInjected() && entry->pendingForegroundDispatches == 0) {
-        mInjectionSyncFinishedCondition.broadcast();
-    }
-}
-
-static bool isValidKeyAction(int32_t action) {
-    switch (action) {
-    case AKEY_EVENT_ACTION_DOWN:
-    case AKEY_EVENT_ACTION_UP:
-        return true;
-    default:
-        return false;
-    }
-}
-
-static bool isValidMotionAction(int32_t action) {
-    switch (action & AMOTION_EVENT_ACTION_MASK) {
-    case AMOTION_EVENT_ACTION_DOWN:
-    case AMOTION_EVENT_ACTION_UP:
-    case AMOTION_EVENT_ACTION_CANCEL:
-    case AMOTION_EVENT_ACTION_MOVE:
-    case AMOTION_EVENT_ACTION_POINTER_DOWN:
-    case AMOTION_EVENT_ACTION_POINTER_UP:
-    case AMOTION_EVENT_ACTION_OUTSIDE:
-        return true;
-    default:
-        return false;
+        if (injectionState->pendingForegroundDispatches == 0) {
+            mInjectionSyncFinishedCondition.broadcast();
+        }
     }
 }
 
@@ -2126,9 +2300,7 @@
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY: {
         const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
-        if (! isValidKeyAction(keyEvent->getAction())) {
-            LOGE("Dropping injected key event since it has invalid action code 0x%x",
-                    keyEvent->getAction());
+        if (! validateKeyEvent(keyEvent->getAction())) {
             return NULL;
         }
 
@@ -2144,16 +2316,10 @@
 
     case AINPUT_EVENT_TYPE_MOTION: {
         const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
-        if (! isValidMotionAction(motionEvent->getAction())) {
-            LOGE("Dropping injected motion event since it has invalid action code 0x%x.",
-                    motionEvent->getAction());
+        if (! validateMotionEvent(motionEvent->getAction(),
+                motionEvent->getPointerCount(), motionEvent->getPointerIds())) {
             return NULL;
         }
-        if (motionEvent->getPointerCount() == 0
-                || motionEvent->getPointerCount() > MAX_POINTERS) {
-            LOGE("Dropping injected motion event since it has an invalid pointer count %d.",
-                    motionEvent->getPointerCount());
-        }
 
         uint32_t policyFlags = POLICY_FLAG_INJECTED;
 
@@ -2182,6 +2348,16 @@
     }
 }
 
+const InputWindow* InputDispatcher::getWindowLocked(const sp<InputChannel>& inputChannel) {
+    for (size_t i = 0; i < mWindows.size(); i++) {
+        const InputWindow* window = & mWindows[i];
+        if (window->inputChannel == inputChannel) {
+            return window;
+        }
+    }
+    return NULL;
+}
+
 void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
 #if DEBUG_FOCUS
     LOGD("setInputWindows");
@@ -2189,22 +2365,8 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        // Clear old window pointers but remember their associated channels.
+        // Clear old window pointers.
         mFocusedWindow = NULL;
-
-        sp<InputChannel> touchedWindowChannel;
-        if (mTouchedWindow) {
-            touchedWindowChannel = mTouchedWindow->inputChannel;
-            mTouchedWindow = NULL;
-        }
-        size_t numTouchedWallpapers = mTouchedWallpaperWindows.size();
-        if (numTouchedWallpapers != 0) {
-            for (size_t i = 0; i < numTouchedWallpapers; i++) {
-                mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel);
-            }
-            mTouchedWallpaperWindows.clear();
-        }
-        mWallpaperWindows.clear();
         mWindows.clear();
 
         // Loop over new windows and rebuild the necessary window pointers for
@@ -2213,26 +2375,23 @@
 
         size_t numWindows = mWindows.size();
         for (size_t i = 0; i < numWindows; i++) {
-            InputWindow* window = & mWindows.editItemAt(i);
+            const InputWindow* window = & mWindows.itemAt(i);
             if (window->hasFocus) {
                 mFocusedWindow = window;
-            }
-
-            if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
-                mWallpaperWindows.push(window);
-
-                for (size_t j = 0; j < numTouchedWallpapers; j++) {
-                    if (window->inputChannel == mTempTouchedWallpaperChannels[i]) {
-                        mTouchedWallpaperWindows.push(window);
-                    }
-                }
-            }
-
-            if (window->inputChannel == touchedWindowChannel) {
-                mTouchedWindow = window;
+                break;
             }
         }
-        mTempTouchedWallpaperChannels.clear();
+
+        for (size_t i = 0; i < mTouchState.windows.size(); ) {
+            TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i);
+            const InputWindow* window = getWindowLocked(touchedWindow.channel);
+            if (window) {
+                touchedWindow.window = window;
+                i += 1;
+            } else {
+                mTouchState.windows.removeAt(i);
+            }
+        }
 
 #if DEBUG_FOCUS
         logDispatchStateLocked();
@@ -2334,12 +2493,13 @@
     }
     dump.appendFormat("  focusedWindow: name='%s'\n",
             mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>");
-    dump.appendFormat("  touchedWindow: name='%s', touchDown=%d\n",
-            mTouchedWindow != NULL ? mTouchedWindow->name.string() : "<null>",
-            mTouchDown);
-    for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
-        dump.appendFormat("  touchedWallpaperWindows[%d]: name='%s'\n",
-                i, mTouchedWallpaperWindows[i]->name.string());
+    dump.appendFormat("  touchState: down=%s, split=%s\n", toString(mTouchState.down),
+            toString(mTouchState.split));
+    for (size_t i = 0; i < mTouchState.windows.size(); i++) {
+        const TouchedWindow& touchedWindow = mTouchState.windows[i];
+        dump.appendFormat("  touchedWindow[%d]: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
+                i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value,
+                touchedWindow.targetFlags);
     }
     for (size_t i = 0; i < mWindows.size(); i++) {
         dump.appendFormat("  windows[%d]: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
@@ -2594,8 +2754,7 @@
 void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
     mLock.unlock();
 
-    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType,
-            commandEntry->userActivityEventType);
+    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
 
     mLock.lock();
 }
@@ -2627,17 +2786,32 @@
 InputDispatcher::Allocator::Allocator() {
 }
 
+InputDispatcher::InjectionState*
+InputDispatcher::Allocator::obtainInjectionState(int32_t injectorPid, int32_t injectorUid) {
+    InjectionState* injectionState = mInjectionStatePool.alloc();
+    injectionState->refCount = 1;
+    injectionState->injectorPid = injectorPid;
+    injectionState->injectorUid = injectorUid;
+    injectionState->injectionIsAsync = false;
+    injectionState->injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    injectionState->pendingForegroundDispatches = 0;
+    return injectionState;
+}
+
 void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type,
         nsecs_t eventTime) {
     entry->type = type;
     entry->refCount = 1;
     entry->dispatchInProgress = false;
     entry->eventTime = eventTime;
-    entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
-    entry->injectionIsAsync = false;
-    entry->injectorPid = -1;
-    entry->injectorUid = -1;
-    entry->pendingForegroundDispatches = 0;
+    entry->injectionState = NULL;
+}
+
+void InputDispatcher::Allocator::releaseEventEntryInjectionState(EventEntry* entry) {
+    if (entry->injectionState) {
+        releaseInjectionState(entry->injectionState);
+        entry->injectionState = NULL;
+    }
 }
 
 InputDispatcher::ConfigurationChangedEntry*
@@ -2720,6 +2894,15 @@
     return entry;
 }
 
+void InputDispatcher::Allocator::releaseInjectionState(InjectionState* injectionState) {
+    injectionState->refCount -= 1;
+    if (injectionState->refCount == 0) {
+        mInjectionStatePool.free(injectionState);
+    } else {
+        assert(injectionState->refCount > 0);
+    }
+}
+
 void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) {
     switch (entry->type) {
     case EventEntry::TYPE_CONFIGURATION_CHANGED:
@@ -2741,6 +2924,7 @@
         ConfigurationChangedEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
+        releaseEventEntryInjectionState(entry);
         mConfigurationChangeEntryPool.free(entry);
     } else {
         assert(entry->refCount > 0);
@@ -2750,6 +2934,7 @@
 void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
+        releaseEventEntryInjectionState(entry);
         mKeyEntryPool.free(entry);
     } else {
         assert(entry->refCount > 0);
@@ -2759,6 +2944,7 @@
 void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
+        releaseEventEntryInjectionState(entry);
         for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) {
             MotionSample* next = sample->next;
             mMotionSamplePool.free(sample);
@@ -2793,22 +2979,12 @@
     motionEntry->lastSample = sample;
 }
 
+void InputDispatcher::Allocator::recycleKeyEntry(KeyEntry* keyEntry) {
+    releaseEventEntryInjectionState(keyEntry);
 
-// --- InputDispatcher::EventEntry ---
-
-void InputDispatcher::EventEntry::recycle() {
-    injectionResult = INPUT_EVENT_INJECTION_PENDING;
-    dispatchInProgress = false;
-    pendingForegroundDispatches = 0;
-}
-
-
-// --- InputDispatcher::KeyEntry ---
-
-void InputDispatcher::KeyEntry::recycle() {
-    EventEntry::recycle();
-    syntheticRepeat = false;
-    interceptKeyResult = INTERCEPT_KEY_RESULT_UNKNOWN;
+    keyEntry->dispatchInProgress = false;
+    keyEntry->syntheticRepeat = false;
+    keyEntry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
 }
 
 
@@ -3057,6 +3233,72 @@
 }
 
 
+// --- InputDispatcher::TouchState ---
+
+InputDispatcher::TouchState::TouchState() :
+    down(false), split(false) {
+}
+
+InputDispatcher::TouchState::~TouchState() {
+}
+
+void InputDispatcher::TouchState::reset() {
+    down = false;
+    split = false;
+    windows.clear();
+}
+
+void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
+    down = other.down;
+    split = other.split;
+    windows.clear();
+    windows.appendVector(other.windows);
+}
+
+void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window,
+        int32_t targetFlags, BitSet32 pointerIds) {
+    if (targetFlags & InputTarget::FLAG_SPLIT) {
+        split = true;
+    }
+
+    for (size_t i = 0; i < windows.size(); i++) {
+        TouchedWindow& touchedWindow = windows.editItemAt(i);
+        if (touchedWindow.window == window) {
+            touchedWindow.targetFlags |= targetFlags;
+            touchedWindow.pointerIds.value |= pointerIds.value;
+            return;
+        }
+    }
+
+    windows.push();
+
+    TouchedWindow& touchedWindow = windows.editTop();
+    touchedWindow.window = window;
+    touchedWindow.targetFlags = targetFlags;
+    touchedWindow.pointerIds = pointerIds;
+    touchedWindow.channel = window->inputChannel;
+}
+
+void InputDispatcher::TouchState::removeOutsideTouchWindows() {
+    for (size_t i = 0 ; i < windows.size(); ) {
+        if (windows[i].targetFlags & InputTarget::FLAG_OUTSIDE) {
+            windows.removeAt(i);
+        } else {
+            i += 1;
+        }
+    }
+}
+
+const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() {
+    for (size_t i = 0; i < windows.size(); i++) {
+        if (windows[i].targetFlags & InputTarget::FLAG_FOREGROUND) {
+            return windows[i].window;
+        }
+    }
+    return NULL;
+}
+
+
 // --- InputDispatcherThread ---
 
 InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 783cbc4..f2b029a 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -3366,7 +3366,7 @@
                 if (id > MAX_POINTER_ID) {
 #if DEBUG_POINTERS
                     LOGD("Pointers: Ignoring driver provided pointer id %d because "
-                            "it is larger than max supported id %d for optimizations",
+                            "it is larger than max supported id %d",
                             id, MAX_POINTER_ID);
 #endif
                     havePointerIds = false;
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index e3bae56..6f52f24 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -207,7 +207,7 @@
     virtual int32_t getMaxEventsPerSecond();
     virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
             const KeyEvent* keyEvent, uint32_t policyFlags);
-    virtual void pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType);
+    virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
     virtual bool checkInjectEventsPermissionNonReentrant(
             int32_t injectorPid, int32_t injectorUid);
 
@@ -973,10 +973,8 @@
     return consumed && ! error;
 }
 
-void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType) {
-    if (windowType != InputWindow::TYPE_KEYGUARD) {
-        android_server_PowerManagerService_userActivity(eventTime, eventType);
-    }
+void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
+    android_server_PowerManagerService_userActivity(eventTime, eventType);
 }