1 /* <lambda>null2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.test.input 17 18 import androidx.test.ext.junit.runners.AndroidJUnit4 19 import androidx.test.platform.app.InstrumentationRegistry 20 import androidx.test.filters.MediumTest 21 22 import android.app.ActivityManager 23 import android.app.ApplicationExitInfo 24 import android.content.Context 25 import android.graphics.Rect 26 import android.hardware.display.DisplayManager 27 import android.os.Build 28 import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS 29 import android.os.SystemClock 30 import android.provider.Settings 31 import android.provider.Settings.Global.HIDE_ERROR_DIALOGS 32 import android.server.wm.CtsWindowInfoUtils.waitForStableWindowGeometry 33 import android.testing.PollingCheck 34 35 import androidx.test.uiautomator.By 36 import androidx.test.uiautomator.UiDevice 37 import androidx.test.uiautomator.UiObject2 38 import androidx.test.uiautomator.Until 39 40 import com.android.cts.input.DebugInputRule 41 import com.android.cts.input.UinputTouchScreen 42 43 import java.time.Duration 44 45 import org.junit.After 46 import org.junit.Assert.assertEquals 47 import org.junit.Assert.assertTrue 48 import org.junit.Assert.fail 49 import org.junit.Before 50 import org.junit.Rule 51 import org.junit.Test 52 import org.junit.runner.RunWith 53 54 /** 55 * This test makes sure that an unresponsive gesture monitor gets an ANR. 56 * 57 * The gesture monitor must be registered from a different process than the instrumented process. 58 * Otherwise, when the test runs, you will get: 59 * Test failed to run to completion. 60 * Reason: 'Instrumentation run failed due to 'keyDispatchingTimedOut''. 61 * Check device logcat for details 62 * RUNNER ERROR: Instrumentation run failed due to 'keyDispatchingTimedOut' 63 */ 64 @MediumTest 65 @RunWith(AndroidJUnit4::class) 66 class AnrTest { 67 companion object { 68 private const val TAG = "AnrTest" 69 private const val ALL_PIDS = 0 70 private const val NO_MAX = 0 71 } 72 73 private val instrumentation = InstrumentationRegistry.getInstrumentation() 74 private var hideErrorDialogs = 0 75 private lateinit var PACKAGE_NAME: String 76 private val DISPATCHING_TIMEOUT = (UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * 77 Build.HW_TIMEOUT_MULTIPLIER) 78 79 @get:Rule 80 val debugInputRule = DebugInputRule() 81 82 @Before 83 fun setUp() { 84 val contentResolver = instrumentation.targetContext.contentResolver 85 hideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0) 86 Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, 0) 87 PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage()!!.getName() 88 } 89 90 @After 91 fun tearDown() { 92 val contentResolver = instrumentation.targetContext.contentResolver 93 Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, hideErrorDialogs) 94 } 95 96 @Test 97 @DebugInputRule.DebugInput(bug = 339924248) 98 fun testGestureMonitorAnr_Close() { 99 triggerAnr() 100 clickCloseAppOnAnrDialog() 101 } 102 103 @Test 104 @DebugInputRule.DebugInput(bug = 339924248) 105 fun testGestureMonitorAnr_Wait() { 106 triggerAnr() 107 clickWaitOnAnrDialog() 108 SystemClock.sleep(500) // Wait at least 500ms after tapping on wait 109 // ANR dialog should reappear after a delay - find the close button on it to verify 110 clickCloseAppOnAnrDialog() 111 } 112 113 private fun clickCloseAppOnAnrDialog() { 114 // Find anr dialog and kill app 115 val timestamp = System.currentTimeMillis() 116 val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) 117 val closeAppButton: UiObject2? = 118 uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000) 119 if (closeAppButton == null) { 120 fail("Could not find anr dialog/close button") 121 return 122 } 123 closeAppButton.click() 124 /** 125 * We must wait for the app to be fully closed before exiting this test. This is because 126 * another test may again invoke 'am start' for the same activity. 127 * If the 1st process that got ANRd isn't killed by the time second 'am start' runs, 128 * the killing logic will apply to the newly launched 'am start' instance, and the second 129 * test will fail because the unresponsive activity will never be launched. 130 */ 131 waitForNewExitReasonAfter(timestamp) 132 } 133 134 private fun clickWaitOnAnrDialog() { 135 // Find anr dialog and tap on wait 136 val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) 137 val waitButton: UiObject2? = 138 uiDevice.wait(Until.findObject(By.res("android:id/aerr_wait")), 20000) 139 if (waitButton == null) { 140 fail("Could not find anr dialog/wait button") 141 return 142 } 143 waitButton.click() 144 } 145 146 private fun getExitReasons(): List<ApplicationExitInfo> { 147 lateinit var infos: List<ApplicationExitInfo> 148 instrumentation.runOnMainSync { 149 val am = instrumentation.getContext().getSystemService(ActivityManager::class.java)!! 150 infos = am.getHistoricalProcessExitReasons(PACKAGE_NAME, ALL_PIDS, NO_MAX) 151 } 152 return infos 153 } 154 155 private fun waitForNewExitReasonAfter(timestamp: Long) { 156 PollingCheck.waitFor { 157 val reasons = getExitReasons() 158 !reasons.isEmpty() && reasons[0].timestamp >= timestamp 159 } 160 val reasons = getExitReasons() 161 assertTrue(reasons[0].timestamp > timestamp) 162 assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason) 163 } 164 165 private fun clickOnObject(obj: UiObject2) { 166 val displayManager = 167 instrumentation.context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager 168 val display = displayManager.getDisplay(obj.getDisplayId()) 169 val rect: Rect = obj.visibleBounds 170 UinputTouchScreen(instrumentation, display).use { touchScreen -> 171 touchScreen 172 .touchDown(rect.centerX(), rect.centerY()) 173 .lift() 174 } 175 } 176 177 private fun triggerAnr() { 178 startUnresponsiveActivity() 179 val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) 180 val obj: UiObject2? = uiDevice.wait(Until.findObject(By.pkg(PACKAGE_NAME)), 10000) 181 182 if (obj == null) { 183 fail("Could not find unresponsive activity") 184 return 185 } 186 187 clickOnObject(obj) 188 189 SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors 190 } 191 192 private fun startUnresponsiveActivity() { 193 val flags = " -W -n " 194 val startCmd = "am start $flags $PACKAGE_NAME/.UnresponsiveGestureMonitorActivity" 195 instrumentation.uiAutomation.executeShellCommand(startCmd) 196 waitForStableWindowGeometry(Duration.ofSeconds(5)) 197 } 198 } 199