Merge "Fix volume problems with insert revert" into gingerbread
diff --git a/api/9.xml b/api/9.xml
index b266352..9ff1287 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -167793,16 +167793,6 @@
  visibility="public"
 >
 </method>
-<field name="mDeviceId"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
 </class>
 <class name="KeyCharacterMap"
  extends="java.lang.Object"
diff --git a/api/current.xml b/api/current.xml
index fbdf02a..d2e1f05 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -46618,6 +46618,16 @@
  visibility="public"
 >
 </field>
+<field name="nativeLibraryDir"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="permission"
  type="java.lang.String"
  transient="false"
@@ -96627,6 +96637,19 @@
 <parameter name="title" type="java.lang.String">
 </parameter>
 </method>
+<method name="setVisibleInDownloadsUi"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="isVisible" type="boolean">
+</parameter>
+</method>
 <field name="NETWORK_MOBILE"
  type="int"
  transient="false"
@@ -177727,7 +177750,7 @@
  native="false"
  synchronized="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -177775,26 +177798,6 @@
  visibility="public"
 >
 </field>
-<field name="mDeviceId"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
-<field name="mSource"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
 </class>
 <class name="InputQueue"
  extends="java.lang.Object"
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index cf24433..0db954d 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -355,8 +355,6 @@
 
     /**
      * Full path to the directory where native JNI libraries are stored.
-     * 
-     * {@hide}
      */
     public String nativeLibraryDir;
 
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index e04367a..e8237c9 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -276,6 +276,7 @@
         private String mMediaType;
         private boolean mRoamingAllowed = true;
         private int mAllowedNetworkTypes = ~0; // default to all network types allowed
+        private boolean mIsVisibleInDownloadsUi = true;
 
         /**
          * @param uri the HTTP URI to download.
@@ -387,6 +388,17 @@
         }
 
         /**
+         * Set whether this download should be displayed in the system's Downloads UI. True by
+         * default.
+         * @param isVisible whether to display this download in the Downloads UI
+         * @return this object
+         */
+        public Request setVisibleInDownloadsUi(boolean isVisible) {
+            mIsVisibleInDownloadsUi = isVisible;
+            return this;
+        }
+
+        /**
          * @return ContentValues to be passed to DownloadProvider.insert()
          */
         ContentValues toContentValues(String packageName) {
@@ -418,6 +430,7 @@
 
             values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes);
             values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed);
+            values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, mIsVisibleInDownloadsUi);
 
             return values;
         }
@@ -458,6 +471,7 @@
         private Integer mStatusFlags = null;
         private String mOrderByColumn = Downloads.COLUMN_LAST_MODIFICATION;
         private int mOrderDirection = ORDER_DESCENDING;
+        private boolean mOnlyIncludeVisibleInDownloadsUi = false;
 
         /**
          * Include only the download with the given ID.
@@ -479,6 +493,19 @@
         }
 
         /**
+         * Controls whether this query includes downloads not visible in the system's Downloads UI.
+         * @param value if true, this query will only include downloads that should be displayed in
+         *            the system's Downloads UI; if false (the default), this query will include
+         *            both visible and invisible downloads.
+         * @return this object
+         * @hide
+         */
+        public Query setOnlyIncludeVisibleInDownloadsUi(boolean value) {
+            mOnlyIncludeVisibleInDownloadsUi = value;
+            return this;
+        }
+
+        /**
          * Change the sort order of the returned Cursor.
          *
          * @param column one of the COLUMN_* constants; currently, only
@@ -511,7 +538,7 @@
          */
         Cursor runQuery(ContentResolver resolver, String[] projection) {
             Uri uri = Downloads.CONTENT_URI;
-            String selection = null;
+            List<String> selectionParts = new ArrayList<String>();
 
             if (mId != null) {
                 uri = Uri.withAppendedPath(uri, mId.toString());
@@ -536,9 +563,14 @@
                     parts.add("(" + statusClause(">=", 400)
                               + " AND " + statusClause("<", 600) + ")");
                 }
-                selection = joinStrings(" OR ", parts);
+                selectionParts.add(joinStrings(" OR ", parts));
             }
 
+            if (mOnlyIncludeVisibleInDownloadsUi) {
+                selectionParts.add(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI + " != '0'");
+            }
+
+            String selection = joinStrings(" AND ", selectionParts);
             String orderDirection = (mOrderDirection == ORDER_ASCENDING ? "ASC" : "DESC");
             String orderBy = mOrderByColumn + " " + orderDirection;
 
@@ -628,6 +660,34 @@
     }
 
     /**
+     * Restart the given download, which must have already completed (successfully or not).  This
+     * method will only work when called from within the download manager's process.
+     * @param id the ID of the download
+     * @hide
+     */
+    public void restartDownload(long id) {
+        Cursor cursor = query(new Query().setFilterById(id));
+        try {
+            if (!cursor.moveToFirst()) {
+                throw new IllegalArgumentException("No download with id " + id);
+            }
+            int status = cursor.getInt(cursor.getColumnIndex(COLUMN_STATUS));
+            if (status != STATUS_SUCCESSFUL && status != STATUS_FAILED) {
+                throw new IllegalArgumentException("Cannot restart incomplete download: " + id);
+            }
+        } finally {
+            cursor.close();
+        }
+
+        ContentValues values = new ContentValues();
+        values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
+        values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
+        values.putNull(Downloads.Impl._DATA);
+        values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
+        mResolver.update(getDownloadUri(id), values, null, null);
+    }
+
+    /**
      * Get the DownloadProvider URI for the download with the given ID.
      */
     private Uri getDownloadUri(long id) {
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index adb11c8..2813863 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -224,8 +224,8 @@
                 msg.next = prev.next;
                 prev.next = msg;
             }
-            nativeWake();
         }
+        nativeWake();
         return true;
     }
 
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 6bf0d5b..603e598 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -880,6 +880,14 @@
          */
         public static final String COLUMN_ALLOWED_NETWORK_TYPES = "allowed_network_types";
 
+        /**
+         * Whether or not this download should be displayed in the system's Downloads UI.  Defaults
+         * to true.
+         * <P>Type: INTEGER</P>
+         * <P>Owner can Init/Read</P>
+         */
+        public static final String COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI = "is_visible_in_downloads_ui";
+
         /*
          * Lists the destinations that an application can specify for a download.
          */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f37ef99..fd60115 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3481,13 +3481,7 @@
          */
         public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
             String allowedProviders = Settings.Secure.getString(cr, LOCATION_PROVIDERS_ALLOWED);
-            if (allowedProviders != null) {
-                return (allowedProviders.equals(provider) ||
-                        allowedProviders.contains("," + provider + ",") ||
-                        allowedProviders.startsWith(provider + ",") ||
-                        allowedProviders.endsWith("," + provider));
-            }
-            return false;
+            return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
         }
 
         /**
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 9589bf3..8675d05 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1651,7 +1651,36 @@
 
         return mode;
     }
-    
+
+    /**
+     * Does a comma-delimited list 'delimitedString' contain a certain item?
+     * (without allocating memory)
+     *
+     * @hide
+     */
+    public static boolean delimitedStringContains(
+            String delimitedString, char delimiter, String item) {
+        if (isEmpty(delimitedString) || isEmpty(item)) {
+            return false;
+        }
+        int pos = -1;
+        int length = delimitedString.length();
+        while ((pos = delimitedString.indexOf(item, pos + 1)) != -1) {
+            if (pos > 0 && delimitedString.charAt(pos - 1) != delimiter) {
+                continue;
+            }
+            int expectedDelimiterPos = pos + item.length();
+            if (expectedDelimiterPos == length) {
+                // Match at end of string.
+                return true;
+            }
+            if (delimitedString.charAt(expectedDelimiterPos) == delimiter) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private static Object sLock = new Object();
     private static char[] sTemp = null;
 }
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 9afd16e..184e0fc 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -23,7 +23,9 @@
  * Common base class for input events.
  */
 public abstract class InputEvent implements Parcelable {
+    /** @hide */
     protected int mDeviceId;
+    /** @hide */
     protected int mSource;
     
     /** @hide */
@@ -76,7 +78,7 @@
         mSource = source;
     }
     
-    public final int describeContents() {
+    public int describeContents() {
         return 0;
     }
     
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index c01068f..4bb11bb 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -463,6 +463,11 @@
     private static final int TOUCH_DONE_MODE = 7;
     private static final int TOUCH_PINCH_DRAG = 8;
 
+    /**
+     * True if we have a touch panel capable of detecting smooth pan/scale at the same time
+     */
+    private boolean mAllowPanAndScale;
+
     // Whether to forward the touch events to WebCore
     private boolean mForwardTouchEvents = false;
 
@@ -976,9 +981,11 @@
 
     void updateMultiTouchSupport(Context context) {
         WebSettings settings = getSettings();
-        mSupportMultiTouch = context.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
+        final PackageManager pm = context.getPackageManager();
+        mSupportMultiTouch = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
                 && settings.supportZoom() && settings.getBuiltInZoomControls();
+        mAllowPanAndScale = pm.hasSystemFeature(
+                PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
         if (mSupportMultiTouch && (mScaleDetector == null)) {
             mScaleDetector = new ScaleGestureDetector(context,
                     new ScaleDetectorListener());
@@ -5008,11 +5015,13 @@
         // FIXME: we may consider to give WebKit an option to handle multi-touch
         // events later.
         if (mSupportMultiTouch && ev.getPointerCount() > 1) {
-            if (mMinZoomScale < mMaxZoomScale) {
+            if (mAllowPanAndScale || mMinZoomScale < mMaxZoomScale) {
                 mScaleDetector.onTouchEvent(ev);
                 if (mScaleDetector.isInProgress()) {
                     mLastTouchTime = eventTime;
-                    return true;
+                    if (!mAllowPanAndScale) {
+                        return true;
+                    }
                 }
                 x = mScaleDetector.getFocusX();
                 y = mScaleDetector.getFocusY();
@@ -5214,15 +5223,21 @@
                         mLastTouchTime = eventTime;
                         break;
                     }
-                    // if it starts nearly horizontal or vertical, enforce it
-                    int ax = Math.abs(deltaX);
-                    int ay = Math.abs(deltaY);
-                    if (ax > MAX_SLOPE_FOR_DIAG * ay) {
-                        mSnapScrollMode = SNAP_X;
-                        mSnapPositive = deltaX > 0;
-                    } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
-                        mSnapScrollMode = SNAP_Y;
-                        mSnapPositive = deltaY > 0;
+
+                    // Only lock dragging to one axis if we don't have a scale in progress.
+                    // Scaling implies free-roaming movement. Note we'll only ever get here
+                    // if mAllowPanAndScale is true.
+                    if (mScaleDetector != null && !mScaleDetector.isInProgress()) {
+                        // if it starts nearly horizontal or vertical, enforce it
+                        int ax = Math.abs(deltaX);
+                        int ay = Math.abs(deltaY);
+                        if (ax > MAX_SLOPE_FOR_DIAG * ay) {
+                            mSnapScrollMode = SNAP_X;
+                            mSnapPositive = deltaX > 0;
+                        } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
+                            mSnapScrollMode = SNAP_Y;
+                            mSnapPositive = deltaY > 0;
+                        }
                     }
 
                     mTouchMode = TOUCH_DRAG_MODE;
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 8b618c7..6e11cff 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -293,4 +293,34 @@
             inputStream.close();
         }
     }
+
+    // Remove the native binaries of a given package. This simply
+    // gets rid of the files in the 'lib' sub-directory.
+    public static void removeNativeBinariesLI(String nativeLibraryPath) {
+        if (DEBUG_NATIVE) {
+            Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryPath);
+        }
+
+        /*
+         * Just remove any file in the directory. Since the directory is owned
+         * by the 'system' UID, the application is not supposed to have written
+         * anything there.
+         */
+        File binaryDir = new File(nativeLibraryPath);
+        if (binaryDir.exists()) {
+            File[] binaries = binaryDir.listFiles();
+            if (binaries != null) {
+                for (int nn = 0; nn < binaries.length; nn++) {
+                    if (DEBUG_NATIVE) {
+                        Slog.d(TAG, "    Deleting " + binaries[nn].getName());
+                    }
+                    if (!binaries[nn].delete()) {
+                        Slog.w(TAG, "Could not delete native binary: " + binaries[nn].getPath());
+                    }
+                }
+            }
+            // Do not delete 'lib' directory itself, or this will prevent
+            // installation of future updates.
+        }
+    }
 }
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 939f118..12cf853 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -84,6 +84,7 @@
     private boolean mCurDown;
     private int mCurNumPointers;
     private int mMaxNumPointers;
+    private int mActivePointerId;
     private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
     
     private final VelocityTracker mVelocity;
@@ -123,6 +124,7 @@
         
         PointerState ps = new PointerState();
         mPointers.add(ps);
+        mActivePointerId = 0;
         
         mVelocity = VelocityTracker.obtain();
         
@@ -183,14 +185,15 @@
             final int NP = mPointers.size();
             
             // Labels
-            if (NP > 0) {
-                final PointerState ps = mPointers.get(0);
+            if (mActivePointerId >= 0) {
+                final PointerState ps = mPointers.get(mActivePointerId);
+                
                 canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
                 canvas.drawText(mText.clear()
                         .append("P: ").append(mCurNumPointers)
                         .append(" / ").append(mMaxNumPointers)
                         .toString(), 1, base, mTextPaint);
-                
+
                 final int N = ps.mTraceCount;
                 if ((mCurDown && ps.mCurDown) || N == 0) {
                     canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
@@ -355,6 +358,11 @@
                     NP++;
                 }
                 
+                if (mActivePointerId < 0 ||
+                        ! mPointers.get(mActivePointerId).mCurDown) {
+                    mActivePointerId = id;
+                }
+                
                 final PointerState ps = mPointers.get(id);
                 ps.mCurDown = true;
                 if (mPrintCoords) {
@@ -396,6 +404,7 @@
             }
             
             if (action == MotionEvent.ACTION_UP
+                    || action == MotionEvent.ACTION_CANCEL
                     || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
                 final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
                         >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
@@ -407,10 +416,14 @@
                     Log.i(TAG, mText.clear().append("Pointer ")
                             .append(id + 1).append(": UP").toString());
                 }
-
-                if (action == MotionEvent.ACTION_UP) {
+                
+                if (action == MotionEvent.ACTION_UP
+                        || action == MotionEvent.ACTION_CANCEL) {
                     mCurDown = false;
                 } else {
+                    if (mActivePointerId == id) {
+                        mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
+                    }
                     ps.addTrace(Float.NaN, Float.NaN);
                 }
             }
@@ -438,9 +451,9 @@
     
     // HACK
     // A quick and dirty string builder implementation optimized for GC.
-    // Using the basic StringBuilder implementation causes the application grind to a halt when
-    // more than a couple of pointers are down due to the number of temporary objects allocated
-    // while formatting strings for drawing or logging.
+    // Using String.format causes the application grind to a halt when
+    // more than a couple of pointers are down due to the number of
+    // temporary objects allocated while formatting strings for drawing or logging.
     private static final class FasterStringBuilder {
         private char[] mChars;
         private int mLength;
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index a5229cc..1beba53 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -349,6 +349,26 @@
         }
     }
 
+    @SmallTest
+    public void testDelimitedStringContains() {
+        assertFalse(TextUtils.delimitedStringContains("", ',', null));
+        assertFalse(TextUtils.delimitedStringContains(null, ',', ""));
+        // Whole match
+        assertTrue(TextUtils.delimitedStringContains("gps", ',', "gps"));
+        // At beginning.
+        assertTrue(TextUtils.delimitedStringContains("gps,gpsx,network,mock", ',', "gps"));
+        assertTrue(TextUtils.delimitedStringContains("gps,network,mock", ',', "gps"));
+        // In middle, both without, before & after a false match.
+        assertTrue(TextUtils.delimitedStringContains("network,gps,mock", ',', "gps"));
+        assertTrue(TextUtils.delimitedStringContains("network,gps,gpsx,mock", ',', "gps"));
+        assertTrue(TextUtils.delimitedStringContains("network,gpsx,gps,mock", ',', "gps"));
+        // At the end.
+        assertTrue(TextUtils.delimitedStringContains("network,mock,gps", ',', "gps"));
+        assertTrue(TextUtils.delimitedStringContains("network,mock,gpsx,gps", ',', "gps"));
+        // Not present (but with a false match)
+        assertFalse(TextUtils.delimitedStringContains("network,mock,gpsx", ',', "gps"));
+    }
+
     /**
      * CharSequence wrapper for testing the cases where text is copied into
      * a char array instead of working from a String or a Spanned.
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index f00f2db..a06208a 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -30,6 +30,7 @@
 
 #include <stddef.h>
 #include <unistd.h>
+#include <limits.h>
 
 
 namespace android {
@@ -108,9 +109,12 @@
     // Flags for the input target.
     int32_t flags;
 
-    // The timeout for event delivery to this target in nanoseconds.  Or -1 if none.
+    // The timeout for event delivery to this target in nanoseconds, or -1 to wait indefinitely.
     nsecs_t timeout;
 
+    // The time already spent waiting for this target in nanoseconds, or 0 if none.
+    nsecs_t timeSpentWaitingForApplication;
+
     // The x and y offset to add to a MotionEvent as it is delivered.
     // (ignored for KeyEvents)
     float xOffset, yOffset;
@@ -118,6 +122,122 @@
 
 
 /*
+ * An input window describes the bounds of a window that can receive input.
+ */
+struct InputWindow {
+    // Window flags from WindowManager.LayoutParams
+    enum {
+        FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001,
+        FLAG_DIM_BEHIND        = 0x00000002,
+        FLAG_BLUR_BEHIND        = 0x00000004,
+        FLAG_NOT_FOCUSABLE      = 0x00000008,
+        FLAG_NOT_TOUCHABLE      = 0x00000010,
+        FLAG_NOT_TOUCH_MODAL    = 0x00000020,
+        FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
+        FLAG_KEEP_SCREEN_ON     = 0x00000080,
+        FLAG_LAYOUT_IN_SCREEN   = 0x00000100,
+        FLAG_LAYOUT_NO_LIMITS   = 0x00000200,
+        FLAG_FULLSCREEN      = 0x00000400,
+        FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800,
+        FLAG_DITHER             = 0x00001000,
+        FLAG_SECURE             = 0x00002000,
+        FLAG_SCALED             = 0x00004000,
+        FLAG_IGNORE_CHEEK_PRESSES    = 0x00008000,
+        FLAG_LAYOUT_INSET_DECOR = 0x00010000,
+        FLAG_ALT_FOCUSABLE_IM = 0x00020000,
+        FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
+        FLAG_SHOW_WHEN_LOCKED = 0x00080000,
+        FLAG_SHOW_WALLPAPER = 0x00100000,
+        FLAG_TURN_SCREEN_ON = 0x00200000,
+        FLAG_DISMISS_KEYGUARD = 0x00400000,
+        FLAG_IMMERSIVE = 0x00800000,
+        FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
+        FLAG_COMPATIBLE_WINDOW = 0x20000000,
+        FLAG_SYSTEM_ERROR = 0x40000000,
+    };
+
+    // Window types from WindowManager.LayoutParams
+    enum {
+        FIRST_APPLICATION_WINDOW = 1,
+        TYPE_BASE_APPLICATION   = 1,
+        TYPE_APPLICATION        = 2,
+        TYPE_APPLICATION_STARTING = 3,
+        LAST_APPLICATION_WINDOW = 99,
+        FIRST_SUB_WINDOW        = 1000,
+        TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW,
+        TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1,
+        TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
+        TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
+        TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW+4,
+        LAST_SUB_WINDOW         = 1999,
+        FIRST_SYSTEM_WINDOW     = 2000,
+        TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW,
+        TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1,
+        TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2,
+        TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3,
+        TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4,
+        TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5,
+        TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6,
+        TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7,
+        TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8,
+        TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9,
+        TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10,
+        TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11,
+        TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
+        TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13,
+        TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14,
+        LAST_SYSTEM_WINDOW      = 2999,
+    };
+
+    sp<InputChannel> inputChannel;
+    int32_t layoutParamsFlags;
+    int32_t layoutParamsType;
+    nsecs_t dispatchingTimeout;
+    int32_t frameLeft;
+    int32_t frameTop;
+    int32_t frameRight;
+    int32_t frameBottom;
+    int32_t visibleFrameLeft;
+    int32_t visibleFrameTop;
+    int32_t visibleFrameRight;
+    int32_t visibleFrameBottom;
+    int32_t touchableAreaLeft;
+    int32_t touchableAreaTop;
+    int32_t touchableAreaRight;
+    int32_t touchableAreaBottom;
+    bool visible;
+    bool hasFocus;
+    bool hasWallpaper;
+    bool paused;
+    int32_t ownerPid;
+    int32_t ownerUid;
+
+    bool visibleFrameIntersects(const InputWindow* other) const;
+    bool touchableAreaContainsPoint(int32_t x, int32_t y) const;
+};
+
+
+/*
+ * A private handle type used by the input manager to track the window.
+ */
+class InputApplicationHandle : public RefBase {
+protected:
+    InputApplicationHandle() { }
+    virtual ~InputApplicationHandle() { }
+};
+
+
+/*
+ * An input application describes properties of an application that can receive input.
+ */
+struct InputApplication {
+    String8 name;
+    nsecs_t dispatchingTimeout;
+    sp<InputApplicationHandle> handle;
+};
+
+
+/*
  * Input dispatcher policy interface.
  *
  * The input reader policy is used by the input reader to interact with the Window Manager
@@ -135,14 +255,16 @@
     /* Notifies the system that a configuration change has occurred. */
     virtual void notifyConfigurationChanged(nsecs_t when) = 0;
 
+    /* Notifies the system that an application is not responding.
+     * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
+    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+
     /* Notifies the system that an input channel is unrecoverably broken. */
     virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0;
 
     /* Notifies the system that an input channel is not responding.
-     * Returns true and a new timeout value if the dispatcher should keep waiting.
-     * Otherwise returns false. */
-    virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
-            nsecs_t& outNewTimeout) = 0;
+     * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
+    virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 0;
 
     /* Notifies the system that an input channel recovered from ANR. */
     virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0;
@@ -153,29 +275,27 @@
     /* Gets the key repeat inter-key delay. */
     virtual nsecs_t getKeyRepeatDelay() = 0;
 
-    /* Waits for key event input targets to become available.
-     * If the event is being injected, injectorPid and injectorUid should specify the
-     * process id and used id of the injecting application, otherwise they should both
-     * be -1.
-     * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
-    virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
-            int32_t injectorPid, int32_t injectorUid,
-            Vector<InputTarget>& outTargets) = 0;
-
-    /* Waits for motion event targets to become available.
-     * If the event is being injected, injectorPid and injectorUid should specify the
-     * process id and used id of the injecting application, otherwise they should both
-     * be -1.
-     * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
-    virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-            int32_t injectorPid, int32_t injectorUid,
-            Vector<InputTarget>& outTargets) = 0;
-
     /* Gets the maximum suggested event delivery rate per second.
      * This value is used to throttle motion event movement actions on a per-device
      * basis.  It is not intended to be a hard limit.
      */
     virtual int32_t getMaxEventsPerSecond() = 0;
+
+    /* Allows the policy a chance to intercept a key before dispatching. */
+    virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
+            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;
+
+    /* Checks whether a given application pid/uid has permission to inject input events
+     * into other applications.
+     *
+     * This method is special in that its implementation promises to be non-reentrant and
+     * is safe to call while holding other locks.  (Most other methods make no such guarantees!)
+     */
+    virtual bool checkInjectEventsPermissionNonReentrant(
+            int32_t injectorPid, int32_t injectorUid) = 0;
 };
 
 
@@ -187,6 +307,11 @@
     virtual ~InputDispatcherInterface() { }
 
 public:
+    /* Dumps the state of the input dispatcher.
+     *
+     * This method may be called on any thread (usually by the input manager). */
+    virtual void dump(String8& dump) = 0;
+
     /* Runs a single iteration of the dispatch loop.
      * Nominally processes one queued event, a timeout, or a response from an input consumer.
      *
@@ -199,7 +324,6 @@
      * These methods should only be called on the input reader thread.
      */
     virtual void notifyConfigurationChanged(nsecs_t eventTime) = 0;
-    virtual void notifyAppSwitchComing(nsecs_t eventTime) = 0;
     virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
             int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
@@ -219,6 +343,24 @@
     virtual int32_t injectInputEvent(const InputEvent* event,
             int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
 
+    /* Sets the list of input windows.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void setInputWindows(const Vector<InputWindow>& inputWindows) = 0;
+
+    /* Sets the focused application.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void setFocusedApplication(const InputApplication* inputApplication) = 0;
+
+    /* Sets the input dispatching mode.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
+
     /* Preempts input dispatch in progress by making pending synchronous
      * dispatches asynchronous instead.  This method is generally called during a focus
      * transition from one application to the next so as to enable the new application
@@ -230,10 +372,11 @@
     virtual void preemptInputDispatch() = 0;
 
     /* Registers or unregister input channels that may be used as targets for input events.
+     * If monitor is true, the channel will receive a copy of all input events.
      *
      * These methods may be called on any thread (usually by the input manager).
      */
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
+    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) = 0;
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
 };
 
@@ -261,10 +404,11 @@
 public:
     explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
 
+    virtual void dump(String8& dump);
+
     virtual void dispatchOnce();
 
     virtual void notifyConfigurationChanged(nsecs_t eventTime);
-    virtual void notifyAppSwitchComing(nsecs_t eventTime);
     virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
             int32_t scanCode, int32_t metaState, nsecs_t downTime);
@@ -277,9 +421,12 @@
     virtual int32_t injectInputEvent(const InputEvent* event,
             int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
 
+    virtual void setInputWindows(const Vector<InputWindow>& inputWindows);
+    virtual void setFocusedApplication(const InputApplication* inputApplication);
+    virtual void setInputDispatchMode(bool enabled, bool frozen);
     virtual void preemptInputDispatch();
 
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
+    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
 private:
@@ -310,6 +457,8 @@
         int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress
 
         inline bool isInjected() { return injectorPid >= 0; }
+
+        void recycle();
     };
 
     struct ConfigurationChangedEntry : EventEntry {
@@ -326,6 +475,17 @@
         int32_t metaState;
         int32_t repeatCount;
         nsecs_t downTime;
+
+        bool syntheticRepeat; // set to true for synthetic key repeats
+
+        enum InterceptKeyResult {
+            INTERCEPT_KEY_RESULT_UNKNOWN,
+            INTERCEPT_KEY_RESULT_SKIP,
+            INTERCEPT_KEY_RESULT_CONTINUE,
+        };
+        InterceptKeyResult interceptKeyResult; // set based on the interception result
+
+        void recycle();
     };
 
     struct MotionSample {
@@ -380,9 +540,13 @@
         //   will be set to NULL.
         MotionSample* tailMotionSample;
 
-        inline bool isSyncTarget() {
+        inline bool isSyncTarget() const {
             return targetFlags & InputTarget::FLAG_SYNC;
         }
+
+        inline void preemptSyncTarget() {
+            targetFlags &= ~ InputTarget::FLAG_SYNC;
+        }
     };
 
     // A command entry captures state and behavior for an action to be performed in the
@@ -413,37 +577,43 @@
 
         // parameters for the command (usage varies by command)
         sp<Connection> connection;
+        nsecs_t eventTime;
+        KeyEntry* keyEntry;
+        sp<InputChannel> inputChannel;
+        sp<InputApplicationHandle> inputApplicationHandle;
+        int32_t windowType;
+        int32_t userActivityEventType;
     };
 
     // Generic queue implementation.
     template <typename T>
     struct Queue {
-        T head;
-        T tail;
+        T headSentinel;
+        T tailSentinel;
 
         inline Queue() {
-            head.prev = NULL;
-            head.next = & tail;
-            tail.prev = & head;
-            tail.next = NULL;
+            headSentinel.prev = NULL;
+            headSentinel.next = & tailSentinel;
+            tailSentinel.prev = & headSentinel;
+            tailSentinel.next = NULL;
         }
 
-        inline bool isEmpty() {
-            return head.next == & tail;
+        inline bool isEmpty() const {
+            return headSentinel.next == & tailSentinel;
         }
 
         inline void enqueueAtTail(T* entry) {
-            T* last = tail.prev;
+            T* last = tailSentinel.prev;
             last->next = entry;
             entry->prev = last;
-            entry->next = & tail;
-            tail.prev = entry;
+            entry->next = & tailSentinel;
+            tailSentinel.prev = entry;
         }
 
         inline void enqueueAtHead(T* entry) {
-            T* first = head.next;
-            head.next = entry;
-            entry->prev = & head;
+            T* first = headSentinel.next;
+            headSentinel.next = entry;
+            entry->prev = & headSentinel;
             entry->next = first;
             first->prev = entry;
         }
@@ -454,7 +624,7 @@
         }
 
         inline T* dequeueAtHead() {
-            T* first = head.next;
+            T* first = headSentinel.next;
             dequeue(first);
             return first;
         }
@@ -476,7 +646,8 @@
                 float xPrecision, float yPrecision,
                 nsecs_t downTime, uint32_t pointerCount,
                 const int32_t* pointerIds, const PointerCoords* pointerCoords);
