Merge "Do not repeat ANR dialogs, except for "input" ANRs"
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8e1f969..e962fb0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6694,6 +6694,10 @@
 
     @Override
     public void appNotResponding(final String reason) {
+        appNotResponding(reason, /*isContinuousAnr*/ false);
+    }
+
+    public void appNotResponding(final String reason, boolean isContinuousAnr) {
         TimeoutRecord timeoutRecord = TimeoutRecord.forApp("App requested: " + reason);
         final int callingPid = Binder.getCallingPid();
 
@@ -6706,7 +6710,7 @@
             }
 
             mAnrHelper.appNotResponding(app, null, app.info, null, null, false,
-                    timeoutRecord);
+                    timeoutRecord, isContinuousAnr);
         }
     }
 
@@ -18401,7 +18405,8 @@
                     }
                 }
                 mAnrHelper.appNotResponding(proc, activityShortComponentName, aInfo,
-                        parentShortComponentName, parentProcess, aboveSystem, timeoutRecord);
+                        parentShortComponentName, parentProcess, aboveSystem, timeoutRecord,
+                        /*isContinuousAnr*/ true);
             }
 
             return true;
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index 71c80ea..463a2f8 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -96,13 +96,13 @@
     void appNotResponding(ProcessRecord anrProcess, TimeoutRecord timeoutRecord) {
         appNotResponding(anrProcess, null /* activityShortComponentName */, null /* aInfo */,
                 null /* parentShortComponentName */, null /* parentProcess */,
-                false /* aboveSystem */, timeoutRecord);
+                false /* aboveSystem */, timeoutRecord, /*isContinuousAnr*/ false);
     }
 
     void appNotResponding(ProcessRecord anrProcess, String activityShortComponentName,
             ApplicationInfo aInfo, String parentShortComponentName,
             WindowProcessController parentProcess, boolean aboveSystem,
-            TimeoutRecord timeoutRecord) {
+            TimeoutRecord timeoutRecord, boolean isContinuousAnr) {
         try {
             timeoutRecord.mLatencyTracker.appNotRespondingStarted();
             final int incomingPid = anrProcess.mPid;
@@ -132,7 +132,7 @@
                 timeoutRecord.mLatencyTracker.anrRecordPlacingOnQueueWithSize(mAnrRecords.size());
                 mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
                         parentShortComponentName, parentProcess, aboveSystem,
-                        mAuxiliaryTaskExecutor, timeoutRecord));
+                        mAuxiliaryTaskExecutor, timeoutRecord, isContinuousAnr));
             }
             startAnrConsumerIfNeeded();
         } finally {
@@ -230,10 +230,12 @@
         final boolean mAboveSystem;
         final ExecutorService mAuxiliaryTaskExecutor;
         final long mTimestamp = SystemClock.uptimeMillis();
+        final boolean mIsContinuousAnr;
         AnrRecord(ProcessRecord anrProcess, String activityShortComponentName,
                 ApplicationInfo aInfo, String parentShortComponentName,
                 WindowProcessController parentProcess, boolean aboveSystem,
-                ExecutorService auxiliaryTaskExecutor, TimeoutRecord timeoutRecord) {
+                ExecutorService auxiliaryTaskExecutor, TimeoutRecord timeoutRecord,
+                boolean isContinuousAnr) {
             mApp = anrProcess;
             mPid = anrProcess.mPid;
             mActivityShortComponentName = activityShortComponentName;
@@ -243,6 +245,7 @@
             mParentProcess = parentProcess;
             mAboveSystem = aboveSystem;
             mAuxiliaryTaskExecutor = auxiliaryTaskExecutor;
+            mIsContinuousAnr = isContinuousAnr;
         }
 
         void appNotResponding(boolean onlyDumpSelf) {
@@ -250,7 +253,8 @@
                 mTimeoutRecord.mLatencyTracker.anrProcessingStarted();
                 mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,
                         mParentShortComponentName, mParentProcess, mAboveSystem,
-                        mTimeoutRecord, mAuxiliaryTaskExecutor, onlyDumpSelf);
+                        mTimeoutRecord, mAuxiliaryTaskExecutor, onlyDumpSelf,
+                        mIsContinuousAnr);
             } finally {
                 mTimeoutRecord.mLatencyTracker.anrProcessingEnded();
             }
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 5fe8427..d3e91da 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -169,8 +169,10 @@
                             errState.getDialogController().clearAnrDialogs();
                         }
                         mService.mServices.scheduleServiceTimeoutLocked(app);
