Fix bug with phantom input windows.

Add dumpsys integration for the native input dispatcher.
Add some InputDevice API stubs.
Add an appendFormat helper method to String8 for printf style
string formatting mainly for debugging purposes.
Use generic ArrayList<WindowState> everywhere in WindowManagerService
to eliminate unnecessary casts all over.

Change-Id: I9d1e3bd90eb7222d10620200477f11b7bfd25e44
diff --git a/api/current.xml b/api/current.xml
index 1ed5811..5070e04 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -173105,6 +173105,19 @@
  visibility="public"
 >
 </constructor>
+<method name="getDevice"
+ return="android.view.InputDevice"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
 <method name="getKeyCharacterMap"
  return="android.view.KeyCharacterMap"
  abstract="false"
@@ -173116,6 +173129,19 @@
  visibility="public"
 >
 </method>
+<method name="getMotionRange"
+ return="android.view.InputDevice.MotionRange"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="range" type="int">
+</parameter>
+</method>
 <method name="getName"
  return="java.lang.String"
  abstract="false"
@@ -173138,6 +173164,118 @@
  visibility="public"
 >
 </method>
+<method name="hasKey"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyCode" type="int">
+</parameter>
+</method>
+<field name="MOTION_RANGE_ORIENTATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOTION_RANGE_PRESSURE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOTION_RANGE_SIZE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOTION_RANGE_TOOL_MAJOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOTION_RANGE_TOOL_MINOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOTION_RANGE_TOUCH_MAJOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOTION_RANGE_TOUCH_MINOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOTION_RANGE_X"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOTION_RANGE_Y"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SOURCE_CLASS_BUTTON"
  type="int"
  transient="false"
@@ -173315,6 +173453,78 @@
 >
 </field>
 </class>
+<class name="InputDevice.MotionRange"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="InputDevice.MotionRange"
+ type="android.view.InputDevice.MotionRange"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getFlat"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFuzz"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMax"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMin"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRange"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
 <class name="InputEvent"
  extends="java.lang.Object"
  abstract="true"
@@ -173325,6 +173535,17 @@
 >
 <implements name="android.os.Parcelable">
 </implements>
+<method name="getDevice"
+ return="android.view.InputDevice"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getDeviceId"
  return="int"
  abstract="false"
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 568caa2..21c2976 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -50,7 +50,7 @@
      * 
      * A {@link KeyEvent} should be interpreted as a button or key press.
      * 