-        DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry);
+        DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry,
+                int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout);
         CommandEntry* obtainCommandEntry(Command command);
 
         void releaseEventEntry(EventEntry* entry);
@@ -500,6 +671,85 @@
         void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime);
     };
 
+    /* Tracks dispatched key and motion event state so that cancelation events can be
+     * synthesized when events are dropped. */
+    class InputState {
+    public:
+        // Specifies whether a given event will violate input state consistency.
+        enum Consistency {
+            // The event is consistent with the current input state.
+            CONSISTENT,
+            // The event is inconsistent with the current input state but applications
+            // will tolerate it.  eg. Down followed by another down.
+            TOLERABLE,
+            // The event is inconsistent with the current input state and will probably
+            // cause applications to crash.  eg. Up without prior down, move with
+            // unexpected number of pointers.
+            BROKEN
+        };
+
+        InputState();
+        ~InputState();
+
+        // Returns true if there is no state to be canceled.
+        bool isNeutral() const;
+
+        // Returns true if the input state believes it is out of sync.
+        bool isOutOfSync() const;
+
+        // Sets the input state to be out of sync if it is not neutral.
+        void setOutOfSync();
+
+        // Resets the input state out of sync flag.
+        void resetOutOfSync();
+
+        // Records tracking information for an event that has just been published.
+        // Returns whether the event is consistent with the current input state.
+        Consistency trackEvent(const EventEntry* entry);
+
+        // Records tracking information for a key event that has just been published.
+        // Returns whether the event is consistent with the current input state.
+        Consistency trackKey(const KeyEntry* entry);
+
+        // Records tracking information for a motion event that has just been published.
+        // Returns whether the event is consistent with the current input state.
+        Consistency trackMotion(const MotionEntry* entry);
+
+        // Synthesizes cancelation events for the current state.
+        void synthesizeCancelationEvents(Allocator* allocator,
+                Vector<EventEntry*>& outEvents) const;
+
+        // Clears the current state.
+        void clear();
+
+    private:
+        bool mIsOutOfSync;
+
+        struct KeyMemento {
+            int32_t deviceId;
+            int32_t source;
+            int32_t keyCode;
+            int32_t scanCode;
+            nsecs_t downTime;
+        };
+
+        struct MotionMemento {
+            int32_t deviceId;
+            int32_t source;
+            float xPrecision;
+            float yPrecision;
+            nsecs_t downTime;
+            uint32_t pointerCount;
+            int32_t pointerIds[MAX_POINTERS];
+            PointerCoords pointerCoords[MAX_POINTERS];
+
+            void setPointers(const MotionEntry* entry);
+        };
+
+        Vector<KeyMemento> mKeyMementos;
+        Vector<MotionMemento> mMotionMementos;
+    };
+
     /* Manages the dispatch state associated with a single input channel. */
     class Connection : public RefBase {
     protected:
@@ -520,6 +770,7 @@
         Status status;
         sp<InputChannel> inputChannel;
         InputPublisher inputPublisher;
+        InputState inputState;
         Queue<DispatchEntry> outboundQueue;
         nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none)
 
@@ -540,28 +791,34 @@
         // Determine whether this connection has a pending synchronous dispatch target.
         // Since there can only ever be at most one such target at a time, if there is one,
         // it must be at the tail because nothing else can be enqueued after it.
-        inline bool hasPendingSyncTarget() {
-            return ! outboundQueue.isEmpty() && outboundQueue.tail.prev->isSyncTarget();
+        inline bool hasPendingSyncTarget() const {
+            return ! outboundQueue.isEmpty() && outboundQueue.tailSentinel.prev->isSyncTarget();
+        }
+
+        // Assuming there is a pending sync target, make it async.
+        inline void preemptSyncTarget() {
+            outboundQueue.tailSentinel.prev->preemptSyncTarget();
         }
 
         // Gets the time since the current event was originally obtained from the input driver.
-        inline double getEventLatencyMillis(nsecs_t currentTime) {
+        inline double getEventLatencyMillis(nsecs_t currentTime) const {
             return (currentTime - lastEventTime) / 1000000.0;
         }
 
         // Gets the time since the current event entered the outbound dispatch queue.
-        inline double getDispatchLatencyMillis(nsecs_t currentTime) {
+        inline double getDispatchLatencyMillis(nsecs_t currentTime) const {
             return (currentTime - lastDispatchTime) / 1000000.0;
         }
 
         // Gets the time since the current event ANR was declared, if applicable.
-        inline double getANRLatencyMillis(nsecs_t currentTime) {
+        inline double getANRLatencyMillis(nsecs_t currentTime) const {
             return (currentTime - lastANRTime) / 1000000.0;
         }
 
         status_t initialize();
 
         void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout);
+        void resetTimeout(nsecs_t currentTime);
     };
 
     sp<InputDispatcherPolicyInterface> mPolicy;
@@ -571,9 +828,26 @@
     Allocator mAllocator;
     sp<PollLoop> mPollLoop;
 
+    EventEntry* mPendingEvent;
     Queue<EventEntry> mInboundQueue;
     Queue<CommandEntry> mCommandQueue;
 
+    Vector<EventEntry*> mTempCancelationEvents;
+
+    void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay,
+            nsecs_t* nextWakeupTime);
+
+    // Enqueues an inbound event.  Returns true if mPollLoop->wake() should be called.
+    bool enqueueInboundEventLocked(EventEntry* entry);
+
+    // App switch latency optimization.
+    nsecs_t mAppSwitchDueTime;
+
+    static bool isAppSwitchKey(int32_t keyCode);
+    bool isAppSwitchPendingLocked();
+    bool detectPendingAppSwitchLocked(KeyEntry* inboundKeyEntry);
+    void resetPendingAppSwitchLocked(bool handled);
+
     // All registered connections mapped by receive pipe file descriptor.
     KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
 
@@ -591,20 +865,15 @@
     // the duration.
     Vector<Connection*> mTimedOutConnections;
 
-    // Preallocated key and motion event objects used only to ask the input dispatcher policy
-    // for the targets of an event that is to be dispatched.
-    KeyEvent mReusableKeyEvent;
-    MotionEvent mReusableMotionEvent;
+    // Input channels that will receive a copy of all input events.
+    Vector<sp<InputChannel> > mMonitoringChannels;
 
-    // The input targets that were most recently identified for dispatch.
-    // If there is a synchronous event dispatch in progress, the current input targets will
-    // remain unchanged until the dispatch has completed or been aborted.
-    Vector<InputTarget> mCurrentInputTargets;
-    bool mCurrentInputTargetsValid; // false while targets are being recomputed
+    // Preallocated key event object used for policy inquiries.
+    KeyEvent mReusableKeyEvent;
 
     // Event injection and synchronization.
     Condition mInjectionResultAvailableCondition;
-    EventEntry* createEntryFromInputEventLocked(const InputEvent* event);
+    EventEntry* createEntryFromInjectedInputEventLocked(const InputEvent* event);
     void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
 
     Condition mInjectionSyncFinishedCondition;
@@ -622,36 +891,108 @@
     } mThrottleState;
 
     // Key repeat tracking.
-    // XXX Move this up to the input reader instead.
     struct KeyRepeatState {
         KeyEntry* lastKeyEntry; // or null if no repeat
         nsecs_t nextRepeatTime;
     } mKeyRepeatState;
 
     void resetKeyRepeatLocked();
+    KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime, nsecs_t keyRepeatTimeout);
 
     // Deferred command processing.
     bool runCommandsLockedInterruptible();
     CommandEntry* postCommandLocked(Command command);
 
-    // Process events that have just been dequeued from the head of the input queue.
-    void processConfigurationChangedLockedInterruptible(
-            nsecs_t currentTime, ConfigurationChangedEntry* entry);
-    void processKeyLockedInterruptible(
-            nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout);
-    void processKeyRepeatLockedInterruptible(
-            nsecs_t currentTime, nsecs_t keyRepeatTimeout);
-    void processMotionLockedInterruptible(
-            nsecs_t currentTime, MotionEntry* entry);
+    // Inbound event processing.
+    void drainInboundQueueLocked();
+    void releasePendingEventLocked(bool wasDropped);
+    void releaseInboundEventLocked(EventEntry* entry, bool wasDropped);
+    bool isEventFromReliableSourceLocked(EventEntry* entry);
 
-    // Identify input targets for an event and dispatch to them.
-    void identifyInputTargetsAndDispatchKeyLockedInterruptible(
-            nsecs_t currentTime, KeyEntry* entry);
-    void identifyInputTargetsAndDispatchMotionLockedInterruptible(
-            nsecs_t currentTime, MotionEntry* entry);
+    // Dispatch state.
+    bool mDispatchEnabled;
+    bool mDispatchFrozen;
+    Vector<InputWindow> mWindows;
+    Vector<InputWindow*> mWallpaperWindows;
+
+    // Focus tracking for keys, trackball, etc.
+    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;
+    };
+    Vector<OutsideTarget> mTempTouchedOutsideTargets; // temporary outside touch targets
+    Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
+
+    // Focused application.
+    InputApplication* mFocusedApplication;
+    InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
+    void releaseFocusedApplicationLocked();
+
+    // Dispatch inbound events.
+    bool dispatchConfigurationChangedLocked(
+            nsecs_t currentTime, ConfigurationChangedEntry* entry);
+    bool dispatchKeyLocked(
+            nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
+            nsecs_t* nextWakeupTime);
+    bool dispatchMotionLocked(
+            nsecs_t currentTime, MotionEntry* entry,
+            nsecs_t* nextWakeupTime);
     void dispatchEventToCurrentInputTargetsLocked(
             nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample);
 
+    void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry);
+    void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry);
+
+    // The input targets that were most recently identified for dispatch.
+    // If there is a synchronous event dispatch in progress, the current input targets will
+    // remain unchanged until the dispatch has completed or been aborted.
+    bool mCurrentInputTargetsValid; // false while targets are being recomputed
+    Vector<InputTarget> mCurrentInputTargets;
+    int32_t mCurrentInputWindowType;
+    sp<InputChannel> mCurrentInputChannel;
+
+    enum InputTargetWaitCause {
+        INPUT_TARGET_WAIT_CAUSE_NONE,
+        INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
+        INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
+    };
+
+    InputTargetWaitCause mInputTargetWaitCause;
+    nsecs_t mInputTargetWaitStartTime;
+    nsecs_t mInputTargetWaitTimeoutTime;
+    bool mInputTargetWaitTimeoutExpired;
+
+    // Finding targets for input events.
+    void startFindingTargetsLocked();
+    void finishFindingTargetsLocked(const InputWindow* window);
+    int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
+            const InputApplication* application, const InputWindow* window,
+            nsecs_t* nextWakeupTime);
+    void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout);
+    nsecs_t getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(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);
+
+    void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+            nsecs_t timeSpentWaitingForApplication);
+    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 isWindowObscuredLocked(const InputWindow* window);
+    void releaseTouchedWindowLocked();
+
     // Manage the dispatch cycle for a single connection.
     // These methods are deliberately not Interruptible because doing all of the work
     // with the mutex held makes it easier to ensure that connection invariants are maintained.
@@ -659,15 +1000,25 @@
     void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             EventEntry* eventEntry, const InputTarget* inputTarget,
             bool resumeWithAppendedMotionSample);
-    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+            nsecs_t timeSpentWaitingForApplication);
     void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+    void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
     void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
     void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
             const sp<Connection>& connection, nsecs_t newTimeout);
     void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             bool broken);
+    void drainOutboundQueueLocked(Connection* connection, DispatchEntry* firstDispatchEntryToDrain);
     static bool handleReceiveCallback(int receiveFd, int events, void* data);
 
+    // Preempting input dispatch.
+    bool preemptInputDispatchInnerLocked();
+
+    // Dump state.
+    void dumpDispatchStateLocked(String8& dump);
+    void logDispatchStateLocked();
+
     // Add or remove a connection to the mActiveConnections vector.
     void activateConnectionLocked(Connection* connection);
     void deactivateConnectionLocked(Connection* connection);
@@ -683,9 +1034,13 @@
             nsecs_t currentTime, const sp<Connection>& connection);
 
     // Outbound policy interactions.
+    void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
     void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
     void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry);
     void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry);
+    void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
+    void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry);
+    void doTargetsNotReadyTimeoutLockedInterruptible(CommandEntry* commandEntry);
 };
 
 /* Enqueues and dispatches input events, endlessly. */
@@ -702,4 +1057,4 @@
 
 } // namespace android
 
-#endif // _UI_INPUT_DISPATCHER_PRIV_H
+#endif // _UI_INPUT_DISPATCHER_H
diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h
index 4012c69..568568b 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -72,51 +72,11 @@
     /* Stops the input manager threads and waits for them to exit. */
     virtual status_t stop() = 0;
 
-    /* Registers an input channel prior to using it as the target of an event. */
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
+    /* Gets the input reader. */
+    virtual sp<InputReaderInterface> getReader() = 0;
 
-    /* Unregisters an input channel. */
-    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
-
-    /* Injects an input event and optionally waits for sync.
-     * The synchronization mode determines whether the method blocks while waiting for
-     * input injection to proceed.
-     * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
-     */
-    virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
-
-    /* Preempts input dispatch in progress by making pending synchronous
-     * dispatches asynchronous instead.  This method is generally called during a focus
-     * transition from one application to the next so as to enable the new application
-     * to start receiving input as soon as possible without having to wait for the
-     * old application to finish up.
-     */
-    virtual void preemptInputDispatch() = 0;
-
-    /* Gets input device configuration. */
-    virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0;
-
-    /* Gets information about the specified input device.
-     * Returns OK if the device information was obtained or NAME_NOT_FOUND if there
-     * was no such device.
-     */
-    virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0;
-
-    /* Gets the list of all registered device ids. */
-    virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0;
-
-    /* Queries current input state. */
-    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t scanCode) = 0;
-    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t keyCode) = 0;
-    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
-            int32_t sw) = 0;
-
-    /* Determines whether physical keys exist for the given framework-domain key codes. */
-    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
-            size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
+    /* Gets the input dispatcher. */
+    virtual sp<InputDispatcherInterface> getDispatcher() = 0;
 };
 
 class InputManager : public InputManagerInterface {
@@ -137,25 +97,8 @@
     virtual status_t start();
     virtual status_t stop();
 
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
-    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
-
-    virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
-
-    virtual void preemptInputDispatch();
-
-    virtual void getInputConfiguration(InputConfiguration* outConfiguration);
-    virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
-    virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
-    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t scanCode);
-    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t keyCode);
-    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
-            int32_t sw);
-    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
-            size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
+    virtual sp<InputReaderInterface> getReader();
+    virtual sp<InputDispatcherInterface> getDispatcher();
 
 private:
     sp<InputReaderInterface> mReader;
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 7a089a45..903c3c4 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -95,10 +95,6 @@
 
         // The input dispatcher should dispatch the input to the application.
         ACTION_DISPATCH = 0x00000001,
-
-        // The input dispatcher should perform special filtering in preparation for
-        // a pending app switch.
-        ACTION_APP_SWITCH_COMING = 0x00000002,
     };
 
     /* Gets information about the display with the specified id.
@@ -168,6 +164,11 @@
     virtual ~InputReaderInterface() { }
 
 public:
+    /* Dumps the state of the input reader.
+     *
+     * This method may be called on any thread (usually by the input manager). */
+    virtual void dump(String8& dump) = 0;
+
     /* Runs a single iteration of the processing loop.
      * Nominally reads and processes one incoming message from the EventHub.
      *
@@ -240,6 +241,8 @@
             const sp<InputDispatcherInterface>& dispatcher);
     virtual ~InputReader();
 
+    virtual void dump(String8& dump);
+
     virtual void loopOnce();
 
     virtual void getInputConfiguration(InputConfiguration* outConfiguration);
@@ -305,6 +308,9 @@
             GetStateFunc getStateFunc);
     bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags);
+
+    // dump state
+    void dumpDeviceInfo(String8& dump);
 };
 
 
@@ -759,9 +765,11 @@
     } mLocked;
 
     virtual void configureParameters();
+    virtual void logParameters();
     virtual void configureRawAxes();
     virtual void logRawAxes();
     virtual bool configureSurfaceLocked();
+    virtual void logMotionRangesLocked();
     virtual void configureVirtualKeysLocked();
     virtual void parseCalibration();
     virtual void resolveCalibration();
diff --git a/include/ui/PowerManager.h b/include/ui/PowerManager.h
new file mode 100644
index 0000000..5434b4f
--- /dev/null
+++ b/include/ui/PowerManager.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_POWER_MANAGER_H
+#define _UI_POWER_MANAGER_H
+
+
+namespace android {
+
+enum {
+    POWER_MANAGER_OTHER_EVENT = 0,
+    POWER_MANAGER_CHEEK_EVENT = 1,
+    POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either
+                                   // up events or LONG_TOUCH events.
+    POWER_MANAGER_LONG_TOUCH_EVENT = 3,
+    POWER_MANAGER_TOUCH_UP_EVENT = 4,
+    POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+
+    POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code.
+};
+
+} // namespace android
+
+#endif // _UI_POWER_MANAGER_H
diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h
index bc616eb..c2dfe5d 100644
--- a/include/utils/PollLoop.h
+++ b/include/utils/PollLoop.h
@@ -172,22 +172,36 @@
         void* data;
     };
     
-    const bool mAllowNonCallbacks;
-    
+    const bool mAllowNonCallbacks; // immutable
+
+    int mWakeReadPipeFd;  // immutable
+    int mWakeWritePipeFd; // immutable
+
+    // The lock guards state used to track whether there is a poll() in progress and whether
+    // there are any other threads waiting in wakeAndLock().  The condition variables
+    // are used to transfer control among these threads such that all waiters are
+    // serviced before a new poll can begin.
+    // The wakeAndLock() method increments mWaiters, wakes the poll, blocks on mAwake
+    // until mPolling becomes false, then decrements mWaiters again.
+    // The poll() method blocks on mResume until mWaiters becomes 0, then sets
+    // mPolling to true, blocks until the poll completes, then resets mPolling to false
+    // and signals mResume if there are waiters.
     Mutex mLock;
-    bool mPolling;
-    uint32_t mWaiters;
-    Condition mAwake;
-    Condition mResume;
+    bool mPolling;      // guarded by mLock
+    uint32_t mWaiters;  // guarded by mLock
+    Condition mAwake;   // guarded by mLock
+    Condition mResume;  // guarded by mLock
 
-    int mWakeReadPipeFd;
-    int mWakeWritePipeFd;
-
+    // The next two vectors are only mutated when mPolling is false since they must
+    // not be changed while the poll() system call is in progress.  To mutate these
+    // vectors, the poll() must first be awoken then the lock acquired.
     Vector<struct pollfd> mRequestedFds;
     Vector<RequestedCallback> mRequestedCallbacks;
 
-    Vector<PendingCallback> mPendingCallbacks; // used privately by pollOnce
-    Vector<PendingCallback> mPendingFds;       // used privately by pollOnce
+    // This state is only used privately by pollOnce and does not require a lock since
+    // it runs on a single thread.
+    Vector<PendingCallback> mPendingCallbacks;
+    Vector<PendingCallback> mPendingFds;
     size_t mPendingFdsPos;
     
     void openWakePipe();
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index df232d4..b8a26b0 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -31,8 +31,15 @@
 // Log debug messages about input event throttling.
 #define DEBUG_THROTTLING 0
 
+// Log debug messages about input focus tracking.
+#define DEBUG_FOCUS 0
+
+// Log debug messages about the app switch latency optimization.
+#define DEBUG_APP_SWITCH 0
+
 #include <cutils/log.h>
 #include <ui/InputDispatcher.h>
+#include <ui/PowerManager.h>
 
 #include <stddef.h>
 #include <unistd.h>
@@ -41,31 +48,62 @@
 
 namespace android {
 
-// TODO, this needs to be somewhere else, perhaps in the policy
-static inline bool isMovementKey(int32_t keyCode) {
-    return keyCode == AKEYCODE_DPAD_UP
-            || keyCode == AKEYCODE_DPAD_DOWN
-            || keyCode == AKEYCODE_DPAD_LEFT
-            || keyCode == AKEYCODE_DPAD_RIGHT;
-}
+// Delay between reporting long touch events to the power manager.
+const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
+
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+
+// Amount of time to allow for all pending events to be processed when an app switch
+// key is on the way.  This is used to preempt input dispatch and drop input events
+// when an application takes too long to respond and the user has pressed an app switch key.
+const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
+
 
 static inline nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
+static inline const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
+
+// --- InputWindow ---
+
+bool InputWindow::visibleFrameIntersects(const InputWindow* other) const {
+    return visibleFrameRight > other->visibleFrameLeft
+        && visibleFrameLeft < other->visibleFrameRight
+        && visibleFrameBottom > other->visibleFrameTop
+        && visibleFrameTop < other->visibleFrameBottom;
+}
+
+bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
+    return x >= touchableAreaLeft && x <= touchableAreaRight
+            && y >= touchableAreaTop && y <= touchableAreaBottom;
+}
+
+
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
-    mPolicy(policy) {
+    mPolicy(policy),
+    mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX),
+    mDispatchEnabled(true), mDispatchFrozen(false),
+    mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
+    mFocusedApplication(NULL),
+    mCurrentInputTargetsValid(false),
+    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
     mPollLoop = new PollLoop(false);
 
-    mInboundQueue.head.refCount = -1;
-    mInboundQueue.head.type = EventEntry::TYPE_SENTINEL;
-    mInboundQueue.head.eventTime = LONG_LONG_MIN;
+    mInboundQueue.headSentinel.refCount = -1;
+    mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL;
+    mInboundQueue.headSentinel.eventTime = LONG_LONG_MIN;
 
-    mInboundQueue.tail.refCount = -1;
-    mInboundQueue.tail.type = EventEntry::TYPE_SENTINEL;
-    mInboundQueue.tail.eventTime = LONG_LONG_MAX;
+    mInboundQueue.tailSentinel.refCount = -1;
+    mInboundQueue.tailSentinel.type = EventEntry::TYPE_SENTINEL;
+    mInboundQueue.tailSentinel.eventTime = LONG_LONG_MAX;
 
     mKeyRepeatState.lastKeyEntry = NULL;
 
@@ -77,189 +115,302 @@
     mThrottleState.originalSampleCount = 0;
     LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
 #endif
-
-    mCurrentInputTargetsValid = false;
 }
 
 InputDispatcher::~InputDispatcher() {
-    resetKeyRepeatLocked();
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        resetKeyRepeatLocked();
+        releasePendingEventLocked(true);
+        drainInboundQueueLocked();
+    }
 
     while (mConnectionsByReceiveFd.size() != 0) {
         unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel);
     }
-
-    for (EventEntry* entry = mInboundQueue.head.next; entry != & mInboundQueue.tail; ) {
-        EventEntry* next = entry->next;
-        mAllocator.releaseEventEntry(next);
-        entry = next;
-    }
 }
 
 void InputDispatcher::dispatchOnce() {
     nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
     nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
 
-    bool skipPoll = false;
-    nsecs_t currentTime;
     nsecs_t nextWakeupTime = LONG_LONG_MAX;
     { // acquire lock
         AutoMutex _l(mLock);
-        currentTime = now();
+        dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
 
-        // Reset the key repeat timer whenever we disallow key events, even if the next event
-        // is not a key.  This is to ensure that we abort a key repeat if the device is just coming
-        // out of sleep.
-        // XXX we should handle resetting input state coming out of sleep more generally elsewhere
-        if (keyRepeatTimeout < 0) {
-            resetKeyRepeatLocked();
+        if (runCommandsLockedInterruptible()) {
+            nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
         }
-
-        // Detect and process timeouts for all connections and determine if there are any
-        // synchronous event dispatches pending.  This step is entirely non-interruptible.
-        bool hasPendingSyncTarget = false;
-        size_t activeConnectionCount = mActiveConnections.size();
-        for (size_t i = 0; i < activeConnectionCount; i++) {
-            Connection* connection = mActiveConnections.itemAt(i);
-
-            if (connection->hasPendingSyncTarget()) {
-                hasPendingSyncTarget = true;
-            }
-
-            nsecs_t connectionTimeoutTime  = connection->nextTimeoutTime;
-            if (connectionTimeoutTime <= currentTime) {
-                mTimedOutConnections.add(connection);
-            } else if (connectionTimeoutTime < nextWakeupTime) {
-                nextWakeupTime = connectionTimeoutTime;
-            }
-        }
-
-        size_t timedOutConnectionCount = mTimedOutConnections.size();
-        for (size_t i = 0; i < timedOutConnectionCount; i++) {
-            Connection* connection = mTimedOutConnections.itemAt(i);
-            timeoutDispatchCycleLocked(currentTime, connection);
-            skipPoll = true;
-        }
-        mTimedOutConnections.clear();
-
-        // If we don't have a pending sync target, then we can begin delivering a new event.
-        // (Otherwise we wait for dispatch to complete for that target.)
-        if (! hasPendingSyncTarget) {
-            if (mInboundQueue.isEmpty()) {
-                if (mKeyRepeatState.lastKeyEntry) {
-                    if (currentTime >= mKeyRepeatState.nextRepeatTime) {
-                        processKeyRepeatLockedInterruptible(currentTime, keyRepeatDelay);
-                        skipPoll = true;
-                    } else {
-                        if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) {
-                            nextWakeupTime = mKeyRepeatState.nextRepeatTime;
-                        }
-                    }
-                }
-            } else {
-                // Inbound queue has at least one entry.
-                EventEntry* entry = mInboundQueue.head.next;
-
-                // Consider throttling the entry if it is a move event and there are no
-                // other events behind it in the queue.  Due to movement batching, additional
-                // samples may be appended to this event by the time the throttling timeout
-                // expires.
-                // TODO Make this smarter and consider throttling per device independently.
-                if (entry->type == EventEntry::TYPE_MOTION) {
-                    MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-                    int32_t deviceId = motionEntry->deviceId;
-                    uint32_t source = motionEntry->source;
-                    if (motionEntry->next == & mInboundQueue.tail
-                            && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
-                            && deviceId == mThrottleState.lastDeviceId
-                            && source == mThrottleState.lastSource) {
-                        nsecs_t nextTime = mThrottleState.lastEventTime
-                                + mThrottleState.minTimeBetweenEvents;
-                        if (currentTime < nextTime) {
-                            // Throttle it!
-#if DEBUG_THROTTLING
-                            LOGD("Throttling - Delaying motion event for "
-                                    "device 0x%x, source 0x%08x by up to %0.3fms.",
-                                    deviceId, source, (nextTime - currentTime) * 0.000001);
-#endif
-                            if (nextTime < nextWakeupTime) {
-                                nextWakeupTime = nextTime;
-                            }
-                            if (mThrottleState.originalSampleCount == 0) {
-                                mThrottleState.originalSampleCount =
-                                        motionEntry->countSamples();
-                            }
-                            goto Throttle;
-                        }
-                    }
-
-#if DEBUG_THROTTLING
-                    if (mThrottleState.originalSampleCount != 0) {
-                        uint32_t count = motionEntry->countSamples();
-                        LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
-                                count - mThrottleState.originalSampleCount,
-                                mThrottleState.originalSampleCount, count);
-                        mThrottleState.originalSampleCount = 0;
-                    }
-#endif
-
-                    mThrottleState.lastEventTime = entry->eventTime < currentTime
-                            ? entry->eventTime : currentTime;
-                    mThrottleState.lastDeviceId = deviceId;
-                    mThrottleState.lastSource = source;
-                }
-
-                // Start processing the entry but leave it on the queue until later so that the
-                // input reader can keep appending samples onto a motion event between the
-                // time we started processing it and the time we finally enqueue dispatch
-                // entries for it.
-                switch (entry->type) {
-                case EventEntry::TYPE_CONFIGURATION_CHANGED: {
-                    ConfigurationChangedEntry* typedEntry =
-                            static_cast<ConfigurationChangedEntry*>(entry);
-                    processConfigurationChangedLockedInterruptible(currentTime, typedEntry);
-                    break;
-                }
-
-                case EventEntry::TYPE_KEY: {
-                    KeyEntry* typedEntry = static_cast<KeyEntry*>(entry);
-                    processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout);
-                    break;
-                }
-
-                case EventEntry::TYPE_MOTION: {
-                    MotionEntry* typedEntry = static_cast<MotionEntry*>(entry);
-                    processMotionLockedInterruptible(currentTime, typedEntry);
-                    break;
-                }
-
-                default:
-                    assert(false);
-                    break;
-                }
-
-                // Dequeue and release the event entry that we just processed.
-                mInboundQueue.dequeue(entry);
-                mAllocator.releaseEventEntry(entry);
-                skipPoll = true;
-
-            Throttle: ;
-            }
-        }
-
-        // Run any deferred commands.
-        skipPoll |= runCommandsLockedInterruptible();
     } // release lock
 
-    // If we dispatched anything, don't poll just now.  Wait for the next iteration.
-    // Contents may have shifted during flight.
-    if (skipPoll) {
+    // Wait for callback or timeout or wake.  (make sure we round up, not down)
+    nsecs_t currentTime = now();
+    int32_t timeoutMillis;
+    if (nextWakeupTime > currentTime) {
+        uint64_t timeout = uint64_t(nextWakeupTime - currentTime);
+        timeout = (timeout + 999999LL) / 1000000LL;
+        timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);
+    } else {
+        timeoutMillis = 0;
+    }
+
+    mPollLoop->pollOnce(timeoutMillis);
+}
+
+void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
+        nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {
+    nsecs_t currentTime = now();
+
+    // Reset the key repeat timer whenever we disallow key events, even if the next event
+    // is not a key.  This is to ensure that we abort a key repeat if the device is just coming
+    // out of sleep.
+    if (keyRepeatTimeout < 0) {
+        resetKeyRepeatLocked();
+    }
+
+    // If dispatching is disabled, drop all events in the queue.
+    if (! mDispatchEnabled) {
+        if (mPendingEvent || ! mInboundQueue.isEmpty()) {
+            LOGI("Dropping pending events because input dispatch is disabled.");
+            releasePendingEventLocked(true);
+            drainInboundQueueLocked();
+        }
         return;
     }
 
-    // Wait for callback or timeout or wake.  (make sure we round up, not down)
-    nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL;
-    int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
-    mPollLoop->pollOnce(timeoutMillis);
+    // If dispatching is frozen, do not process timeouts or try to deliver any new events.
+    if (mDispatchFrozen) {
+#if DEBUG_FOCUS
+        LOGD("Dispatch frozen.  Waiting some more.");
+#endif
+        return;
+    }
+
+    // Optimize latency of app switches.
+    // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
+    // been pressed.  When it expires, we preempt dispatch and drop all other pending events.
+    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
+    if (mAppSwitchDueTime < *nextWakeupTime) {
+        *nextWakeupTime = mAppSwitchDueTime;
+    }
+
+    // Detect and process timeouts for all connections and determine if there are any
+    // synchronous event dispatches pending.  This step is entirely non-interruptible.
+    bool havePendingSyncTarget = false;
+    size_t activeConnectionCount = mActiveConnections.size();
+    for (size_t i = 0; i < activeConnectionCount; i++) {
+        Connection* connection = mActiveConnections.itemAt(i);
+
+        if (connection->hasPendingSyncTarget()) {
+            if (isAppSwitchDue) {
+                connection->preemptSyncTarget();
+            } else {
+                havePendingSyncTarget = true;
+            }
+        }
+
+        nsecs_t connectionTimeoutTime  = connection->nextTimeoutTime;
+        if (connectionTimeoutTime <= currentTime) {
+            mTimedOutConnections.add(connection);
+        } else if (connectionTimeoutTime < *nextWakeupTime) {
+            *nextWakeupTime = connectionTimeoutTime;
+        }
+    }
+
+    size_t timedOutConnectionCount = mTimedOutConnections.size();
+    for (size_t i = 0; i < timedOutConnectionCount; i++) {
+        Connection* connection = mTimedOutConnections.itemAt(i);
+        timeoutDispatchCycleLocked(currentTime, connection);
+        *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
+    }
+    mTimedOutConnections.clear();
+
+    // If we have a pending synchronous target, skip dispatch.
+    if (havePendingSyncTarget) {
+        return;
+    }
+
+    // Ready to start a new event.
+    // If we don't already have a pending event, go grab one.
+    if (! mPendingEvent) {
+        if (mInboundQueue.isEmpty()) {
+            if (isAppSwitchDue) {
+                // The inbound queue is empty so the app switch key we were waiting
+                // for will never arrive.  Stop waiting for it.
+                resetPendingAppSwitchLocked(false);
+                isAppSwitchDue = false;
+            }
+
+            // Synthesize a key repeat if appropriate.
+            if (mKeyRepeatState.lastKeyEntry) {
+                if (currentTime >= mKeyRepeatState.nextRepeatTime) {
+                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime, keyRepeatDelay);
+                } else {
+                    if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
+                        *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
+                    }
+                }
+            }
+            if (! mPendingEvent) {
+                return;
+            }
+        } else {
+            // Inbound queue has at least one entry.
+            EventEntry* entry = mInboundQueue.headSentinel.next;
+
+            // Throttle the entry if it is a move event and there are no
+            // other events behind it in the queue.  Due to movement batching, additional
+            // samples may be appended to this event by the time the throttling timeout
+            // expires.
+            // TODO Make this smarter and consider throttling per device independently.
+            if (entry->type == EventEntry::TYPE_MOTION) {
+                MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+                int32_t deviceId = motionEntry->deviceId;
+                uint32_t source = motionEntry->source;
+                if (! isAppSwitchDue
+                        && motionEntry->next == & mInboundQueue.tailSentinel // exactly one event
+                        && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
+                        && deviceId == mThrottleState.lastDeviceId
+                        && source == mThrottleState.lastSource) {
+                    nsecs_t nextTime = mThrottleState.lastEventTime
+                            + mThrottleState.minTimeBetweenEvents;
+                    if (currentTime < nextTime) {
+                        // Throttle it!
+#if DEBUG_THROTTLING
+                        LOGD("Throttling - Delaying motion event for "
+                                "device 0x%x, source 0x%08x by up to %0.3fms.",
+                                deviceId, source, (nextTime - currentTime) * 0.000001);
+#endif
+                        if (nextTime < *nextWakeupTime) {
+                            *nextWakeupTime = nextTime;
+                        }
+                        if (mThrottleState.originalSampleCount == 0) {
+                            mThrottleState.originalSampleCount =
+                                    motionEntry->countSamples();
+                        }
+                        return;
+                    }
+                }
+
+#if DEBUG_THROTTLING
+                if (mThrottleState.originalSampleCount != 0) {
+                    uint32_t count = motionEntry->countSamples();
+                    LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
+                            count - mThrottleState.originalSampleCount,
+                            mThrottleState.originalSampleCount, count);
+                    mThrottleState.originalSampleCount = 0;
+                }
+#endif
+
+                mThrottleState.lastEventTime = entry->eventTime < currentTime
+                        ? entry->eventTime : currentTime;
+                mThrottleState.lastDeviceId = deviceId;
+                mThrottleState.lastSource = source;
+            }
+
+            mInboundQueue.dequeue(entry);
+            mPendingEvent = entry;
+        }
+    }
+
+    // Now we have an event to dispatch.
+    assert(mPendingEvent != NULL);
+    bool wasDispatched = false;
+    bool wasDropped = false;
+    switch (mPendingEvent->type) {
+    case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+        ConfigurationChangedEntry* typedEntry =
+                static_cast<ConfigurationChangedEntry*>(mPendingEvent);
+        wasDispatched = dispatchConfigurationChangedLocked(currentTime, typedEntry);
+        break;
+    }
+
+    case EventEntry::TYPE_KEY: {
+        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
+        if (isAppSwitchPendingLocked()) {
+            if (isAppSwitchKey(typedEntry->keyCode)) {
+                resetPendingAppSwitchLocked(true);
+            } else if (isAppSwitchDue) {
+                LOGI("Dropping key because of pending overdue app switch.");
+                wasDropped = true;
+                break;
+            }
+        }
+        wasDispatched = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,
+                nextWakeupTime);
+        break;
+    }
+
+    case EventEntry::TYPE_MOTION: {
+        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
+        if (isAppSwitchDue) {
+            LOGI("Dropping motion because of pending overdue app switch.");
+            wasDropped = true;
+            break;
+        }
+        wasDispatched = dispatchMotionLocked(currentTime, typedEntry, nextWakeupTime);
+        break;
+    }
+
+    default:
+        assert(false);
+        wasDropped = true;
+        break;
+    }
+
+    if (wasDispatched || wasDropped) {
+        releasePendingEventLocked(wasDropped);
+        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
+    }
+}
+
+bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
+    bool needWake = mInboundQueue.isEmpty();
+    mInboundQueue.enqueueAtTail(entry);
+
+    switch (entry->type) {
+    case EventEntry::TYPE_KEY:
+        needWake |= detectPendingAppSwitchLocked(static_cast<KeyEntry*>(entry));
+        break;
+    }
+
+    return needWake;
+}
+
+bool InputDispatcher::isAppSwitchKey(int32_t keyCode) {
+    return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
+}
+
+bool InputDispatcher::isAppSwitchPendingLocked() {
+    return mAppSwitchDueTime != LONG_LONG_MAX;
+}
+
+bool InputDispatcher::detectPendingAppSwitchLocked(KeyEntry* inboundKeyEntry) {
+    if (inboundKeyEntry->action == AKEY_EVENT_ACTION_UP
+            && ! (inboundKeyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
+            && isAppSwitchKey(inboundKeyEntry->keyCode)
+            && isEventFromReliableSourceLocked(inboundKeyEntry)) {
+#if DEBUG_APP_SWITCH
+        LOGD("App switch is pending!");
+#endif
+        mAppSwitchDueTime = inboundKeyEntry->eventTime + APP_SWITCH_TIMEOUT;
+        return true; // need wake
+    }
+    return false;
+}
+
+void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
+    mAppSwitchDueTime = LONG_LONG_MAX;
+
+#if DEBUG_APP_SWITCH
+    if (handled) {
+        LOGD("App switch has arrived.");
+    } else {
+        LOGD("App switch was abandoned.");
+    }
+#endif
 }
 
 bool InputDispatcher::runCommandsLockedInterruptible() {
@@ -285,78 +436,52 @@
     return commandEntry;
 }
 
-void InputDispatcher::processConfigurationChangedLockedInterruptible(
-        nsecs_t currentTime, ConfigurationChangedEntry* entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime);
-#endif
-
-    // Reset key repeating in case a keyboard device was added or removed or something.
-    resetKeyRepeatLocked();
-
-    mLock.unlock();
-
-    mPolicy->notifyConfigurationChanged(entry->eventTime);
-
-    mLock.lock();
-}
-
-void InputDispatcher::processKeyLockedInterruptible(
-        nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, "
-            "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
-            entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action,
-            entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
-            entry->downTime);
-#endif
-
-    if (entry->action == AKEY_EVENT_ACTION_DOWN && ! entry->isInjected()) {
-        if (mKeyRepeatState.lastKeyEntry
-                && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
-            // We have seen two identical key downs in a row which indicates that the device
-            // driver is automatically generating key repeats itself.  We take note of the
-            // repeat here, but we disable our own next key repeat timer since it is clear that
-            // we will not need to synthesize key repeats ourselves.
-            entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
-            resetKeyRepeatLocked();
-            mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
-        } else {
-            // Not a repeat.  Save key down state in case we do see a repeat later.
-            resetKeyRepeatLocked();
-            mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
-        }
-        mKeyRepeatState.lastKeyEntry = entry;
-        entry->refCount += 1;
-    } else {
-        resetKeyRepeatLocked();
+void InputDispatcher::drainInboundQueueLocked() {
+    while (! mInboundQueue.isEmpty()) {
+        EventEntry* entry = mInboundQueue.dequeueAtHead();
+        releaseInboundEventLocked(entry, true /*wasDropped*/);
     }
