1 /* 2 * Copyright (C) 2022 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 17 package android.input.cts 18 19 import android.cts.input.EventVerifier 20 import android.graphics.Point 21 import android.hardware.input.InputManager 22 import android.view.MotionEvent 23 import android.view.MotionEvent.ACTION_MOVE 24 import androidx.test.ext.junit.runners.AndroidJUnit4 25 import androidx.test.filters.MediumTest 26 import androidx.test.platform.app.InstrumentationRegistry 27 import com.android.cts.input.DebugInputRule 28 import com.android.cts.input.UinputTouchDevice 29 import com.android.cts.input.UinputTouchScreen 30 import com.android.cts.input.VirtualDisplayActivityScenario 31 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.DEFAULT_HEIGHT 32 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.DEFAULT_WIDTH 33 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.ORIENTATION_0 34 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.ORIENTATION_180 35 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.ORIENTATION_270 36 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.ORIENTATION_90 37 import com.android.cts.input.inputeventmatchers.withCoords 38 import com.android.cts.input.inputeventmatchers.withFlags 39 import com.android.cts.input.inputeventmatchers.withMotionAction 40 import com.android.cts.input.inputeventmatchers.withPointerCount 41 import org.hamcrest.Description 42 import org.hamcrest.Matchers.allOf 43 import org.hamcrest.TypeSafeMatcher 44 import org.junit.After 45 import org.junit.Assert.assertNull 46 import org.junit.Before 47 import org.junit.Rule 48 import org.junit.Test 49 import org.junit.rules.TestName 50 import org.junit.runner.RunWith 51 52 @MediumTest 53 @RunWith(AndroidJUnit4::class) 54 class TouchScreenTest { 55 private val instrumentation = InstrumentationRegistry.getInstrumentation() 56 private lateinit var touchScreen: UinputTouchDevice 57 private lateinit var verifier: EventVerifier 58 59 @get:Rule 60 val debugInputRule = DebugInputRule() 61 @get:Rule 62 val testName = TestName() 63 @get:Rule 64 val virtualDisplayRule = VirtualDisplayActivityScenario.Rule<CaptureEventActivity>(testName) 65 66 @Before setUpnull67 fun setUp() { 68 touchScreen = UinputTouchScreen(instrumentation, virtualDisplayRule.virtualDisplay.display) 69 verifier = EventVerifier(virtualDisplayRule.activity::getInputEvent) 70 } 71 72 @After tearDownnull73 fun tearDown() { 74 if (this::touchScreen.isInitialized) { 75 touchScreen.close() 76 } 77 } 78 79 @Test testHostUsiVersionIsNullnull80 fun testHostUsiVersionIsNull() { 81 assertNull( 82 instrumentation.targetContext.getSystemService(InputManager::class.java) 83 .getHostUsiVersion(virtualDisplayRule.virtualDisplay.display) 84 ) 85 } 86 87 @DebugInputRule.DebugInput(bug = 288321659) 88 @Test testSingleTouchnull89 fun testSingleTouch() { 90 val x = 100 91 val y = 100 92 93 val pointer = touchScreen.touchDown(x, y) 94 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_DOWN)) 95 96 pointer.moveTo(x + 1, y + 1) 97 verifier.assertReceivedMotion(withMotionAction(ACTION_MOVE)) 98 99 pointer.lift() 100 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_UP)) 101 } 102 103 @DebugInputRule.DebugInput(bug = 288321659) 104 @Test testMultiTouchnull105 fun testMultiTouch() { 106 val location1 = Point(100, 100) 107 val location2 = Point(150, 150) 108 109 // ACTION_DOWN 110 val pointer1 = touchScreen.touchDown(location1.x, location1.y) 111 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_DOWN)) 112 113 // ACTION_POINTER_DOWN 114 val pointer2 = touchScreen.touchDown(location2.x, location2.y) 115 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_POINTER_DOWN, 1)) 116 117 // ACTION_MOVE 118 location1.offset(1, 1) 119 pointer1.moveTo(location1.x, location1.y) 120 verifier.assertReceivedMotion(withMotionAction(ACTION_MOVE)) 121 122 // ACTION_POINTER_UP 123 pointer1.lift() 124 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_POINTER_UP, 0)) 125 126 // ACTION_UP 127 pointer2.lift() 128 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_UP)) 129 } 130 131 @DebugInputRule.DebugInput(bug = 288321659) 132 @Test testDeviceCancelnull133 fun testDeviceCancel() { 134 val location = Point(100, 100) 135 136 // ACTION_DOWN 137 val pointer = touchScreen.touchDown(location.x, location.y) 138 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_DOWN)) 139 140 // ACTION_MOVE 141 location.offset(1, 1) 142 pointer.moveTo(location.x, location.y) 143 verifier.assertReceivedMotion(withMotionAction(ACTION_MOVE)) 144 145 // ACTION_CANCEL 146 pointer.close() 147 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_CANCEL)) 148 149 // No event 150 virtualDisplayRule.activity.assertNoEvents() 151 } 152 153 /** 154 * Check that pointer cancel is received by the activity via uinput device. 155 */ 156 @DebugInputRule.DebugInput(bug = 288321659) 157 @Test testDevicePointerCancelnull158 fun testDevicePointerCancel() { 159 val location1 = Point(100, 100) 160 val location2 = Point(150, 150) 161 162 // ACTION_DOWN 163 val pointer1 = touchScreen.touchDown(location1.x, location1.y) 164 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_DOWN)) 165 166 // ACTION_MOVE 167 location1.offset(1, 1) 168 pointer1.moveTo(location1.x, location1.y) 169 verifier.assertReceivedMotion(withMotionAction(ACTION_MOVE)) 170 171 // ACTION_POINTER_DOWN(1) 172 val pointer2 = touchScreen.touchDown(location2.x, location2.y) 173 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_POINTER_DOWN, 1)) 174 175 // ACTION_POINTER_UP(1) with cancel flag 176 pointer2.close() 177 verifier.assertReceivedMotion( 178 allOf( 179 withMotionAction(MotionEvent.ACTION_POINTER_UP, 1), 180 withFlags(MotionEvent.FLAG_CANCELED) 181 ) 182 ) 183 184 // ACTION_UP 185 pointer1.lift() 186 // TODO(b/341773832): Fix the implementation so that this unexpected MOVE is not generated. 187 verifier.assertReceivedMotion(allOf(withMotionAction(ACTION_MOVE), withPointerCount(1))) 188 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_UP)) 189 } 190 191 @Test testTouchScreenPrecisionOrientation0null192 fun testTouchScreenPrecisionOrientation0() { 193 virtualDisplayRule.runInDisplayOrientation(ORIENTATION_0) { 194 verifyTapsOnFourCorners( 195 arrayOf( 196 Point(0, 0), 197 Point(DEFAULT_WIDTH - 1, 0), 198 Point(DEFAULT_WIDTH - 1, DEFAULT_HEIGHT - 1), 199 Point(0, DEFAULT_HEIGHT - 1), 200 ) 201 ) 202 } 203 } 204 205 @Test testTouchScreenPrecisionOrientation90null206 fun testTouchScreenPrecisionOrientation90() { 207 virtualDisplayRule.runInDisplayOrientation(ORIENTATION_90) { 208 verifyTapsOnFourCorners( 209 arrayOf( 210 Point(0, DEFAULT_WIDTH - 1), 211 Point(0, 0), 212 Point(DEFAULT_HEIGHT - 1, 0), 213 Point(DEFAULT_HEIGHT - 1, DEFAULT_WIDTH - 1), 214 ) 215 ) 216 } 217 } 218 219 @Test testTouchScreenPrecisionOrientation180null220 fun testTouchScreenPrecisionOrientation180() { 221 virtualDisplayRule.runInDisplayOrientation(ORIENTATION_180) { 222 verifyTapsOnFourCorners( 223 arrayOf( 224 Point(DEFAULT_WIDTH - 1, DEFAULT_HEIGHT - 1), 225 Point(0, DEFAULT_HEIGHT - 1), 226 Point(0, 0), 227 Point(DEFAULT_WIDTH - 1, 0), 228 ) 229 ) 230 } 231 } 232 233 @Test testTouchScreenPrecisionOrientation270null234 fun testTouchScreenPrecisionOrientation270() { 235 virtualDisplayRule.runInDisplayOrientation(ORIENTATION_270) { 236 verifyTapsOnFourCorners( 237 arrayOf( 238 Point(DEFAULT_HEIGHT - 1, 0), 239 Point(DEFAULT_HEIGHT - 1, DEFAULT_WIDTH - 1), 240 Point(0, DEFAULT_WIDTH - 1), 241 Point(0, 0), 242 ) 243 ) 244 } 245 } 246 247 @Test testEventTimenull248 fun testEventTime() { 249 val location = Point(100, 100) 250 251 val withConsistentEventTime = object : TypeSafeMatcher<MotionEvent>() { 252 override fun describeTo(description: Description) { 253 description.appendText("getEventTimeNanos() is consistent with getEventTime()") 254 } 255 256 override fun matchesSafely(event: MotionEvent): Boolean { 257 return event.eventTimeNanos / 1_000_000 == event.eventTime 258 } 259 } 260 261 // ACTION_DOWN 262 val pointer = touchScreen.touchDown(location.x, location.y) 263 verifier.assertReceivedMotion( 264 allOf(withMotionAction(MotionEvent.ACTION_DOWN), withConsistentEventTime) 265 ) 266 267 // ACTION_MOVE 268 location.offset(1, 1) 269 pointer.moveTo(location.x, location.y) 270 verifier.assertReceivedMotion( 271 allOf(withMotionAction(ACTION_MOVE), withConsistentEventTime) 272 ) 273 274 // ACTION_UP 275 pointer.lift() 276 verifier.assertReceivedMotion( 277 allOf(withMotionAction(MotionEvent.ACTION_UP), withConsistentEventTime) 278 ) 279 } 280 281 // Verifies that each of the four corners of the touch screen (lt, rt, rb, lb) map to the 282 // given four points by tapping on the corners in order and asserting the location of the 283 // received events match the provided values. verifyTapsOnFourCornersnull284 private fun verifyTapsOnFourCorners(expectedPoints: Array<Point>) { 285 val pointerId = 0 286 for (i in 0 until 4) { 287 touchScreen.sendBtnTouch(true) 288 touchScreen.sendDown(pointerId, CORNERS[i]) 289 touchScreen.sync() 290 verifier.assertReceivedMotion( 291 allOf(withMotionAction(MotionEvent.ACTION_DOWN), withCoords(expectedPoints[i])) 292 ) 293 294 touchScreen.sendBtnTouch(false) 295 touchScreen.sendUp(pointerId) 296 touchScreen.sync() 297 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_UP)) 298 } 299 } 300 301 companion object { 302 // The four corners of the touchscreen: lt, rt, rb, lb 303 val CORNERS = arrayOf( 304 Point(0, 0), 305 Point(DEFAULT_WIDTH - 1, 0), 306 Point(DEFAULT_WIDTH - 1, DEFAULT_HEIGHT - 1), 307 Point(0, DEFAULT_HEIGHT - 1), 308 ) 309 } 310 } 311