-     * Use {@link #hasKeyCode} to query whether the device supports a particular button or key.
+     * Use {@link #hasKey} to query whether the device supports a particular button or key.
      */
     public static final int SOURCE_CLASS_BUTTON = 0x00000001;
     
@@ -154,7 +154,7 @@
     
     /**
      * The input source is a touch pad or digitizer tablet that is not
-     * associated with a display (unlike {@link SOURCE_TOUCHSCREEN}).
+     * associated with a display (unlike {@link #SOURCE_TOUCHSCREEN}).
      * 
      * @see #SOURCE_CLASS_POSITION
      */
@@ -174,20 +174,79 @@
      */
     public static final int SOURCE_JOYSTICK_RIGHT = 0x02000000 | SOURCE_CLASS_JOYSTICK;
 
-    /*
+    /**
+     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#x}.
+     * 
+     * @see #getMotionRange
+     */
     public static final int MOTION_RANGE_X = 0;
-    public static final int MOTION_RANGE_Y = 1;
-    public static final int MOTION_RANGE_PRESSURE = 2;
-    public static final int MOTION_RANGE_SIZE = 3;
-    public static final int MOTION_RANGE_TOUCH_MAJOR = 4;
-    public static final int MOTION_RANGE_TOUCH_MINOR = 5;
-    public static final int MOTION_RANGE_TOOL_MAJOR = 6;
-    public static final int MOTION_RANGE_TOOL_MINOR = 7;
-    public static final int MOTION_RANGE_ORIENTATION = 8;
     
+    /**
+     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#y}.
+     * 
+     * @see #getMotionRange
+     */
+    public static final int MOTION_RANGE_Y = 1;
+    
+    /**
+     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#pressure}.
+     * 
+     * @see #getMotionRange
+     */
+    public static final int MOTION_RANGE_PRESSURE = 2;
+    
+    /**
+     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#size}.
+     * 
+     * @see #getMotionRange
+     */
+    public static final int MOTION_RANGE_SIZE = 3;
+    
+    /**
+     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#touchMajor}.
+     * 
+     * @see #getMotionRange
+     */
+    public static final int MOTION_RANGE_TOUCH_MAJOR = 4;
+    
+    /**
+     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#touchMinor}.
+     * 
+     * @see #getMotionRange
+     */
+    public static final int MOTION_RANGE_TOUCH_MINOR = 5;
+    
+    /**
+     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#toolMajor}.
+     * 
+     * @see #getMotionRange
+     */
+    public static final int MOTION_RANGE_TOOL_MAJOR = 6;
+    
+    /**
+     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#toolMinor}.
+     * 
+     * @see #getMotionRange
+     */
+    public static final int MOTION_RANGE_TOOL_MINOR = 7;
+    
+    /**
+     * Constant for retrieving the range of values for
+     * {@link MotionEvent.PointerCoords#orientation}.
+     * 
+     * @see #getMotionRange
+     */
+    public static final int MOTION_RANGE_ORIENTATION = 8;
+
+    /**
+     * Gets information about the input device with the specified id.
+     * @param id The device id.
+     * @return The input device or null if not found.
+     */
     public static InputDevice getDevice(int id) {
+        // TODO
+        return null;
     }
-    */
     
     /**
      * Gets the name of this input device.
@@ -213,19 +272,80 @@
         return KeyCharacterMap.load(mId);
     }
     
-    /*
-    
+    /**
+     * Gets information about the range of values for a particular {@link MotionEvent}
+     * coordinate.
+     * @param range The motion range constant.
+     * @return The range of values, or null if the requested coordinate is not
+     * supported by the device.
+     */
     public MotionRange getMotionRange(int range) {
+        // TODO
+        return null;
     }
     
-    public boolean hasKeyCode(int keyCode) {
+    /**
+     * Returns true if the device supports a particular button or key.
+     * @param keyCode The key code.
+     * @return True if the device supports the key.
+     */
+    public boolean hasKey(int keyCode) {
+        // TODO
+        return false;
     }
     
+    /**
+     * Provides information about the range of values for a particular {@link MotionEvent}
+     * coordinate.
+     */
     public static final class MotionRange {
-        public float min;
-        public float max;
-        public float range;
-        public float flat;
-        public float fuzz;
-    }*/
+        /**
+         * Gets the minimum value for the coordinate.
+         * @return The minimum value.
+         */
+        public float getMin() {
+            // TODO
+            return 0;
+        }
+        
+        /**
+         * Gets the maximum value for the coordinate.
+         * @return The minimum value.
+         */
+        public float getMax() {
+            // TODO
+            return 0;
+        }
+        
+        /**
+         * Gets the range of the coordinate (difference between maximum and minimum).
+         * @return The range of values.
+         */
+        public float getRange() {
+            // TODO
+            return 0;
+        }
+        
+        /**
+         * Gets the extent of the center flat position with respect to this coordinate.
+         * For example, a flat value of 8 means that the center position is between -8 and +8.
+         * This value is mainly useful for calibrating joysticks.
+         * @return The extent of the center flat position.
+         */
+        public float getFlat() {
+            // TODO
+            return 0;
+        }
+        
+        /**
+         * Gets the error tolerance for input device measurements with respect to this coordinate.
+         * For example, a value of 2 indicates that the measured value may be up to +/- 2 units
+         * away from the actual value due to noise and device sensitivity limitations.
+         * @return The error tolerance.
+         */
+        public float getFuzz() {
+            // TODO
+            return 0;
+        }
+    }
 }
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 445a980..78b73fe 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -42,9 +42,18 @@
     }
     
     /**
+     * Gets the device that this event came from.
+     * 
+     * @return The device, or null if unknown.
+     */
+    public final InputDevice getDevice() {
+        return InputDevice.getDevice(mDeviceId);
+    }
+    
+    /**
      * Gets the source of the event.
      * 
-     * @return The event source or {@link InputDevice.SOURCE_UNKNOWN} if unknown.
+     * @return The event source or {@link InputDevice#SOURCE_UNKNOWN} if unknown.
      * @see InputDevice#getSourceInfo
      */
     public final int getSource() {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 0015db0..13360d9 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1403,7 +1403,7 @@
      * current location, position and size is updated to the new values.
      * The current values in the event are added to a list of historical values.
      * 
-     * Only applies to {@link ACTION_MOVE} events.
+     * Only applies to {@link #ACTION_MOVE} events.
      *
      * @param eventTime The time stamp (in ms) for this data.
      * @param x The new X position.
@@ -1427,7 +1427,7 @@
      * current location, position and size is updated to the new values.
      * The current values in the event are added to a list of historical values.
      * 
-     * Only applies to {@link ACTION_MOVE} events.
+     * Only applies to {@link #ACTION_MOVE} events.
      *
      * @param eventTime The time stamp (in ms) for this data.
      * @param pointerCoords The new pointer coordinates.
diff --git a/include/utils/String8.h b/include/utils/String8.h
index c4b18a4..0b18fe3 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -171,6 +171,8 @@
             status_t            append(const char* other);
             status_t            append(const char* other, size_t numChars);
 
+            status_t            appendFormat(const char* fmt, ...);
+
             // Note that this function takes O(N) time to calculate the value.
             // No cache value is stored.
             size_t              getUtf32Length() const;
diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp
index 82776f4..1c4f80c 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -372,6 +372,27 @@
     return real_append(other, otherLen);
 }
 
+status_t String8::appendFormat(const char* fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+
+    int result = NO_ERROR;
+    int n = vsnprintf(NULL, 0, fmt, ap);
+    if (n != 0) {
+        size_t oldLength = length();
+        char* buf = lockBuffer(oldLength + n);
+        if (buf) {
+            vsnprintf(buf + oldLength, n + 1, fmt, ap);
+        } else {
+            result = NO_MEMORY;
+        }
+    }
+
+    va_end(ap);
+    return result;
+}
+
 status_t String8::real_append(const char* other, size_t otherLen)
 {
     const size_t myLen = bytes();
@@ -411,15 +432,16 @@
     if (size != this->size()) {
         SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
             ->editResize(size+1);
-        if (buf) {
-            char* str = (char*)buf->data();
-            str[size] = 0;
-            mString = str;
-            return NO_ERROR;
+        if (! buf) {
+            return NO_MEMORY;
         }
+
+        char* str = (char*)buf->data();
+        str[size] = 0;
+        mString = str;
     }
-    
-    return NO_MEMORY;
+
+    return NO_ERROR;
 }
 
 ssize_t String8::find(const char* other, size_t start) const
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 3c60a98..b4f46ab 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -56,8 +56,6 @@
     private final Callbacks mCallbacks;
     private final Context mContext;
     private final WindowManagerService mWindowManagerService;
-    private final PowerManager mPowerManager;
-    private final PowerManagerService mPowerManagerService;
     
     private int mTouchScreenConfig;
     private int mKeyboardConfig;
@@ -85,6 +83,7 @@
     private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
     private static native void nativeSetFocusedApplication(InputApplication application);
     private static native void nativePreemptInputDispatch();
+    private static native String nativeDump();
     
     // Device class as defined by EventHub.
     private static final int CLASS_KEYBOARD = 0x00000001;
@@ -100,14 +99,9 @@
     static final int INPUT_EVENT_INJECTION_FAILED = 2;
     static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
     
-    public InputManager(Context context,
-            WindowManagerService windowManagerService,
-            PowerManager powerManager,
-            PowerManagerService powerManagerService) {
+    public InputManager(Context context, WindowManagerService windowManagerService) {
         this.mContext = context;
         this.mWindowManagerService = windowManagerService;
-        this.mPowerManager = powerManager;
-        this.mPowerManagerService = powerManagerService;
         
         this.mCallbacks = new Callbacks();
         
@@ -297,7 +291,10 @@
     }
     
     public void dump(PrintWriter pw) {
-        // TODO
+        String dumpStr = nativeDump();
+        if (dumpStr != null) {
+            pw.println(dumpStr);
+        }
     }
     
     private static final class VirtualKeyDefinition {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 37a2a58..2e28afb 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -332,7 +332,7 @@
     /**
      * Z-ordered (bottom-most first) list of all Window objects.
      */
-    final ArrayList mWindows = new ArrayList();
+    final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
 
     /**
      * Windows that are being resized.  Used so we can tell the client about
@@ -630,7 +630,7 @@
                 "KEEP_SCREEN_ON_FLAG");
         mHoldingScreenWakeLock.setReferenceCounted(false);
 
-        mInputManager = new InputManager(context, this, pmc, mPowerManager);
+        mInputManager = new InputManager(context, this);
 
         PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
         thr.start();
@@ -665,7 +665,7 @@
         }
     }
 
-    private void placeWindowAfter(Object pos, WindowState window) {
+    private void placeWindowAfter(WindowState pos, WindowState window) {
         final int i = mWindows.indexOf(pos);
         if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v(
             TAG, "Adding window " + window + " at "
@@ -674,7 +674,7 @@
         mWindowsChanged = true;
     }
 
-    private void placeWindowBefore(Object pos, WindowState window) {
+    private void placeWindowBefore(WindowState pos, WindowState window) {
         final int i = mWindows.indexOf(pos);
         if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v(
             TAG, "Adding window " + window + " at "
@@ -687,13 +687,13 @@
     //win. used for z ordering the windows in mWindows
     private int findIdxBasedOnAppTokens(WindowState win) {
         //use a local variable to cache mWindows
-        ArrayList localmWindows = mWindows;
+        ArrayList<WindowState> localmWindows = mWindows;
         int jmax = localmWindows.size();
         if(jmax == 0) {
             return -1;
         }
         for(int j = (jmax-1); j >= 0; j--) {
-            WindowState wentry = (WindowState)localmWindows.get(j);
+            WindowState wentry = localmWindows.get(j);
             if(wentry.mAppToken == win.mAppToken) {
                 return j;
             }
@@ -704,7 +704,7 @@
     private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {
         final IWindow client = win.mClient;
         final WindowToken token = win.mToken;
-        final ArrayList localmWindows = mWindows;
+        final ArrayList<WindowState> localmWindows = mWindows;
 
         final int N = localmWindows.size();
         final WindowState attached = win.mAttachedWindow;
@@ -749,7 +749,7 @@
                     // Figure out where the window should go, based on the
                     // order of applications.
                     final int NA = mAppTokens.size();
-                    Object pos = null;
+                    WindowState pos = null;
                     for (i=NA-1; i>=0; i--) {
                         AppWindowToken t = mAppTokens.get(i);
                         if (t == token) {
@@ -769,8 +769,7 @@
                     // we need to look some more.
                     if (pos != null) {
                         // Move behind any windows attached to this one.
-                        WindowToken atoken =
-                            mTokenMap.get(((WindowState)pos).mClient.asBinder());
+                        WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
                         if (atoken != null) {
                             final int NC = atoken.windows.size();
                             if (NC > 0) {
@@ -796,8 +795,7 @@
                         if (pos != null) {
                             // Move in front of any windows attached to this
                             // one.
-                            WindowToken atoken =
-                                mTokenMap.get(((WindowState)pos).mClient.asBinder());
+                            WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
                             if (atoken != null) {
                                 final int NC = atoken.windows.size();
                                 if (NC > 0) {
@@ -812,7 +810,7 @@
                             // Just search for the start of this layer.
                             final int myLayer = win.mBaseLayer;
                             for (i=0; i<N; i++) {
-                                WindowState w = (WindowState)localmWindows.get(i);
+                                WindowState w = localmWindows.get(i);
                                 if (w.mBaseLayer > myLayer) {
                                     break;
                                 }
@@ -829,7 +827,7 @@
                 // Figure out where window should go, based on layer.
                 final int myLayer = win.mBaseLayer;
                 for (i=N-1; i>=0; i--) {
-                    if (((WindowState)localmWindows.get(i)).mBaseLayer <= myLayer) {
+                    if (localmWindows.get(i).mBaseLayer <= myLayer) {
                         i++;
                         break;
                     }
@@ -912,13 +910,13 @@
     }
 
     int findDesiredInputMethodWindowIndexLocked(boolean willMove) {
-        final ArrayList localmWindows = mWindows;
+        final ArrayList<WindowState> localmWindows = mWindows;
         final int N = localmWindows.size();
         WindowState w = null;
         int i = N;
         while (i > 0) {
             i--;
-            w = (WindowState)localmWindows.get(i);
+            w = localmWindows.get(i);
 
             //Slog.i(TAG, "Checking window @" + i + " " + w + " fl=0x"
             //        + Integer.toHexString(w.mAttrs.flags));
@@ -933,7 +931,7 @@
                 if (!willMove
                         && w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
                         && i > 0) {
-                    WindowState wb = (WindowState)localmWindows.get(i-1);
+                    WindowState wb = localmWindows.get(i-1);
                     if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) {
                         i--;
                         w = wb;
@@ -963,7 +961,7 @@
                     int pos = 0;
                     pos = localmWindows.indexOf(curTarget);
                     while (pos >= 0) {
-                        WindowState win = (WindowState)localmWindows.get(pos);
+                        WindowState win = localmWindows.get(pos);
                         if (win.mAppToken != token) {
                             break;
                         }
@@ -1068,7 +1066,7 @@
             int wi = imw.mChildWindows.size();
             while (wi > 0) {
                 wi--;
-                WindowState cw = (WindowState)imw.mChildWindows.get(wi);
+                WindowState cw = imw.mChildWindows.get(wi);
                 cw.mAnimLayer = cw.mLayer + adj;
                 if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + cw
                         + " anim layer: " + cw.mAnimLayer);
@@ -1094,7 +1092,7 @@
             int NC = win.mChildWindows.size();
             while (NC > 0) {
                 NC--;
-                WindowState cw = (WindowState)win.mChildWindows.get(NC);
+                WindowState cw = win.mChildWindows.get(NC);
                 int cpos = mWindows.indexOf(cw);
                 if (cpos >= 0) {
                     if (cpos < interestingPos) interestingPos--;
@@ -1146,7 +1144,7 @@
         if (pos >= 0) {
             final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken;
             if (pos < mWindows.size()) {
-                WindowState wp = (WindowState)mWindows.get(pos);
+                WindowState wp = mWindows.get(pos);
                 if (wp == mInputMethodWindow) {
                     pos++;
                 }
@@ -1190,14 +1188,14 @@
             // located here, and contiguous.
             final int N = mWindows.size();
             WindowState firstImWin = imPos < N
-                    ? (WindowState)mWindows.get(imPos) : null;
+                    ? mWindows.get(imPos) : null;
 
             // Figure out the actual input method window that should be
             // at the bottom of their stack.
             WindowState baseImWin = imWin != null
                     ? imWin : mInputMethodDialogs.get(0);
             if (baseImWin.mChildWindows.size() > 0) {
-                WindowState cw = (WindowState)baseImWin.mChildWindows.get(0);
+                WindowState cw = baseImWin.mChildWindows.get(0);
                 if (cw.mSubLayer < 0) baseImWin = cw;
             }
 
@@ -1206,7 +1204,7 @@
                 // First find the top IM window.
                 int pos = imPos+1;
                 while (pos < N) {
-                    if (!((WindowState)mWindows.get(pos)).mIsImWindow) {
+                    if (!(mWindows.get(pos)).mIsImWindow) {
                         break;
                     }
                     pos++;
@@ -1214,7 +1212,7 @@
                 pos++;
                 // Now there should be no more input method windows above.
                 while (pos < N) {
-                    if (((WindowState)mWindows.get(pos)).mIsImWindow) {
+                    if ((mWindows.get(pos)).mIsImWindow) {
                         break;
                     }
                     pos++;
@@ -1302,7 +1300,7 @@
 
         // First find top-most window that has asked to be on top of the
         // wallpaper; all wallpapers go behind it.
-        final ArrayList localmWindows = mWindows;
+        final ArrayList<WindowState> localmWindows = mWindows;
         int N = localmWindows.size();
         WindowState w = null;
         WindowState foundW = null;
@@ -1312,7 +1310,7 @@
         int i = N;
         while (i > 0) {
             i--;
-            w = (WindowState)localmWindows.get(i);
+            w = localmWindows.get(i);
             if ((w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER)) {
                 if (topCurW == null) {
                     topCurW = w;
@@ -1483,7 +1481,7 @@
             // AND any starting window associated with it, AND below the
             // maximum layer the policy allows for wallpapers.
             while (foundI > 0) {
-                WindowState wb = (WindowState)localmWindows.get(foundI-1);
+                WindowState wb = localmWindows.get(foundI-1);
                 if (wb.mBaseLayer < maxLayer &&
                         wb.mAttachedWindow != foundW &&
                         (wb.mAttrs.type != TYPE_APPLICATION_STARTING ||
@@ -1507,7 +1505,7 @@
         } else {
             // Okay i is the position immediately above the wallpaper.  Look at
             // what is below it for later.
-            foundW = foundI > 0 ? (WindowState)localmWindows.get(foundI-1) : null;
+            foundW = foundI > 0 ? localmWindows.get(foundI-1) : null;
         }
 
         if (visible) {
@@ -1566,7 +1564,7 @@
                 if (wallpaper == foundW) {
                     foundI--;
                     foundW = foundI > 0
-                            ? (WindowState)localmWindows.get(foundI-1) : null;
+                            ? localmWindows.get(foundI-1) : null;
                     continue;
                 }
 
@@ -2403,6 +2401,8 @@
                         outSurface.release();
                     }
                 } catch (Exception e) {
+                    mInputMonitor.updateInputWindowsLw();
+                    
                     Slog.w(TAG, "Exception thrown when creating surface for client "
                              + client + " (" + win.mAttrs.getTitle() + ")",
                              e);
@@ -2449,7 +2449,6 @@
                               applyAnimationLocked(win, transit, false)) {
                             focusMayChange = true;
                             win.mExiting = true;
-                            mInputMonitor.windowIsBecomingInvisibleLw(win);
                         } else if (win.isAnimating()) {
                             // Currently in a hide animation... turn this into
                             // an exit.
@@ -2544,6 +2543,8 @@
                 TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
 
             inTouchMode = mInTouchMode;
+            
+            mInputMonitor.updateInputWindowsLw();
         }
 
         if (configChanged) {
@@ -2999,7 +3000,7 @@
     public int getOrientationFromWindowsLocked() {
         int pos = mWindows.size() - 1;
         while (pos >= 0) {
-            WindowState wtoken = (WindowState) mWindows.get(pos);
+            WindowState wtoken = mWindows.get(pos);
             pos--;
             if (wtoken.mAppToken != null) {
                 // We hit an application window. so the orientation will be determined by the
@@ -3553,7 +3554,6 @@
                         applyAnimationLocked(win,
                                 WindowManagerPolicy.TRANSIT_EXIT, false);
                     }
-                    mInputMonitor.windowIsBecomingInvisibleLw(win);
                     changed = true;
                 }
             }
@@ -3581,6 +3581,8 @@
                 if (performLayout) {
                     updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
                     performLayoutAndPlaceSurfacesLocked();
+                } else {
+                    mInputMonitor.updateInputWindowsLw();
                 }
             }
         }
@@ -3868,7 +3870,7 @@
             int j = win.mChildWindows.size();
             while (j > 0) {
                 j--;
-                WindowState cwin = (WindowState)win.mChildWindows.get(j);
+                WindowState cwin = win.mChildWindows.get(j);
                 if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
                         "Tmp removing child window " + cwin);
                 mWindows.remove(cwin);
@@ -3896,7 +3898,7 @@
             int i = NW;
             while (i > 0) {
                 i--;
-                WindowState win = (WindowState)mWindows.get(i);
+                WindowState win = mWindows.get(i);
                 if (win.getAppToken() != null) {
                     return i+1;
                 }
@@ -3922,7 +3924,7 @@
                 int j = win.mChildWindows.size();
                 while (j > 0) {
                     j--;
-                    WindowState cwin = (WindowState)win.mChildWindows.get(j);
+                    WindowState cwin = win.mChildWindows.get(j);
                     if (cwin.mSubLayer >= 0) {
                         for (int pos=NW-1; pos>=0; pos--) {
                             if (mWindows.get(pos) == cwin) {
@@ -3950,7 +3952,7 @@
         final int NCW = win.mChildWindows.size();
         boolean added = false;
         for (int j=0; j<NCW; j++) {
-            WindowState cwin = (WindowState)win.mChildWindows.get(j);
+            WindowState cwin = win.mChildWindows.get(j);
             if (!added && cwin.mSubLayer >= 0) {
                 if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding child window at "
                         + index + ": " + cwin);
@@ -4234,7 +4236,7 @@
     public void closeSystemDialogs(String reason) {
         synchronized(mWindowMap) {
             for (int i=mWindows.size()-1; i>=0; i--) {
-                WindowState w = (WindowState)mWindows.get(i);
+                WindowState w = mWindows.get(i);
                 if (w.mSurface != null) {
                     try {
                         w.mClient.closeSystemDialogs(reason);
@@ -4418,7 +4420,7 @@
             // have been drawn.
             final int N = mWindows.size();
             for (int i=0; i<N; i++) {
-                WindowState w = (WindowState)mWindows.get(i);
+                WindowState w = mWindows.get(i);
                 if (w.isVisibleLw() && !w.mObscured
                         && (w.mOrientationChanging || !w.isDrawnLw())) {
                     return;
@@ -4531,7 +4533,7 @@
                 Surface.setOrientation(0, rotation, animFlags);
             }
             for (int i=mWindows.size()-1; i>=0; i--) {
-                WindowState w = (WindowState)mWindows.get(i);
+                WindowState w = mWindows.get(i);
                 if (w.mSurface != null) {
                     w.mOrientationChanging = true;
                 }
@@ -4687,11 +4689,10 @@
 
         boolean result = true;
 
-        Object[] windows;
+        WindowState[] windows;
         synchronized (mWindowMap) {
-            windows = new Object[mWindows.size()];
             //noinspection unchecked
-            windows = mWindows.toArray(windows);
+            windows = mWindows.toArray(new WindowState[mWindows.size()]);
         }
 
         BufferedWriter out = null;
@@ -4703,7 +4704,7 @@
 
             final int count = windows.length;
             for (int i = 0; i < count; i++) {
-                final WindowState w = (WindowState) windows[i];
+                final WindowState w = windows[i];
                 out.write(Integer.toHexString(System.identityHashCode(w)));
                 out.write(' ');
                 out.append(w.mAttrs.getTitle());
@@ -4856,11 +4857,11 @@
         }
 
         synchronized (mWindowMap) {
-            final ArrayList windows = mWindows;
+            final ArrayList<WindowState> windows = mWindows;
             final int count = windows.size();
 
             for (int i = 0; i < count; i++) {
-                WindowState w = (WindowState) windows.get(i);
+                WindowState w = windows.get(i);
                 if (System.identityHashCode(w) == hashCode) {
                     return w;
                 }
@@ -5094,7 +5095,7 @@
         private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
             int windowCount = mWindows.size();
             for (int i = 0; i < windowCount; i++) {
-                WindowState windowState = (WindowState) mWindows.get(i);
+                WindowState windowState = mWindows.get(i);
                 if (windowState.mInputChannel == inputChannel) {
                     return windowState;
                 }
@@ -5110,10 +5111,10 @@
             // As an optimization, we could try to prune the list of windows but this turns
             // out to be difficult because only the native code knows for sure which window
             // currently has touch focus.
-            final ArrayList windows = mWindows;
+            final ArrayList<WindowState> windows = mWindows;
             final int N = windows.size();
             for (int i = N - 1; i >= 0; i--) {
-                final WindowState child = (WindowState) windows.get(i);
+                final WindowState child = windows.get(i);
                 if (child.mInputChannel == null || child.mRemoved) {
                     // Skip this window because it cannot possibly receive input.
                     continue;
@@ -5258,17 +5259,6 @@
             }
         }
         
-        public void windowIsBecomingInvisibleLw(WindowState window) {
-            // The window is becoming invisible.  Preempt input dispatch in progress
-            // so that the next window below can receive focus.
-            if (window == mInputFocus) {
-                mInputFocus = null;
-                preemptInputDispatchLw();
-            }
-            
-            updateInputWindowsLw();
-        }
-        
         /* Tells the dispatcher to stop waiting for its current synchronous event targets.
          * Essentially, just makes those dispatches asynchronous so a new dispatch cycle
          * can begin.
@@ -5779,7 +5769,7 @@
         final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
         final DeathRecipient mDeathRecipient;
         final WindowState mAttachedWindow;
-        final ArrayList mChildWindows = new ArrayList();
+        final ArrayList<WindowState> mChildWindows = new ArrayList<WindowState>();
         final int mBaseLayer;
         final int mSubLayer;
         final boolean mLayoutAttached;
@@ -6335,10 +6325,8 @@
                 int i = mChildWindows.size();
                 while (i > 0) {
                     i--;
-                    WindowState c = (WindowState)mChildWindows.get(i);
+                    WindowState c = mChildWindows.get(i);
                     c.mAttachedHidden = true;
-                    
-                    mInputMonitor.windowIsBecomingInvisibleLw(c);
                 }
 
                 if (mReportDestroySurface) {
@@ -6448,7 +6436,7 @@
                 int i = mChildWindows.size();
                 while (i > 0) {
                     i--;
-                    WindowState c = (WindowState)mChildWindows.get(i);
+                    WindowState c = mChildWindows.get(i);
                     if (c.mAttachedHidden) {
                         c.mAttachedHidden = false;
                         if (c.mSurface != null) {
@@ -6621,7 +6609,7 @@
 
             final int N = mChildWindows.size();
             for (int i=0; i<N; i++) {
-                ((WindowState)mChildWindows.get(i)).finishExit();
+                mChildWindows.get(i).finishExit();
             }
 
             if (!mExiting) {
@@ -6646,10 +6634,6 @@
                     Slog.w(TAG, "Error hiding surface in " + this, e);
                 }
                 mLastHidden = true;
-                
-                for (int i=0; i<N; i++) {
-                    mInputMonitor.windowIsBecomingInvisibleLw((WindowState)mChildWindows.get(i));
-                }
             }
             mExiting = false;
             if (mRemoveOnExit) {
@@ -7554,7 +7538,7 @@
 
             final int N = windows.size();
             for (int i=0; i<N; i++) {
-                ((WindowState)windows.get(i)).finishExit();
+                windows.get(i).finishExit();
             }
             updateReportedVisibilityLocked();
 
@@ -7980,7 +7964,7 @@
                         int i = mWindows.size();
                         while (i > 0) {
                             i--;
-                            WindowState w = (WindowState)mWindows.get(i);
+                            WindowState w = mWindows.get(i);
                             if (w.mOrientationChanging) {
                                 w.mOrientationChanging = false;
                                 Slog.w(TAG, "Force clearing orientation change: " + w);
@@ -8119,7 +8103,7 @@
             int idx = findDesiredInputMethodWindowIndexLocked(false);
             WindowState imFocus;
             if (idx > 0) {
-                imFocus = (WindowState)mWindows.get(idx-1);
+                imFocus = mWindows.get(idx-1);
                 if (imFocus != null) {
                     if (imFocus.mSession.mClient != null &&
                             imFocus.mSession.mClient.asBinder() == client.asBinder()) {
@@ -8177,9 +8161,9 @@
         // First remove all existing app windows.
         i=0;
         while (i < NW) {
-            WindowState w = (WindowState)mWindows.get(i);
+            WindowState w = mWindows.get(i);
             if (w.mAppToken != null) {
-                WindowState win = (WindowState)mWindows.remove(i);
+                WindowState win = mWindows.remove(i);
                 mWindowsChanged = true;
                 if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
                         "Rebuild removing window: " + win);
@@ -8227,7 +8211,7 @@
         int i;
 
         for (i=0; i<N; i++) {
-            WindowState w = (WindowState)mWindows.get(i);
+            WindowState w = mWindows.get(i);
             if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
                     || (i > 0 && w.mIsWallpaper)) {
                 curLayer += WINDOW_LAYER_MULTIPLIER;
@@ -8352,7 +8336,7 @@
         // to another window).
         int topAttached = -1;
         for (i = N-1; i >= 0; i--) {
-            WindowState win = (WindowState) mWindows.get(i);
+            WindowState win = mWindows.get(i);
 
             // Don't do layout of a window if it is not visible, or
             // soon won't be visible, to avoid wasting time and funky
@@ -8401,7 +8385,7 @@
         // XXX does not deal with windows that are attached to windows
         // that are themselves attached.
         for (i = topAttached; i >= 0; i--) {
-            WindowState win = (WindowState) mWindows.get(i);
+            WindowState win = mWindows.get(i);
 
             // If this view is GONE, then skip it -- keep the current
             // frame, and let the caller know so they can ignore it
@@ -8548,7 +8532,7 @@
                 final int N = mWindows.size();
 
                 for (i=N-1; i>=0; i--) {
-                    WindowState w = (WindowState)mWindows.get(i);
+                    WindowState w = mWindows.get(i);
 
                     final WindowManager.LayoutParams attrs = w.mAttrs;
 
@@ -8981,7 +8965,7 @@
                         // Clear them out.
                         forceHiding = false;
                         for (i=N-1; i>=0; i--) {
-                            WindowState w = (WindowState)mWindows.get(i);
+                            WindowState w = mWindows.get(i);
                             if (w.mSurface != null) {
                                 final WindowManager.LayoutParams attrs = w.mAttrs;
                                 if (mPolicy.doesForceHide(w, attrs) && w.isVisibleLw()) {
@@ -9031,6 +9015,7 @@
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
                         + Integer.toHexString(changes));
                 
+                mInputMonitor.updateInputWindowsLw();
             } while (changes != 0);
 
             // THIRD LOOP: Update the surfaces of all windows.
@@ -9047,7 +9032,7 @@
             final int N = mWindows.size();
 
             for (i=N-1; i>=0; i--) {
-                WindowState w = (WindowState)mWindows.get(i);
+                WindowState w = mWindows.get(i);
 
                 boolean displayed = false;
                 final WindowManager.LayoutParams attrs = w.mAttrs;
@@ -9227,7 +9212,6 @@
                                     Slog.w(TAG, "Exception hiding surface in " + w);
                                 }
                             }
-                            mInputMonitor.windowIsBecomingInvisibleLw(w);
                         }
                         // If we are waiting for this window to handle an
                         // orientation change, well, it is hidden, so
@@ -9487,6 +9471,8 @@
             Slog.e(TAG, "Unhandled exception in Window Manager", e);
         }
 
+        mInputMonitor.updateInputWindowsLw();
+        
         Surface.closeTransaction();
 
         if (DEBUG_ORIENTATION && mDisplayFrozen) Slog.v(TAG,
@@ -9612,6 +9598,8 @@
             requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
         }
         
+        mInputMonitor.updateInputWindowsLw();
+        
         if (DEBUG_FREEZE) Slog.v(TAG, "Layout: mDisplayFrozen=" + mDisplayFrozen
                 + " holdScreen=" + holdScreen);
         if (!mDisplayFrozen) {
@@ -9718,7 +9706,7 @@
             boolean leakedSurface = false;
             Slog.i(TAG, "Out of memory for surface!  Looking for leaks...");
             for (int i=0; i<N; i++) {
-                WindowState ws = (WindowState)mWindows.get(i);
+                WindowState ws = mWindows.get(i);
                 if (ws.mSurface != null) {
                     if (!mSessions.contains(ws.mSession)) {
                         Slog.w(TAG, "LEAKED SURFACE (session doesn't exist): "
@@ -9750,7 +9738,7 @@
                 Slog.w(TAG, "No leaked surfaces; killing applicatons!");
                 SparseIntArray pidCandidates = new SparseIntArray();
                 for (int i=0; i<N; i++) {
-                    WindowState ws = (WindowState)mWindows.get(i);
+                    WindowState ws = mWindows.get(i);
                     if (ws.mSurface != null) {
                         pidCandidates.append(ws.mSession.mPid, ws.mSession.mPid);
                     }
@@ -9842,7 +9830,7 @@
             ? mAppTokens.get(nextAppIndex) : null;
 
         while (i >= 0) {
-            win = (WindowState)mWindows.get(i);
+            win = mWindows.get(i);
 
             if (localLOGV || DEBUG_FOCUS) Slog.v(
                 TAG, "Looking for focus: " + i
@@ -9990,14 +9978,13 @@
             return;
         }
 
-        pw.println("Input Dispatcher State:");
         mInputManager.dump(pw);
         pw.println(" ");
         
         synchronized(mWindowMap) {
             pw.println("Current Window Manager state:");
             for (int i=mWindows.size()-1; i>=0; i--) {
-                WindowState w = (WindowState)mWindows.get(i);
+                WindowState w = mWindows.get(i);
                 pw.print("  Window #"); pw.print(i); pw.print(' ');
                         pw.print(w); pw.println(":");
                 w.dump(pw, "    ");
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index bc052a0..f19f1ec 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -199,6 +199,8 @@
 
     inline sp<InputManager> getInputManager() const { return mInputManager; }
 
+    String8 dump();
+
     void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
     void setDisplayOrientation(int32_t displayId, int32_t orientation);
 
@@ -341,7 +343,8 @@
     InputApplication* mFocusedApplication;
     InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
 
-    void dumpDispatchStateLd();
+    void dumpDispatchStateLd(String8& dump);
+    void logDispatchStateLd();
 
     bool notifyANR(jobject tokenObj, nsecs_t& outNewTimeout);
     void releaseFocusedApplicationLd(JNIEnv* env);
@@ -404,6 +407,13 @@
     releaseFocusedApplicationLd(env);
 }
 
+String8 NativeInputManager::dump() {
+    String8 dump;
+    dump.append("Native Input Dispatcher State:\n");
+    dumpDispatchStateLd(dump);
+    return dump;
+}
+
 bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
     return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
 }
@@ -921,6 +931,8 @@
             mTouchedWallpaperWindows.clear();
         }
 
+        bool hadFocusedWindow = mFocusedWindow != NULL;
+
         mWindows.clear();
         mFocusedWindow = NULL;
         mWallpaperWindows.clear();
@@ -972,10 +984,15 @@
 
         mTempTouchedWallpaperChannels.clear();
 
+        if (hadFocusedWindow && ! mFocusedWindow
+                || mFocusedWindow && ! mFocusedWindow->visible) {
+            preemptInputDispatch();
+        }
+
         mDispatchStateChanged.broadcast();
 
 #if DEBUG_FOCUS
-        dumpDispatchStateLd();
+        logDispatchStateLd();
 #endif
     } // release lock
 }
@@ -1092,7 +1109,7 @@
         mDispatchStateChanged.broadcast();
 
 #if DEBUG_FOCUS
-        dumpDispatchStateLd();
+        logDispatchStateLd();
 #endif
     } // release lock
 }
@@ -1120,7 +1137,7 @@
         }
 
 #if DEBUG_FOCUS
-        dumpDispatchStateLd();
+        logDispatchStateLd();
 #endif
     } // release lock
 }
@@ -1217,7 +1234,7 @@
 #if DEBUG_FOCUS
     LOGD("waitForFocusedWindow finished: injectionResult=%d",
             injectionResult);
-    dumpDispatchStateLd();
+    logDispatchStateLd();
 #endif
     return injectionResult;
 }
@@ -1490,7 +1507,7 @@
 #if DEBUG_FOCUS
     LOGD("waitForTouchedWindow finished: injectionResult=%d",
             injectionResult);
-    dumpDispatchStateLd();
+    logDispatchStateLd();
 #endif
     return injectionResult;
 }
@@ -1697,31 +1714,38 @@
     android_server_PowerManagerService_userActivity(eventTime, eventType);
 }
 
-void NativeInputManager::dumpDispatchStateLd() {
-#if DEBUG_FOCUS
-    LOGD("  dispatcherState: dispatchEnabled=%d, dispatchFrozen=%d, windowsReady=%d",
-            mDispatchEnabled, mDispatchFrozen, mWindowsReady);
+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) {
-        LOGD("  focusedApplication: name='%s', dispatchingTimeout=%0.3fms",
+        dump.appendFormat("  focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
                 mFocusedApplication->name.string(),
                 mFocusedApplication->dispatchingTimeout / 1000000.0);
     } else {
-        LOGD("  focusedApplication: <null>");
+        dump.append("  focusedApplication: <null>\n");
     }
-    LOGD("  focusedWindow: '%s'",
+    dump.appendFormat("  focusedWindow: '%s'\n",
             mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
-    LOGD("  touchedWindow: '%s', touchDown=%d",
+    dump.appendFormat("  touchedWindow: '%s', touchDown=%d\n",
             mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
             mTouchDown);
     for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
-        LOGD("  touchedWallpaperWindows[%d]: '%s'",
+        dump.appendFormat("  touchedWallpaperWindows[%d]: '%s'\n",
                 i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
     }
     for (size_t i = 0; i < mWindows.size(); i++) {
-        LOGD("  windows[%d]: '%s', paused=%d, hasFocus=%d, hasWallpaper=%d, visible=%d, "
-                "flags=0x%08x, type=0x%08x, "
+        dump.appendFormat("  windows[%d]: '%s', paused=%d, hasFocus=%d, hasWallpaper=%d, "
+                "visible=%d, flags=0x%08x, type=0x%08x, "
                 "frame=[%d,%d], touchableArea=[%d,%d][%d,%d], "
-                "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms",
+                "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,
@@ -1731,7 +1755,6 @@
                 mWindows[i].ownerPid, mWindows[i].ownerUid,
                 mWindows[i].dispatchingTimeout / 1000000.0);
     }
-#endif
 }
 
 // ----------------------------------------------------------------------------
@@ -2048,6 +2071,15 @@
     gNativeInputManager->preemptInputDispatch();
 }
 
+static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) {
+    if (checkInputManagerUnitialized(env)) {
+        return NULL;
+    }
+
+    String8 dump(gNativeInputManager->dump());
+    return env->NewStringUTF(dump.string());
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gInputManagerMethods[] = {
@@ -2083,7 +2115,9 @@
     { "nativeSetInputDispatchMode", "(ZZ)V",
             (void*) android_server_InputManager_nativeSetInputDispatchMode },
     { "nativePreemptInputDispatch", "()V",
-            (void*) android_server_InputManager_nativePreemptInputDispatch }
+            (void*) android_server_InputManager_nativePreemptInputDispatch },
+    { "nativeDump", "()Ljava/lang/String;",
+            (void*) android_server_InputManager_nativeDump },
 };
 
 #define FIND_CLASS(var, className) \