-
-    identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
 }
 
-void InputDispatcher::processKeyRepeatLockedInterruptible(
+void InputDispatcher::releasePendingEventLocked(bool wasDropped) {
+    if (mPendingEvent) {
+        releaseInboundEventLocked(mPendingEvent, wasDropped);
+        mPendingEvent = NULL;
+    }
+}
+
+void InputDispatcher::releaseInboundEventLocked(EventEntry* entry, bool wasDropped) {
+    if (wasDropped) {
+#if DEBUG_DISPATCH_CYCLE
+        LOGD("Pending event was dropped.");
+#endif
+        setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+    }
+    mAllocator.releaseEventEntry(entry);
+}
+
+bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) {
+    return ! entry->isInjected()
+            || entry->injectorUid == 0
+            || mPolicy->checkInjectEventsPermissionNonReentrant(
+                    entry->injectorPid, entry->injectorUid);
+}
+
+void InputDispatcher::resetKeyRepeatLocked() {
+    if (mKeyRepeatState.lastKeyEntry) {
+        mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
+        mKeyRepeatState.lastKeyEntry = NULL;
+    }
+}
+
+InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(
         nsecs_t currentTime, nsecs_t keyRepeatDelay) {
     KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
 
-    // Search the inbound queue for a key up corresponding to this device.
-    // It doesn't make sense to generate a key repeat event if the key is already up.
-    for (EventEntry* queuedEntry = mInboundQueue.head.next;
-            queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) {
-        if (queuedEntry->type == EventEntry::TYPE_KEY) {
-            KeyEntry* queuedKeyEntry = static_cast<KeyEntry*>(queuedEntry);
-            if (queuedKeyEntry->deviceId == entry->deviceId
-                    && entry->action == AKEY_EVENT_ACTION_UP) {
-                resetKeyRepeatLocked();
-                return;
-            }
-        }
-    }
-
-    // Synthesize a key repeat.
     // 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();
         entry->eventTime = currentTime;
         entry->policyFlags = policyFlags;
         entry->repeatCount += 1;
@@ -371,31 +496,194 @@
 
         entry = newEntry;
     }
+    entry->syntheticRepeat = true;
+
+    // Increment reference count since we keep a reference to the event in
+    // mKeyRepeatState.lastKeyEntry in addition to the one we return.
+    entry->refCount += 1;
 
     if (entry->repeatCount == 1) {
         entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
     }
 
     mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay;
-
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
-            "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
-            "repeatCount=%d, downTime=%lld",
-            entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
-            entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
-            entry->repeatCount, entry->downTime);
-#endif
-
-    identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
+    return entry;
 }
 
-void InputDispatcher::processMotionLockedInterruptible(
-        nsecs_t currentTime, MotionEntry* entry) {
+bool InputDispatcher::dispatchConfigurationChangedLocked(
+        nsecs_t currentTime, ConfigurationChangedEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+    LOGD("dispatchConfigurationChanged - eventTime=%lld", entry->eventTime);
+#endif
+
+    // Reset key repeating in case a keyboard device was added or removed or something.
+    resetKeyRepeatLocked();
+
+    // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doNotifyConfigurationChangedInterruptible);
+    commandEntry->eventTime = entry->eventTime;
+    return true;
+}
+
+bool InputDispatcher::dispatchKeyLocked(
+        nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
+        nsecs_t* nextWakeupTime) {
+    // Preprocessing.
+    if (! entry->dispatchInProgress) {
+        logOutboundKeyDetailsLocked("dispatchKey - ", entry);
+
+        if (entry->repeatCount == 0
+                && entry->action == AKEY_EVENT_ACTION_DOWN
+                && ! entry->isInjected()) {
+            if (mKeyRepeatState.lastKeyEntry
+                    && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+                // We have seen two identical key downs in a row which indicates that the device
+                // driver is automatically generating key repeats itself.  We take note of the
+                // repeat here, but we disable our own next key repeat timer since it is clear that
+                // we will not need to synthesize key repeats ourselves.
+                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
+                resetKeyRepeatLocked();
+                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
+            } else {
+                // Not a repeat.  Save key down state in case we do see a repeat later.
+                resetKeyRepeatLocked();
+                mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
+            }
+            mKeyRepeatState.lastKeyEntry = entry;
+            entry->refCount += 1;
+        } else if (! entry->syntheticRepeat) {
+            resetKeyRepeatLocked();
+        }
+
+        entry->dispatchInProgress = true;
+        startFindingTargetsLocked();
+    }
+
+    // Identify targets.
+    if (! mCurrentInputTargetsValid) {
+        InputWindow* window = NULL;
+        int32_t injectionResult = findFocusedWindowLocked(currentTime,
+                entry, nextWakeupTime, & window);
+        if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+            return false;
+        }
+
+        setInjectionResultLocked(entry, injectionResult);
+        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+            return true;
+        }
+
+        addMonitoringTargetsLocked();
+        finishFindingTargetsLocked(window);
+    }
+
+    // Give the policy a chance to intercept the key.
+    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
+        CommandEntry* commandEntry = postCommandLocked(
+                & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
+        commandEntry->inputChannel = mCurrentInputChannel;
+        commandEntry->keyEntry = entry;
+        entry->refCount += 1;
+        return false; // wait for the command to run
+    }
+    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
+        return true;
+    }
+
+    // Dispatch the key.
+    dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+
+    // Poke user activity.
+    pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT);
+    return true;
+}
+
+void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+    LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+            "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
+            "downTime=%lld",
+            prefix,
+            entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+            entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
+            entry->downTime);
+#endif
+}
+
+bool InputDispatcher::dispatchMotionLocked(
+        nsecs_t currentTime, MotionEntry* entry, nsecs_t* nextWakeupTime) {
+    // Preprocessing.
+    if (! entry->dispatchInProgress) {
+        logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
+
+        entry->dispatchInProgress = true;
+        startFindingTargetsLocked();
+    }
+
+    bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
+
+    // Identify targets.
+    if (! mCurrentInputTargetsValid) {
+        InputWindow* window = NULL;
+        int32_t injectionResult;
+        if (isPointerEvent) {
+            // Pointer event.  (eg. touchscreen)
+            injectionResult = findTouchedWindowLocked(currentTime,
+                    entry, nextWakeupTime, & window);
+        } else {
+            // Non touch event.  (eg. trackball)
+            injectionResult = findFocusedWindowLocked(currentTime,
+                    entry, nextWakeupTime, & window);
+        }
+        if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+            return false;
+        }
+
+        setInjectionResultLocked(entry, injectionResult);
+        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+            return true;
+        }
+
+        addMonitoringTargetsLocked();
+        finishFindingTargetsLocked(window);
+    }
+
+    // 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) {
+                eventType = POWER_MANAGER_TOUCH_EVENT;
+            } else {
+                eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+            }
+            break;
+        }
+    } else {
+        eventType = POWER_MANAGER_BUTTON_EVENT;
+    }
+    pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType);
+    return true;
+}
+
+
+void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+    LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
             "action=0x%x, flags=0x%x, "
             "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
+            prefix,
             entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
             entry->action, entry->flags,
             entry->metaState, entry->edgeFlags, entry->xPrecision, entry->yPrecision,
@@ -403,7 +691,7 @@
 
     // Print the most recent sample that we have available, this may change due to batching.
     size_t sampleCount = 1;
-    MotionSample* sample = & entry->firstSample;
+    const MotionSample* sample = & entry->firstSample;
     for (; sample->next != NULL; sample = sample->next) {
         sampleCount += 1;
     }
@@ -425,68 +713,6 @@
         LOGD("  ... Total movement samples currently batched %d ...", sampleCount);
     }
 #endif
-
-    identifyInputTargetsAndDispatchMotionLockedInterruptible(currentTime, entry);
-}
-
-void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
-        nsecs_t currentTime, KeyEntry* entry) {
-#if DEBUG_DISPATCH_CYCLE
-    LOGD("identifyInputTargetsAndDispatchKey");
-#endif
-
-    entry->dispatchInProgress = true;
-    mCurrentInputTargetsValid = false;
-    mLock.unlock();
-
-    mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
-            entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
-            entry->downTime, entry->eventTime);
-
-    mCurrentInputTargets.clear();
-    int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent,
-            entry->policyFlags, entry->injectorPid, entry->injectorUid,
-            mCurrentInputTargets);
-
-    mLock.lock();
-    mCurrentInputTargetsValid = true;
-
-    setInjectionResultLocked(entry, injectionResult);
-
-    if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
-        dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
-    }
-}
-
-void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
-        nsecs_t currentTime, MotionEntry* entry) {
-#if DEBUG_DISPATCH_CYCLE
-    LOGD("identifyInputTargetsAndDispatchMotion");
-#endif
-
-    entry->dispatchInProgress = true;
-    mCurrentInputTargetsValid = false;
-    mLock.unlock();
-
-    mReusableMotionEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
-            entry->edgeFlags, entry->metaState,
-            0, 0, entry->xPrecision, entry->yPrecision,
-            entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds,
-            entry->firstSample.pointerCoords);
-
-    mCurrentInputTargets.clear();
-    int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent,
-            entry->policyFlags, entry->injectorPid, entry->injectorUid,
-            mCurrentInputTargets);
-
-    mLock.lock();
-    mCurrentInputTargetsValid = true;
-
-    setInjectionResultLocked(entry, injectionResult);
-
-    if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
-        dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
-    }
 }
 
 void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
@@ -494,7 +720,7 @@
 #if DEBUG_DISPATCH_CYCLE
     LOGD("dispatchEventToCurrentInputTargets - "
             "resumeWithAppendedMotionSample=%s",
-            resumeWithAppendedMotionSample ? "true" : "false");
+            toString(resumeWithAppendedMotionSample));
 #endif
 
     assert(eventEntry->dispatchInProgress); // should already have been set to true
@@ -515,6 +741,515 @@
     }
 }
 
+void InputDispatcher::startFindingTargetsLocked() {
+    mCurrentInputTargetsValid = false;
+    mCurrentInputTargets.clear();
+    mCurrentInputChannel.clear();
+    mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
+}
+
+void InputDispatcher::finishFindingTargetsLocked(const InputWindow* window) {
+    mCurrentInputWindowType = window->layoutParamsType;
+    mCurrentInputChannel = window->inputChannel;
+    mCurrentInputTargetsValid = true;
+}
+
+int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
+        const EventEntry* entry, const InputApplication* application, const InputWindow* window,
+        nsecs_t* nextWakeupTime) {
+    if (application == NULL && window == NULL) {
+        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
+#if DEBUG_FOCUS
+            LOGD("Waiting for system to become ready for input.");
+#endif
+            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
+            mInputTargetWaitStartTime = currentTime;
+            mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
+            mInputTargetWaitTimeoutExpired = false;
+        }
+    } else {
+        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
+#if DEBUG_FOCUS
+            LOGD("Waiting for application to become ready for input: name=%s, window=%s",
+                    application ? application->name.string() : "<unknown>",
+                    window ? window->inputChannel->getName().string() : "<unknown>");
+#endif
+            nsecs_t timeout = window ? window->dispatchingTimeout :
+                application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+
+            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
+            mInputTargetWaitStartTime = currentTime;
+            mInputTargetWaitTimeoutTime = currentTime + timeout;
+            mInputTargetWaitTimeoutExpired = false;
+        }
+    }
+
+    if (mInputTargetWaitTimeoutExpired) {
+        return INPUT_EVENT_INJECTION_TIMED_OUT;
+    }
+
+    if (currentTime >= mInputTargetWaitTimeoutTime) {
+        LOGI("Application is not ready for input: name=%s, window=%s,"
+                "%01.1fms since event, %01.1fms since wait started",
+                application ? application->name.string() : "<unknown>",
+                window ? window->inputChannel->getName().string() : "<unknown>",
+                (currentTime - entry->eventTime) / 1000000.0,
+                (currentTime - mInputTargetWaitStartTime) / 1000000.0);
+
+        CommandEntry* commandEntry = postCommandLocked(
+                & InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible);
+        if (application) {
+            commandEntry->inputApplicationHandle = application->handle;
+        }
+        if (window) {
+            commandEntry->inputChannel = window->inputChannel;
+        }
+
+        // Force poll loop to wake up immediately on next iteration once we get the
+        // ANR response back from the policy.
+        *nextWakeupTime = LONG_LONG_MIN;
+        return INPUT_EVENT_INJECTION_PENDING;
+    } else {
+        // Force poll loop to wake up when timeout is due.
+        if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
+            *nextWakeupTime = mInputTargetWaitTimeoutTime;
+        }
+        return INPUT_EVENT_INJECTION_PENDING;
+    }
+}
+
+void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout) {
+    if (newTimeout > 0) {
+        // Extend the timeout.
+        mInputTargetWaitTimeoutTime = now() + newTimeout;
+    } else {
+        // Give up.
+        mInputTargetWaitTimeoutExpired = true;
+    }
+}
+
+nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(
+        nsecs_t currentTime) {
+    if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
+        return currentTime - mInputTargetWaitStartTime;
+    }
+    return 0;
+}
+
+void InputDispatcher::resetANRTimeoutsLocked() {
+#if DEBUG_FOCUS
+        LOGD("Resetting ANR timeouts.");
+#endif
+
+    // Reset timeouts for all active connections.
+    nsecs_t currentTime = now();
+    for (size_t i = 0; i < mActiveConnections.size(); i++) {
+        Connection* connection = mActiveConnections[i];
+        connection->resetTimeout(currentTime);
+    }
+
+    // Reset input target wait timeout.
+    mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
+}
+
+int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
+        nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+    *outWindow = NULL;
+    mCurrentInputTargets.clear();
+
+    int32_t injectionResult;
+
+    // If there is no currently focused window and no focused application
+    // then drop the event.
+    if (! mFocusedWindow) {
+        if (mFocusedApplication) {
+#if DEBUG_FOCUS
+            LOGD("Waiting because there is no focused window but there is a "
+                    "focused application that may eventually add a window: '%s'.",
+                    mFocusedApplication->name.string());
+#endif
+            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                    mFocusedApplication, NULL, nextWakeupTime);
+            goto Unresponsive;
+        }
+
+        LOGI("Dropping event because there is no focused window or focused application.");
+        injectionResult = INPUT_EVENT_INJECTION_FAILED;
+        goto Failed;
+    }
+
+    // Check permissions.
+    if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) {
+        injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+        goto Failed;
+    }
+
+    // If the currently focused window is paused then keep waiting.
+    if (mFocusedWindow->paused) {
+#if DEBUG_FOCUS
+        LOGD("Waiting because focused window is paused.");
+#endif
+        injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                mFocusedApplication, mFocusedWindow, nextWakeupTime);
+        goto Unresponsive;
+    }
+
+    // Success!  Output targets.
+    injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+    *outWindow = mFocusedWindow;
+    addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_SYNC,
+            getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+
+    // Done.
+Failed:
+Unresponsive:
+#if DEBUG_FOCUS
+    LOGD("findFocusedWindow finished: injectionResult=%d",
+            injectionResult);
+    logDispatchStateLocked();
+#endif
+    return injectionResult;
+}
+
+int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
+        nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+    enum InjectionPermission {
+        INJECTION_PERMISSION_UNKNOWN,
+        INJECTION_PERMISSION_GRANTED,
+        INJECTION_PERMISSION_DENIED
+    };
+
+    *outWindow = NULL;
+    mCurrentInputTargets.clear();
+
+    nsecs_t startTime = now();
+
+    // For security reasons, we defer updating the touch state until we are sure that
+    // event injection will be allowed.
+    //
+    // FIXME In the original code, screenWasOff could never be set to true.
+    //       The reason is that the POLICY_FLAG_WOKE_HERE
+    //       and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
+    //       EV_KEY, EV_REL and EV_ABS events.  As it happens, the touch event was
+    //       actually enqueued using the policyFlags that appeared in the final EV_SYN
+    //       events upon which no preprocessing took place.  So policyFlags was always 0.
+    //       In the new native input dispatcher we're a bit more careful about event
+    //       preprocessing so the touches we receive can actually have non-zero policyFlags.
+    //       Unfortunately we obtain undesirable behavior.
+    //
+    //       Here's what happens:
+    //
+    //       When the device dims in anticipation of going to sleep, touches
+    //       in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
+    //       the device to brighten and reset the user activity timer.
+    //       Touches on other windows (such as the launcher window)
+    //       are dropped.  Then after a moment, the device goes to sleep.  Oops.
+    //
+    //       Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
+    //       instead of POLICY_FLAG_WOKE_HERE...
+    //
+    bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
+
+    int32_t action = entry->action;
+
+    // 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 */
+
+        InputWindow* newTouchedWindow = NULL;
+        mTempTouchedOutsideTargets.clear();
+
+        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;
+
+        // 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);
+            int32_t flags = window->layoutParamsFlags;
+
+            if (flags & InputWindow::FLAG_SYSTEM_ERROR) {
+                if (! topErrorWindow) {
+                    topErrorWindow = window;
+                }
+            }
+
+            if (window->visible) {
+                if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) {
+                    bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE
+                            | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0;
+                    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 there is an error window but it is not taking focus (typically because
+        // it is invisible) then wait for it.  Any other focused window may in
+        // fact be in ANR state.
+        if (topErrorWindow && newTouchedWindow != topErrorWindow) {
+#if DEBUG_FOCUS
+            LOGD("Waiting because system error window is pending.");
+#endif
+            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                    NULL, NULL, nextWakeupTime);
+            injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+            goto Unresponsive;
+        }
+
+        // If we did not find a touched window then fail.
+        if (! newTouchedWindow) {
+            if (mFocusedApplication) {
+#if DEBUG_FOCUS
+                LOGD("Waiting because there is no touched window but there is a "
+                        "focused application that may eventually add a new window: '%s'.",
+                        mFocusedApplication->name.string());
+#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;
+        }
+
+        // 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;
+        }
+
+        // Success!  Update the touch dispatch state for real.
+        releaseTouchedWindowLocked();
+
+        mTouchedWindow = newTouchedWindow;
+        mTouchedWindowIsObscured = obscured;
+
+        if (newTouchedWindow->hasWallpaper) {
+            mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
+        }
+    } 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;
+        }
+
+        // If the pointer is not currently down, then ignore the event.
+        if (! mTouchDown) {
+            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) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+            LOGD("Dropping event because there is no touched 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;
+        }
+    }
+
+    // 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, 0);
+        }
+
+        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, 0);
+        }
+        mTempTouchedOutsideTargets.clear();
+
+        int32_t targetFlags = InputTarget::FLAG_SYNC;
+        if (mTouchedWindowIsObscured) {
+            targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+        }
+        addWindowTargetLocked(mTouchedWindow, targetFlags,
+                getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+        *outWindow = mTouchedWindow;
+    }
+
+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)) {
+            injectionPermission = INJECTION_PERMISSION_GRANTED;
+        } else {
+            injectionPermission = INJECTION_PERMISSION_DENIED;
+        }
+    }
+
+    // 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!
+                LOGW("Pointer down received while already down.");
+            } else {
+                mTouchDown = true;
+            }
+
+            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();
+            }
+        } else if (action == AMOTION_EVENT_ACTION_UP) {
+            mTouchDown = false;
+            releaseTouchedWindowLocked();
+        }
+    } else {
+        LOGW("Not updating touch focus because injection was denied.");
+    }
+
+Unresponsive:
+#if DEBUG_FOCUS
+    LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d",
+            injectionResult, injectionPermission);
+    logDispatchStateLocked();
+#endif
+    return injectionResult;
+}
+
+void InputDispatcher::releaseTouchedWindowLocked() {
+    mTouchedWindow = NULL;
+    mTouchedWindowIsObscured = false;
+    mTouchedWallpaperWindows.clear();
+}
+
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+        nsecs_t timeSpentWaitingForApplication) {
+    mCurrentInputTargets.push();
+
+    InputTarget& target = mCurrentInputTargets.editTop();
+    target.inputChannel = window->inputChannel;
+    target.flags = targetFlags;
+    target.timeout = window->dispatchingTimeout;
+    target.timeSpentWaitingForApplication = timeSpentWaitingForApplication;
+    target.xOffset = - window->frameLeft;
+    target.yOffset = - window->frameTop;
+}
+
+void InputDispatcher::addMonitoringTargetsLocked() {
+    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+        mCurrentInputTargets.push();
+
+        InputTarget& target = mCurrentInputTargets.editTop();
+        target.inputChannel = mMonitoringChannels[i];
+        target.flags = 0;
+        target.timeout = -1;
+        target.timeSpentWaitingForApplication = 0;
+        target.xOffset = 0;
+        target.yOffset = 0;
+    }
+}
+
+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);
+        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(),
+                        window->ownerUid);
+            } else {
+                LOGW("Permission denied: injecting event from pid %d uid %d",
+                        injectorPid, injectorUid);
+            }
+            return false;
+        }
+    }
+    return true;
+}
+
+bool InputDispatcher::isWindowObscuredLocked(const InputWindow* window) {
+    size_t numWindows = mWindows.size();
+    for (size_t i = 0; i < numWindows; i++) {
+        const InputWindow* other = & mWindows.itemAt(i);
+        if (other == window) {
+            break;
+        }
+        if (other->visible && window->visibleFrameIntersects(other)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
+        int32_t windowType, int32_t eventType) {
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doPokeUserActivityLockedInterruptible);
+    commandEntry->eventTime = eventTime;
+    commandEntry->windowType = windowType;
+    commandEntry->userActivityEventType = eventType;
+}
+
 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
         const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
         bool resumeWithAppendedMotionSample) {
@@ -523,15 +1258,21 @@
             "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
             connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout,
             inputTarget->xOffset, inputTarget->yOffset,
-            resumeWithAppendedMotionSample ? "true" : "false");
+            toString(resumeWithAppendedMotionSample));
 #endif
 
     // Skip this event if the connection status is not normal.