-                        // If the app remains unresponsive, show the dialog again after a delay.
-                        mService.mInternal.rescheduleAnrDialog(mData);
+                        if (mData.isContinuousAnr) {
+                            // If the app remains unresponsive, show the dialog again after a delay.
+                            mService.mInternal.rescheduleAnrDialog(mData);
+                        }
                     }
                     break;
             }
@@ -197,10 +199,17 @@
         final ApplicationInfo aInfo;
         final boolean aboveSystem;
 
-        Data(ProcessRecord proc, ApplicationInfo aInfo, boolean aboveSystem) {
+        // If true, then even if the user presses "WAIT" on the ANR dialog,
+        // we'll show it again until the app start responding again.
+        // (we only use it for input dispatch ANRs)
+        final boolean isContinuousAnr;
+
+        Data(ProcessRecord proc, ApplicationInfo aInfo, boolean aboveSystem,
+                boolean isContinuousAnr) {
             this.proc = proc;
             this.aInfo = aInfo;
             this.aboveSystem = aboveSystem;
+            this.isContinuousAnr = isContinuousAnr;
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 68d906b..9bb63d3 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -263,7 +263,8 @@
     void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
             String parentShortComponentName, WindowProcessController parentProcess,
             boolean aboveSystem, TimeoutRecord timeoutRecord,
-            ExecutorService auxiliaryTaskExecutor, boolean onlyDumpSelf) {
+            ExecutorService auxiliaryTaskExecutor, boolean onlyDumpSelf,
+            boolean isContinuousAnr) {
         String annotation = timeoutRecord.mReason;
         AnrLatencyTracker latencyTracker = timeoutRecord.mLatencyTracker;
         Future<?> updateCpuStatsNowFirstCall = null;
@@ -630,7 +631,8 @@
                 // Bring up the infamous App Not Responding dialog
                 Message msg = Message.obtain();
                 msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
-                msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem);
+                msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem,
+                        isContinuousAnr);
 
                 mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);
             }
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index e6ab73a..162855a 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -119,12 +119,13 @@
         final TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(
                 annotation);
         mAnrHelper.appNotResponding(mAnrApp, activityShortComponentName, appInfo,
-                parentShortComponentName, parentProcess, aboveSystem, timeoutRecord);
+                parentShortComponentName, parentProcess, aboveSystem, timeoutRecord,
+                /*isContinuousAnr*/ false);
 
         verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding(
                 eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName),
                 eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mExecutorService),
-                eq(false) /* onlyDumpSelf */);
+                eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/);
     }
 
     @Test
@@ -137,13 +138,14 @@
             processingLatch.await();
             return null;
         }).when(mAnrApp.mErrorState).appNotResponding(anyString(), any(), any(), any(),
-                anyBoolean(), any(), any(), anyBoolean());
+                anyBoolean(), any(), any(), anyBoolean(), anyBoolean());
         final ApplicationInfo appInfo = new ApplicationInfo();
         final TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(
                 "annotation");
         final Runnable reportAnr = () -> mAnrHelper.appNotResponding(mAnrApp,
                 "activityShortComponentName", appInfo, "parentShortComponentName",
-                null /* parentProcess */, false /* aboveSystem */, timeoutRecord);
+                null /* parentProcess */, false /* aboveSystem */, timeoutRecord,
+                false /*isContinuousAnr*/);
         reportAnr.run();
         // This should be skipped because the pid is pending in queue.
         reportAnr.run();
@@ -160,6 +162,6 @@
         // There is only one ANR reported.
         verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS).only()).appNotResponding(
                 anyString(), any(), any(), any(), anyBoolean(), any(), eq(mExecutorService),
-                anyBoolean());
+                anyBoolean(), anyBoolean());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
index 9cada91..6350e22 100644
--- a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
@@ -202,6 +202,7 @@
         TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchNoFocusedWindow(annotation);
         processErrorState.appNotResponding(null /* activityShortComponentName */, null /* aInfo */,
                 null /* parentShortComponentName */, null /* parentProcess */,
-                false /* aboveSystem */, timeoutRecord, mExecutorService, false /* onlyDumpSelf */);
+                false /* aboveSystem */, timeoutRecord, mExecutorService, false /* onlyDumpSelf */,
+                false /*isContinuousAnr*/);
     }
 }