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 android.app.ActivityManager 19 import android.app.ActivityTaskManager 20 import android.app.ApplicationExitInfo 21 import android.app.Instrumentation 22 import android.content.Intent 23 import android.hardware.display.DisplayManager 24 import android.os.Build 25 import android.os.Bundle 26 import android.os.IBinder 27 import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS 28 import android.os.SystemClock 29 import android.server.wm.CtsWindowInfoUtils.getWindowCenter 30 import android.server.wm.CtsWindowInfoUtils.waitForWindowOnTop 31 import android.testing.PollingCheck 32 import android.util.Log 33 import android.view.InputEvent 34 import android.view.MotionEvent 35 import android.view.MotionEvent.ACTION_DOWN 36 import androidx.test.ext.junit.runners.AndroidJUnit4 37 import androidx.test.filters.MediumTest 38 import androidx.test.platform.app.InstrumentationRegistry 39 import androidx.test.uiautomator.By 40 import androidx.test.uiautomator.UiDevice 41 import androidx.test.uiautomator.UiObject2 42 import androidx.test.uiautomator.Until 43 import com.android.cts.input.BlockingQueueEventVerifier 44 import com.android.cts.input.DebugInputRule 45 import com.android.cts.input.ShowErrorDialogsRule 46 import com.android.cts.input.UinputTouchScreen 47 import com.android.cts.input.inputeventmatchers.withMotionAction 48 import java.time.Duration 49 import java.util.concurrent.LinkedBlockingQueue 50 import java.util.function.Supplier 51 import org.junit.Assert.assertEquals 52 import org.junit.Assert.assertTrue 53 import org.junit.Assert.fail 54 import org.junit.Assume.assumeTrue 55 import org.junit.Rule 56 import org.junit.Test 57 import org.junit.runner.RunWith 58 59 /** 60 * Click on the center of the window identified by the provided window token. The click is performed 61 * using "UinputTouchScreen" device. If the touchscreen device is closed too soon, it may cause the 62 * click to be dropped. Therefore, the provided runnable can ensure that the click is delivered 63 * before the device is closed, thus avoiding this race. 64 */ 65 private fun clickOnWindow( 66 token: IBinder, 67 displayId: Int, 68 instrumentation: Instrumentation, 69 waitForEvent: Runnable, 70 ) { 71 val displayManager = instrumentation.context.getSystemService(DisplayManager::class.java) 72 val display = displayManager.getDisplay(displayId) 73 val point = getWindowCenter({ token }, display.displayId) 74 UinputTouchScreen(instrumentation, display).use { touchScreen -> 75 touchScreen.touchDown(point.x, point.y).lift() 76 // If the device is allowed to close without waiting here, the injected click may be dropped 77 waitForEvent.run() 78 } 79 } 80 81 /** 82 * This test makes sure that an unresponsive gesture monitor gets an ANR. 83 * 84 * The gesture monitor must be registered from a different process than the instrumented process. 85 * Otherwise, when the test runs, you will get: Test failed to run to completion. Reason: 86 * 'Instrumentation run failed due to 'keyDispatchingTimedOut''. Check device logcat for details 87 * RUNNER ERROR: Instrumentation run failed due to 'keyDispatchingTimedOut' 88 */ 89 @MediumTest 90 @RunWith(AndroidJUnit4::class) 91 class AnrTest { 92 companion object { 93 private const val TAG = "AnrTest" 94 private const val NO_MAX = 0 95 } 96 97 private val instrumentation = InstrumentationRegistry.getInstrumentation() 98 private lateinit var PACKAGE_NAME: String 99 private val DISPATCHING_TIMEOUT = 100 (UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * Build.HW_TIMEOUT_MULTIPLIER) 101 private var remoteWindowToken: IBinder? = null 102 private var remoteDisplayId: Int? = null 103 private var remotePid: Int? = null 104 private val remoteInputEvents = LinkedBlockingQueue<InputEvent>() 105 private val verifier = BlockingQueueEventVerifier(remoteInputEvents) 106 107 // Some devices don't support ANR error dialogs, such as cars, TVs, etc. 108 private val anrDialogsAreSupported = 109 ActivityTaskManager.currentUiModeSupportsErrorDialogs(instrumentation.targetContext) 110 111 val binder = 112 object : IAnrTestService.Stub() { provideActivityInfonull113 override fun provideActivityInfo(token: IBinder, displayId: Int, pid: Int) { 114 remoteWindowToken = token 115 remoteDisplayId = displayId 116 remotePid = pid 117 } 118 notifyMotionnull119 override fun notifyMotion(event: MotionEvent) { 120 remoteInputEvents.add(event) 121 } 122 } 123 124 @get:Rule val showErrorDialogs = ShowErrorDialogsRule() 125 126 @get:Rule val debugInputRule = DebugInputRule() 127 128 @Test 129 @DebugInputRule.DebugInput(bug = 339924248) testGestureMonitorAnr_Closenull130 fun testGestureMonitorAnr_Close() { 131 startUnresponsiveActivity() 132 val timestamp = System.currentTimeMillis() 133 triggerAnr() 134 if (anrDialogsAreSupported) { 135 clickCloseAppOnAnrDialog() 136 } else { 137 Log.i(TAG, "The device does not support ANR dialogs, skipping check for ANR window") 138 // We still want to wait for the app to get killed by the ActivityManager 139 } 140 waitForNewExitReasonAfter(timestamp) 141 } 142 143 @Test 144 @DebugInputRule.DebugInput(bug = 339924248) testGestureMonitorAnr_Waitnull145 fun testGestureMonitorAnr_Wait() { 146 assumeTrue(anrDialogsAreSupported) 147 startUnresponsiveActivity() 148 triggerAnr() 149 clickWaitOnAnrDialog() 150 SystemClock.sleep(500) // Wait at least 500ms after tapping on wait 151 // ANR dialog should reappear after a delay - find the close button on it to verify 152 val timestamp = System.currentTimeMillis() 153 clickCloseAppOnAnrDialog() 154 waitForNewExitReasonAfter(timestamp) 155 } 156 clickCloseAppOnAnrDialognull157 private fun clickCloseAppOnAnrDialog() { 158 // Find anr dialog and kill app 159 val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) 160 val closeAppButton: UiObject2? = 161 uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000) 162 if (closeAppButton == null) { 163 fail("Could not find anr dialog/close button") 164 return 165 } 166 closeAppButton.click() 167 } 168 clickWaitOnAnrDialognull169 private fun clickWaitOnAnrDialog() { 170 // Find anr dialog and tap on wait 171 val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) 172 val waitButton: UiObject2? = 173 uiDevice.wait(Until.findObject(By.res("android:id/aerr_wait")), 20000) 174 if (waitButton == null) { 175 fail("Could not find anr dialog/wait button") 176 return 177 } 178 waitButton.click() 179 } 180 getExitReasonsnull181 private fun getExitReasons(): List<ApplicationExitInfo> { 182 val packageName = UnresponsiveGestureMonitorActivity::class.java.getPackage()!!.name 183 lateinit var infos: List<ApplicationExitInfo> 184 instrumentation.runOnMainSync { 185 val am = instrumentation.getContext().getSystemService(ActivityManager::class.java)!! 186 infos = am.getHistoricalProcessExitReasons(packageName, remotePid!!, NO_MAX) 187 } 188 return infos 189 } 190 191 /** 192 * We must wait for the app to be fully closed before exiting this test. This is because another 193 * test may again invoke 'am start' for the same activity. If the 1st process that got ANRd 194 * isn't killed by the time second 'am start' runs, the killing logic will apply to the newly 195 * launched 'am start' instance, and the second test will fail because the unresponsive activity 196 * will never be launched. 197 * 198 * Also, we must ensure that we wait until it's killed, so that the next test can launch this 199 * activity again. 200 */ waitForNewExitReasonAfternull201 private fun waitForNewExitReasonAfter(timestamp: Long) { 202 PollingCheck.waitFor(Duration.ofSeconds(20).toMillis() * Build.HW_TIMEOUT_MULTIPLIER) { 203 val reasons = getExitReasons() 204 !reasons.isEmpty() && reasons[0].timestamp >= timestamp 205 } 206 val reasons = getExitReasons() 207 assertTrue(reasons[0].timestamp > timestamp) 208 assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason) 209 } 210 triggerAnrnull211 private fun triggerAnr() { 212 clickOnWindow(remoteWindowToken!!, remoteDisplayId!!, instrumentation) { 213 verifier.assertReceivedMotion(withMotionAction(ACTION_DOWN)) 214 } 215 216 SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors 217 } 218 startUnresponsiveActivitynull219 private fun startUnresponsiveActivity() { 220 remoteWindowToken = null 221 val intent = 222 Intent(instrumentation.targetContext, UnresponsiveGestureMonitorActivity::class.java) 223 intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NEW_DOCUMENT 224 val bundle = Bundle() 225 bundle.putBinder("serviceBinder", binder) 226 intent.putExtra("serviceBundle", bundle) 227 instrumentation.targetContext.startActivity(intent) 228 // first, wait for the token to become valid 229 PollingCheck.check( 230 "UnresponsiveGestureMonitorActivity failed to call 'provideActivityInfo'", 231 Duration.ofSeconds(10).toMillis() * Build.HW_TIMEOUT_MULTIPLIER, 232 ) { 233 remoteWindowToken != null 234 } 235 // next, wait for the window of the activity to get on top 236 // we could combine the two checks above, but the current setup makes it easier to detect 237 // errors 238 assertTrue( 239 "Remote activity window did not become visible", 240 waitForWindowOnTop(Duration.ofSeconds(5), Supplier { remoteWindowToken }), 241 ) 242 } 243 } 244