-    // We don't want to queue outbound events at all if the connection is broken or
+    // We don't want to enqueue additional outbound events if the connection is broken or
     // not responding.
     if (connection->status != Connection::STATUS_NORMAL) {
-        LOGV("channel '%s' ~ Dropping event because the channel status is %s",
-                connection->getStatusLabel());
+        LOGW("channel '%s' ~ Dropping event because the channel status is %s",
+                connection->getInputChannelName(), connection->getStatusLabel());
+
+        // If the connection is not responding but the user is poking the application anyways,
+        // retrigger the original timeout.
+        if (connection->status == Connection::STATUS_NOT_RESPONDING) {
+            timeoutDispatchCycleLocked(currentTime, connection);
+        }
         return;
     }
 
@@ -612,17 +1353,45 @@
         }
     }
 
+    // Bring the input state back in line with reality in case it drifted off during an ANR.
+    if (connection->inputState.isOutOfSync()) {
+        mTempCancelationEvents.clear();
+        connection->inputState.synthesizeCancelationEvents(& mAllocator, mTempCancelationEvents);
+        connection->inputState.resetOutOfSync();
+
+        if (! mTempCancelationEvents.isEmpty()) {
+            LOGI("channel '%s' ~ Generated %d cancelation events to bring channel back in sync "
+                    "with reality.",
+                    connection->getInputChannelName(), mTempCancelationEvents.size());
+
+            for (size_t i = 0; i < mTempCancelationEvents.size(); i++) {
+                EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i);
+                switch (cancelationEventEntry->type) {
+                case EventEntry::TYPE_KEY:
+                    logOutboundKeyDetailsLocked("  ",
+                            static_cast<KeyEntry*>(cancelationEventEntry));
+                    break;
+                case EventEntry::TYPE_MOTION:
+                    logOutboundMotionDetailsLocked("  ",
+                            static_cast<MotionEntry*>(cancelationEventEntry));
+                    break;
+                }
+
+                DispatchEntry* cancelationDispatchEntry =
+                        mAllocator.obtainDispatchEntry(cancelationEventEntry,
+                        0, inputTarget->xOffset, inputTarget->yOffset, inputTarget->timeout);
+                connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
+
+                mAllocator.releaseEventEntry(cancelationEventEntry);
+            }
+        }
+    }
+
     // This is a new event.
     // Enqueue a new dispatch entry onto the outbound queue for this connection.
-    DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry); // increments ref
-    dispatchEntry->targetFlags = inputTarget->flags;
-    dispatchEntry->xOffset = inputTarget->xOffset;
-    dispatchEntry->yOffset = inputTarget->yOffset;
-    dispatchEntry->timeout = inputTarget->timeout;
-    dispatchEntry->inProgress = false;
-    dispatchEntry->headMotionSample = NULL;
-    dispatchEntry->tailMotionSample = NULL;
-
+    DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
+            inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset,
+            inputTarget->timeout);
     if (dispatchEntry->isSyncTarget()) {
         eventEntry->pendingSyncDispatches += 1;
     }
@@ -647,12 +1416,13 @@
     // If the outbound queue was previously empty, start the dispatch cycle going.
     if (wasEmpty) {
         activateConnectionLocked(connection.get());
-        startDispatchCycleLocked(currentTime, connection);
+        startDispatchCycleLocked(currentTime, connection,
+                inputTarget->timeSpentWaitingForApplication);
     }
 }
 
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection) {
+        const sp<Connection>& connection, nsecs_t timeSpentWaitingForApplication) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ startDispatchCycle",
             connection->getInputChannelName());
@@ -661,12 +1431,37 @@
     assert(connection->status == Connection::STATUS_NORMAL);
     assert(! connection->outboundQueue.isEmpty());
 
-    DispatchEntry* dispatchEntry = connection->outboundQueue.head.next;
+    DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
     assert(! dispatchEntry->inProgress);
 
-    // TODO throttle successive ACTION_MOVE motion events for the same device
-    //      possible implementation could set a brief poll timeout here and resume starting the
-    //      dispatch cycle when elapsed
+    // Mark the dispatch entry as in progress.
+    dispatchEntry->inProgress = true;
+
+    // Update the connection's input state.
+    InputState::Consistency consistency = connection->inputState.trackEvent(
+            dispatchEntry->eventEntry);
+
+#if FILTER_INPUT_EVENTS
+    // Filter out inconsistent sequences of input events.
+    // The input system may drop or inject events in a way that could violate implicit
+    // invariants on input state and potentially cause an application to crash
+    // or think that a key or pointer is stuck down.  Technically we make no guarantees
+    // of consistency but it would be nice to improve on this where possible.
+    // XXX: This code is a proof of concept only.  Not ready for prime time.
+    if (consistency == InputState::TOLERABLE) {
+#if DEBUG_DISPATCH_CYCLE
+        LOGD("channel '%s' ~ Sending an event that is inconsistent with the connection's "
+                "current input state but that is likely to be tolerated by the application.",
+                connection->getInputChannelName());
+#endif
+    } else if (consistency == InputState::BROKEN) {
+        LOGI("channel '%s' ~ Dropping an event that is inconsistent with the connection's "
+                "current input state and that is likely to cause the application to crash.",
+                connection->getInputChannelName());
+        startNextDispatchCycleLocked(currentTime, connection);
+        return;
+    }
+#endif
 
     // Publish the event.
     status_t status;
@@ -790,12 +1585,10 @@
     }
 
     // Record information about the newly started dispatch cycle.
-    dispatchEntry->inProgress = true;
-
     connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
     connection->lastDispatchTime = currentTime;
 
-    nsecs_t timeout = dispatchEntry->timeout;
+    nsecs_t timeout = dispatchEntry->timeout - timeSpentWaitingForApplication;
     connection->setNextTimeoutTime(currentTime, timeout);
 
     // Notify other system components.
@@ -844,9 +1637,14 @@
         return;
     }
 
+    startNextDispatchCycleLocked(currentTime, connection);
+}
+
+void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection) {
     // Start the next dispatch cycle for this connection.
     while (! connection->outboundQueue.isEmpty()) {
-        DispatchEntry* dispatchEntry = connection->outboundQueue.head.next;
+        DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
         if (dispatchEntry->inProgress) {
              // Finish or resume current event in progress.
             if (dispatchEntry->tailMotionSample) {
@@ -855,7 +1653,7 @@
                 dispatchEntry->inProgress = false;
                 dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample;
                 dispatchEntry->tailMotionSample = NULL;
-                startDispatchCycleLocked(currentTime, connection);
+                startDispatchCycleLocked(currentTime, connection, 0);
                 return;
             }
             // Finished.
@@ -868,7 +1666,7 @@
             // If the head is not in progress, then we must have already dequeued the in
             // progress event, which means we actually aborted it (due to ANR).
             // So just start the next event for this connection.
-            startDispatchCycleLocked(currentTime, connection);
+            startDispatchCycleLocked(currentTime, connection, 0);
             return;
         }
     }
@@ -884,58 +1682,74 @@
             connection->getInputChannelName());
 #endif
 
-    if (connection->status != Connection::STATUS_NORMAL) {
+    if (connection->status == Connection::STATUS_NORMAL) {
+        // Enter the not responding state.
+        connection->status = Connection::STATUS_NOT_RESPONDING;
+        connection->lastANRTime = currentTime;
+    } else if (connection->status != Connection::STATUS_NOT_RESPONDING) {
+        // Connection is broken or dead.
         return;
     }
 
-    // Enter the not responding state.
-    connection->status = Connection::STATUS_NOT_RESPONDING;
-    connection->lastANRTime = currentTime;
-
     // Notify other system components.
-    // This enqueues a command which will eventually either call
-    // resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked.
+    // This enqueues a command which will eventually call resumeAfterTimeoutDispatchCycleLocked.
     onDispatchCycleANRLocked(currentTime, connection);
 }
 
 void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
         const sp<Connection>& connection, nsecs_t newTimeout) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked",
-            connection->getInputChannelName());
+    LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked - newTimeout=%lld",
+            connection->getInputChannelName(), newTimeout);
 #endif
 
     if (connection->status != Connection::STATUS_NOT_RESPONDING) {
         return;
     }
 
-    // Resume normal dispatch.
-    connection->status = Connection::STATUS_NORMAL;
-    connection->setNextTimeoutTime(currentTime, newTimeout);
+    if (newTimeout > 0) {
+        // The system has decided to give the application some more time.
+        // Keep waiting synchronously and resume normal dispatch.
+        connection->status = Connection::STATUS_NORMAL;
+        connection->setNextTimeoutTime(currentTime, newTimeout);
+    } else {
+        // The system is about to throw up an ANR dialog and has requested that we abort dispatch.
+        // Reset the timeout.
+        connection->nextTimeoutTime = LONG_LONG_MAX;
+
+        // Input state will no longer be realistic.
+        connection->inputState.setOutOfSync();
+
+        if (! connection->outboundQueue.isEmpty()) {
+            // Make the current pending dispatch asynchronous (if it isn't already) so that
+            // subsequent events can be delivered to the ANR dialog or to another application.
+            DispatchEntry* currentDispatchEntry = connection->outboundQueue.headSentinel.next;
+            currentDispatchEntry->preemptSyncTarget();
+
+            // Drain all but the first entry in the outbound queue.  We keep the first entry
+            // since that is the one that dispatch is stuck on.  We throw away the others
+            // so that we don't spam the application with stale messages if it eventually
+            // wakes up and recovers from the ANR.
+            drainOutboundQueueLocked(connection.get(), currentDispatchEntry->next);
+        }
+    }
 }
 
 void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
         const sp<Connection>& connection, bool broken) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ abortDispatchCycle - broken=%s",
-            connection->getInputChannelName(), broken ? "true" : "false");
+            connection->getInputChannelName(), toString(broken));
 #endif
 
     // Clear the pending timeout.
     connection->nextTimeoutTime = LONG_LONG_MAX;
 
-    // Clear the outbound queue.
-    if (! connection->outboundQueue.isEmpty()) {
-        do {
-            DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
-            if (dispatchEntry->isSyncTarget()) {
-                decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
-            }
-            mAllocator.releaseDispatchEntry(dispatchEntry);
-        } while (! connection->outboundQueue.isEmpty());
+    // Input state will no longer be realistic.
+    connection->inputState.setOutOfSync();
 
-        deactivateConnectionLocked(connection.get());
-    }
+    // Clear the outbound queue.
+    drainOutboundQueueLocked(connection.get(), connection->outboundQueue.headSentinel.next);
 
     // Handle the case where the connection appears to be unrecoverably broken.
     // Ignore already broken or zombie connections.
@@ -950,6 +1764,26 @@
     }
 }
 
+void InputDispatcher::drainOutboundQueueLocked(Connection* connection,
+        DispatchEntry* firstDispatchEntryToDrain) {
+    for (DispatchEntry* dispatchEntry = firstDispatchEntryToDrain;
+            dispatchEntry != & connection->outboundQueue.tailSentinel;) {
+        DispatchEntry* next = dispatchEntry->next;
+        connection->outboundQueue.dequeue(dispatchEntry);
+
+        if (dispatchEntry->isSyncTarget()) {
+            decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+        }
+        mAllocator.releaseDispatchEntry(dispatchEntry);
+
+        dispatchEntry = next;
+    }
+
+    if (connection->outboundQueue.isEmpty()) {
+        deactivateConnectionLocked(connection);
+    }
+}
+
 bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
     InputDispatcher* d = static_cast<InputDispatcher*>(data);
 
@@ -1000,57 +1834,19 @@
     LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
 #endif
 
-    bool wasEmpty;
+    bool needWake;
     { // acquire lock
         AutoMutex _l(mLock);
 
         ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime);
-
-        wasEmpty = mInboundQueue.isEmpty();
-        mInboundQueue.enqueueAtTail(newEntry);
+        needWake = enqueueInboundEventLocked(newEntry);
     } // release lock
 
-    if (wasEmpty) {
+    if (needWake) {
         mPollLoop->wake();
     }
 }
 
-void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    LOGD("notifyAppSwitchComing - eventTime=%lld", eventTime);
-#endif
-
-    // Remove movement keys from the queue from most recent to least recent, stopping at the
-    // first non-movement key.
-    // TODO: Include a detailed description of why we do this...
-
-    { // acquire lock
-        AutoMutex _l(mLock);
-
-        for (EventEntry* entry = mInboundQueue.tail.prev; entry != & mInboundQueue.head; ) {
-            EventEntry* prev = entry->prev;
-
-            if (entry->type == EventEntry::TYPE_KEY) {
-                KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
-                if (isMovementKey(keyEntry->keyCode)) {
-                    LOGV("Dropping movement key during app switch: keyCode=%d, action=%d",
-                            keyEntry->keyCode, keyEntry->action);
-                    mInboundQueue.dequeue(keyEntry);
-
-                    setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
-
-                    mAllocator.releaseKeyEntry(keyEntry);
-                } else {
-                    // stop at last non-movement key
-                    break;
-                }
-            }
-
-            entry = prev;
-        }
-    } // release lock
-}
-
 void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
         uint32_t policyFlags, int32_t action, int32_t flags,
         int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
@@ -1061,7 +1857,7 @@
             keyCode, scanCode, metaState, downTime);
 #endif
 
-    bool wasEmpty;
+    bool needWake;
     { // acquire lock
         AutoMutex _l(mLock);
 
@@ -1070,11 +1866,10 @@
                 deviceId, source, policyFlags, action, flags, keyCode, scanCode,
                 metaState, repeatCount, downTime);
 
-        wasEmpty = mInboundQueue.isEmpty();
-        mInboundQueue.enqueueAtTail(newEntry);
+        needWake = enqueueInboundEventLocked(newEntry);
     } // release lock
 
-    if (wasEmpty) {
+    if (needWake) {
         mPollLoop->wake();
     }
 }
@@ -1101,7 +1896,7 @@
     }
 #endif
 
-    bool wasEmpty;
+    bool needWake;
     { // acquire lock
         AutoMutex _l(mLock);
 
@@ -1112,8 +1907,8 @@
             // Try to append a move sample to the tail of the inbound queue for this device.
             // Give up if we encounter a non-move motion event for this device since that
             // means we cannot append any new samples until a new motion event has started.
-            for (EventEntry* entry = mInboundQueue.tail.prev;
-                    entry != & mInboundQueue.head; entry = entry->prev) {
+            for (EventEntry* entry = mInboundQueue.tailSentinel.prev;
+                    entry != & mInboundQueue.headSentinel; entry = entry->prev) {
                 if (entry->type != EventEntry::TYPE_MOTION) {
                     // Keep looking for motion events.
                     continue;
@@ -1140,18 +1935,6 @@
                 LOGD("Appended motion sample onto batch for most recent "
                         "motion event for this device in the inbound queue.");
 #endif
-
-                // Sanity check for special case because dispatch is interruptible.
-                // The dispatch logic is partially interruptible and releases its lock while
-                // identifying targets.  However, as soon as the targets have been identified,
-                // the dispatcher proceeds to write a dispatch entry into all relevant outbound
-                // queues and then promptly removes the motion entry from the queue.
-                //
-                // Consequently, we should never observe the case where the inbound queue contains
-                // an in-progress motion entry unless the current input targets are invalid
-                // (currently being computed).  Check for this!
-                assert(! (motionEntry->dispatchInProgress && mCurrentInputTargetsValid));
-
                 return; // done!
             }
 
@@ -1178,7 +1961,7 @@
                 for (size_t i = 0; i < mActiveConnections.size(); i++) {
                     Connection* connection = mActiveConnections.itemAt(i);
                     if (! connection->outboundQueue.isEmpty()) {
-                        DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev;
+                        DispatchEntry* dispatchEntry = connection->outboundQueue.tailSentinel.prev;
                         if (dispatchEntry->isSyncTarget()) {
                             if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
                                 goto NoBatchingOrStreaming;
@@ -1220,11 +2003,10 @@
                 xPrecision, yPrecision, downTime,
                 pointerCount, pointerIds, pointerCoords);
 
-        wasEmpty = mInboundQueue.isEmpty();
-        mInboundQueue.enqueueAtTail(newEntry);
+        needWake = enqueueInboundEventLocked(newEntry);
     } // release lock
 
-    if (wasEmpty) {
+    if (needWake) {
         mPollLoop->wake();
     }
 }
@@ -1240,11 +2022,15 @@
     nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
 
     EventEntry* injectedEntry;
-    bool wasEmpty;
+    bool needWake;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        injectedEntry = createEntryFromInputEventLocked(event);
+        injectedEntry = createEntryFromInjectedInputEventLocked(event);
+        if (! injectedEntry) {
+            return INPUT_EVENT_INJECTION_FAILED;
+        }
+
         injectedEntry->refCount += 1;
         injectedEntry->injectorPid = injectorPid;
         injectedEntry->injectorUid = injectorUid;
@@ -1253,12 +2039,10 @@
             injectedEntry->injectionIsAsync = true;
         }
 
-        wasEmpty = mInboundQueue.isEmpty();
-        mInboundQueue.enqueueAtTail(injectedEntry);
-
+        needWake = enqueueInboundEventLocked(injectedEntry);
     } // release lock
 
-    if (wasEmpty) {
+    if (needWake) {
         mPollLoop->wake();
     }
 
@@ -1361,11 +2145,42 @@
     }
 }
 
-InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
+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;
+    }
+}
+
+InputDispatcher::EventEntry* InputDispatcher::createEntryFromInjectedInputEventLocked(
         const InputEvent* event) {
     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());
+            return NULL;
+        }
+
         uint32_t policyFlags = POLICY_FLAG_INJECTED;
 
         KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(),
@@ -1378,6 +2193,17 @@
 
     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());
+            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;
 
         const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
@@ -1405,33 +2231,143 @@
     }
 }
 
-void InputDispatcher::resetKeyRepeatLocked() {
-    if (mKeyRepeatState.lastKeyEntry) {
-        mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
-        mKeyRepeatState.lastKeyEntry = NULL;
+void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
+#if DEBUG_FOCUS
+    LOGD("setInputWindows");
+#endif
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        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();
+        }
+
+        bool hadFocusedWindow = mFocusedWindow != NULL;
+
+        mFocusedWindow = NULL;
+        mWallpaperWindows.clear();
+
+        mWindows.clear();
+        mWindows.appendVector(inputWindows);
+
+        size_t numWindows = mWindows.size();
+        for (size_t i = 0; i < numWindows; i++) {
+            InputWindow* window = & mWindows.editItemAt(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;
+            }
+        }
+
+        mTempTouchedWallpaperChannels.clear();
+
+        if ((hadFocusedWindow && ! mFocusedWindow)
+                || (mFocusedWindow && ! mFocusedWindow->visible)) {
+            preemptInputDispatchInnerLocked();
+        }
+
+#if DEBUG_FOCUS
+        logDispatchStateLocked();
+#endif
+    } // release lock
+
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mPollLoop->wake();
+}
+
+void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) {
+#if DEBUG_FOCUS
+    LOGD("setFocusedApplication");
+#endif
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        releaseFocusedApplicationLocked();
+
+        if (inputApplication) {
+            mFocusedApplicationStorage = *inputApplication;
+            mFocusedApplication = & mFocusedApplicationStorage;
+        }
+
+#if DEBUG_FOCUS
+        logDispatchStateLocked();
+#endif
+    } // release lock
+
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mPollLoop->wake();
+}
+
+void InputDispatcher::releaseFocusedApplicationLocked() {
+    if (mFocusedApplication) {
+        mFocusedApplication = NULL;
+        mFocusedApplicationStorage.handle.clear();
+    }
+}
+
+void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
+#if DEBUG_FOCUS
+    LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
+#endif
+
+    bool changed;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
+            if (mDispatchFrozen && ! frozen) {
+                resetANRTimeoutsLocked();
+            }
+
+            mDispatchEnabled = enabled;
+            mDispatchFrozen = frozen;
+            changed = true;
+        } else {
+            changed = false;
+        }
+
+#if DEBUG_FOCUS
+        logDispatchStateLocked();
+#endif
+    } // release lock
+
+    if (changed) {
+        // Wake up poll loop since it may need to make new input dispatching choices.
+        mPollLoop->wake();
     }
 }
 
 void InputDispatcher::preemptInputDispatch() {
-#if DEBUG_DISPATCH_CYCLE
+#if DEBUG_FOCUS
     LOGD("preemptInputDispatch");
 #endif
 
-    bool preemptedOne = false;
+    bool preemptedOne;
     { // acquire lock
         AutoMutex _l(mLock);
-
-        for (size_t i = 0; i < mActiveConnections.size(); i++) {
-            Connection* connection = mActiveConnections[i];
-            if (connection->hasPendingSyncTarget()) {
-#if DEBUG_DISPATCH_CYCLE
-                LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
-                        connection->getInputChannelName());
-#endif
-                connection->outboundQueue.tail.prev->targetFlags &= ~ InputTarget::FLAG_SYNC;
-                preemptedOne = true;
-            }
-        }
+        preemptedOne = preemptInputDispatchInnerLocked();
     } // release lock
 
     if (preemptedOne) {
@@ -1440,9 +2376,99 @@
     }
 }
 
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
+bool InputDispatcher::preemptInputDispatchInnerLocked() {
+    bool preemptedOne = false;
+    for (size_t i = 0; i < mActiveConnections.size(); i++) {
+        Connection* connection = mActiveConnections[i];
+        if (connection->hasPendingSyncTarget()) {
+#if DEBUG_DISPATCH_CYCLE
+            LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
+                    connection->getInputChannelName());
+#endif
+            connection->preemptSyncTarget();
+            preemptedOne = true;
+        }
+    }
+    return preemptedOne;
+}
+
+void InputDispatcher::logDispatchStateLocked() {
+    String8 dump;
+    dumpDispatchStateLocked(dump);
+    LOGD("%s", dump.string());
+}
+
+void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
+    dump.appendFormat("  dispatchEnabled: %d\n", mDispatchEnabled);
+    dump.appendFormat("  dispatchFrozen: %d\n", mDispatchFrozen);
+
+    if (mFocusedApplication) {
+        dump.appendFormat("  focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
+                mFocusedApplication->name.string(),
+                mFocusedApplication->dispatchingTimeout / 1000000.0);
+    } else {
+        dump.append("  focusedApplication: <null>\n");
+    }
+    dump.appendFormat("  focusedWindow: '%s'\n",
+            mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
+    dump.appendFormat("  touchedWindow: '%s', touchDown=%d\n",
+            mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
+            mTouchDown);
+    for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
+        dump.appendFormat("  touchedWallpaperWindows[%d]: '%s'\n",
+                i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
+    }
+    for (size_t i = 0; i < mWindows.size(); i++) {
+        dump.appendFormat("  windows[%d]: '%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
+                "visible=%s, flags=0x%08x, type=0x%08x, "
+                "frame=[%d,%d][%d,%d], "
+                "visibleFrame=[%d,%d][%d,%d], "
+                "touchableArea=[%d,%d][%d,%d], "
+                "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+                i, mWindows[i].inputChannel->getName().string(),
+                toString(mWindows[i].paused),
+                toString(mWindows[i].hasFocus),
+                toString(mWindows[i].hasWallpaper),
+                toString(mWindows[i].visible),
+                mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
+                mWindows[i].frameLeft, mWindows[i].frameTop,
+                mWindows[i].frameRight, mWindows[i].frameBottom,
+                mWindows[i].visibleFrameLeft, mWindows[i].visibleFrameTop,
+                mWindows[i].visibleFrameRight, mWindows[i].visibleFrameBottom,
+                mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
+                mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
+                mWindows[i].ownerPid, mWindows[i].ownerUid,
+                mWindows[i].dispatchingTimeout / 1000000.0);
+    }
+
+    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+        const sp<InputChannel>& channel = mMonitoringChannels[i];
+        dump.appendFormat("  monitoringChannel[%d]: '%s'\n",
+                i, channel->getName().string());
+    }
+
+    for (size_t i = 0; i < mActiveConnections.size(); i++) {
+        const Connection* connection = mActiveConnections[i];
+        dump.appendFormat("  activeConnection[%d]: '%s', status=%s, hasPendingSyncTarget=%s, "
+                "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
+                i, connection->getInputChannelName(), connection->getStatusLabel(),
+                toString(connection->hasPendingSyncTarget()),
+                toString(connection->inputState.isNeutral()),
+                toString(connection->inputState.isOutOfSync()));
+    }
+
+    if (isAppSwitchPendingLocked()) {
+        dump.appendFormat("  appSwitch: pending, due in %01.1fms\n",
+                (mAppSwitchDueTime - now()) / 1000000.0);
+    } else {
+        dump.append("  appSwitch: not pending\n");
+    }
+}
+
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {
 #if DEBUG_REGISTRATION
-    LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string());
+    LOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
+            toString(monitor));
 #endif
 
     { // acquire lock
@@ -1465,6 +2491,10 @@
         int32_t receiveFd = inputChannel->getReceivePipeFd();
         mConnectionsByReceiveFd.add(receiveFd, connection);
 
+        if (monitor) {
+            mMonitoringChannels.push(inputChannel);
+        }
+
         mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
 
         runCommandsLockedInterruptible();
@@ -1492,6 +2522,13 @@
 
         connection->status = Connection::STATUS_ZOMBIE;
 
+        for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+            if (mMonitoringChannels[i] == inputChannel) {
+                mMonitoringChannels.removeAt(i);
+                break;
+            }
+        }
+
         mPollLoop->removeCallback(inputChannel->getReceivePipeFd());
 
         nsecs_t currentTime = now();
