Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b735166 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

UinputRecordingIntegrationTests: Integrate test with existing infra

This test used its own infra to parse EVEMU files and inject their
contents into the uinput shell command.

In this CL, we introduce the EvemuDevice class, which uses our exising
VirtualInputDevice test infra to manage the virtual device. This means
the test will benefit from the existing synchronization points present
in the infra.

Bug: 385141667
Bug: 389901828
Test: Presubmit
Flag: TEST_ONLY
Change-Id: Id009eaf828581a41b0466dff43aebe7bf9b1bc26
parent d79f47b1
Loading
Loading
Loading
Loading
+28 −66
Original line number Diff line number Diff line
@@ -15,21 +15,19 @@
 */
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.server.wm.CtsWindowInfoUtils.waitForWindowOnTop
import android.util.Log
import android.util.Size
import android.view.InputDevice
import android.view.InputEvent
import android.view.MotionEvent
import androidx.test.platform.app.InstrumentationRegistry
import com.android.cts.input.BatchedEventSplitter
import com.android.cts.input.CaptureEventActivity
import com.android.cts.input.DebugInputRule
import com.android.cts.input.EvemuDevice
import com.android.cts.input.InputJsonParser
import com.android.cts.input.VirtualDisplayActivityScenario
import com.android.cts.input.inputeventmatchers.isResampled
@@ -69,9 +67,13 @@ class UinputRecordingIntegrationTests {
        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
                    "GooglePixelTabletTouchscreen",
                    R.raw.google_pixel_tablet_touchscreen,
                    R.raw.google_pixel_tablet_touchscreen_events,
                    Size(1600, 2560),
                    vendorId = 0x0603,
                    productId = 0x7806,
                    deviceSources = InputDevice.SOURCE_TOUCHSCREEN,
                ),
            )

@@ -91,6 +93,7 @@ class UinputRecordingIntegrationTests {
        val displaySize: Size,
        val vendorId: Int,
        val productId: Int,
        val deviceSources: Int,
    ) {
        override fun toString(): String = name
    }
@@ -98,7 +101,6 @@ class UinputRecordingIntegrationTests {
    private lateinit var instrumentation: Instrumentation
    private lateinit var parser: InputJsonParser


    @get:Rule
    val debugInputRule = DebugInputRule()

@@ -121,24 +123,19 @@ class UinputRecordingIntegrationTests {
            testName,
            size = testData.displaySize
        ).use { scenario ->
            waitForWindowOnTop(scenario.activity.window)
            scenario.activity.window.decorView.requestUnbufferedDispatch(INPUT_DEVICE_SOURCE_ALL)

            try {
                instrumentation.uiAutomation.adoptShellPermissionIdentity(
                    ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
                )
            EvemuDevice(
                instrumentation,
                testData.deviceSources,
                testData.vendorId,
                testData.productId,
                testData.uinputRecordingResource,
                scenario.virtualDisplay.display
            ).use { evemuDevice ->

                val inputPort = "uinput:1:${testData.vendorId}:${testData.productId}"
                val inputManager =
                    instrumentation.context.getSystemService(InputManager::class.java)!!
                try {
                    inputManager.addUniqueIdAssociationByPort(
                        inputPort,
                        scenario.virtualDisplay.display.uniqueId!!,
                    )
                evemuDevice.injectEvents()

                    injectUinputEvents().use {
                if (DEBUG_RECEIVED_EVENTS) {
                    printReceivedEventsToLogcat(scenario.activity)
                    fail("Test cannot pass in debug mode!")
@@ -150,12 +147,6 @@ class UinputRecordingIntegrationTests {
                verifyEvents(verifier)
                scenario.activity.assertNoEvents()
            }
                } finally {
                    inputManager.removeUniqueIdAssociationByPort(inputPort)
                }
            } finally {
                instrumentation.uiAutomation.dropShellPermissionIdentity()
            }
        }
    }

@@ -171,35 +162,6 @@ class UinputRecordingIntegrationTests {
        }
    }

    /**
     * Plays back the evemu recording associated with the current test case by injecting it via
     * the `uinput` shell command in interactive mode. The recording playback will begin
     * immediately, and the shell command (and the associated input device) will remain alive
     * until the returned [AutoCloseable] is closed.
     */
    private fun injectUinputEvents(): AutoCloseable {
        val fds = instrumentation.uiAutomation!!.executeShellCommandRw("uinput -")
        // We do not need to use stdout in this test.
        fds[0].close()

        return ParcelFileDescriptor.AutoCloseOutputStream(fds[1]).also { stdin ->
            instrumentation.context.resources.openRawResource(
                testData.uinputRecordingResource,
            ).use { inputStream ->
                stdin.write(inputStream.readBytes())

                // TODO(b/367419268): Remove extra event injection when uinput parsing is fixed.
                // Inject an extra sync event with an arbitrarily large timestamp, because the
                // uinput command will not process the last event until either the next event is
                // parsed, or fd is closed. Injecting this sync allows us complete injection of
                // the evemu recording and extend the lifetime of the input device by keeping this
                // fd open.
                stdin.write("\nE: 9999.99 0 0 0\n".toByteArray())
                stdin.flush()
            }
        }
    }

    private fun verifyEvents(verifier: EventVerifier) {
        val uinputTestData = parser.getUinputTestData(testData.expectedEventsResource)
        for (test in uinputTestData) {