Propagate input ANR reason to activity manager log.

Make it a little easier to diagnose input dispatch timeouts by
providing the detailed reason as the ANR annotation in the log.

Bug: 10689184
Change-Id: Ie18fd9ad066b0673d1f57c030e027ad0085f4650
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 75cf5d0..367646b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -7970,8 +7970,8 @@
         return KEY_DISPATCHING_TIMEOUT;
     }
 
-
-    public long inputDispatchingTimedOut(int pid, final boolean aboveSystem) {
+    @Override
+    public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
         if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires permission "
@@ -7986,7 +7986,7 @@
             timeout = getInputDispatchingTimeoutLocked(proc);
         }
 
-        if (!inputDispatchingTimedOut(proc, null, null, aboveSystem)) {
+        if (!inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
             return -1;
         }
 
@@ -7999,13 +7999,20 @@
      */
     public boolean inputDispatchingTimedOut(final ProcessRecord proc,
             final ActivityRecord activity, final ActivityRecord parent,
-            final boolean aboveSystem) {
+            final boolean aboveSystem, String reason) {
         if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires permission "
                     + android.Manifest.permission.FILTER_EVENTS);
         }
 
+        final String annotation;
+        if (reason == null) {
+            annotation = "Input dispatching timed out";
+        } else {
+            annotation = "Input dispatching timed out (" + reason + ")";
+        }
+
         if (proc != null) {
             synchronized (this) {
                 if (proc.debugging) {
@@ -8021,7 +8028,7 @@
                 if (proc.instrumentationClass != null) {
                     Bundle info = new Bundle();
                     info.putString("shortMsg", "keyDispatchingTimedOut");
-                    info.putString("longMsg", "Timed out while dispatching key event");
+                    info.putString("longMsg", annotation);
                     finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
                     return true;
                 }
@@ -8029,7 +8036,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    appNotResponding(proc, activity, parent, aboveSystem, "keyDispatchingTimedOut");
+                    appNotResponding(proc, activity, parent, aboveSystem, annotation);
                 }
             });
         }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index bf3713b..6e50808 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -305,9 +305,9 @@
             }
         }
 
-        @Override public boolean keyDispatchingTimedOut() {
+        @Override public boolean keyDispatchingTimedOut(String reason) {
             ActivityRecord activity = weakActivity.get();
-            return activity != null && activity.keyDispatchingTimedOut();
+            return activity != null && activity.keyDispatchingTimedOut(reason);
         }
 
         @Override public long getKeyDispatchingTimeout() {
@@ -960,14 +960,14 @@
         return r;
     }
 
-    public boolean keyDispatchingTimedOut() {
+    public boolean keyDispatchingTimedOut(String reason) {
         ActivityRecord r;
         ProcessRecord anrApp;
         synchronized(service) {
             r = getWaitingHistoryRecordLocked();
             anrApp = r != null ? r.app : null;
         }
-        return service.inputDispatchingTimedOut(anrApp, r, this, false);
+        return service.inputDispatchingTimedOut(anrApp, r, this, false, reason);
     }
 
     /** Returns the key dispatching timeout for this application token. */
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 7b4c077..d749e6c 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -1292,8 +1292,9 @@
 
     // Native callback.
     private long notifyANR(InputApplicationHandle inputApplicationHandle,
-            InputWindowHandle inputWindowHandle) {
-        return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, inputWindowHandle);
+            InputWindowHandle inputWindowHandle, String reason) {
+        return mWindowManagerCallbacks.notifyANR(
+                inputApplicationHandle, inputWindowHandle, reason);
     }
 
     // Native callback.
@@ -1477,7 +1478,7 @@
         public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle);
 
         public long notifyANR(InputApplicationHandle inputApplicationHandle,
-                InputWindowHandle inputWindowHandle);
+                InputWindowHandle inputWindowHandle, String reason);
 
         public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn);
 
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index 9620612..ea3af26 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -88,7 +88,7 @@
      */
     @Override
     public long notifyANR(InputApplicationHandle inputApplicationHandle,
-            InputWindowHandle inputWindowHandle) {
+            InputWindowHandle inputWindowHandle, String reason) {
         AppWindowToken appWindowToken = null;
         WindowState windowState = null;
         boolean aboveSystem = false;
@@ -105,7 +105,8 @@
 
             if (windowState != null) {
                 Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "
-                        + "sending to " + windowState.mAttrs.getTitle());
+                        + "sending to " + windowState.mAttrs.getTitle()
+                        + ".  Reason: " + reason);
                 // Figure out whether this window is layered above system windows.
                 // We need to do this here to help the activity manager know how to
                 // layer its ANR dialog.
@@ -114,19 +115,21 @@
                 aboveSystem = windowState.mBaseLayer > systemAlertLayer;
             } else if (appWindowToken != null) {
                 Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "
-                        + "sending to application " + appWindowToken.stringName);
+                        + "sending to application " + appWindowToken.stringName
+                        + ".  Reason: " + reason);
             } else {
-                Slog.i(WindowManagerService.TAG, "Input event dispatching timed out.");
+                Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "
+                        + ".  Reason: " + reason);
             }
 
-            mService.saveANRStateLocked(appWindowToken, windowState);
+            mService.saveANRStateLocked(appWindowToken, windowState, reason);
         }
 
         if (appWindowToken != null && appWindowToken.appToken != null) {
             try {
                 // Notify the activity manager about the timeout and let it decide whether
                 // to abort dispatching or keep waiting.
-                boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
+                boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason);
                 if (! abort) {
                     // The activity manager declined to abort dispatching.
                     // Wait a bit longer and timeout again later.
@@ -139,7 +142,7 @@
                 // Notify the activity manager about the timeout and let it decide whether
                 // to abort dispatching or keep waiting.
                 long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut(
-                        windowState.mSession.mPid, aboveSystem);
+                        windowState.mSession.mPid, aboveSystem, reason);
                 if (timeout >= 0) {
                     // The activity manager declined to abort dispatching.
                     // Wait a bit longer and timeout again later.
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 34d8973..b8d2050 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -10541,8 +10541,10 @@
      *
      * @param appWindowToken The application that ANR'd, may be null.
      * @param windowState The window that ANR'd, may be null.
+     * @param reason The reason for the ANR, may be null.
      */
-    public void saveANRStateLocked(AppWindowToken appWindowToken, WindowState windowState) {
+    public void saveANRStateLocked(AppWindowToken appWindowToken, WindowState windowState,
+            String reason) {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new FastPrintWriter(sw, false, 1024);
         pw.println("  ANR time: " + DateFormat.getInstance().format(new Date()));
@@ -10552,6 +10554,9 @@
         if (windowState != null) {
             pw.println("  Window at fault: " + windowState.mAttrs.getTitle());
         }
+        if (reason != null) {
+            pw.println("  Reason: " + reason);
+        }
         pw.println();
         dumpWindowsNoHeaderLocked(pw, true, null);
         pw.close();