@@ -1578,6 +2615,15 @@
     commandEntry->connection = connection;
 }
 
+void InputDispatcher::doNotifyConfigurationChangedInterruptible(
+        CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
+
+    mLock.lock();
+}
+
 void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
         CommandEntry* commandEntry) {
     sp<Connection> connection = commandEntry->connection;
@@ -1598,17 +2644,12 @@
     if (connection->status != Connection::STATUS_ZOMBIE) {
         mLock.unlock();
 
-        nsecs_t newTimeout;
-        bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout);
+        nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel);
 
         mLock.lock();
 
         nsecs_t currentTime = now();
-        if (resume) {
-            resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
-        } else {
-            abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/);
-        }
+        resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
     }
 }
 
@@ -1625,6 +2666,57 @@
     }
 }
 
+void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
+        CommandEntry* commandEntry) {
+    KeyEntry* entry = commandEntry->keyEntry;
+    mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
+            entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
+            entry->downTime, entry->eventTime);
+
+    mLock.unlock();
+
+    bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel,
+            & mReusableKeyEvent, entry->policyFlags);
+
+    mLock.lock();
+
+    entry->interceptKeyResult = consumed
+            ? KeyEntry::INTERCEPT_KEY_RESULT_SKIP
+            : KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+    mAllocator.releaseKeyEntry(entry);
+}
+
+void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType,
+            commandEntry->userActivityEventType);
+
+    mLock.lock();
+}
+
+void InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible(
+        CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    nsecs_t newTimeout;
+    if (commandEntry->inputChannel.get()) {
+        newTimeout = mPolicy->notifyInputChannelANR(commandEntry->inputChannel);
+    } else if (commandEntry->inputApplicationHandle.get()) {
+        newTimeout = mPolicy->notifyANR(commandEntry->inputApplicationHandle);
+    } else {
+        newTimeout = 0;
+    }
+
+    mLock.lock();
+
+    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout);
+}
+
+void InputDispatcher::dump(String8& dump) {
+    dumpDispatchStateLocked(dump);
+}
+
 
 // --- InputDispatcher::Allocator ---
 
@@ -1668,6 +2760,8 @@
     entry->metaState = metaState;
     entry->repeatCount = repeatCount;
     entry->downTime = downTime;
+    entry->syntheticRepeat = false;
+    entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
     return entry;
 }
 
@@ -1702,10 +2796,18 @@
 }
 
 InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry(
-        EventEntry* eventEntry) {
+        EventEntry* eventEntry,
+        int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout) {
     DispatchEntry* entry = mDispatchEntryPool.alloc();
     entry->eventEntry = eventEntry;
     eventEntry->refCount += 1;
+    entry->targetFlags = targetFlags;
+    entry->xOffset = xOffset;
+    entry->yOffset = yOffset;
+    entry->timeout = timeout;
+    entry->inProgress = false;
+    entry->headMotionSample = NULL;
+    entry->tailMotionSample = NULL;
     return entry;
 }
 
@@ -1788,6 +2890,25 @@
     motionEntry->lastSample = sample;
 }
 
+
+// --- InputDispatcher::EventEntry ---
+
+void InputDispatcher::EventEntry::recycle() {
+    injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    dispatchInProgress = false;
+    pendingSyncDispatches = 0;
+}
+
+
+// --- InputDispatcher::KeyEntry ---
+
+void InputDispatcher::KeyEntry::recycle() {
+    EventEntry::recycle();
+    syntheticRepeat = false;
+    interceptKeyResult = INTERCEPT_KEY_RESULT_UNKNOWN;
+}
+
+
 // --- InputDispatcher::MotionEntry ---
 
 uint32_t InputDispatcher::MotionEntry::countSamples() const {
@@ -1798,6 +2919,189 @@
     return count;
 }
 
+
+// --- InputDispatcher::InputState ---
+
+InputDispatcher::InputState::InputState() :
+        mIsOutOfSync(false) {
+}
+
+InputDispatcher::InputState::~InputState() {
+}
+
+bool InputDispatcher::InputState::isNeutral() const {
+    return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
+}
+
+bool InputDispatcher::InputState::isOutOfSync() const {
+    return mIsOutOfSync;
+}
+
+void InputDispatcher::InputState::setOutOfSync() {
+    if (! isNeutral()) {
+        mIsOutOfSync = true;
+    }
+}
+
+void InputDispatcher::InputState::resetOutOfSync() {
+    mIsOutOfSync = false;
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackEvent(
+        const EventEntry* entry) {
+    switch (entry->type) {
+    case EventEntry::TYPE_KEY:
+        return trackKey(static_cast<const KeyEntry*>(entry));
+
+    case EventEntry::TYPE_MOTION:
+        return trackMotion(static_cast<const MotionEntry*>(entry));
+
+    default:
+        return CONSISTENT;
+    }
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey(
+        const KeyEntry* entry) {
+    int32_t action = entry->action;
+    for (size_t i = 0; i < mKeyMementos.size(); i++) {
+        KeyMemento& memento = mKeyMementos.editItemAt(i);
+        if (memento.deviceId == entry->deviceId
+                && memento.source == entry->source
+                && memento.keyCode == entry->keyCode
+                && memento.scanCode == entry->scanCode) {
+            switch (action) {
+            case AKEY_EVENT_ACTION_UP:
+                mKeyMementos.removeAt(i);
+                if (isNeutral()) {
+                    mIsOutOfSync = false;
+                }
+                return CONSISTENT;
+
+            case AKEY_EVENT_ACTION_DOWN:
+                return TOLERABLE;
+
+            default:
+                return BROKEN;
+            }
+        }
+    }
+
+    switch (action) {
+    case AKEY_EVENT_ACTION_DOWN: {
+        mKeyMementos.push();
+        KeyMemento& memento = mKeyMementos.editTop();
+        memento.deviceId = entry->deviceId;
+        memento.source = entry->source;
+        memento.keyCode = entry->keyCode;
+        memento.scanCode = entry->scanCode;
+        memento.downTime = entry->downTime;
+        return CONSISTENT;
+    }
+
+    default:
+        return BROKEN;
+    }
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotion(
+        const MotionEntry* entry) {
+    int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK;
+    for (size_t i = 0; i < mMotionMementos.size(); i++) {
+        MotionMemento& memento = mMotionMementos.editItemAt(i);
+        if (memento.deviceId == entry->deviceId
+                && memento.source == entry->source) {
+            switch (action) {
+            case AMOTION_EVENT_ACTION_UP:
+            case AMOTION_EVENT_ACTION_CANCEL:
+                mMotionMementos.removeAt(i);
+                if (isNeutral()) {
+                    mIsOutOfSync = false;
+                }
+                return CONSISTENT;
+
+            case AMOTION_EVENT_ACTION_DOWN:
+                return TOLERABLE;
+
+            case AMOTION_EVENT_ACTION_POINTER_DOWN:
+                if (entry->pointerCount == memento.pointerCount + 1) {
+                    memento.setPointers(entry);
+                    return CONSISTENT;
+                }
+                return BROKEN;
+
+            case AMOTION_EVENT_ACTION_POINTER_UP:
+                if (entry->pointerCount == memento.pointerCount - 1) {
+                    memento.setPointers(entry);
+                    return CONSISTENT;
+                }
+                return BROKEN;
+
+            case AMOTION_EVENT_ACTION_MOVE:
+                if (entry->pointerCount == memento.pointerCount) {
+                    return CONSISTENT;
+                }
+                return BROKEN;
+
+            default:
+                return BROKEN;
+            }
+        }
+    }
+
+    switch (action) {
+    case AMOTION_EVENT_ACTION_DOWN: {
+        mMotionMementos.push();
+        MotionMemento& memento = mMotionMementos.editTop();
+        memento.deviceId = entry->deviceId;
+        memento.source = entry->source;
+        memento.xPrecision = entry->xPrecision;
+        memento.yPrecision = entry->yPrecision;
+        memento.downTime = entry->downTime;
+        memento.setPointers(entry);
+        return CONSISTENT;
+    }
+
+    default:
+        return BROKEN;
+    }
+}
+
+void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
+    pointerCount = entry->pointerCount;
+    for (uint32_t i = 0; i < entry->pointerCount; i++) {
+        pointerIds[i] = entry->pointerIds[i];
+        pointerCoords[i] = entry->lastSample->pointerCoords[i];
+    }
+}
+
+void InputDispatcher::InputState::synthesizeCancelationEvents(
+        Allocator* allocator, Vector<EventEntry*>& outEvents) const {
+    for (size_t i = 0; i < mKeyMementos.size(); i++) {
+        const KeyMemento& memento = mKeyMementos.itemAt(i);
+        outEvents.push(allocator->obtainKeyEntry(now(),
+                memento.deviceId, memento.source, 0,
+                AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_CANCELED,
+                memento.keyCode, memento.scanCode, 0, 0, memento.downTime));
+    }
+
+    for (size_t i = 0; i < mMotionMementos.size(); i++) {
+        const MotionMemento& memento = mMotionMementos.itemAt(i);
+        outEvents.push(allocator->obtainMotionEntry(now(),
+                memento.deviceId, memento.source, 0,
+                AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0,
+                memento.xPrecision, memento.yPrecision, memento.downTime,
+                memento.pointerCount, memento.pointerIds, memento.pointerCoords));
+    }
+}
+
+void InputDispatcher::InputState::clear() {
+    mKeyMementos.clear();
+    mMotionMementos.clear();
+    mIsOutOfSync = false;
+}
+
+
 // --- InputDispatcher::Connection ---
 
 InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
@@ -1818,6 +3122,14 @@
     nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
 }
 
+void InputDispatcher::Connection::resetTimeout(nsecs_t currentTime) {
+    if (outboundQueue.isEmpty()) {
+        nextTimeoutTime = LONG_LONG_MAX;
+    } else {
+        setNextTimeoutTime(currentTime, outboundQueue.headSentinel.next->timeout);
+    }
+}
+
 const char* InputDispatcher::Connection::getStatusLabel() const {
     switch (status) {
     case STATUS_NORMAL:
@@ -1839,8 +3151,8 @@
 
 InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent(
         const EventEntry* eventEntry) const {
-    for (DispatchEntry* dispatchEntry = outboundQueue.tail.prev;
-            dispatchEntry != & outboundQueue.head; dispatchEntry = dispatchEntry->prev) {
+    for (DispatchEntry* dispatchEntry = outboundQueue.tailSentinel.prev;
+            dispatchEntry != & outboundQueue.headSentinel; dispatchEntry = dispatchEntry->prev) {
         if (dispatchEntry->eventEntry == eventEntry) {
             return dispatchEntry;
         }
@@ -1848,9 +3160,11 @@
     return NULL;
 }
 
+
 // --- InputDispatcher::CommandEntry ---
 
-InputDispatcher::CommandEntry::CommandEntry() {
+InputDispatcher::CommandEntry::CommandEntry() :
+    keyEntry(NULL) {
 }
 
 InputDispatcher::CommandEntry::~CommandEntry() {
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
index ed4f07b..09fce38 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -72,52 +72,12 @@
     return OK;
 }
 
-status_t InputManager::registerInputChannel(const sp<InputChannel>& inputChannel) {
-    return mDispatcher->registerInputChannel(inputChannel);
+sp<InputReaderInterface> InputManager::getReader() {
+    return mReader;
 }
 
-status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
-    return mDispatcher->unregisterInputChannel(inputChannel);
-}
-
-int32_t InputManager::injectInputEvent(const InputEvent* event,
-        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
-    return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
-}
-
-void InputManager::preemptInputDispatch() {
-    mDispatcher->preemptInputDispatch();
-}
-
-void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) {
-    mReader->getInputConfiguration(outConfiguration);
-}
-
-status_t InputManager::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
-    return mReader->getInputDeviceInfo(deviceId, outDeviceInfo);
-}
-
-void InputManager::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
-    mReader->getInputDeviceIds(outDeviceIds);
-}
-
-int32_t InputManager::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-        int32_t scanCode) {
-    return mReader->getScanCodeState(deviceId, sourceMask, scanCode);
-}
-
-int32_t InputManager::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-        int32_t keyCode) {
-    return mReader->getKeyCodeState(deviceId, sourceMask, keyCode);
-}
-
-int32_t InputManager::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) {
-    return mReader->getSwitchState(deviceId, sourceMask, sw);
-}
-
-bool InputManager::hasKeys(int32_t deviceId, uint32_t sourceMask,
-        size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
-    return mReader->hasKeys(deviceId, sourceMask, numCodes, keyCodes, outFlags);
+sp<InputDispatcherInterface> InputManager::getDispatcher() {
+    return mDispatcher;
 }
 
 } // namespace android
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index d57b38c..88084c0 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -573,6 +573,60 @@
     } // release device registy reader lock
 }
 
+void InputReader::dump(String8& dump) {
+    dumpDeviceInfo(dump);
+}
+
+static void dumpMotionRange(String8& dump,
+        const char* name, const InputDeviceInfo::MotionRange* range) {
+    if (range) {
+        dump.appendFormat("      %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
+                name, range->min, range->max, range->flat, range->fuzz);
+    }
+}
+
+#define DUMP_MOTION_RANGE(range) \
+    dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
+
+void InputReader::dumpDeviceInfo(String8& dump) {
+    Vector<int32_t> deviceIds;
+    getInputDeviceIds(deviceIds);
+
+    InputDeviceInfo deviceInfo;
+    for (size_t i = 0; i < deviceIds.size(); i++) {
+        int32_t deviceId = deviceIds[i];
+
+        status_t result = getInputDeviceInfo(deviceId, & deviceInfo);
+        if (result == NAME_NOT_FOUND) {
+            continue;
+        } else if (result != OK) {
+            dump.appendFormat("  ** Unexpected error %d getting information about input devices.\n",
+                    result);
+            continue;
+        }
+
+        dump.appendFormat("  Device %d: '%s'\n",
+                deviceInfo.getId(), deviceInfo.getName().string());
+        dump.appendFormat("    sources = 0x%08x\n",
+                deviceInfo.getSources());
+        dump.appendFormat("    keyboardType = %d\n",
+                deviceInfo.getKeyboardType());
+
+        dump.append("    motion ranges:\n");
+        DUMP_MOTION_RANGE(X);
+        DUMP_MOTION_RANGE(Y);
+        DUMP_MOTION_RANGE(PRESSURE);
+        DUMP_MOTION_RANGE(SIZE);
+        DUMP_MOTION_RANGE(TOUCH_MAJOR);
+        DUMP_MOTION_RANGE(TOUCH_MINOR);
+        DUMP_MOTION_RANGE(TOOL_MAJOR);
+        DUMP_MOTION_RANGE(TOOL_MINOR);
+        DUMP_MOTION_RANGE(ORIENTATION);
+    }
+}
+
+#undef DUMP_MOTION_RANGE
+
 
 // --- InputReaderThread ---
 
@@ -740,10 +794,6 @@
 }
 
 bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) {
-    if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) {
-        getDispatcher()->notifyAppSwitchComing(when);
-    }
-
     return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
 }
 
@@ -1249,20 +1299,12 @@
     mLocked.orientedRanges.haveOrientation = false;
 }
 
-static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) {
-    if (axis.valid) {
-        LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d",
-                name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
-    } else {
-        LOGI(INDENT "Raw %s axis: unknown range", name);
-    }
-}
-
 void TouchInputMapper::configure() {
     InputMapper::configure();
 
     // Configure basic parameters.
     configureParameters();
+    logParameters();
 
     // Configure absolute axis information.
     configureRawAxes();
@@ -1287,6 +1329,18 @@
     mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
 }
 
+void TouchInputMapper::logParameters() {
+    if (mParameters.useBadTouchFilter) {
+        LOGI(INDENT "Bad touch filter enabled.");
+    }
+    if (mParameters.useAveragingTouchFilter) {
+        LOGI(INDENT "Averaging touch filter enabled.");
+    }
+    if (mParameters.useJumpyTouchFilter) {
+        LOGI(INDENT "Jumpy touch filter enabled.");
+    }
+}
+
 void TouchInputMapper::configureRawAxes() {
     mRawAxes.x.clear();
     mRawAxes.y.clear();
@@ -1298,6 +1352,15 @@
     mRawAxes.orientation.clear();
 }
 
+static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) {
+    if (axis.valid) {
+        LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d",
+                name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
+    } else {
+        LOGI(INDENT "Raw %s axis: unknown range", name);
+    }
+}
+
 void TouchInputMapper::logRawAxes() {
     logAxisInfo(mRawAxes.x, "x");
     logAxisInfo(mRawAxes.y, "y");
@@ -1331,8 +1394,10 @@
 
     bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
     if (sizeChanged) {
-        LOGI("Device configured: id=0x%x, name=%s (display size was changed)",
+        LOGI("Device reconfigured (display size changed): id=0x%x, name=%s",
                 getDeviceId(), getDeviceName().string());
+        LOGI(INDENT "Width: %dpx", width);
+        LOGI(INDENT "Height: %dpx", height);
 
         mLocked.surfaceWidth = width;
         mLocked.surfaceHeight = height;
@@ -1500,9 +1565,41 @@
         mLocked.orientedRanges.y.fuzz = orientedYScale;
     }
 
+    if (sizeChanged) {
+        logMotionRangesLocked();
+    }
+
     return true;
 }
 
+static void logMotionRangeInfo(InputDeviceInfo::MotionRange* range, const char* name) {
+    if (range) {
+        LOGI(INDENT "Output %s range: min=%f, max=%f, flat=%f, fuzz=%f",
+                name, range->min, range->max, range->flat, range->fuzz);
+    } else {
+        LOGI(INDENT "Output %s range: unsupported", name);
+    }
+}
+
+void TouchInputMapper::logMotionRangesLocked() {
+    logMotionRangeInfo(& mLocked.orientedRanges.x, "x");
+    logMotionRangeInfo(& mLocked.orientedRanges.y, "y");
+    logMotionRangeInfo(mLocked.orientedRanges.havePressure
+            ? & mLocked.orientedRanges.pressure : NULL, "pressure");
+    logMotionRangeInfo(mLocked.orientedRanges.haveSize
+            ? & mLocked.orientedRanges.size : NULL, "size");
+    logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
+            ? & mLocked.orientedRanges.touchMajor : NULL, "touchMajor");
+    logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
+            ? & mLocked.orientedRanges.touchMinor : NULL, "touchMinor");
+    logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
+            ? & mLocked.orientedRanges.toolMajor : NULL, "toolMajor");
+    logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
+            ? & mLocked.orientedRanges.toolMinor : NULL, "toolMinor");
+    logMotionRangeInfo(mLocked.orientedRanges.haveOrientation
+            ? & mLocked.orientedRanges.orientation : NULL, "orientation");
+}
+
 void TouchInputMapper::configureVirtualKeysLocked() {
     assert(mRawAxes.x.valid && mRawAxes.y.valid);
 
@@ -1768,16 +1865,18 @@
 }
 
 void TouchInputMapper::logCalibration() {
+    LOGI(INDENT "Calibration:");
+
     // Touch Area
     switch (mCalibration.touchAreaCalibration) {
     case Calibration::TOUCH_AREA_CALIBRATION_NONE:
-        LOGI(INDENT "  touch.touchArea.calibration: none");
+        LOGI(INDENT INDENT "touch.touchArea.calibration: none");
         break;
     case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
-        LOGI(INDENT "  touch.touchArea.calibration: geometric");
+        LOGI(INDENT INDENT "touch.touchArea.calibration: geometric");
         break;
     case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
-        LOGI(INDENT "  touch.touchArea.calibration: pressure");
+        LOGI(INDENT INDENT "touch.touchArea.calibration: pressure");
         break;
     default:
         assert(false);
@@ -1786,40 +1885,40 @@
     // Tool Area
     switch (mCalibration.toolAreaCalibration) {
     case Calibration::TOOL_AREA_CALIBRATION_NONE:
-        LOGI(INDENT "  touch.toolArea.calibration: none");
+        LOGI(INDENT INDENT "touch.toolArea.calibration: none");
         break;
     case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
-        LOGI(INDENT "  touch.toolArea.calibration: geometric");
+        LOGI(INDENT INDENT "touch.toolArea.calibration: geometric");
         break;
     case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
-        LOGI(INDENT "  touch.toolArea.calibration: linear");
+        LOGI(INDENT INDENT "touch.toolArea.calibration: linear");
         break;
     default:
         assert(false);
     }
 
     if (mCalibration.haveToolAreaLinearScale) {
-        LOGI(INDENT "  touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale);
+        LOGI(INDENT INDENT "touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale);
     }
 
     if (mCalibration.haveToolAreaLinearBias) {
-        LOGI(INDENT "  touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias);
+        LOGI(INDENT INDENT "touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias);
     }
 
     if (mCalibration.haveToolAreaIsSummed) {
-        LOGI(INDENT "  touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed);
+        LOGI(INDENT INDENT "touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed);
     }
 
     // Pressure
     switch (mCalibration.pressureCalibration) {
     case Calibration::PRESSURE_CALIBRATION_NONE:
-        LOGI(INDENT "  touch.pressure.calibration: none");
+        LOGI(INDENT INDENT "touch.pressure.calibration: none");
         break;
     case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
-        LOGI(INDENT "  touch.pressure.calibration: physical");
+        LOGI(INDENT INDENT "touch.pressure.calibration: physical");
         break;
     case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
-        LOGI(INDENT "  touch.pressure.calibration: amplitude");
+        LOGI(INDENT INDENT "touch.pressure.calibration: amplitude");
         break;
     default:
         assert(false);
@@ -1827,10 +1926,10 @@
 
     switch (mCalibration.pressureSource) {
     case Calibration::PRESSURE_SOURCE_PRESSURE:
-        LOGI(INDENT "  touch.pressure.source: pressure");
+        LOGI(INDENT INDENT "touch.pressure.source: pressure");
         break;
     case Calibration::PRESSURE_SOURCE_TOUCH:
-        LOGI(INDENT "  touch.pressure.source: touch");
+        LOGI(INDENT INDENT "touch.pressure.source: touch");
         break;
     case Calibration::PRESSURE_SOURCE_DEFAULT:
         break;
@@ -1839,16 +1938,16 @@
     }
 
     if (mCalibration.havePressureScale) {
-        LOGI(INDENT "  touch.pressure.scale: %f", mCalibration.pressureScale);
+        LOGI(INDENT INDENT "touch.pressure.scale: %f", mCalibration.pressureScale);
     }
 
     // Size
     switch (mCalibration.sizeCalibration) {
     case Calibration::SIZE_CALIBRATION_NONE:
-        LOGI(INDENT "  touch.size.calibration: none");
+        LOGI(INDENT INDENT "touch.size.calibration: none");
         break;
     case Calibration::SIZE_CALIBRATION_NORMALIZED:
-        LOGI(INDENT "  touch.size.calibration: normalized");
+        LOGI(INDENT INDENT "touch.size.calibration: normalized");
         break;
     default:
         assert(false);
@@ -1857,10 +1956,10 @@
     // Orientation
     switch (mCalibration.orientationCalibration) {
     case Calibration::ORIENTATION_CALIBRATION_NONE:
-        LOGI(INDENT "  touch.orientation.calibration: none");
+        LOGI(INDENT INDENT "touch.orientation.calibration: none");
         break;
     case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
-        LOGI(INDENT "  touch.orientation.calibration: interpolated");
+        LOGI(INDENT INDENT "touch.orientation.calibration: interpolated");
         break;
     default:
         assert(false);
diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp
index 6d3eeee..fe76cd0 100644
--- a/libs/utils/PollLoop.cpp
+++ b/libs/utils/PollLoop.cpp
@@ -119,7 +119,8 @@
         if (outData != NULL) *outData = pending.data;
         return pending.ident;
     }
-    
+
+    // Wait for wakeAndLock() waiters to run then set mPolling to true.
     mLock.lock();
     while (mWaiters != 0) {
         mResume.wait(mLock);
@@ -127,6 +128,7 @@
     mPolling = true;
     mLock.unlock();
 
+    // Poll.
     int32_t result;
     size_t requestedCount = mRequestedFds.size();
 
@@ -168,6 +170,7 @@
     }
 #endif
 
+    // Process the poll results.
     mPendingCallbacks.clear();
     mPendingFds.clear();
     mPendingFdsPos = 0;
@@ -218,6 +221,7 @@
     }
 
 Done:
+    // Set mPolling to false and wake up the wakeAndLock() waiters.
     mLock.lock();
     mPolling = false;
     if (mWaiters != 0) {
@@ -357,11 +361,13 @@
 
 void PollLoop::wakeAndLock() {
     mLock.lock();
+
     mWaiters += 1;
     while (mPolling) {
         wake();
         mAwake.wait(mLock);
     }
+
     mWaiters -= 1;
     if (mWaiters == 0) {
         mResume.signal();
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index b9232c8..dffccf8 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1317,18 +1317,20 @@
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN: {
-                AudioManager audioManager = (AudioManager) getContext().getSystemService(
-                        Context.AUDIO_SERVICE);
-                if (audioManager != null) {
-                    /*
-                     * Play a sound. This is done on key up since we don't want the
-                     * sound to play when a user holds down volume down to mute.
-                     */
-                    audioManager.adjustSuggestedStreamVolume(
-                            AudioManager.ADJUST_SAME,
-                            mVolumeControlStreamType,
-                            AudioManager.FLAG_PLAY_SOUND);
-                    mVolumeKeyUpTime = SystemClock.uptimeMillis();
+                if (!event.isCanceled()) {
+                    AudioManager audioManager = (AudioManager) getContext().getSystemService(
+                            Context.AUDIO_SERVICE);
+                    if (audioManager != null) {
+                        /*
+                         * Play a sound. This is done on key up since we don't want the
+                         * sound to play when a user holds down volume down to mute.
+                         */
+                        audioManager.adjustSuggestedStreamVolume(
+                                AudioManager.ADJUST_SAME,
+                                mVolumeControlStreamType,
+                                AudioManager.FLAG_PLAY_SOUND);
+                        mVolumeKeyUpTime = SystemClock.uptimeMillis();
+                    }
                 }
                 return true;
             }
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 314dd8a..90d036a 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -433,11 +433,6 @@
         }
         
         @SuppressWarnings("unused")
-        public void notifyAppSwitchComing() {
-            mWindowManagerService.mInputMonitor.notifyAppSwitchComing();
-        }
-        
-        @SuppressWarnings("unused")
         public boolean filterTouchEvents() {
             return mContext.getResources().getBoolean(
                     com.android.internal.R.bool.config_filterTouchEvents);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index c61baad..9efc708 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1751,24 +1751,28 @@
             p.println("  mSystemReady=" + mSystemReady + " mScreenOn=" + mScreenOn);
         }
 
+        p.println(" ");
         if (client != null) {
-            p.println(" ");
             pw.flush();
             try {
                 client.client.asBinder().dump(fd, args);
             } catch (RemoteException e) {
                 p.println("Input method client dead: " + e);
             }
+        } else {
+            p.println("No input method client.");
         }
 
+        p.println(" ");
         if (method != null) {
-            p.println(" ");
             pw.flush();
             try {
                 method.asBinder().dump(fd, args);
             } catch (RemoteException e) {
                 p.println("Input method service dead: " + e);
             }
+        } else {
+            p.println("No input method service.");
         }
     }
 }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 1279cf7..685bee4 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -3026,7 +3026,8 @@
             // Just create the setting, don't add it yet. For already existing packages
             // the PkgSetting exists already and doesn't have to be created.
             pkgSetting = mSettings.getPackageLP(pkg, origPackage, realName, suid, destCodeFile,
-                            destResourceFile, pkg.applicationInfo.flags, true, false);
+                    destResourceFile, pkg.applicationInfo.nativeLibraryDir,
+                    pkg.applicationInfo.flags, true, false);
             if (pkgSetting == null) {
                 Slog.w(TAG, "Creating application package " + pkg.packageName + " failed");
                 mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -3244,14 +3245,21 @@
                 }
             }
 
+            pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString;
+
             /*
              * Set the data dir to the default "/data/data/<package name>/lib"
              * if we got here without anyone telling us different (e.g., apps
              * stored on SD card have their native libraries stored in the ASEC
              * container with the APK).
+             *
+             * This happens during an upgrade from a package settings file that
+             * doesn't have a native library path attribute at all.
              */
-            if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null) {
-                pkg.applicationInfo.nativeLibraryDir = new File(dataPath, LIB_DIR_NAME).getPath();
+            if (pkgSetting.nativeLibraryPathString == null && pkg.applicationInfo.dataDir != null) {
+                final String nativeLibraryPath = new File(dataPath, LIB_DIR_NAME).getPath();
+                pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
+                pkgSetting.nativeLibraryPathString = nativeLibraryPath;
             }
 
             pkgSetting.uidError = uidError;
@@ -3273,7 +3281,6 @@
             if ((!isSystemApp(pkg) || isUpdatedSystemApp(pkg)) && !isExternal(pkg)) {
                 Log.i(TAG, path + " changed; unpacking");
                 File sharedLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
-                sharedLibraryDir.mkdir();
                 NativeLibraryHelper.copyNativeBinariesLI(scanFile, sharedLibraryDir);
             }
             pkg.mScanPath = path;
@@ -3591,40 +3598,6 @@
         }
     }
 
-    // Convenience call for removeNativeBinariesLI(File)
-    private void removeNativeBinariesLI(PackageParser.Package pkg) {
-        File nativeLibraryDir = getNativeBinaryDirForPackage(pkg);
-        removeNativeBinariesLI(nativeLibraryDir);
-    }
-
-    // Remove the native binaries of a given package. This simply
-    // gets rid of the files in the 'lib' sub-directory.
-    public void removeNativeBinariesLI(File binaryDir) {
-        if (DEBUG_NATIVE) {
-            Slog.w(TAG, "Deleting native binaries from: " + binaryDir.getPath());
-        }
-
-        // Just remove any file in the directory. Since the directory
-        // is owned by the 'system' UID, the application is not supposed
-        // to have written anything there.
-        //
-        if (binaryDir.exists()) {
-            File[] binaries = binaryDir.listFiles();
-            if (binaries != null) {
-                for (int nn = 0; nn < binaries.length; nn++) {
-                    if (DEBUG_NATIVE) {
-                        Slog.d(TAG, "    Deleting " + binaries[nn].getName());
-                    }
-                    if (!binaries[nn].delete()) {
-                        Slog.w(TAG, "Could not delete native binary: " + binaries[nn].getPath());
-                    }
-                }
-            }
-            // Do not delete 'lib' directory itself, or this will prevent
-            // installation of future updates.
-        }
-    }
-
     void removePackageLI(PackageParser.Package pkg, boolean chatty) {
         if (chatty && Config.LOGD) Log.d(
             TAG, "Removing package " + pkg.applicationInfo.packageName );
@@ -5128,7 +5101,7 @@
                 }
             }
             if (libraryPath != null) {
-                removeNativeBinariesLI(new File(libraryPath));
+                NativeLibraryHelper.removeNativeBinariesLI(libraryPath);
             }
         }
 
@@ -6202,8 +6175,8 @@
         synchronized (mPackages) {
             // Reinstate the old system package
             mSettings.enableSystemPackageLP(p.packageName);
-            // Remove any native libraries. XXX needed?
-            removeNativeBinariesLI(p);
+            // Remove any native libraries from the upgraded package.
+            NativeLibraryHelper.removeNativeBinariesLI(p.applicationInfo.nativeLibraryDir);
         }
         // Install the system package
         PackageParser.Package newPkg = scanPackageLI(ps.codePath,
@@ -7583,18 +7556,20 @@
         String installerPackageName;
 
         PackageSettingBase(String name, String realName, File codePath, File resourcePath,
-                int pVersionCode, int pkgFlags) {
+                String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
             super(pkgFlags);
             this.name = name;
             this.realName = realName;
-            init(codePath, resourcePath, pVersionCode);
+            init(codePath, resourcePath, nativeLibraryPathString, pVersionCode);
         }
 
-        void init(File codePath, File resourcePath, int pVersionCode) {
+        void init(File codePath, File resourcePath, String nativeLibraryPathString,
+                int pVersionCode) {
             this.codePath = codePath;
             this.codePathString = codePath.toString();
             this.resourcePath = resourcePath;
             this.resourcePathString = resourcePath.toString();
+            this.nativeLibraryPathString = nativeLibraryPathString;
             this.versionCode = pVersionCode;
         }
 
@@ -7687,8 +7662,9 @@
         SharedUserSetting sharedUser;
 
         PackageSetting(String name, String realName, File codePath, File resourcePath,
-                int pVersionCode, int pkgFlags) {
-            super(name, realName, codePath, resourcePath, pVersionCode, pkgFlags);
+                String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
+            super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode,
+                    pkgFlags);
         }
 
         @Override
@@ -7800,8 +7776,9 @@
             final int sharedId;
 
             PendingPackage(String name, String realName, File codePath, File resourcePath,
-                    int sharedId, int pVersionCode, int pkgFlags) {
-                super(name, realName, codePath, resourcePath, pVersionCode, pkgFlags);
+                    String nativeLibraryPathString, int sharedId, int pVersionCode, int pkgFlags) {
+                super(name, realName, codePath, resourcePath, nativeLibraryPathString,
+                        pVersionCode, pkgFlags);
                 this.sharedId = sharedId;
             }
         }
@@ -7830,10 +7807,10 @@
 
         PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage,
                 String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
-                int pkgFlags, boolean create, boolean add) {
+                String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) {
             final String name = pkg.packageName;
             PackageSetting p = getPackageLP(name, origPackage, realName, sharedUser, codePath,
-                    resourcePath, pkg.mVersionCode, pkgFlags, create, add);
+                    resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add);
             return p;
         }
 
@@ -7929,14 +7906,14 @@
             if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
                 p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             }
-            PackageSetting ret = addPackageLP(name, p.realName, p.codePath,
-                    p.resourcePath, p.userId, p.versionCode, p.pkgFlags);
+            PackageSetting ret = addPackageLP(name, p.realName, p.codePath, p.resourcePath,
+                    p.nativeLibraryPathString, p.userId, p.versionCode, p.pkgFlags);
             mDisabledSysPackages.remove(name);
             return ret;
         }
 
