Wait for ANRd process to be killed before starting next test

We must wait for the ANRd process to be fully killed. If we simply click
on "close" button on the dialog, this is not sufficient. The system
would still be processing ANR and trying to kill the process. This
killing action will therefore impact the next 'am start' command. The
second activity will never be launched.

In this CL, add a wait by polling the 'ApplicationExitInfo' lists before
and after the 'close' button is clicked.

Bug: 196111779
Test: atest InputTests:AnrTest
Change-Id: I35e8f4ba302d0f1ed4a03be2257541a4e9a638f4
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 6e65350..de9bbb6 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -19,6 +19,7 @@
         "androidx.test.ext.junit",
         "androidx.test.rules",
         "services.core.unboosted",
+        "testables",
         "truth-prebuilt",
         "ub-uiautomator",
     ],
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 3eeba7d..1d65cc3 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -19,7 +19,11 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.filters.MediumTest
 
+import android.app.ActivityManager
+import android.app.ApplicationExitInfo
 import android.graphics.Rect
+import android.os.Build
+import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS
 import android.os.SystemClock
 import android.provider.Settings
 import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
@@ -27,10 +31,13 @@
 import android.support.test.uiautomator.UiDevice
 import android.support.test.uiautomator.UiObject2
 import android.support.test.uiautomator.Until
+import android.testing.PollingCheck
 import android.view.InputDevice
 import android.view.MotionEvent
 
 import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
 import org.junit.Before
 import org.junit.Test
@@ -51,22 +58,28 @@
 class AnrTest {
     companion object {
         private const val TAG = "AnrTest"
+        private const val ALL_PIDS = 0
+        private const val NO_MAX = 0
     }
 
-    val mInstrumentation = InstrumentationRegistry.getInstrumentation()
-    var mHideErrorDialogs = 0
+    private val instrumentation = InstrumentationRegistry.getInstrumentation()
+    private var hideErrorDialogs = 0
+    private lateinit var PACKAGE_NAME: String
+    private val DISPATCHING_TIMEOUT = (UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+            Build.HW_TIMEOUT_MULTIPLIER)
 
     @Before
     fun setUp() {
-        val contentResolver = mInstrumentation.targetContext.contentResolver
-        mHideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
+        val contentResolver = instrumentation.targetContext.contentResolver
+        hideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
         Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
+        PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage().getName()
     }
 
     @After
     fun tearDown() {
-        val contentResolver = mInstrumentation.targetContext.contentResolver
-        Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, mHideErrorDialogs)
+        val contentResolver = instrumentation.targetContext.contentResolver
+        Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, hideErrorDialogs)
     }
 
     @Test
@@ -86,19 +99,28 @@
 
     private fun clickCloseAppOnAnrDialog() {
         // Find anr dialog and kill app
-        val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+        val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
         val closeAppButton: UiObject2? =
                 uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
         if (closeAppButton == null) {
             fail("Could not find anr dialog")
             return
         }
+        val initialReasons = getExitReasons()
         closeAppButton.click()
+        /**
+         * We must wait for the app to be fully closed before exiting this test. This is because
+         * another test may again invoke 'am start' for the same activity.
+         * If the 1st process that got ANRd isn't killed by the time second 'am start' runs,
+         * the killing logic will apply to the newly launched 'am start' instance, and the second
+         * test will fail because the unresponsive activity will never be launched.
+         */
+        waitForNewExitReason(initialReasons[0].timestamp)
     }
 
     private fun clickWaitOnAnrDialog() {
         // Find anr dialog and tap on wait
-        val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+        val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
         val waitButton: UiObject2? =
                 uiDevice.wait(Until.findObject(By.res("android:id/aerr_wait")), 20000)
         if (waitButton == null) {
@@ -108,9 +130,27 @@
         waitButton.click()
     }
 
+    private fun getExitReasons(): List<ApplicationExitInfo> {
+        lateinit var infos: List<ApplicationExitInfo>
+        instrumentation.runOnMainSync {
+            val am = instrumentation.getContext().getSystemService(ActivityManager::class.java)
+            infos = am.getHistoricalProcessExitReasons(PACKAGE_NAME, ALL_PIDS, NO_MAX)
+        }
+        return infos
+    }
+
+    private fun waitForNewExitReason(previousExitTimestamp: Long) {
+        PollingCheck.waitFor {
+            getExitReasons()[0].timestamp > previousExitTimestamp
+        }
+        val reasons = getExitReasons()
+        assertTrue(reasons[0].timestamp > previousExitTimestamp)
+        assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason)
+    }
+
     private fun triggerAnr() {
         startUnresponsiveActivity()
-        val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+        val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
         val obj: UiObject2? = uiDevice.wait(Until.findObject(
                 By.text("Unresponsive gesture monitor")), 10000)
 
@@ -125,15 +165,14 @@
                 MotionEvent.ACTION_DOWN, rect.left.toFloat(), rect.top.toFloat(), 0 /* metaState */)
         downEvent.source = InputDevice.SOURCE_TOUCHSCREEN
 
-        mInstrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)
+        instrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)
 
-        // Todo: replace using timeout from android.hardware.input.IInputManager
-        SystemClock.sleep(5000) // default ANR timeout for gesture monitors
+        SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors
     }
 
     private fun startUnresponsiveActivity() {
         val flags = " -W -n "
-        val startCmd = "am start $flags com.android.test.input/.UnresponsiveGestureMonitorActivity"
-        mInstrumentation.uiAutomation.executeShellCommand(startCmd)
+        val startCmd = "am start $flags $PACKAGE_NAME/.UnresponsiveGestureMonitorActivity"
+        instrumentation.uiAutomation.executeShellCommand(startCmd)
     }
-}
\ No newline at end of file
+}