Loading tests/Input/AndroidManifest.xml +8 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,14 @@ android:process=":externalProcess"> </activity> <activity android:name="com.android.test.input.CaptureEventActivity" android:label="Capture events" android:configChanges="touchscreen|uiMode|orientation|screenSize|screenLayout|keyboardHidden|uiMode|navigation|keyboard|density|fontScale|layoutDirection|locale|mcc|mnc|smallestScreenSize" android:enableOnBackInvokedCallback="false" android:turnScreenOn="true" android:exported="true"> </activity> </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.test.input" Loading tests/Input/res/raw/google_pixel_tablet_touchscreen.evemu 0 → 100644 +150 −0 Original line number Diff line number Diff line # EVEMU 1.2 # One finger swipe gesture on the Google Pixel Tablet touchscreen N: NVTCapacitiveTouchScreen I: 001c 0603 7806 0100 P: 02 00 00 00 00 00 00 00 B: 00 0b 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 80 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 04 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 02 00 00 00 00 00 00 00 00 B: 03 00 00 00 00 00 80 f3 46 B: 04 00 00 00 00 00 00 00 00 B: 05 00 00 00 00 00 00 00 00 B: 11 00 00 00 00 00 00 00 00 B: 12 00 00 00 00 00 00 00 00 A: 2f 0 9 0 0 0 A: 30 0 2559 0 0 11 A: 31 0 1599 0 0 11 A: 34 -4096 4096 0 0 0 A: 35 0 1599 0 0 11 A: 36 0 2559 0 0 11 A: 37 0 2 0 0 0 A: 39 0 65535 0 0 0 A: 3a 0 256 0 0 0 A: 3e 0 8 0 0 0 E: 0.000001 0001 014a 0001 E: 0.000001 0003 0039 0003 E: 0.000001 0003 0035 0810 E: 0.000001 0003 0036 1650 E: 0.000001 0003 0030 0082 E: 0.000001 0003 0031 0077 E: 0.000001 0003 003a 0215 E: 0.000001 0003 0034 3218 E: 0.000001 0000 0000 0000 E: 0.008818 0003 0035 0825 E: 0.008818 0003 0036 1645 E: 0.008818 0003 0034 3217 E: 0.008818 0000 0000 0000 E: 0.016306 0003 0035 0841 E: 0.016306 0003 0036 1639 E: 0.016306 0003 0034 3102 E: 0.016306 0000 0000 0000 E: 0.025653 0003 0035 0862 E: 0.025653 0003 0036 1630 E: 0.025653 0003 0034 3092 E: 0.025653 0000 0000 0000 E: 0.032936 0003 0035 0883 E: 0.032936 0003 0036 1619 E: 0.032936 0003 0034 3030 E: 0.032936 0000 0000 0000 E: 0.042072 0003 0035 0905 E: 0.042072 0003 0036 1604 E: 0.042072 0003 0034 2848 E: 0.042072 0000 0000 0000 E: 0.049569 0003 0035 0924 E: 0.049569 0003 0036 1591 E: 0.049569 0003 0034 2830 E: 0.049569 0000 0000 0000 E: 0.058706 0003 0035 0942 E: 0.058706 0003 0036 1573 E: 0.058706 0000 0000 0000 E: 0.066207 0003 0035 0954 E: 0.066207 0003 0036 1557 E: 0.066207 0003 0034 2790 E: 0.066207 0000 0000 0000 E: 0.075337 0003 0035 0966 E: 0.075337 0003 0036 1535 E: 0.075337 0000 0000 0000 E: 0.082841 0003 0035 0973 E: 0.082841 0003 0036 1511 E: 0.082841 0003 0034 2788 E: 0.082841 0000 0000 0000 E: 0.091972 0003 0035 0971 E: 0.091972 0003 0036 1480 E: 0.091972 0003 0034 2770 E: 0.091972 0000 0000 0000 E: 0.099474 0003 0035 0961 E: 0.099474 0003 0036 1445 E: 0.099474 0003 0034 2644 E: 0.099474 0000 0000 0000 E: 0.108631 0003 0035 0937 E: 0.108631 0003 0036 1400 E: 0.108631 0003 0030 0083 E: 0.108631 0003 0034 2461 E: 0.108631 0000 0000 0000 E: 0.116109 0003 0035 0909 E: 0.116109 0003 0036 1361 E: 0.116109 0003 0034 2278 E: 0.116109 0000 0000 0000 E: 0.125263 0003 0035 0865 E: 0.125263 0003 0036 1311 E: 0.125263 0003 0034 2096 E: 0.125263 0000 0000 0000 E: 0.132741 0003 0035 0820 E: 0.132741 0003 0036 1261 E: 0.132741 0003 0034 2083 E: 0.132741 0000 0000 0000 E: 0.141876 0003 0035 0755 E: 0.141876 0003 0036 1193 E: 0.141876 0003 003a 0216 E: 0.141876 0003 0034 2266 E: 0.141876 0000 0000 0000 E: 0.149376 0003 0035 0691 E: 0.149376 0003 0036 1124 E: 0.149376 0003 0034 2448 E: 0.149376 0000 0000 0000 E: 0.158510 0003 0035 0609 E: 0.158510 0003 0036 1033 E: 0.158510 0003 0034 2631 E: 0.158510 0000 0000 0000 E: 0.166011 0003 0035 0543 E: 0.166011 0003 0036 0957 E: 0.166011 0003 0034 2813 E: 0.166011 0000 0000 0000 E: 0.175182 0003 0035 0471 E: 0.175182 0003 0036 0864 E: 0.175182 0003 0031 0076 E: 0.175182 0003 0034 2996 E: 0.175182 0000 0000 0000 E: 0.182683 0003 0035 0417 E: 0.182683 0003 0036 0792 E: 0.182683 0003 003a 0214 E: 0.182683 0003 0034 3178 E: 0.182683 0000 0000 0000 E: 0.191777 0003 0035 0361 E: 0.191777 0003 0036 0719 E: 0.191777 0003 0031 0075 E: 0.191777 0003 003a 0213 E: 0.191777 0003 0034 2996 E: 0.191777 0000 0000 0000 E: 0.199431 0003 0035 0271 E: 0.199431 0003 0036 0603 E: 0.199431 0003 0030 0060 E: 0.199431 0003 0031 0029 E: 0.199431 0003 003a 0060 E: 0.199431 0003 0034 2813 E: 0.199431 0000 0000 0000 E: 0.207943 0003 003a 0000 E: 0.207943 0003 0039 -001 E: 0.207943 0001 014a 0000 E: 0.207943 0000 0000 0000 tests/Input/res/raw/google_pixel_tablet_touchscreen_events.json 0 → 100644 +34 −0 Original line number Diff line number Diff line [ { "name": "One finger swipe", "source": "TOUCHSCREEN", "events": [ {"action":"DOWN","axes":{"AXIS_X":810,"AXIS_Y":1650,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.234087586402893},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":825,"AXIS_Y":1645,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.2337040901184082},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":841,"AXIS_Y":1639,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.1896021366119385},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":862,"AXIS_Y":1630,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.1857671737670898},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":883,"AXIS_Y":1619,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.1619905233383179},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":905,"AXIS_Y":1604,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0921943187713623},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":924,"AXIS_Y":1591,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0852913856506348},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":942,"AXIS_Y":1573,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0852913856506348},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":954,"AXIS_Y":1557,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0699516534805298},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":966,"AXIS_Y":1535,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0699516534805298},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":973,"AXIS_Y":1511,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.06918466091156},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":971,"AXIS_Y":1480,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0622817277908325},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":961,"AXIS_Y":1445,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0139613151550293},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":937,"AXIS_Y":1400,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.9437817335128784},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":909,"AXIS_Y":1361,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.8736020922660828},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":865,"AXIS_Y":1311,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.803805947303772},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":820,"AXIS_Y":1261,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.7988204956054688},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":755,"AXIS_Y":1193,"AXIS_PRESSURE":0.84375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.8690001368522644},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":691,"AXIS_Y":1124,"AXIS_PRESSURE":0.84375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.9387962818145752},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":609,"AXIS_Y":1033,"AXIS_PRESSURE":0.84375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.008975863456726},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":543,"AXIS_Y":957,"AXIS_PRESSURE":0.84375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0787720680236816},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":471,"AXIS_Y":864,"AXIS_PRESSURE":0.84375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":76,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":76,"AXIS_ORIENTATION":1.1489516496658325},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":417,"AXIS_Y":792,"AXIS_PRESSURE":0.8359375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":76,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":76,"AXIS_ORIENTATION":1.2187477350234985},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":361,"AXIS_Y":719,"AXIS_PRESSURE":0.83203125,"AXIS_SIZE":0.03087143413722515,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":75,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":75,"AXIS_ORIENTATION":1.1489516496658325},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":271,"AXIS_Y":603,"AXIS_PRESSURE":0.234375,"AXIS_SIZE":0.017389604821801186,"AXIS_TOUCH_MAJOR":60,"AXIS_TOUCH_MINOR":29,"AXIS_TOOL_MAJOR":60,"AXIS_TOOL_MINOR":29,"AXIS_ORIENTATION":1.0787720680236816},"buttonState":[]}, {"action":"UP","axes":{"AXIS_X":271,"AXIS_Y":603,"AXIS_PRESSURE":0.234375,"AXIS_SIZE":0.017389604821801186,"AXIS_TOUCH_MAJOR":60,"AXIS_TOUCH_MINOR":29,"AXIS_TOOL_MAJOR":60,"AXIS_TOOL_MINOR":29,"AXIS_ORIENTATION":1.0787720680236816},"buttonState":[]} ] } ] tests/Input/src/com/android/test/input/CaptureEventActivity.kt 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.test.input import android.app.Activity import android.os.Bundle import android.view.InputEvent import android.view.KeyEvent import android.view.MotionEvent import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit import org.junit.Assert.assertNull class CaptureEventActivity : Activity() { private val events = LinkedBlockingQueue<InputEvent>() var shouldHandleKeyEvents = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Set the fixed orientation if requested if (intent.hasExtra(EXTRA_FIXED_ORIENTATION)) { val orientation = intent.getIntExtra(EXTRA_FIXED_ORIENTATION, 0) setRequestedOrientation(orientation) } // Set the flag if requested if (intent.hasExtra(EXTRA_WINDOW_FLAGS)) { val flags = intent.getIntExtra(EXTRA_WINDOW_FLAGS, 0) window.addFlags(flags) } } override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean { events.add(MotionEvent.obtain(ev)) return true } override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { events.add(MotionEvent.obtain(ev)) return true } override fun dispatchKeyEvent(event: KeyEvent?): Boolean { events.add(KeyEvent(event)) return shouldHandleKeyEvents } override fun dispatchTrackballEvent(ev: MotionEvent?): Boolean { events.add(MotionEvent.obtain(ev)) return true } fun getInputEvent(): InputEvent? { return events.poll(5, TimeUnit.SECONDS) } fun hasReceivedEvents(): Boolean { return !events.isEmpty() } fun assertNoEvents() { val event = events.poll(100, TimeUnit.MILLISECONDS) assertNull("Expected no events, but received $event", event) } companion object { const val EXTRA_FIXED_ORIENTATION = "fixed_orientation" const val EXTRA_WINDOW_FLAGS = "window_flags" } } tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt 0 → 100644 +170 −0 Original line number Diff line number Diff line /* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.test.input import android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY import android.app.Instrumentation import android.cts.input.EventVerifier import android.graphics.PointF import android.hardware.input.InputManager import android.os.ParcelFileDescriptor import android.util.Size import android.view.MotionEvent import androidx.test.platform.app.InstrumentationRegistry import com.android.cts.input.BatchedEventSplitter import com.android.cts.input.InputJsonParser import com.android.cts.input.VirtualDisplayActivityScenario import com.android.cts.input.inputeventmatchers.isResampled import com.android.cts.input.inputeventmatchers.withButtonState import com.android.cts.input.inputeventmatchers.withHistorySize import com.android.cts.input.inputeventmatchers.withMotionAction import com.android.cts.input.inputeventmatchers.withPressure import com.android.cts.input.inputeventmatchers.withRawCoords import com.android.cts.input.inputeventmatchers.withSource import java.io.InputStream import org.hamcrest.Matchers.allOf import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TestName import org.junit.runner.RunWith import org.junit.runners.Parameterized /** * Integration tests for the input pipeline that replays recording taken from physical input devices * at the evdev interface level, and makes assertions on the events that are received by a test app. * * These tests associate the playback input device with a virtual display to make these tests * agnostic to the device form factor. * * New recordings can be taken using the `evemu-record` shell command. */ @RunWith(Parameterized::class) class UinputRecordingIntegrationTests { companion object { /** * Add new test cases by adding a new [TestData] to the following list. */ @JvmStatic @Parameterized.Parameters(name = "{0}") fun data(): Iterable<Any> = listOf( TestData( "GooglePixelTabletTouchscreen", R.raw.google_pixel_tablet_touchscreen, R.raw.google_pixel_tablet_touchscreen_events, Size(1600, 2560), vendorId = 0x0603, productId = 0x7806 ), ) const val INPUT_DEVICE_SOURCE_ALL = -1 } class TestData( val name: String, val uinputRecordingResource: Int, val expectedEventsResource: Int, val displaySize: Size, val vendorId: Int, val productId: Int, ) { override fun toString(): String = name } private lateinit var instrumentation: Instrumentation private lateinit var parser: InputJsonParser @get:Rule val testName = TestName() @Parameterized.Parameter(0) lateinit var testData: TestData @Before fun setUp() { instrumentation = InstrumentationRegistry.getInstrumentation() parser = InputJsonParser(instrumentation.context) } @Test fun testEvemuRecording() { VirtualDisplayActivityScenario.AutoClose<CaptureEventActivity>( testName, size = testData.displaySize ).use { scenario -> scenario.activity.window.decorView.requestUnbufferedDispatch(INPUT_DEVICE_SOURCE_ALL) try { instrumentation.uiAutomation.adoptShellPermissionIdentity( ASSOCIATE_INPUT_DEVICE_TO_DISPLAY, ) val inputPort = "uinput:1:${testData.vendorId}:${testData.productId}" val inputManager = instrumentation.context.getSystemService(InputManager::class.java)!! try { inputManager.addUniqueIdAssociationByPort( inputPort, scenario.virtualDisplay.display.uniqueId!!, ) val verifier = EventVerifier(BatchedEventSplitter { scenario.activity.getInputEvent() }) injectUinputEvents() verifyEvents(verifier) scenario.activity.assertNoEvents() } finally { inputManager.removeUniqueIdAssociationByPort(inputPort) } } finally { instrumentation.uiAutomation.dropShellPermissionIdentity() } } } private fun injectUinputEvents() { val fds = instrumentation.uiAutomation!!.executeShellCommandRw("uinput -") ParcelFileDescriptor.AutoCloseOutputStream(fds[1]).use { stdIn -> val inputStream: InputStream = instrumentation.context.resources.openRawResource( testData.uinputRecordingResource, ) stdIn.write(inputStream.readBytes()) } } private fun verifyEvents(verifier: EventVerifier) { val uinputTestData = parser.getUinputTestData(testData.expectedEventsResource) for (test in uinputTestData) { for ((index, expectedEvent) in test.events.withIndex()) { if (expectedEvent is MotionEvent) { verifier.assertReceivedMotion( allOf( withMotionAction(expectedEvent.action), withSource(expectedEvent.source), withButtonState(expectedEvent.buttonState), withRawCoords(PointF(expectedEvent.rawX, expectedEvent.rawY)), withPressure(expectedEvent.pressure), isResampled(false), withHistorySize(0), ), "${test.name}: Expected event at index $index", ) } } } } } Loading
tests/Input/AndroidManifest.xml +8 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,14 @@ android:process=":externalProcess"> </activity> <activity android:name="com.android.test.input.CaptureEventActivity" android:label="Capture events" android:configChanges="touchscreen|uiMode|orientation|screenSize|screenLayout|keyboardHidden|uiMode|navigation|keyboard|density|fontScale|layoutDirection|locale|mcc|mnc|smallestScreenSize" android:enableOnBackInvokedCallback="false" android:turnScreenOn="true" android:exported="true"> </activity> </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.test.input" Loading
tests/Input/res/raw/google_pixel_tablet_touchscreen.evemu 0 → 100644 +150 −0 Original line number Diff line number Diff line # EVEMU 1.2 # One finger swipe gesture on the Google Pixel Tablet touchscreen N: NVTCapacitiveTouchScreen I: 001c 0603 7806 0100 P: 02 00 00 00 00 00 00 00 B: 00 0b 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 80 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 04 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 01 00 00 00 00 00 00 00 00 B: 02 00 00 00 00 00 00 00 00 B: 03 00 00 00 00 00 80 f3 46 B: 04 00 00 00 00 00 00 00 00 B: 05 00 00 00 00 00 00 00 00 B: 11 00 00 00 00 00 00 00 00 B: 12 00 00 00 00 00 00 00 00 A: 2f 0 9 0 0 0 A: 30 0 2559 0 0 11 A: 31 0 1599 0 0 11 A: 34 -4096 4096 0 0 0 A: 35 0 1599 0 0 11 A: 36 0 2559 0 0 11 A: 37 0 2 0 0 0 A: 39 0 65535 0 0 0 A: 3a 0 256 0 0 0 A: 3e 0 8 0 0 0 E: 0.000001 0001 014a 0001 E: 0.000001 0003 0039 0003 E: 0.000001 0003 0035 0810 E: 0.000001 0003 0036 1650 E: 0.000001 0003 0030 0082 E: 0.000001 0003 0031 0077 E: 0.000001 0003 003a 0215 E: 0.000001 0003 0034 3218 E: 0.000001 0000 0000 0000 E: 0.008818 0003 0035 0825 E: 0.008818 0003 0036 1645 E: 0.008818 0003 0034 3217 E: 0.008818 0000 0000 0000 E: 0.016306 0003 0035 0841 E: 0.016306 0003 0036 1639 E: 0.016306 0003 0034 3102 E: 0.016306 0000 0000 0000 E: 0.025653 0003 0035 0862 E: 0.025653 0003 0036 1630 E: 0.025653 0003 0034 3092 E: 0.025653 0000 0000 0000 E: 0.032936 0003 0035 0883 E: 0.032936 0003 0036 1619 E: 0.032936 0003 0034 3030 E: 0.032936 0000 0000 0000 E: 0.042072 0003 0035 0905 E: 0.042072 0003 0036 1604 E: 0.042072 0003 0034 2848 E: 0.042072 0000 0000 0000 E: 0.049569 0003 0035 0924 E: 0.049569 0003 0036 1591 E: 0.049569 0003 0034 2830 E: 0.049569 0000 0000 0000 E: 0.058706 0003 0035 0942 E: 0.058706 0003 0036 1573 E: 0.058706 0000 0000 0000 E: 0.066207 0003 0035 0954 E: 0.066207 0003 0036 1557 E: 0.066207 0003 0034 2790 E: 0.066207 0000 0000 0000 E: 0.075337 0003 0035 0966 E: 0.075337 0003 0036 1535 E: 0.075337 0000 0000 0000 E: 0.082841 0003 0035 0973 E: 0.082841 0003 0036 1511 E: 0.082841 0003 0034 2788 E: 0.082841 0000 0000 0000 E: 0.091972 0003 0035 0971 E: 0.091972 0003 0036 1480 E: 0.091972 0003 0034 2770 E: 0.091972 0000 0000 0000 E: 0.099474 0003 0035 0961 E: 0.099474 0003 0036 1445 E: 0.099474 0003 0034 2644 E: 0.099474 0000 0000 0000 E: 0.108631 0003 0035 0937 E: 0.108631 0003 0036 1400 E: 0.108631 0003 0030 0083 E: 0.108631 0003 0034 2461 E: 0.108631 0000 0000 0000 E: 0.116109 0003 0035 0909 E: 0.116109 0003 0036 1361 E: 0.116109 0003 0034 2278 E: 0.116109 0000 0000 0000 E: 0.125263 0003 0035 0865 E: 0.125263 0003 0036 1311 E: 0.125263 0003 0034 2096 E: 0.125263 0000 0000 0000 E: 0.132741 0003 0035 0820 E: 0.132741 0003 0036 1261 E: 0.132741 0003 0034 2083 E: 0.132741 0000 0000 0000 E: 0.141876 0003 0035 0755 E: 0.141876 0003 0036 1193 E: 0.141876 0003 003a 0216 E: 0.141876 0003 0034 2266 E: 0.141876 0000 0000 0000 E: 0.149376 0003 0035 0691 E: 0.149376 0003 0036 1124 E: 0.149376 0003 0034 2448 E: 0.149376 0000 0000 0000 E: 0.158510 0003 0035 0609 E: 0.158510 0003 0036 1033 E: 0.158510 0003 0034 2631 E: 0.158510 0000 0000 0000 E: 0.166011 0003 0035 0543 E: 0.166011 0003 0036 0957 E: 0.166011 0003 0034 2813 E: 0.166011 0000 0000 0000 E: 0.175182 0003 0035 0471 E: 0.175182 0003 0036 0864 E: 0.175182 0003 0031 0076 E: 0.175182 0003 0034 2996 E: 0.175182 0000 0000 0000 E: 0.182683 0003 0035 0417 E: 0.182683 0003 0036 0792 E: 0.182683 0003 003a 0214 E: 0.182683 0003 0034 3178 E: 0.182683 0000 0000 0000 E: 0.191777 0003 0035 0361 E: 0.191777 0003 0036 0719 E: 0.191777 0003 0031 0075 E: 0.191777 0003 003a 0213 E: 0.191777 0003 0034 2996 E: 0.191777 0000 0000 0000 E: 0.199431 0003 0035 0271 E: 0.199431 0003 0036 0603 E: 0.199431 0003 0030 0060 E: 0.199431 0003 0031 0029 E: 0.199431 0003 003a 0060 E: 0.199431 0003 0034 2813 E: 0.199431 0000 0000 0000 E: 0.207943 0003 003a 0000 E: 0.207943 0003 0039 -001 E: 0.207943 0001 014a 0000 E: 0.207943 0000 0000 0000
tests/Input/res/raw/google_pixel_tablet_touchscreen_events.json 0 → 100644 +34 −0 Original line number Diff line number Diff line [ { "name": "One finger swipe", "source": "TOUCHSCREEN", "events": [ {"action":"DOWN","axes":{"AXIS_X":810,"AXIS_Y":1650,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.234087586402893},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":825,"AXIS_Y":1645,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.2337040901184082},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":841,"AXIS_Y":1639,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.1896021366119385},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":862,"AXIS_Y":1630,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.1857671737670898},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":883,"AXIS_Y":1619,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.1619905233383179},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":905,"AXIS_Y":1604,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0921943187713623},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":924,"AXIS_Y":1591,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0852913856506348},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":942,"AXIS_Y":1573,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0852913856506348},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":954,"AXIS_Y":1557,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0699516534805298},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":966,"AXIS_Y":1535,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0699516534805298},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":973,"AXIS_Y":1511,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.06918466091156},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":971,"AXIS_Y":1480,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0622817277908325},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":961,"AXIS_Y":1445,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":82,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":82,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0139613151550293},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":937,"AXIS_Y":1400,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.9437817335128784},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":909,"AXIS_Y":1361,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.8736020922660828},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":865,"AXIS_Y":1311,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.803805947303772},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":820,"AXIS_Y":1261,"AXIS_PRESSURE":0.83984375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.7988204956054688},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":755,"AXIS_Y":1193,"AXIS_PRESSURE":0.84375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.8690001368522644},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":691,"AXIS_Y":1124,"AXIS_PRESSURE":0.84375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":0.9387962818145752},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":609,"AXIS_Y":1033,"AXIS_PRESSURE":0.84375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.008975863456726},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":543,"AXIS_Y":957,"AXIS_PRESSURE":0.84375,"AXIS_SIZE":0.03126221150159836,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":77,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":77,"AXIS_ORIENTATION":1.0787720680236816},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":471,"AXIS_Y":864,"AXIS_PRESSURE":0.84375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":76,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":76,"AXIS_ORIENTATION":1.1489516496658325},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":417,"AXIS_Y":792,"AXIS_PRESSURE":0.8359375,"AXIS_SIZE":0.03106682375073433,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":76,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":76,"AXIS_ORIENTATION":1.2187477350234985},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":361,"AXIS_Y":719,"AXIS_PRESSURE":0.83203125,"AXIS_SIZE":0.03087143413722515,"AXIS_TOUCH_MAJOR":83,"AXIS_TOUCH_MINOR":75,"AXIS_TOOL_MAJOR":83,"AXIS_TOOL_MINOR":75,"AXIS_ORIENTATION":1.1489516496658325},"buttonState":[]}, {"action":"MOVE","axes":{"AXIS_X":271,"AXIS_Y":603,"AXIS_PRESSURE":0.234375,"AXIS_SIZE":0.017389604821801186,"AXIS_TOUCH_MAJOR":60,"AXIS_TOUCH_MINOR":29,"AXIS_TOOL_MAJOR":60,"AXIS_TOOL_MINOR":29,"AXIS_ORIENTATION":1.0787720680236816},"buttonState":[]}, {"action":"UP","axes":{"AXIS_X":271,"AXIS_Y":603,"AXIS_PRESSURE":0.234375,"AXIS_SIZE":0.017389604821801186,"AXIS_TOUCH_MAJOR":60,"AXIS_TOUCH_MINOR":29,"AXIS_TOOL_MAJOR":60,"AXIS_TOOL_MINOR":29,"AXIS_ORIENTATION":1.0787720680236816},"buttonState":[]} ] } ]
tests/Input/src/com/android/test/input/CaptureEventActivity.kt 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.test.input import android.app.Activity import android.os.Bundle import android.view.InputEvent import android.view.KeyEvent import android.view.MotionEvent import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit import org.junit.Assert.assertNull class CaptureEventActivity : Activity() { private val events = LinkedBlockingQueue<InputEvent>() var shouldHandleKeyEvents = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Set the fixed orientation if requested if (intent.hasExtra(EXTRA_FIXED_ORIENTATION)) { val orientation = intent.getIntExtra(EXTRA_FIXED_ORIENTATION, 0) setRequestedOrientation(orientation) } // Set the flag if requested if (intent.hasExtra(EXTRA_WINDOW_FLAGS)) { val flags = intent.getIntExtra(EXTRA_WINDOW_FLAGS, 0) window.addFlags(flags) } } override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean { events.add(MotionEvent.obtain(ev)) return true } override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { events.add(MotionEvent.obtain(ev)) return true } override fun dispatchKeyEvent(event: KeyEvent?): Boolean { events.add(KeyEvent(event)) return shouldHandleKeyEvents } override fun dispatchTrackballEvent(ev: MotionEvent?): Boolean { events.add(MotionEvent.obtain(ev)) return true } fun getInputEvent(): InputEvent? { return events.poll(5, TimeUnit.SECONDS) } fun hasReceivedEvents(): Boolean { return !events.isEmpty() } fun assertNoEvents() { val event = events.poll(100, TimeUnit.MILLISECONDS) assertNull("Expected no events, but received $event", event) } companion object { const val EXTRA_FIXED_ORIENTATION = "fixed_orientation" const val EXTRA_WINDOW_FLAGS = "window_flags" } }
tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt 0 → 100644 +170 −0 Original line number Diff line number Diff line /* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.test.input import android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY import android.app.Instrumentation import android.cts.input.EventVerifier import android.graphics.PointF import android.hardware.input.InputManager import android.os.ParcelFileDescriptor import android.util.Size import android.view.MotionEvent import androidx.test.platform.app.InstrumentationRegistry import com.android.cts.input.BatchedEventSplitter import com.android.cts.input.InputJsonParser import com.android.cts.input.VirtualDisplayActivityScenario import com.android.cts.input.inputeventmatchers.isResampled import com.android.cts.input.inputeventmatchers.withButtonState import com.android.cts.input.inputeventmatchers.withHistorySize import com.android.cts.input.inputeventmatchers.withMotionAction import com.android.cts.input.inputeventmatchers.withPressure import com.android.cts.input.inputeventmatchers.withRawCoords import com.android.cts.input.inputeventmatchers.withSource import java.io.InputStream import org.hamcrest.Matchers.allOf import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TestName import org.junit.runner.RunWith import org.junit.runners.Parameterized /** * Integration tests for the input pipeline that replays recording taken from physical input devices * at the evdev interface level, and makes assertions on the events that are received by a test app. * * These tests associate the playback input device with a virtual display to make these tests * agnostic to the device form factor. * * New recordings can be taken using the `evemu-record` shell command. */ @RunWith(Parameterized::class) class UinputRecordingIntegrationTests { companion object { /** * Add new test cases by adding a new [TestData] to the following list. */ @JvmStatic @Parameterized.Parameters(name = "{0}") fun data(): Iterable<Any> = listOf( TestData( "GooglePixelTabletTouchscreen", R.raw.google_pixel_tablet_touchscreen, R.raw.google_pixel_tablet_touchscreen_events, Size(1600, 2560), vendorId = 0x0603, productId = 0x7806 ), ) const val INPUT_DEVICE_SOURCE_ALL = -1 } class TestData( val name: String, val uinputRecordingResource: Int, val expectedEventsResource: Int, val displaySize: Size, val vendorId: Int, val productId: Int, ) { override fun toString(): String = name } private lateinit var instrumentation: Instrumentation private lateinit var parser: InputJsonParser @get:Rule val testName = TestName() @Parameterized.Parameter(0) lateinit var testData: TestData @Before fun setUp() { instrumentation = InstrumentationRegistry.getInstrumentation() parser = InputJsonParser(instrumentation.context) } @Test fun testEvemuRecording() { VirtualDisplayActivityScenario.AutoClose<CaptureEventActivity>( testName, size = testData.displaySize ).use { scenario -> scenario.activity.window.decorView.requestUnbufferedDispatch(INPUT_DEVICE_SOURCE_ALL) try { instrumentation.uiAutomation.adoptShellPermissionIdentity( ASSOCIATE_INPUT_DEVICE_TO_DISPLAY, ) val inputPort = "uinput:1:${testData.vendorId}:${testData.productId}" val inputManager = instrumentation.context.getSystemService(InputManager::class.java)!! try { inputManager.addUniqueIdAssociationByPort( inputPort, scenario.virtualDisplay.display.uniqueId!!, ) val verifier = EventVerifier(BatchedEventSplitter { scenario.activity.getInputEvent() }) injectUinputEvents() verifyEvents(verifier) scenario.activity.assertNoEvents() } finally { inputManager.removeUniqueIdAssociationByPort(inputPort) } } finally { instrumentation.uiAutomation.dropShellPermissionIdentity() } } } private fun injectUinputEvents() { val fds = instrumentation.uiAutomation!!.executeShellCommandRw("uinput -") ParcelFileDescriptor.AutoCloseOutputStream(fds[1]).use { stdIn -> val inputStream: InputStream = instrumentation.context.resources.openRawResource( testData.uinputRecordingResource, ) stdIn.write(inputStream.readBytes()) } } private fun verifyEvents(verifier: EventVerifier) { val uinputTestData = parser.getUinputTestData(testData.expectedEventsResource) for (test in uinputTestData) { for ((index, expectedEvent) in test.events.withIndex()) { if (expectedEvent is MotionEvent) { verifier.assertReceivedMotion( allOf( withMotionAction(expectedEvent.action), withSource(expectedEvent.source), withButtonState(expectedEvent.buttonState), withRawCoords(PointF(expectedEvent.rawX, expectedEvent.rawY)), withPressure(expectedEvent.pressure), isResampled(false), withHistorySize(0), ), "${test.name}: Expected event at index $index", ) } } } } }