-        PackageSetting addPackageLP(String name, String realName, File codePath,
-                File resourcePath, int uid, int vc, int pkgFlags) {
+        PackageSetting addPackageLP(String name, String realName, File codePath, File resourcePath,
+                String nativeLibraryPathString, int uid, int vc, int pkgFlags) {
             PackageSetting p = mPackages.get(name);
             if (p != null) {
                 if (p.userId == uid) {
@@ -7946,7 +7923,8 @@
                         "Adding duplicate package, keeping first: " + name);
                 return null;
             }
-            p = new PackageSetting(name, realName, codePath, resourcePath, vc, pkgFlags);
+            p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString,
+                    vc, pkgFlags);
             p.userId = uid;
             if (addUserIdLP(uid, p, name)) {
                 mPackages.put(name, p);
@@ -8001,7 +7979,7 @@
         
         private PackageSetting getPackageLP(String name, PackageSetting origPackage,
                 String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
-                int vc, int pkgFlags, boolean create, boolean add) {
+                String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) {
             PackageSetting p = mPackages.get(name);
             if (p != null) {
                 if (!p.codePath.equals(codePath)) {
@@ -8044,8 +8022,8 @@
                 }
                 if (origPackage != null) {
                     // We are consuming the data from an existing package.
-                    p = new PackageSetting(origPackage.name, name, codePath,
-                            resourcePath, vc, pkgFlags);
+                    p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
+                            nativeLibraryPathString, vc, pkgFlags);
                     if (DEBUG_UPGRADE) Log.v(TAG, "Package " + name
                             + " is adopting original package " + origPackage.name);
                     // Note that we will retain the new package's signature so
@@ -8061,7 +8039,8 @@
                     // Update new package state.
                     p.setTimeStamp(codePath.lastModified());
                 } else {
-                    p = new PackageSetting(name, realName, codePath, resourcePath, vc, pkgFlags);
+                    p = new PackageSetting(name, realName, codePath, resourcePath,
+                            nativeLibraryPathString, vc, pkgFlags);
                     p.setTimeStamp(codePath.lastModified());
                     p.sharedUser = sharedUser;
                     if (sharedUser != null) {
@@ -8782,8 +8761,8 @@
                 Object idObj = getUserIdLP(pp.sharedId);
                 if (idObj != null && idObj instanceof SharedUserSetting) {
                     PackageSetting p = getPackageLP(pp.name, null, pp.realName,
-                            (SharedUserSetting)idObj, pp.codePath, pp.resourcePath,
-                            pp.versionCode, pp.pkgFlags, true, true);
+                            (SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
+                            pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true);
                     if (p == null) {
                         Slog.w(TAG, "Unable to create application package for "
                                 + pp.name);
@@ -8888,6 +8867,7 @@
             String realName = parser.getAttributeValue(null, "realName");
             String codePathStr = parser.getAttributeValue(null, "codePath");
             String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+            String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
             if (resourcePathStr == null) {
                 resourcePathStr = codePathStr;
             }
@@ -8902,9 +8882,8 @@
 
             int pkgFlags = 0;
             pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
-            PackageSetting ps = new PackageSetting(name, realName,
-                    new File(codePathStr),
-                    new File(resourcePathStr), versionCode, pkgFlags);
+            PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
+                    new File(resourcePathStr), nativeLibraryPathStr, versionCode, pkgFlags);
             String timeStampStr = parser.getAttributeValue(null, "ts");
             if (timeStampStr != null) {
                 try {
@@ -9023,9 +9002,9 @@
                             "Error in package manager settings: <package> has no codePath at "
                             + parser.getPositionDescription());
                 } else if (userId > 0) {
-                    packageSetting = addPackageLP(name.intern(), realName,
-                            new File(codePathStr), new File(resourcePathStr),
-                            userId, versionCode, pkgFlags);
+                    packageSetting = addPackageLP(name.intern(), realName, new File(codePathStr),
+                            new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode,
+                            pkgFlags);
                     if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name
                             + ": userId=" + userId + " pkg=" + packageSetting);
                     if (packageSetting == null) {
@@ -9042,7 +9021,7 @@
                     if (userId > 0) {
                         packageSetting = new PendingPackage(name.intern(), realName,
                                 new File(codePathStr), new File(resourcePathStr),
-                                userId, versionCode, pkgFlags);
+                                nativeLibraryPathStr, userId, versionCode, pkgFlags);
                         packageSetting.setTimeStamp(timeStamp, timeStampStr);
                         mPendingPackages.add((PendingPackage) packageSetting);
                         if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 0bc9b61..27d875e 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5097,8 +5097,7 @@
         }
         
         /* Notifies the window manager about an input channel that is not responding.
-         * The method can either cause dispatching to be aborted by returning -2 or
-         * return a new timeout in nanoseconds.
+         * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
          * 
          * Called by the InputManager.
          */
@@ -5107,7 +5106,7 @@
             synchronized (mWindowMap) {
                 WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
                 if (windowState == null) {
-                    return -2; // irrelevant, abort dispatching (-2)
+                    return 0; // window is unknown, abort dispatching
                 }
                 
                 Slog.i(TAG, "Input event dispatching timed out sending to "
@@ -5130,8 +5129,7 @@
         
         /* Notifies the window manager about an application that is not responding
          * in general rather than with respect to a particular input channel.
-         * The method can either cause dispatching to be aborted by returning -2 or
-         * return a new timeout in nanoseconds.
+         * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
          * 
          * Called by the InputManager.
          */
@@ -5157,7 +5155,7 @@
                 } catch (RemoteException ex) {
                 }
             }
-            return -2; // abort dispatching
+            return 0; // abort dispatching
         }
         
         private WindowState getWindowStateForInputChannel(InputChannel inputChannel) {
@@ -5271,15 +5269,6 @@
             }
         }
         
-        /* Notifies that an app switch key (BACK / HOME) has just been pressed.
-         * This essentially starts a .5 second timeout for the application to process
-         * subsequent input events while waiting for the app switch to occur.  If it takes longer
-         * than this, the pending events will be dropped.
-         */
-        public void notifyAppSwitchComing() {
-            // TODO Not implemented yet.  Should go in the native side.
-        }
-        
         /* Notifies that the lid switch changed state. */
         public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
             mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java
index eee97c3..c3786f5 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/services/java/com/android/server/sip/SipService.java
@@ -27,6 +27,7 @@
 import android.net.sip.ISipService;
 import android.net.sip.ISipSession;
 import android.net.sip.ISipSessionListener;
+import android.net.sip.SipErrorCode;
 import android.net.sip.SipManager;
 import android.net.sip.SipProfile;
 import android.net.sip.SipSessionAdapter;
@@ -528,6 +529,8 @@
         private int mBackoff = 1;
         private boolean mRegistered;
         private long mExpiryTime;
+        private SipErrorCode mErrorCode;
+        private String mErrorMessage;
 
         private String getAction() {
             return toString();
@@ -551,15 +554,25 @@
         }
 
         public void stop() {
+            stop(false);
+        }
+
+        private void stopButKeepStates() {
+            stop(true);
+        }
+
+        private void stop(boolean keepStates) {
             if (mSession == null) return;
-            if (mConnected) mSession.unregister();
+            if (mConnected && mRegistered) mSession.unregister();
             mTimer.cancel(this);
             if (mKeepAliveProcess != null) {
                 mKeepAliveProcess.stop();
                 mKeepAliveProcess = null;
             }
-            mSession = null;
-            mRegistered = false;
+            if (!keepStates) {
+                mSession = null;
+                mRegistered = false;
+            }
         }
 
         private boolean isStopped() {
@@ -568,20 +581,33 @@
 
         public void setListener(ISipSessionListener listener) {
             Log.v(TAG, "setListener(): " + listener);
-            mProxy.setListener(listener);
-            if (mSession == null) return;
+            synchronized (SipService.this) {
+                mProxy.setListener(listener);
+                if (mSession == null) return;
 
-            try {
-                if ((mSession != null) && SipSessionState.REGISTERING.equals(
-                        mSession.getState())) {
-                    mProxy.onRegistering(mSession);
-                } else if (mRegistered) {
-                    int duration = (int)
-                            (mExpiryTime - SystemClock.elapsedRealtime());
-                    mProxy.onRegistrationDone(mSession, duration);
+                try {
+                    SipSessionState state = (mSession == null)
+                            ? SipSessionState.READY_TO_CALL
+                            : Enum.valueOf(
+                                    SipSessionState.class, mSession.getState());
+                    if ((state == SipSessionState.REGISTERING)
+                            || (state == SipSessionState.DEREGISTERING)) {
+                        mProxy.onRegistering(mSession);
+                    } else if (mRegistered) {
+                        int duration = (int)
+                                (mExpiryTime - SystemClock.elapsedRealtime());
+                        mProxy.onRegistrationDone(mSession, duration);
+                    } else if (mErrorCode != null) {
+                        if (mErrorCode == SipErrorCode.TIME_OUT) {
+                            mProxy.onRegistrationTimeout(mSession);
+                        } else {
+                            mProxy.onRegistrationFailed(mSession,
+                                    mErrorCode.toString(), mErrorMessage);
+                        }
+                    }
+                } catch (Throwable t) {
+                    Log.w(TAG, "setListener(): " + t);
                 }
-            } catch (Throwable t) {
-                Log.w(TAG, "setListener(): " + t);
             }
         }
 
@@ -590,6 +616,8 @@
         }
 
         public void run() {
+            mErrorCode = null;
+            mErrorMessage = null;
             Log.v(TAG, "  ~~~ registering");
             synchronized (SipService.this) {
                 if (mConnected && !isStopped()) mSession.register(EXPIRY_TIME);
@@ -634,11 +662,7 @@
             synchronized (SipService.this) {
                 if (!isStopped() && (session != mSession)) return;
                 mRegistered = false;
-                try {
-                    mProxy.onRegistering(session);
-                } catch (Throwable t) {
-                    Log.w(TAG, "onRegistering()", t);
-                }
+                mProxy.onRegistering(session);
             }
         }
 
@@ -647,11 +671,9 @@
             Log.v(TAG, "onRegistrationDone(): " + session + ": " + mSession);
             synchronized (SipService.this) {
                 if (!isStopped() && (session != mSession)) return;
-                try {
-                    mProxy.onRegistrationDone(session, duration);
-                } catch (Throwable t) {
-                    Log.w(TAG, "onRegistrationDone()", t);
-                }
+
+                mProxy.onRegistrationDone(session, duration);
+
                 if (isStopped()) return;
 
                 if (duration > 0) {
@@ -687,19 +709,25 @@
         }
 
         @Override
-        public void onRegistrationFailed(ISipSession session, String className,
-                String message) {
+        public void onRegistrationFailed(ISipSession session,
+                String errorCodeString, String message) {
+            SipErrorCode errorCode =
+                    Enum.valueOf(SipErrorCode.class, errorCodeString);
             Log.v(TAG, "onRegistrationFailed(): " + session + ": " + mSession
-                    + ": " + className + ": " + message);
+                    + ": " + errorCode + ": " + message);
             synchronized (SipService.this) {
                 if (!isStopped() && (session != mSession)) return;
-                try {
-                    mProxy.onRegistrationFailed(session, className, message);
-                } catch (Throwable t) {
-                    Log.w(TAG, "onRegistrationFailed(): " + t);
-                }
+                mErrorCode = errorCode;
+                mErrorMessage = message;
+                mProxy.onRegistrationFailed(session, errorCode.toString(),
+                        message);
 
-                if (!isStopped()) onError();
+                if (errorCode == SipErrorCode.INVALID_CREDENTIALS) {
+                    Log.d(TAG, "   pause auto-registration");
+                    stopButKeepStates();
+                } else if (!isStopped()) {
+                    onError();
+                }
             }
         }
 
@@ -708,11 +736,8 @@
             Log.v(TAG, "onRegistrationTimeout(): " + session + ": " + mSession);
             synchronized (SipService.this) {
                 if (!isStopped() && (session != mSession)) return;
-                try {
-                    mProxy.onRegistrationTimeout(session);
-                } catch (Throwable t) {
-                    Log.w(TAG, "onRegistrationTimeout(): " + t);
-                }
+                mErrorCode = SipErrorCode.TIME_OUT;
+                mProxy.onRegistrationTimeout(session);
 
                 if (!isStopped()) {
                     mRegistered = false;
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java
index 2f7ddc4..da8e9b8 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/services/java/com/android/server/sip/SipSessionGroup.java
@@ -412,13 +412,7 @@
                             processCommand(command);
                         } catch (SipException e) {
                             Log.w(TAG, "command error: " + command, e);
-                            // TODO: find a better way to do this
-                            if ((command instanceof RegisterCommand)
-                                    || (command == DEREGISTER)) {
-                                onRegistrationFailed(e);
-                            } else {
-                                onError(e);
-                            }
+                            onError(e);
                         }
                     }
             }).start();
@@ -480,7 +474,9 @@
 
         private void processCommand(EventObject command) throws SipException {
             if (!process(command)) {
-                throw new SipException("wrong state to execute: " + command);
+                onError(SipErrorCode.IN_PROGRESS,
+                        "cannot initiate a new transaction to execute: "
+                        + command);
             }
         }
 
@@ -562,11 +558,8 @@
                 if (evt instanceof TimeoutEvent) {
                     processTimeout((TimeoutEvent) evt);
                 } else {
-                    Log.d(TAG, "Transaction terminated:" + this);
-                    if (!SipSessionState.IN_CALL.equals(mState)) {
-                        removeSipSession(this);
-                    }
-                    return true;
+                    processTransactionTerminated(
+                            (TransactionTerminatedEvent) evt);
                 }
                 return true;
             } else if (evt instanceof DialogTerminatedEvent) {
@@ -585,6 +578,20 @@
             }
         }
 
+        private void processTransactionTerminated(
+                TransactionTerminatedEvent event) {
+            switch (mState) {
+                case IN_CALL:
+                case READY_TO_CALL:
+                    Log.d(TAG, "Transaction terminated; do nothing");
+                    break;
+                default:
+                    Log.d(TAG, "Transaction terminated early: " + this);
+                    onError(SipErrorCode.TRANSACTION_TERMINTED,
+                            "transaction terminated");
+            }
+        }
+
         private void processTimeout(TimeoutEvent event) {
             Log.d(TAG, "processing Timeout..." + event);
             Transaction current = event.isServerTransaction()
@@ -600,25 +607,26 @@
                 return;
             }
             switch (mState) {
-            case REGISTERING:
-            case DEREGISTERING:
-                reset();
-                mProxy.onRegistrationTimeout(this);
-                break;
-            case INCOMING_CALL:
-            case INCOMING_CALL_ANSWERING:
-            case OUTGOING_CALL_CANCELING:
-                endCallOnError(SipErrorCode.TIME_OUT, event.toString());
-                break;
-            case PINGING:
-                reset();
-                mReRegisterFlag = true;
-                mState = SipSessionState.READY_TO_CALL;
-                break;
+                case REGISTERING:
+                case DEREGISTERING:
+                    reset();
+                    mProxy.onRegistrationTimeout(this);
+                    break;
+                case INCOMING_CALL:
+                case INCOMING_CALL_ANSWERING:
+                case OUTGOING_CALL:
+                case OUTGOING_CALL_CANCELING:
+                    onError(SipErrorCode.TIME_OUT, event.toString());
+                    break;
+                case PINGING:
+                    reset();
+                    mReRegisterFlag = true;
+                    mState = SipSessionState.READY_TO_CALL;
+                    break;
 
-            default:
-                // do nothing
-                break;
+                default:
+                    Log.d(TAG, "   do nothing");
+                    break;
             }
         }
 
@@ -665,7 +673,7 @@
                 } else {
                     Log.w(TAG, "peer did not respect our rport request");
                 }
-                mState = SipSessionState.READY_TO_CALL;
+                reset();
                 return true;
             }
             return false;
@@ -687,7 +695,6 @@
                 switch (statusCode) {
                 case Response.OK:
                     SipSessionState state = mState;
-                    reset();
                     onRegistrationDone((state == SipSessionState.REGISTERING)
                             ? getExpiryTime(((ResponseEvent) evt).getResponse())
                             : -1);
@@ -698,15 +705,13 @@
                 case Response.PROXY_AUTHENTICATION_REQUIRED:
                     if (!handleAuthentication(event)) {
                         Log.v(TAG, "Incorrect username/password");
-                        reset();
                         onRegistrationFailed(SipErrorCode.INVALID_CREDENTIALS,
                                 "incorrect username or password");
                     }
                     return true;
                 default:
                     if (statusCode >= 500) {
-                        reset();
-                        onRegistrationFailed(createCallbackException(response));
+                        onRegistrationFailed(response);
                         return true;
                     }
                 }
@@ -720,7 +725,6 @@
             String nonce = getNonceFromResponse(response);
             if (((nonce != null) && nonce.equals(mLastNonce)) ||
                     (nonce == mLastNonce)) {
-                Log.v(TAG, "Incorrect username/password");
                 return false;
             } else {
                 mClientTransaction = mSipHelper.handleChallenge(
@@ -906,7 +910,7 @@
                 }
 
                 if (statusCode >= 400) {
-                    onError(createCallbackException(response));
+                    onError(response);
                     return true;
                 }
             } else if (evt instanceof TransactionTerminatedEvent) {
@@ -954,10 +958,6 @@
                     response.getReasonPhrase(), response.getStatusCode());
         }
 
-        private Exception createCallbackException(Response response) {
-            return new SipException(createErrorMessage(response));
-        }
-
         private void establishCall() {
             mState = SipSessionState.IN_CALL;
             mInCall = true;
@@ -965,22 +965,22 @@
         }
 
         private void fallbackToPreviousInCall(Throwable exception) {
-            mState = SipSessionState.IN_CALL;
             exception = getRootCause(exception);
-            mProxy.onCallChangeFailed(this, getErrorCode(exception).toString(),
+            fallbackToPreviousInCall(getErrorCode(exception),
                     exception.toString());
         }
 
+        private void fallbackToPreviousInCall(SipErrorCode errorCode,
+                String message) {
+            mState = SipSessionState.IN_CALL;
+            mProxy.onCallChangeFailed(this, errorCode.toString(), message);
+        }
+
         private void endCallNormally() {
             reset();
             mProxy.onCallEnded(this);
         }
 
-        private void endCallOnError(Throwable exception) {
-            exception = getRootCause(exception);
-            endCallOnError(getErrorCode(exception), exception.toString());
-        }
-
         private void endCallOnError(SipErrorCode errorCode, String message) {
             reset();
             mProxy.onError(this, errorCode.toString(), message);
@@ -991,26 +991,34 @@
             mProxy.onCallBusy(this);
         }
 
-        private void onError(Throwable exception) {
-            if (mInCall) {
-                fallbackToPreviousInCall(exception);
-            } else {
-                endCallOnError(exception);
+        private void onError(SipErrorCode errorCode, String message) {
+            switch (mState) {
+                case REGISTERING:
+                case DEREGISTERING:
+                    onRegistrationFailed(errorCode, message);
+                    break;
+                default:
+                    if (mInCall) {
+                        fallbackToPreviousInCall(errorCode, message);
+                    } else {
+                        endCallOnError(errorCode, message);
+                    }
             }
         }
 
+
+        private void onError(Throwable exception) {
+            exception = getRootCause(exception);
+            onError(getErrorCode(exception), exception.toString());
+        }
+
         private void onError(Response response) {
-            if (mInCall) {
-                fallbackToPreviousInCall(createCallbackException(response));
+            int statusCode = response.getStatusCode();
+            if (!mInCall && ((statusCode == Response.TEMPORARILY_UNAVAILABLE)
+                    || (statusCode == Response.BUSY_HERE))) {
+                endCallOnBusy();
             } else {
-                int statusCode = response.getStatusCode();
-                if ((statusCode == Response.TEMPORARILY_UNAVAILABLE)
-                        || (statusCode == Response.BUSY_HERE)) {
-                    endCallOnBusy();
-                } else {
-                    endCallOnError(getErrorCode(statusCode),
-                            createErrorMessage(response));
-                }
+                onError(getErrorCode(statusCode), createErrorMessage(response));
             }
         }
 
@@ -1053,19 +1061,29 @@
         }
 
         private void onRegistrationDone(int duration) {
+            reset();
             mProxy.onRegistrationDone(this, duration);
         }
 
         private void onRegistrationFailed(SipErrorCode errorCode,
                 String message) {
+            reset();
             mProxy.onRegistrationFailed(this, errorCode.toString(), message);
         }
 
         private void onRegistrationFailed(Throwable exception) {
+            reset();
             exception = getRootCause(exception);
             onRegistrationFailed(getErrorCode(exception),
                     exception.toString());
         }
+
+        private void onRegistrationFailed(Response response) {
+            reset();
+            int statusCode = response.getStatusCode();
+            onRegistrationFailed(getErrorCode(statusCode),
+                    createErrorMessage(response));
+        }
     }
 
     /**
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 7af5e95..6dd619c 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -24,9 +24,6 @@
 // Log debug messages about InputDispatcherPolicy
 #define DEBUG_INPUT_DISPATCHER_POLICY 0
 
-// Log debug messages about input focus tracking
-#define DEBUG_FOCUS 0
-
 #include "JNIHelp.h"
 #include "jni.h"
 #include <limits.h>
@@ -44,81 +41,6 @@
 
 namespace android {
 
-// Window flags from WindowManager.LayoutParams
-enum {
-    FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001,
-    FLAG_DIM_BEHIND        = 0x00000002,
-    FLAG_BLUR_BEHIND        = 0x00000004,
-    FLAG_NOT_FOCUSABLE      = 0x00000008,
-    FLAG_NOT_TOUCHABLE      = 0x00000010,
-    FLAG_NOT_TOUCH_MODAL    = 0x00000020,
-    FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
-    FLAG_KEEP_SCREEN_ON     = 0x00000080,
-    FLAG_LAYOUT_IN_SCREEN   = 0x00000100,
-    FLAG_LAYOUT_NO_LIMITS   = 0x00000200,
-    FLAG_FULLSCREEN      = 0x00000400,
-    FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800,
-    FLAG_DITHER             = 0x00001000,
-    FLAG_SECURE             = 0x00002000,
-    FLAG_SCALED             = 0x00004000,
-    FLAG_IGNORE_CHEEK_PRESSES    = 0x00008000,
-    FLAG_LAYOUT_INSET_DECOR = 0x00010000,
-    FLAG_ALT_FOCUSABLE_IM = 0x00020000,
-    FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
-    FLAG_SHOW_WHEN_LOCKED = 0x00080000,
-    FLAG_SHOW_WALLPAPER = 0x00100000,
-    FLAG_TURN_SCREEN_ON = 0x00200000,
-    FLAG_DISMISS_KEYGUARD = 0x00400000,
-    FLAG_IMMERSIVE = 0x00800000,
-    FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
-    FLAG_COMPATIBLE_WINDOW = 0x20000000,
-    FLAG_SYSTEM_ERROR = 0x40000000,
-};
-
-// Window types from WindowManager.LayoutParams
-enum {
-    FIRST_APPLICATION_WINDOW = 1,
-    TYPE_BASE_APPLICATION   = 1,
-    TYPE_APPLICATION        = 2,
-    TYPE_APPLICATION_STARTING = 3,
-    LAST_APPLICATION_WINDOW = 99,
-    FIRST_SUB_WINDOW        = 1000,
-    TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW,
-    TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1,
-    TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
-    TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
-    TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW+4,
-    LAST_SUB_WINDOW         = 1999,
-    FIRST_SYSTEM_WINDOW     = 2000,
-    TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW,
-    TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1,
-    TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2,
-    TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3,
-    TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4,
-    TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5,
-    TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6,
-    TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7,
-    TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8,
-    TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9,
-    TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10,
-    TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11,
-    TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
-    TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13,
-    TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14,
-    LAST_SYSTEM_WINDOW      = 2999,
-};
-
-// Delay between reporting long touch events to the power manager.
-const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
-
-// Default input dispatching timeout if there is no focused application or paused window
-// from which to determine an appropriate dispatching timeout.
-const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
-
-// Minimum amount of time to provide to the input dispatcher for delivery of an event
-// regardless of how long the application window was paused.
-const nsecs_t MIN_INPUT_DISPATCHING_TIMEOUT = 1000 * 1000000LL; // 1 sec
-
 // ----------------------------------------------------------------------------
 
 static struct {
@@ -134,7 +56,6 @@
     jmethodID interceptKeyBeforeQueueing;
     jmethodID interceptKeyBeforeDispatching;
     jmethodID checkInjectEventsPermission;
-    jmethodID notifyAppSwitchComing;
     jmethodID filterTouchEvents;
     jmethodID filterJumpyTouchEvents;
     jmethodID getVirtualKeyDefinitions;
@@ -235,7 +156,7 @@
 
     inline sp<InputManager> getInputManager() const { return mInputManager; }
 
-    String8 dump();
+    void dump(String8& dump);
 
     void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
     void setDisplayOrientation(int32_t displayId, int32_t orientation);
@@ -270,72 +191,33 @@
     /* --- InputDispatcherPolicyInterface implementation --- */
 
     virtual void notifyConfigurationChanged(nsecs_t when);
+    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle);
     virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel);
