Merge "Add logic to re-raise ANR dialogs if the user taps wait."
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7145c0f..b658204 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -412,6 +412,13 @@
public abstract void inputDispatchingResumed(int pid);
/**
+ * User tapped "wait" in the ANR dialog - reschedule the dialog to be shown again at a later
+ * time.
+ * @param data AppNotRespondingDialog.Data object
+ */
+ public abstract void rescheduleAnrDialog(Object data);
+
+ /**
* Sends {@link android.content.Intent#ACTION_CONFIGURATION_CHANGED} with all the appropriate
* flags.
*/
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b3d048d..cdfc795 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -257,6 +257,7 @@
import android.os.IPermissionController;
import android.os.IProcessInfoService;
import android.os.IProgressListener;
+import android.os.InputConstants;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
@@ -15992,8 +15993,22 @@
@Override
public void inputDispatchingResumed(int pid) {
- // TODO (b/171218828)
- return;
+ final ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ if (proc != null) {
+ mAppErrors.handleDismissAnrDialogs(proc);
+ }
+ }
+
+ @Override
+ public void rescheduleAnrDialog(Object data) {
+ Message msg = Message.obtain();
+ msg.what = SHOW_NOT_RESPONDING_UI_MSG;
+ msg.obj = (AppNotRespondingDialog.Data) data;
+
+ mUiHandler.sendMessageDelayed(msg, InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
}
@Override
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index bcb42bb..0dfdfe9 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -1058,6 +1058,7 @@
}
synchronized (mProcLock) {
final ProcessErrorStateRecord errState = proc.mErrorState;
+ errState.setAnrData(data);
if (!proc.isPersistent()) {
packageList = proc.getPackageListWithVersionCode();
}
@@ -1109,6 +1110,24 @@
}
}
+ void handleDismissAnrDialogs(ProcessRecord proc) {
+ synchronized (mProcLock) {
+ final ProcessErrorStateRecord errState = proc.mErrorState;
+
+ // Cancel any rescheduled ANR dialogs
+ mService.mUiHandler.removeMessages(
+ ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG, errState.getAnrData());
+
+ // Dismiss any ANR dialogs currently visible
+ if (errState.getDialogController().hasAnrDialogs()) {
+ errState.setNotResponding(false);
+ errState.setNotRespondingReport(null);
+ errState.getDialogController().clearAnrDialogs();
+ }
+ proc.mErrorState.setAnrData(null);
+ }
+ }
+
/**
* Information about a process that is currently marked as bad.
*/
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index b233a2c..878ef31 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -48,12 +48,14 @@
private final ActivityManagerService mService;
private final ProcessRecord mProc;
+ private final Data mData;
public AppNotRespondingDialog(ActivityManagerService service, Context context, Data data) {
super(context);
mService = service;
mProc = data.proc;
+ mData = data;
Resources res = context.getResources();
setCancelable(false);
@@ -165,6 +167,8 @@
errState.getDialogController().clearAnrDialogs();
}
mService.mServices.scheduleServiceTimeoutLocked(app);
+ // If the app remains unresponsive, show the dialog again after a delay.
+ mService.mInternal.rescheduleAnrDialog(mData);
}
break;
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 7e79ef5..8d3e442 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -117,6 +117,12 @@
private ComponentName mErrorReportReceiver;
/**
+ * ANR dialog data used to dismiss any visible ANR dialogs if the app becomes responsive.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private AppNotRespondingDialog.Data mAnrData;
+
+ /**
* Optional local handler to be invoked in the process crash.
*/
@CompositeRWLock({"mService", "mProcLock"})
@@ -209,6 +215,16 @@
return mDialogController;
}
+ @GuardedBy({"mService", "mProcLock"})
+ void setAnrData(AppNotRespondingDialog.Data data) {
+ mAnrData = data;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ AppNotRespondingDialog.Data getAnrData() {
+ return mAnrData;
+ }
+
ProcessErrorStateRecord(ProcessRecord app) {
mApp = app;
mService = app.mService;
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 4da3eca..3eeba7d 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -70,7 +70,45 @@
}
@Test
- fun testGestureMonitorAnr() {
+ fun testGestureMonitorAnr_Close() {
+ triggerAnr()
+ clickCloseAppOnAnrDialog()
+ }
+
+ @Test
+ fun testGestureMonitorAnr_Wait() {
+ triggerAnr()
+ clickWaitOnAnrDialog()
+ SystemClock.sleep(500) // Wait at least 500ms after tapping on wait
+ // ANR dialog should reappear after a delay - find the close button on it to verify
+ clickCloseAppOnAnrDialog()
+ }
+
+ private fun clickCloseAppOnAnrDialog() {
+ // Find anr dialog and kill app
+ val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+ val closeAppButton: UiObject2? =
+ uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
+ if (closeAppButton == null) {
+ fail("Could not find anr dialog")
+ return
+ }
+ closeAppButton.click()
+ }
+
+ private fun clickWaitOnAnrDialog() {
+ // Find anr dialog and tap on wait
+ val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+ val waitButton: UiObject2? =
+ uiDevice.wait(Until.findObject(By.res("android:id/aerr_wait")), 20000)
+ if (waitButton == null) {
+ fail("Could not find anr dialog/wait button")
+ return
+ }
+ waitButton.click()
+ }
+
+ private fun triggerAnr() {
startUnresponsiveActivity()
val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
val obj: UiObject2? = uiDevice.wait(Until.findObject(
@@ -91,20 +129,6 @@
// Todo: replace using timeout from android.hardware.input.IInputManager
SystemClock.sleep(5000) // default ANR timeout for gesture monitors
-
- clickCloseAppOnAnrDialog()
- }
-
- private fun clickCloseAppOnAnrDialog() {
- // Find anr dialog and kill app
- val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
- val closeAppButton: UiObject2? =
- uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
- if (closeAppButton == null) {
- fail("Could not find anr dialog")
- return
- }
- closeAppButton.click()
}
private fun startUnresponsiveActivity() {