-    virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
-            nsecs_t& outNewTimeout);
+    virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel);
     virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
     virtual nsecs_t getKeyRepeatTimeout();
     virtual nsecs_t getKeyRepeatDelay();
-    virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
-            int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
-    virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-            int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
     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 bool checkInjectEventsPermissionNonReentrant(
+            int32_t injectorPid, int32_t injectorUid);
 
 private:
-    struct InputWindow {
-        sp<InputChannel> inputChannel;
-        int32_t layoutParamsFlags;
-        int32_t layoutParamsType;
-        nsecs_t dispatchingTimeout;
-        int32_t frameLeft;
-        int32_t frameTop;
-        int32_t frameRight;
-        int32_t frameBottom;
-        int32_t visibleFrameLeft;
-        int32_t visibleFrameTop;
-        int32_t visibleFrameRight;
-        int32_t visibleFrameBottom;
-        int32_t touchableAreaLeft;
-        int32_t touchableAreaTop;
-        int32_t touchableAreaRight;
-        int32_t touchableAreaBottom;
-        bool visible;
-        bool hasFocus;
-        bool hasWallpaper;
-        bool paused;
-        int32_t ownerPid;
-        int32_t ownerUid;
-
-        bool visibleFrameIntersects(const InputWindow* other) const;
-        bool touchableAreaContainsPoint(int32_t x, int32_t y) const;
-    };
-
-    struct InputApplication {
-        String8 name;
-        nsecs_t dispatchingTimeout;
-        jweak tokenObjWeak;
-    };
-
-    class ANRTimer {
-        enum Budget {
-            SYSTEM = 0,
-            APPLICATION = 1
-        };
-
-        Budget mBudget;
-        nsecs_t mStartTime;
-        bool mFrozen;
-        InputWindow* mPausedWindow;
+    class ApplicationToken : public InputApplicationHandle {
+        jweak mTokenObjWeak;
 
     public:
-        ANRTimer();
+        ApplicationToken(jweak tokenObjWeak) :
+            mTokenObjWeak(tokenObjWeak) { }
 
-        void dispatchFrozenBySystem();
-        void dispatchPausedByApplication(InputWindow* pausedWindow);
-        bool waitForDispatchStateChangeLd(NativeInputManager* inputManager);
+        virtual ~ApplicationToken() {
+            JNIEnv* env = NativeInputManager::jniEnv();
+            env->DeleteWeakGlobalRef(mTokenObjWeak);
+        }
 
-        nsecs_t getTimeSpentWaitingForApplication() const;
+        inline jweak getTokenObj() { return mTokenObjWeak; }
     };
 
     sp<InputManager> mInputManager;
@@ -364,80 +246,14 @@
 
     jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel);
 
-    // Input target and focus tracking.  (lock mDispatchLock)
-    Mutex mDispatchLock;
-    Condition mDispatchStateChanged;
-
-    bool mDispatchEnabled;
-    bool mDispatchFrozen;
-    bool mWindowsReady;
-    Vector<InputWindow> mWindows;
-    Vector<InputWindow*> mWallpaperWindows;
-    Vector<sp<InputChannel> > mMonitoringChannels;
-
-    // Focus tracking for keys, trackball, etc.
-    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;
-    };
-    Vector<OutsideTarget> mTempTouchedOutsideTargets; // temporary outside touch targets
-    Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
-
-    // Focused application.
-    InputApplication* mFocusedApplication;
-    InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
-
-    void dumpDeviceInfo(String8& dump);
-    void dumpDispatchStateLd(String8& dump);
-    void logDispatchStateLd();
-
-    bool notifyANR(jobject tokenObj, nsecs_t& outNewTimeout);
-    void releaseFocusedApplicationLd(JNIEnv* env);
-
-    int32_t waitForFocusedWindowLd(uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
-            Vector<InputTarget>& outTargets, InputWindow*& outFocusedWindow);
-    int32_t waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
-            int32_t injectorPid, int32_t injectorUid,
-            Vector<InputTarget>& outTargets, InputWindow*& outTouchedWindow);
-    bool isWindowObscuredLocked(const InputWindow* window);
-
-    void releaseTouchedWindowLd();
-
-    int32_t waitForNonTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-            int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
-    int32_t waitForTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-            int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
-
-    bool interceptKeyBeforeDispatching(const InputTarget& target,
-            const KeyEvent* keyEvent, uint32_t policyFlags);
-
-    void pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType);
-    void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
-    bool checkInjectionPermission(const InputWindow* window,
-            int32_t injectorPid, int32_t injectorUid);
-
     static bool populateWindow(JNIEnv* env, jobject windowObj, InputWindow& outWindow);
-    static void addTarget(const InputWindow* window, int32_t targetFlags,
-            nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets);
 
-    void registerMonitoringChannel(const sp<InputChannel>& inputChannel);
-    void unregisterMonitoringChannel(const sp<InputChannel>& inputChannel);
-    void addMonitoringTargetsLd(Vector<InputTarget>& outTargets);
+    static bool isPolicyKey(int32_t keyCode, bool isScreenOn);
+    static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
 
     static inline JNIEnv* jniEnv() {
         return AndroidRuntime::getJNIEnv();
     }
-
-    static bool isAppSwitchKey(int32_t keyCode);
-    static bool isPolicyKey(int32_t keyCode, bool isScreenOn);
-    static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
 };
 
 // ----------------------------------------------------------------------------
@@ -445,10 +261,7 @@
 NativeInputManager::NativeInputManager(jobject callbacksObj) :
     mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
     mMaxEventsPerSecond(-1),
-    mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0),
-    mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true),
-    mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
-    mFocusedApplication(NULL) {
+    mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0) {
     JNIEnv* env = jniEnv();
 
     mCallbacksObj = env->NewGlobalRef(callbacksObj);
@@ -461,27 +274,16 @@
     JNIEnv* env = jniEnv();
 
     env->DeleteGlobalRef(mCallbacksObj);
-
-    releaseFocusedApplicationLd(env);
 }
 
-String8 NativeInputManager::dump() {
-    String8 dump;
-    { // acquire lock
-        AutoMutex _l(mDisplayLock);
-        dump.append("Native Input Dispatcher State:\n");
-        dumpDispatchStateLd(dump);
-        dump.append("\n");
-    } // release lock
+void NativeInputManager::dump(String8& dump) {
+    dump.append("Input Reader State:\n");
+    mInputManager->getReader()->dump(dump);
+    dump.append("\n");
 
-    dump.append("Input Devices:\n");
-    dumpDeviceInfo(dump);
-
-    return dump;
-}
-
-bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
-    return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
+    dump.append("Input Dispatcher State:\n");
+    mInputManager->getDispatcher()->dump(dump);
+    dump.append("\n");
 }
 
 bool NativeInputManager::isPolicyKey(int32_t keyCode, bool isScreenOn) {
@@ -503,6 +305,7 @@
     case AKEYCODE_MEDIA_PREVIOUS:
     case AKEYCODE_MEDIA_REWIND:
     case AKEYCODE_MEDIA_FAST_FORWARD:
+        // The policy always cares about these keys.
         return true;
     default:
         // We need to pass all keys to the policy in the following cases:
@@ -565,12 +368,9 @@
         mInputChannelObjWeakTable.add(inputChannel.get(), inputChannelObjWeak);
     }
 
-    status = mInputManager->registerInputChannel(inputChannel);
+    status = mInputManager->getDispatcher()->registerInputChannel(inputChannel, monitor);
     if (! status) {
         // Success.
-        if (monitor) {
-            registerMonitoringChannel(inputChannel);
-        }
         return OK;
     }
 
@@ -604,9 +404,7 @@
 
     env->DeleteWeakGlobalRef(inputChannelObjWeak);
 
-    unregisterMonitoringChannel(inputChannel);
-
-    return mInputManager->unregisterInputChannel(inputChannel);
+    return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel);
 }
 
 jobject NativeInputManager::getInputChannelObjLocal(JNIEnv* env,
@@ -710,20 +508,11 @@
     }
 
     if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
-        pokeUserActivity(when, POWER_MANAGER_BUTTON_EVENT);
+        android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT);
     }
 
     if (wmActions & WM_ACTION_PASS_TO_USER) {
         actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
-
-        if (down && isAppSwitchKey(keyCode)) {
-            JNIEnv* env = jniEnv();
-
-            env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing);
-            checkAndClearExceptionFromCallback(env, "notifyAppSwitchComing");
-
-            actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING;
-        }
     }
 
     return actions;
@@ -906,13 +695,42 @@
     JNIEnv* env = jniEnv();
 
     InputConfiguration config;
-    mInputManager->getInputConfiguration(& config);
+    mInputManager->getReader()->getInputConfiguration(& config);
 
     env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged,
             when, config.touchScreen, config.keyboard, config.navigation);
     checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
 }
 
+nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("notifyANR");
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    ApplicationToken* token = static_cast<ApplicationToken*>(inputApplicationHandle.get());
+    jweak tokenObjWeak = token->getTokenObj();
+
+    jlong newTimeout;
+    jobject tokenObjLocal = env->NewLocalRef(tokenObjWeak);
+    if (tokenObjLocal) {
+        newTimeout = env->CallLongMethod(mCallbacksObj,
+                gCallbacksClassInfo.notifyANR, tokenObjLocal);
+        if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
+            newTimeout = 0; // abort dispatch
+        } else {
+            assert(newTimeout >= 0);
+        }
+
+        env->DeleteLocalRef(tokenObjLocal);
+    } else {
+        newTimeout = 0; // abort dispatch
+    }
+
+    return newTimeout;
+}
+
 void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputChannel) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
     LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string());
@@ -928,12 +746,9 @@
 
         env->DeleteLocalRef(inputChannelObjLocal);
     }
-
-    unregisterMonitoringChannel(inputChannel);
 }
 
-bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel,
-        nsecs_t& outNewTimeout) {
+nsecs_t NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
     LOGD("notifyInputChannelANR - inputChannel='%s'",
             inputChannel->getName().string());
@@ -947,20 +762,17 @@
         newTimeout = env->CallLongMethod(mCallbacksObj,
                 gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal);
         if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) {
-            newTimeout = -2;
+            newTimeout = 0; // abort dispatch
+        } else {
+            assert(newTimeout >= 0);
         }
 
         env->DeleteLocalRef(inputChannelObjLocal);
     } else {
-        newTimeout = -2;
+        newTimeout = 0; // abort dispatch
     }
 
-    if (newTimeout == -2) {
-        return false; // abort
-    }
-
-    outNewTimeout = newTimeout;
-    return true; // resume
+    return newTimeout;
 }
 
 void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) {
@@ -981,27 +793,6 @@
     }
 }
 
-bool NativeInputManager::notifyANR(jobject tokenObj, nsecs_t& outNewTimeout) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("notifyANR");
-#endif
-
-    JNIEnv* env = jniEnv();
-
-    jlong newTimeout = env->CallLongMethod(mCallbacksObj,
-            gCallbacksClassInfo.notifyANR, tokenObj);
-    if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
-        newTimeout = -2;
-    }
-
-    if (newTimeout == -2) {
-        return false; // abort
-    }
-
-    outNewTimeout = newTimeout;
-    return true; // resume
-}
-
 nsecs_t NativeInputManager::getKeyRepeatTimeout() {
     if (! isScreenOn()) {
         // Disable key repeat when the screen is off.
@@ -1032,89 +823,26 @@
 }
 
 void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
-#if DEBUG_FOCUS
-    LOGD("setInputWindows");
-#endif
-    { // acquire lock
-        AutoMutex _l(mDispatchLock);
+    Vector<InputWindow> windows;
 
-        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();
+    jsize length = env->GetArrayLength(windowObjArray);
+    for (jsize i = 0; i < length; i++) {
+        jobject inputTargetObj = env->GetObjectArrayElement(windowObjArray, i);
+        if (! inputTargetObj) {
+            break; // found null element indicating end of used portion of the array
         }
 
-        bool hadFocusedWindow = mFocusedWindow != NULL;
-
-        mWindows.clear();
-        mFocusedWindow = NULL;
-        mWallpaperWindows.clear();
-
-        if (windowObjArray) {
-            mWindowsReady = true;
-
-            jsize length = env->GetArrayLength(windowObjArray);
-            for (jsize i = 0; i < length; i++) {
-                jobject inputTargetObj = env->GetObjectArrayElement(windowObjArray, i);
-                if (! inputTargetObj) {
-                    break; // found null element indicating end of used portion of the array
-                }
-
-                mWindows.push();
-                InputWindow& window = mWindows.editTop();
-                bool valid = populateWindow(env, inputTargetObj, window);
-                if (! valid) {
-                    mWindows.pop();
-                }
-
-                env->DeleteLocalRef(inputTargetObj);
-            }
-
-            size_t numWindows = mWindows.size();
-            for (size_t i = 0; i < numWindows; i++) {
-                InputWindow* window = & mWindows.editItemAt(i);
-                if (window->hasFocus) {
-                    mFocusedWindow = window;
-                }
-
-                if (window->layoutParamsType == 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;
-                }
-            }
-        } else {
-            mWindowsReady = false;
+        windows.push();
+        InputWindow& window = windows.editTop();
+        bool valid = populateWindow(env, inputTargetObj, window);
+        if (! valid) {
+            windows.pop();
         }
 
-        mTempTouchedWallpaperChannels.clear();
+        env->DeleteLocalRef(inputTargetObj);
+    }
 
-        if ((hadFocusedWindow && ! mFocusedWindow)
-                || (mFocusedWindow && ! mFocusedWindow->visible)) {
-            preemptInputDispatch();
-        }
-
-        mDispatchStateChanged.broadcast();
-
-#if DEBUG_FOCUS
-        logDispatchStateLd();
-#endif
-    } // release lock
+    mInputManager->getDispatcher()->setInputWindows(windows);
 }
 
 bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj,
@@ -1205,663 +933,60 @@
 }
 
 void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationObj) {
-#if DEBUG_FOCUS
-    LOGD("setFocusedApplication");
-#endif
-    { // acquire lock
-        AutoMutex _l(mDispatchLock);
+    if (applicationObj) {
+        jstring nameObj = jstring(env->GetObjectField(applicationObj,
+                gInputApplicationClassInfo.name));
+        jlong dispatchingTimeoutNanos = env->GetLongField(applicationObj,
+                gInputApplicationClassInfo.dispatchingTimeoutNanos);
+        jobject tokenObj = env->GetObjectField(applicationObj,
+                gInputApplicationClassInfo.token);
+        jweak tokenObjWeak = env->NewWeakGlobalRef(tokenObj);
+        if (! tokenObjWeak) {
+            LOGE("Could not create weak reference for application token.");
+            LOGE_EX(env);
+            env->ExceptionClear();
+        }
+        env->DeleteLocalRef(tokenObj);
 
-        releaseFocusedApplicationLd(env);
-
-        if (applicationObj) {
-            jstring nameObj = jstring(env->GetObjectField(applicationObj,
-                    gInputApplicationClassInfo.name));
-            jlong dispatchingTimeoutNanos = env->GetLongField(applicationObj,
-                    gInputApplicationClassInfo.dispatchingTimeoutNanos);
-            jobject tokenObj = env->GetObjectField(applicationObj,
-                    gInputApplicationClassInfo.token);
-            jweak tokenObjWeak = env->NewWeakGlobalRef(tokenObj);
-            if (! tokenObjWeak) {
-                LOGE("Could not create weak reference for application token.");
-                LOGE_EX(env);
-                env->ExceptionClear();
-            }
-            env->DeleteLocalRef(tokenObj);
-
-            mFocusedApplication = & mFocusedApplicationStorage;
-
-            if (nameObj) {
-                const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
-                mFocusedApplication->name.setTo(nameStr);
-                env->ReleaseStringUTFChars(nameObj, nameStr);
-                env->DeleteLocalRef(nameObj);
-            } else {
-                LOGE("InputApplication.name should not be null.");
-                mFocusedApplication->name.setTo("unknown");
-            }
-
-            mFocusedApplication->dispatchingTimeout = dispatchingTimeoutNanos;
-            mFocusedApplication->tokenObjWeak = tokenObjWeak;
+        String8 name;
+        if (nameObj) {
+            const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
+            name.setTo(nameStr);
+            env->ReleaseStringUTFChars(nameObj, nameStr);
+            env->DeleteLocalRef(nameObj);
+        } else {
+            LOGE("InputApplication.name should not be null.");
+            name.setTo("unknown");
         }
 
-        mDispatchStateChanged.broadcast();
-
-#if DEBUG_FOCUS
-        logDispatchStateLd();
-#endif
-    } // release lock
-}
-
-void NativeInputManager::releaseFocusedApplicationLd(JNIEnv* env) {
-    if (mFocusedApplication) {
-        env->DeleteWeakGlobalRef(mFocusedApplication->tokenObjWeak);
-        mFocusedApplication = NULL;
+        InputApplication application;
+        application.name = name;
+        application.dispatchingTimeout = dispatchingTimeoutNanos;
+        application.handle = new ApplicationToken(tokenObjWeak);
+        mInputManager->getDispatcher()->setFocusedApplication(& application);
+    } else {
+        mInputManager->getDispatcher()->setFocusedApplication(NULL);
     }
 }
 
 void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
-#if DEBUG_FOCUS
-    LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
-#endif
-
-    { // acquire lock
-        AutoMutex _l(mDispatchLock);
-
-        if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
-            mDispatchEnabled = enabled;
-            mDispatchFrozen = frozen;
-
-            mDispatchStateChanged.broadcast();
-        }
-
-#if DEBUG_FOCUS
-        logDispatchStateLd();
-#endif
-    } // release lock
+    mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen);
 }
 
 void NativeInputManager::preemptInputDispatch() {
-#if DEBUG_FOCUS
-    LOGD("preemptInputDispatch");
-#endif
-
-    mInputManager->preemptInputDispatch();
+    mInputManager->getDispatcher()->preemptInputDispatch();
 }
 
-int32_t NativeInputManager::waitForFocusedWindowLd(uint32_t policyFlags,
-        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
-        InputWindow*& outFocusedWindow) {
-
-    int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-    bool firstIteration = true;
-    ANRTimer anrTimer;
-    for (;;) {
-        if (firstIteration) {
-            firstIteration = false;
-        } else {
-            if (! anrTimer.waitForDispatchStateChangeLd(this)) {
-                LOGW("Dropping event because the dispatcher timed out waiting to identify "
-                        "the window that should receive it.");
-                injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
-                break;
-            }
-        }
-
-        // If dispatch is not enabled then fail.
-        if (! mDispatchEnabled) {
-            LOGI("Dropping event because input dispatch is disabled.");
-            injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            break;
-        }
-
-        // If dispatch is frozen or we don't have valid window data yet then wait.
-        if (mDispatchFrozen || ! mWindowsReady) {
-#if DEBUG_FOCUS
-            LOGD("Waiting because dispatch is frozen or windows are not ready.");
-#endif
-            anrTimer.dispatchFrozenBySystem();
-            continue;
-        }
-
-        // If there is no currently focused window and no focused application
-        // then drop the event.
-        if (! mFocusedWindow) {
-            if (mFocusedApplication) {
-#if DEBUG_FOCUS
-                LOGD("Waiting because there is no focused window but there is a "
-                        "focused application that may yet introduce a new target: '%s'.",
-                        mFocusedApplication->name.string());
-#endif
-                continue;
-            }
-
-            LOGI("Dropping event because there is no focused window or focused application.");
-            injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            break;
-        }
-
-        // Check permissions.
-        if (! checkInjectionPermission(mFocusedWindow, injectorPid, injectorUid)) {
-            injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-            break;
-        }
-
-        // If the currently focused window is paused then keep waiting.
-        if (mFocusedWindow->paused) {
-#if DEBUG_FOCUS
-            LOGD("Waiting because focused window is paused.");
-#endif
-            anrTimer.dispatchPausedByApplication(mFocusedWindow);
-            continue;
-        }
-
-        // Success!
-        break; // done waiting, exit loop
-    }
-
-    // Output targets.
-    if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
-        addTarget(mFocusedWindow, InputTarget::FLAG_SYNC,
-                anrTimer.getTimeSpentWaitingForApplication(), outTargets);
-
-        outFocusedWindow = mFocusedWindow;
-    } else {
-        outFocusedWindow = NULL;
-    }
-
-#if DEBUG_FOCUS
-    LOGD("waitForFocusedWindow finished: injectionResult=%d",
-            injectionResult);
-    logDispatchStateLd();
-#endif
-    return injectionResult;
-}
-
-enum InjectionPermission {
-    INJECTION_PERMISSION_UNKNOWN,
-    INJECTION_PERMISSION_GRANTED,
-    INJECTION_PERMISSION_DENIED
-};
-
-int32_t NativeInputManager::waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
-        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
-        InputWindow*& outTouchedWindow) {
-    nsecs_t startTime = now();
-
-    // For security reasons, we defer updating the touch state until we are sure that
-    // event injection will be allowed.
-    //
-    // FIXME In the original code, screenWasOff could never be set to true.
-    //       The reason is that the POLICY_FLAG_WOKE_HERE
-    //       and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
-    //       EV_KEY, EV_REL and EV_ABS events.  As it happens, the touch event was
-    //       actually enqueued using the policyFlags that appeared in the final EV_SYN
-    //       events upon which no preprocessing took place.  So policyFlags was always 0.
-    //       In the new native input dispatcher we're a bit more careful about event
-    //       preprocessing so the touches we receive can actually have non-zero policyFlags.
-    //       Unfortunately we obtain undesirable behavior.
-    //
-    //       Here's what happens:
-    //
-    //       When the device dims in anticipation of going to sleep, touches
-    //       in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
-    //       the device to brighten and reset the user activity timer.
-    //       Touches on other windows (such as the launcher window)
-    //       are dropped.  Then after a moment, the device goes to sleep.  Oops.
-    //
-    //       Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
-    //       instead of POLICY_FLAG_WOKE_HERE...
-    //
-    bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
-
-    int32_t action = motionEvent->getAction();
-
-    bool firstIteration = true;
-    ANRTimer anrTimer;
-    int32_t injectionResult;
-    InjectionPermission injectionPermission;
-    for (;;) {
-        if (firstIteration) {
-            firstIteration = false;
-        } else {
-            if (! anrTimer.waitForDispatchStateChangeLd(this)) {
-                LOGW("Dropping event because the dispatcher timed out waiting to identify "
-                        "the window that should receive it.");
-                injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
-                injectionPermission = INJECTION_PERMISSION_UNKNOWN;
-                break; // timed out, exit wait loop
-            }
-        }
-
-        // If dispatch is not enabled then fail.
-        if (! mDispatchEnabled) {
-            LOGI("Dropping event because input dispatch is disabled.");
-            injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            injectionPermission = INJECTION_PERMISSION_UNKNOWN;
-            break; // failed, exit wait loop
-        }
-
-        // If dispatch is frozen or we don't have valid window data yet then wait.
-        if (mDispatchFrozen || ! mWindowsReady) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-            LOGD("Waiting because dispatch is frozen or windows are not ready.");
-#endif
-            anrTimer.dispatchFrozenBySystem();
-            continue;
-        }
-
-        // Update the touch state as needed based on the properties of the touch event.
-        if (action == AMOTION_EVENT_ACTION_DOWN) {
-            /* Case 1: ACTION_DOWN */
-
-            InputWindow* newTouchedWindow = NULL;
-            mTempTouchedOutsideTargets.clear();
-
-            int32_t x = int32_t(motionEvent->getX(0));
-            int32_t y = int32_t(motionEvent->getY(0));
-            InputWindow* topErrorWindow = NULL;
-            bool obscured = false;
-
-            // 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);
-                int32_t flags = window->layoutParamsFlags;
-
-                if (flags & FLAG_SYSTEM_ERROR) {
-                    if (! topErrorWindow) {
-                        topErrorWindow = window;
-                    }
-                }
-
-                if (window->visible) {
-                    if (! (flags & FLAG_NOT_TOUCHABLE)) {
-                        bool isTouchModal = (flags &
-                                (FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL)) == 0;
-                        if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
-                            if (! screenWasOff || flags & FLAG_TOUCHABLE_WHEN_WAKING) {
-                                newTouchedWindow = window;
-                                obscured = isWindowObscuredLocked(window);
-                            }
-                            break; // found touched window, exit window loop
-                        }
-                    }
-
-                    if (flags & FLAG_WATCH_OUTSIDE_TOUCH) {
-                        OutsideTarget outsideTarget;
-                        outsideTarget.window = window;
-                        outsideTarget.obscured = isWindowObscuredLocked(window);
-                        mTempTouchedOutsideTargets.push(outsideTarget);
-                    }
-                }
-            }
-
-            // If there is an error window but it is not taking focus (typically because
-            // it is invisible) then wait for it.  Any other focused window may in
-            // fact be in ANR state.
-            if (topErrorWindow && newTouchedWindow != topErrorWindow) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-                LOGD("Waiting because system error window is pending.");
-#endif
-                anrTimer.dispatchFrozenBySystem();
-                continue; // wait some more
-            }
-
-            // If we did not find a touched window then fail.
-            if (! newTouchedWindow) {
-                if (mFocusedApplication) {
-#if DEBUG_FOCUS
-                    LOGD("Waiting because there is no focused window but there is a "
-                            "focused application that may yet introduce a new target: '%s'.",
-                            mFocusedApplication->name.string());
-#endif
-                    continue;
-                }
-
-                LOGI("Dropping event because there is no touched window or focused application.");
-                injectionResult = INPUT_EVENT_INJECTION_FAILED;
-                injectionPermission = INJECTION_PERMISSION_UNKNOWN;
-                break; // failed, exit wait loop
-            }
-
-            // Check permissions.
-            if (! checkInjectionPermission(newTouchedWindow, injectorPid, injectorUid)) {
-                injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-                injectionPermission = INJECTION_PERMISSION_DENIED;
-                break; // failed, exit wait loop
-            }
-
-            // 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
-                anrTimer.dispatchPausedByApplication(newTouchedWindow);
-                continue; // wait some more
-            }
-
-            // Success!  Update the touch dispatch state for real.
-            releaseTouchedWindowLd();
-
-            mTouchedWindow = newTouchedWindow;
-            mTouchedWindowIsObscured = obscured;
-
-            if (newTouchedWindow->hasWallpaper) {
-                mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
-            }
-
-            injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            break; // done
-        } else {
-            /* Case 2: Everything but ACTION_DOWN */
-
-            // Check permissions.
-            if (! checkInjectionPermission(mTouchedWindow, injectorPid, injectorUid)) {
-                injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-                injectionPermission = INJECTION_PERMISSION_DENIED;
-                break; // failed, exit wait loop
-            }
-
-            // If the pointer is not currently down, then ignore the event.
-            if (! mTouchDown) {
-                LOGI("Dropping event because the pointer is not down.");
-                injectionResult = INPUT_EVENT_INJECTION_FAILED;
-                injectionPermission = INJECTION_PERMISSION_GRANTED;
-                break; // failed, exit wait loop
-            }
-
-            // If there is no currently touched window then fail.
-            if (! mTouchedWindow) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-                LOGD("Dropping event because there is no touched window to receive it.");
-#endif
-                injectionResult = INPUT_EVENT_INJECTION_FAILED;
-                injectionPermission = INJECTION_PERMISSION_GRANTED;
-                break; // failed, exit wait loop
-            }
-
-            // 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
-                anrTimer.dispatchPausedByApplication(mTouchedWindow);
-                continue; // wait some more
-            }
-
-            // Success!
-            injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            break; // done
-        }
-    }
-
-    // Output targets.
-    if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
-        size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
-        for (size_t i = 0; i < numWallpaperWindows; i++) {
-            addTarget(mTouchedWallpaperWindows[i],
-                    InputTarget::FLAG_WINDOW_IS_OBSCURED, 0, outTargets);
-        }
-
-        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;
-            }
-            addTarget(outsideTarget.window, outsideTargetFlags, 0, outTargets);
-        }
-
-        int32_t targetFlags = InputTarget::FLAG_SYNC;
-        if (mTouchedWindowIsObscured) {
-            targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
-        }
-        addTarget(mTouchedWindow, targetFlags,
-                anrTimer.getTimeSpentWaitingForApplication(), outTargets);
-        outTouchedWindow = mTouchedWindow;
-    } else {
-        outTouchedWindow = NULL;
-    }
-    mTempTouchedOutsideTargets.clear();
-
-    // Check injection permission once and for all.
-    if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
-        if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow,
-                injectorPid, injectorUid)) {
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-        } else {
-            injectionPermission = INJECTION_PERMISSION_DENIED;
-        }
-    }
-
-    // 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!
-                LOGW("Pointer down received while already down.");
-            } else {
-                mTouchDown = true;
-            }
-
-            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.
-                releaseTouchedWindowLd();
-            }
-        } else if (action == AMOTION_EVENT_ACTION_UP) {
-            mTouchDown = false;
-            releaseTouchedWindowLd();
-        }
-    } else {
-        LOGW("Not updating touch focus because injection was denied.");
-    }
-
-#if DEBUG_FOCUS
-    LOGD("waitForTouchedWindow finished: injectionResult=%d",
-            injectionResult);
-    logDispatchStateLd();
-#endif
-    return injectionResult;
-}
-
-void NativeInputManager::releaseTouchedWindowLd() {
-    mTouchedWindow = NULL;
-    mTouchedWindowIsObscured = false;
-    mTouchedWallpaperWindows.clear();
-}
-
-void NativeInputManager::addTarget(const InputWindow* window, int32_t targetFlags,
-        nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets) {
-    nsecs_t timeout = window->dispatchingTimeout - timeSpentWaitingForApplication;
-    if (timeout < MIN_INPUT_DISPATCHING_TIMEOUT) {
-        timeout = MIN_INPUT_DISPATCHING_TIMEOUT;
-    }
-
-    outTargets.push();
-
-    InputTarget& target = outTargets.editTop();
-    target.inputChannel = window->inputChannel;
-    target.flags = targetFlags;
-    target.timeout = timeout;
-    target.xOffset = - window->frameLeft;
-    target.yOffset = - window->frameTop;
-}
-
-bool NativeInputManager::checkInjectionPermission(const InputWindow* window,
-        int32_t injectorPid, int32_t injectorUid) {
-    if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
-        JNIEnv* env = jniEnv();
-        jboolean result = env->CallBooleanMethod(mCallbacksObj,
-                gCallbacksClassInfo.checkInjectEventsPermission, injectorPid, injectorUid);
-        checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission");
-
-        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(),
-                        window->ownerUid);
-            } else {
-                LOGW("Permission denied: injecting event from pid %d uid %d",
-                        injectorPid, injectorUid);
-            }
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool NativeInputManager::isWindowObscuredLocked(const InputWindow* window) {
-    size_t numWindows = mWindows.size();
-    for (size_t i = 0; i < numWindows; i++) {
-        const InputWindow* other = & mWindows.itemAt(i);
-        if (other == window) {
-            break;
-        }
-        if (other->visible && window->visibleFrameIntersects(other)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-int32_t NativeInputManager::waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
-        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("waitForKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
-            policyFlags, injectorPid, injectorUid);
-#endif
-
-    int32_t windowType;
-    { // acquire lock
-        AutoMutex _l(mDispatchLock);
-
-        InputWindow* focusedWindow;
-        int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
-                injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
-        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
-            return injectionResult;
-        }
-
-        windowType = focusedWindow->layoutParamsType;
-    } // release lock
-
-    if (isPolicyKey(keyEvent->getKeyCode(), isScreenOn())) {
-        const InputTarget& target = outTargets.top();
-        bool consumed = interceptKeyBeforeDispatching(target, keyEvent, policyFlags);
-        if (consumed) {
-            outTargets.clear();
-            return INPUT_EVENT_INJECTION_SUCCEEDED;
-        }
-
-        addMonitoringTargetsLd(outTargets);
-    }
-
-    pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
-    return INPUT_EVENT_INJECTION_SUCCEEDED;
-}
-
-int32_t NativeInputManager::waitForMotionEventTargets(MotionEvent* motionEvent,
-        uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
-        Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("waitForMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
-            policyFlags, injectorPid, injectorUid);
-#endif
-
-    int32_t source = motionEvent->getSource();
-    if (source & AINPUT_SOURCE_CLASS_POINTER) {
-        return waitForTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
-                outTargets);
-    } else {
-        return waitForNonTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
-                outTargets);
-    }
-}
-
-int32_t NativeInputManager::waitForNonTouchEventTargets(MotionEvent* motionEvent,
-        uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
-        Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("waitForNonTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
-            policyFlags, injectorPid, injectorUid);
-#endif
-
-    int32_t windowType;
-    { // acquire lock
-        AutoMutex _l(mDispatchLock);
-
-        InputWindow* focusedWindow;
-        int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
-                injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
-        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
-            return injectionResult;
-        }
-
-        windowType = focusedWindow->layoutParamsType;
-
-        addMonitoringTargetsLd(outTargets);
-    } // release lock
-
-    pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
-    return INPUT_EVENT_INJECTION_SUCCEEDED;
-}
-
-int32_t NativeInputManager::waitForTouchEventTargets(MotionEvent* motionEvent,
-        uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
-        Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("waitForTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
-            policyFlags, injectorPid, injectorUid);
-#endif
-
-    int32_t windowType;
-    { // acquire lock
-        AutoMutex _l(mDispatchLock);
-
-        InputWindow* touchedWindow;
-        int32_t injectionResult = waitForTouchedWindowLd(motionEvent, policyFlags,
-                injectorPid, injectorUid, outTargets, /*out*/ touchedWindow);
-        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
-            return injectionResult;
-        }
-
-        windowType = touchedWindow->layoutParamsType;
-
-        addMonitoringTargetsLd(outTargets);
-    } // release lock
-
-    int32_t eventType;
-    switch (motionEvent->getAction()) {
-    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 (motionEvent->getEventTime() - motionEvent->getDownTime()
-                >= EVENT_IGNORE_DURATION) {
-            eventType = POWER_MANAGER_TOUCH_EVENT;
-        } else {
-            eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
-        }
-        break;
-    }
-    pokeUserActivityIfNeeded(windowType, eventType);
-    return INPUT_EVENT_INJECTION_SUCCEEDED;
-}
-
-bool NativeInputManager::interceptKeyBeforeDispatching(const InputTarget& target,
+bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
         const KeyEvent* keyEvent, uint32_t policyFlags) {
+    bool isScreenOn = this->isScreenOn();
+    if (! isPolicyKey(keyEvent->getKeyCode(), isScreenOn)) {
+        return false;
+    }
+
     JNIEnv* env = jniEnv();
 
-    jobject inputChannelObj = getInputChannelObjLocal(env, target.inputChannel);
+    jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel);
     if (inputChannelObj) {
         jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
                 gCallbacksClassInfo.interceptKeyBeforeDispatching,
@@ -1875,253 +1000,27 @@
         return consumed && ! error;
     } else {
         LOGW("Could not apply key dispatch policy because input channel '%s' is "
-                "no longer valid.", target.inputChannel->getName().string());
+                "no longer valid.", inputChannel->getName().string());
         return false;
     }
 }
 
-void NativeInputManager::pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType) {
-    if (windowType != TYPE_KEYGUARD) {
-        nsecs_t eventTime = now();
-        pokeUserActivity(eventTime, eventType);
+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);
-}
 
-void NativeInputManager::registerMonitoringChannel(const sp<InputChannel>& inputChannel) {
-    { // acquire lock
-         AutoMutex _l(mDispatchLock);
-         mMonitoringChannels.push(inputChannel);
-    } // release lock
-}
-
-void NativeInputManager::unregisterMonitoringChannel(const sp<InputChannel>& inputChannel) {
-    { // acquire lock
-         AutoMutex _l(mDispatchLock);
-
-         for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-             if (mMonitoringChannels[i] == inputChannel) {
-                 mMonitoringChannels.removeAt(i);
-                 break;
-             }
-         }
-    } // release lock
-}
-
-void NativeInputManager::addMonitoringTargetsLd(Vector<InputTarget>& outTargets) {
-    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-        outTargets.push();
-
-        InputTarget& target = outTargets.editTop();
-        target.inputChannel = mMonitoringChannels[i];
-        target.flags = 0;
-        target.timeout = -1;
-        target.xOffset = 0;
-        target.yOffset = 0;
-    }
-}
-
-static void dumpMotionRange(String8& dump,
-        const char* name, const InputDeviceInfo::MotionRange* range) {
-    if (range) {
-        dump.appendFormat("      %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
-                name, range->min, range->max, range->flat, range->fuzz);
-    }
-}
-
-#define DUMP_MOTION_RANGE(range) \
-    dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
-
-void NativeInputManager::dumpDeviceInfo(String8& dump) {
-    Vector<int32_t> deviceIds;
-    mInputManager->getInputDeviceIds(deviceIds);
-
-    InputDeviceInfo deviceInfo;
-    for (size_t i = 0; i < deviceIds.size(); i++) {
-        int32_t deviceId = deviceIds[i];
-
-        status_t result = mInputManager->getInputDeviceInfo(deviceId, & deviceInfo);
-        if (result == NAME_NOT_FOUND) {
-            continue;
-        } else if (result != OK) {
-            dump.appendFormat("  ** Unexpected error %d getting information about input devices.\n",
-                    result);
-            continue;
-        }
-
-        dump.appendFormat("  Device %d: '%s'\n",
-                deviceInfo.getId(), deviceInfo.getName().string());
-        dump.appendFormat("    sources = 0x%08x\n",
-                deviceInfo.getSources());
-        dump.appendFormat("    keyboardType = %d\n",
-                deviceInfo.getKeyboardType());
-
-        dump.append("    motion ranges:\n");
-        DUMP_MOTION_RANGE(X);
-        DUMP_MOTION_RANGE(Y);
-        DUMP_MOTION_RANGE(PRESSURE);
-        DUMP_MOTION_RANGE(SIZE);
-        DUMP_MOTION_RANGE(TOUCH_MAJOR);
-        DUMP_MOTION_RANGE(TOUCH_MINOR);
-        DUMP_MOTION_RANGE(TOOL_MAJOR);
-        DUMP_MOTION_RANGE(TOOL_MINOR);
-        DUMP_MOTION_RANGE(ORIENTATION);
-    }
-}
-
-#undef DUMP_MOTION_RANGE
-
-void NativeInputManager::logDispatchStateLd() {
-    String8 dump;
-    dumpDispatchStateLd(dump);
-    LOGD("%s", dump.string());
-}
-
-void NativeInputManager::dumpDispatchStateLd(String8& dump) {
-    dump.appendFormat("  dispatchEnabled: %d\n", mDispatchEnabled);
-    dump.appendFormat("  dispatchFrozen: %d\n", mDispatchFrozen);
-    dump.appendFormat("  windowsReady: %d\n", mWindowsReady);
-
-    if (mFocusedApplication) {
-        dump.appendFormat("  focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
-                mFocusedApplication->name.string(),
-                mFocusedApplication->dispatchingTimeout / 1000000.0);
-    } else {
-        dump.append("  focusedApplication: <null>\n");
-    }
-    dump.appendFormat("  focusedWindow: '%s'\n",
-            mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
-    dump.appendFormat("  touchedWindow: '%s', touchDown=%d\n",
-            mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
-            mTouchDown);
-    for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
-        dump.appendFormat("  touchedWallpaperWindows[%d]: '%s'\n",
-                i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
-    }
-    for (size_t i = 0; i < mWindows.size(); i++) {
-        dump.appendFormat("  windows[%d]: '%s', paused=%d, hasFocus=%d, hasWallpaper=%d, "
-                "visible=%d, flags=0x%08x, type=0x%08x, "
-                "frame=[%d,%d][%d,%d], "
-                "visibleFrame=[%d,%d][%d,%d], "
-                "touchableArea=[%d,%d][%d,%d], "
-                "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
-                i, mWindows[i].inputChannel->getName().string(),
-                mWindows[i].paused, mWindows[i].hasFocus, mWindows[i].hasWallpaper,
-                mWindows[i].visible, mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
-                mWindows[i].frameLeft, mWindows[i].frameTop,
-                mWindows[i].frameRight, mWindows[i].frameBottom,
-                mWindows[i].visibleFrameLeft, mWindows[i].visibleFrameTop,
-                mWindows[i].visibleFrameRight, mWindows[i].visibleFrameBottom,
-                mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
-                mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
-                mWindows[i].ownerPid, mWindows[i].ownerUid,
-                mWindows[i].dispatchingTimeout / 1000000.0);
-    }
-
-    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-        dump.appendFormat("  monitoringChannel[%d]: '%s'\n",
-                i, mMonitoringChannels[i]->getName().string());
-    }
-}
-
-// ----------------------------------------------------------------------------
-
-bool NativeInputManager::InputWindow::visibleFrameIntersects(const InputWindow* other) const {
-    return visibleFrameRight > other->visibleFrameLeft
-        && visibleFrameLeft < other->visibleFrameRight
-        && visibleFrameBottom > other->visibleFrameTop
-        && visibleFrameTop < other->visibleFrameBottom;
-}
-
-bool NativeInputManager::InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
-    return x >= touchableAreaLeft && x <= touchableAreaRight
-            && y >= touchableAreaTop && y <= touchableAreaBottom;
-}
-
-// ----------------------------------------------------------------------------
-
-NativeInputManager::ANRTimer::ANRTimer() :
-        mBudget(APPLICATION), mStartTime(now()), mFrozen(false), mPausedWindow(NULL) {
-}
-
-void NativeInputManager::ANRTimer::dispatchFrozenBySystem() {
-    mFrozen = true;
-}
-
-void NativeInputManager::ANRTimer::dispatchPausedByApplication(InputWindow* pausedWindow) {
-    mPausedWindow = pausedWindow;
-}
-
-bool NativeInputManager::ANRTimer::waitForDispatchStateChangeLd(NativeInputManager* inputManager) {
-    nsecs_t currentTime = now();
-
-    Budget newBudget;
-    nsecs_t dispatchingTimeout;
-    sp<InputChannel> pausedChannel = NULL;
-    jobject tokenObj = NULL;
-    if (mFrozen) {
-        newBudget = SYSTEM;
-        dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
-        mFrozen = false;
-    } else if (mPausedWindow) {
-        newBudget = APPLICATION;
-        dispatchingTimeout = mPausedWindow->dispatchingTimeout;
-        pausedChannel = mPausedWindow->inputChannel;
-        mPausedWindow = NULL;
-    } else if (inputManager->mFocusedApplication) {
-        newBudget = APPLICATION;
-        dispatchingTimeout = inputManager->mFocusedApplication->dispatchingTimeout;
-        tokenObj = jniEnv()->NewLocalRef(inputManager->mFocusedApplication->tokenObjWeak);
-    } else {
-        newBudget = APPLICATION;
-        dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
-    }
-
-    if (mBudget != newBudget) {
-        mBudget = newBudget;
-        mStartTime = currentTime;
-    }
-
-    bool result = false;
-    nsecs_t timeoutRemaining = mStartTime + dispatchingTimeout - currentTime;
-    if (timeoutRemaining > 0
-            && inputManager->mDispatchStateChanged.waitRelative(inputManager->mDispatchLock,
-                    timeoutRemaining) == OK) {
-        result = true;
-    } else {
-        if (pausedChannel != NULL || tokenObj != NULL) {
-            bool resumed;
-            nsecs_t newTimeout = 0;
-
-            inputManager->mDispatchLock.unlock(); // release lock
-            if (pausedChannel != NULL) {
-                resumed = inputManager->notifyInputChannelANR(pausedChannel, /*out*/ newTimeout);
-            } else {
-                resumed = inputManager->notifyANR(tokenObj, /*out*/ newTimeout);
-            }
-            inputManager->mDispatchLock.lock(); // re-acquire lock
-
-            if (resumed) {
-                mStartTime = now() - dispatchingTimeout + newTimeout;
-                result = true;
-            }
-        }
-    }
-
-    if (tokenObj) {
-        jniEnv()->DeleteLocalRef(tokenObj);
-    }
-
+bool NativeInputManager::checkInjectEventsPermissionNonReentrant(
+        int32_t injectorPid, int32_t injectorUid) {
+    JNIEnv* env = jniEnv();
+    jboolean result = env->CallBooleanMethod(mCallbacksObj,
+            gCallbacksClassInfo.checkInjectEventsPermission, injectorPid, injectorUid);
+    checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission");
     return result;
 }
 
-nsecs_t NativeInputManager::ANRTimer::getTimeSpentWaitingForApplication() const {
-    return mBudget == APPLICATION ? now() - mStartTime : 0;
-}
-
 // ----------------------------------------------------------------------------
 
 static sp<NativeInputManager> gNativeInputManager;
@@ -2184,7 +1083,7 @@
         return AKEY_STATE_UNKNOWN;
     }
 
-    return gNativeInputManager->getInputManager()->getScanCodeState(
+    return gNativeInputManager->getInputManager()->getReader()->getScanCodeState(
             deviceId, uint32_t(sourceMask), scanCode);
 }
 
@@ -2194,7 +1093,7 @@
         return AKEY_STATE_UNKNOWN;
     }
 
-    return gNativeInputManager->getInputManager()->getKeyCodeState(
+    return gNativeInputManager->getInputManager()->getReader()->getKeyCodeState(
             deviceId, uint32_t(sourceMask), keyCode);
 }
 
@@ -2204,7 +1103,7 @@
         return AKEY_STATE_UNKNOWN;
     }
 
-    return gNativeInputManager->getInputManager()->getSwitchState(
+    return gNativeInputManager->getInputManager()->getReader()->getSwitchState(
             deviceId, uint32_t(sourceMask), sw);
 }
 
@@ -2219,7 +1118,7 @@
     jsize numCodes = env->GetArrayLength(keyCodes);
     jboolean result;
     if (numCodes == env->GetArrayLength(keyCodes)) {
-        result = gNativeInputManager->getInputManager()->hasKeys(
+        result = gNativeInputManager->getInputManager()->getReader()->hasKeys(
                 deviceId, uint32_t(sourceMask), numCodes, codes, flags);
     } else {
         result = JNI_FALSE;
@@ -2306,14 +1205,14 @@
         KeyEvent keyEvent;
         android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent);
 
-        return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent,
-                injectorPid, injectorUid, syncMode, timeoutMillis);
+        return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
+                & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
     } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
         MotionEvent motionEvent;
         android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent);
 
-        return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent,
-                injectorPid, injectorUid, syncMode, timeoutMillis);
+        return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
+                & motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
     } else {
         jniThrowRuntimeException(env, "Invalid input event type.");
         return INPUT_EVENT_INJECTION_FAILED;
@@ -2363,7 +1262,7 @@
     }
 
     InputDeviceInfo deviceInfo;
-    status_t status = gNativeInputManager->getInputManager()->getInputDeviceInfo(
+    status_t status = gNativeInputManager->getInputManager()->getReader()->getInputDeviceInfo(
             deviceId, & deviceInfo);
     if (status) {
         return NULL;
@@ -2405,7 +1304,7 @@
     }
 
     Vector<int> deviceIds;
-    gNativeInputManager->getInputManager()->getInputDeviceIds(deviceIds);
+    gNativeInputManager->getInputManager()->getReader()->getInputDeviceIds(deviceIds);
 
     jintArray deviceIdsObj = env->NewIntArray(deviceIds.size());
     if (! deviceIdsObj) {
@@ -2421,7 +1320,8 @@
         return NULL;
     }
 
-    String8 dump(gNativeInputManager->dump());
+    String8 dump;
+    gNativeInputManager->dump(dump);
     return env->NewStringUTF(dump.string());
 }
 
@@ -2519,9 +1419,6 @@
     GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
             "checkInjectEventsPermission", "(II)Z");
 
-    GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz,
-            "notifyAppSwitchComing", "()V");
-
     GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, gCallbacksClassInfo.clazz,
             "filterTouchEvents", "()Z");
 
diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h
index 7c329b2..af10711 100644
--- a/services/jni/com_android_server_PowerManagerService.h
+++ b/services/jni/com_android_server_PowerManagerService.h
@@ -20,20 +20,10 @@
 #include "JNIHelp.h"
 #include "jni.h"
 
+#include <ui/PowerManager.h>
+
 namespace android {
 
-enum {
-    POWER_MANAGER_OTHER_EVENT = 0,
-    POWER_MANAGER_CHEEK_EVENT = 1,
-    POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either
-                                   // up events or LONG_TOUCH events.
-    POWER_MANAGER_LONG_TOUCH_EVENT = 3,
-    POWER_MANAGER_TOUCH_UP_EVENT = 4,
-    POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
-
-    POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code.
-};
-
 extern bool android_server_PowerManagerService_isScreenOn();
 extern bool android_server_PowerManagerService_isScreenBright();
 extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType);
diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java
index e5456e4..c20c200 100644
--- a/telephony/java/com/android/internal/telephony/Connection.java
+++ b/telephony/java/com/android/internal/telephony/Connection.java
@@ -40,6 +40,7 @@
         MMI,                            /* not presently used; dial() returns null */
         INVALID_NUMBER,                 /* invalid dial string */
         INVALID_CREDENTIALS,            /* invalid credentials */
+        TIMED_OUT,                      /* client timed out */
         LOST_SIGNAL,
         LIMIT_EXCEEDED,                 /* eg GSM ACM limit exceeded */
         INCOMING_REJECTED,              /* an incoming call that was rejected */
diff --git a/telephony/java/com/android/internal/telephony/sip/SipCallBase.java b/telephony/java/com/android/internal/telephony/sip/SipCallBase.java
index e7eda4f..7e407b2 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipCallBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipCallBase.java
@@ -26,7 +26,6 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import javax.sip.SipException;
 
 abstract class SipCallBase extends Call {
     private static final int MAX_CONNECTIONS_PER_CALL = 5;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index bf1b939..2c99145 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -24,6 +24,7 @@
 import android.net.rtp.AudioStream;
 import android.net.sip.SipAudioCall;
 import android.net.sip.SipErrorCode;
+import android.net.sip.SipException;
 import android.net.sip.SipManager;
 import android.net.sip.SipProfile;
 import android.net.sip.SipSessionState;
@@ -67,8 +68,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import javax.sip.SipException;
-
 /**
  * {@hide}
  */
@@ -827,7 +826,8 @@
                     onError(Connection.DisconnectCause.INVALID_NUMBER);
                     break;
                 case TIME_OUT:
-                    onError(Connection.DisconnectCause.CONGESTION);
+                case TRANSACTION_TERMINTED:
+                    onError(Connection.DisconnectCause.TIMED_OUT);
                     break;
                 case INVALID_CREDENTIALS:
                     onError(Connection.DisconnectCause.INVALID_CREDENTIALS);
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 02f82b3..39083a5 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -20,8 +20,6 @@
 import android.net.rtp.AudioStream;
 import android.os.Message;
 
-import javax.sip.SipException;
-
 /**
  * Interface for making audio calls over SIP.
  * @hide
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index 2e2ca5b..67ba97f 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -43,7 +43,6 @@
 import java.util.List;
 import java.util.Map;
 import javax.sdp.SdpException;
-import javax.sip.SipException;
 
 /**
  * Class that handles an audio call over SIP. 
diff --git a/voip/java/android/net/sip/SipErrorCode.java b/voip/java/android/net/sip/SipErrorCode.java
index 2eb67e8..8624811 100644
--- a/voip/java/android/net/sip/SipErrorCode.java
+++ b/voip/java/android/net/sip/SipErrorCode.java
@@ -31,6 +31,9 @@
     /** When server responds with an error. */
     SERVER_ERROR,
 
+    /** When transaction is terminated unexpectedly. */
+    TRANSACTION_TERMINTED,
+
     /** When some error occurs on the device, possibly due to a bug. */
     CLIENT_ERROR,
 
@@ -41,5 +44,8 @@
     INVALID_REMOTE_URI,
 
     /** When invalid credentials are provided. */
-    INVALID_CREDENTIALS;
+    INVALID_CREDENTIALS,
+
+    /** The client is in a transaction and cannot initiate a new one. */
+    IN_PROGRESS;
 }
diff --git a/voip/java/android/net/sip/SipException.java b/voip/java/android/net/sip/SipException.java
new file mode 100644
index 0000000..d615342
--- /dev/null
+++ b/voip/java/android/net/sip/SipException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.sip;
+
+/**
+ * @hide
+ */
+public class SipException extends Exception {
+    public SipException() {
+    }
+
+    public SipException(String message) {
+        super(message);
+    }
+
+    public SipException(String message, Throwable cause) {
+        // we want to eliminate the dependency on javax.sip.SipException
+        super(message, ((cause instanceof javax.sip.SipException)
+                && (cause.getCause() != null))
+                ? cause.getCause()
+                : cause);
+    }
+}
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index beec8fe..ccae7f9 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -25,7 +25,6 @@
 import android.os.ServiceManager;
 
 import java.text.ParseException;
-import javax.sip.SipException;
 
 /**
  * The class provides API for various SIP related tasks. Specifically, the API
@@ -501,15 +500,15 @@
         }
 
         @Override
-        public void onRegistrationFailed(ISipSession session, String className,
+        public void onRegistrationFailed(ISipSession session, String errorCode,
                 String message) {
-            mListener.onRegistrationFailed(getUri(session), className, message);
+            mListener.onRegistrationFailed(getUri(session), errorCode, message);
         }
 
         @Override
         public void onRegistrationTimeout(ISipSession session) {
             mListener.onRegistrationFailed(getUri(session),
-                    SipException.class.getName(), "registration timed out");
+                    SipErrorCode.TIME_OUT.toString(), "registration timed out");
         }
     }
 }