Test tool type of 'adb shell input tap' events
When the events are injected with "adb shell input tap" command, they
should have a tool type that corresponds to the source of the event.
Ensure that the injected events have the correct tool type with this
test.
Bug: 148307381
Test: atest InputShellCommandTest
Change-Id: I34bbbe01ce1d06a1fb542b8bcee8fc6e219e0022
diff --git a/tests/input/AndroidManifest.xml b/tests/input/AndroidManifest.xml
index 5218e7d..bf37d50 100644
--- a/tests/input/AndroidManifest.xml
+++ b/tests/input/AndroidManifest.xml
@@ -39,6 +39,9 @@
<activity android:name="android.input.cts.IncompleteMotionActivity"
android:label="IncompleteMotion activity">
</activity>
+ <activity android:name="android.input.cts.CaptureEventActivity"
+ android:label="Capture events">
+ </activity>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/input/src/android/input/cts/CaptureEventActivity.kt b/tests/input/src/android/input/cts/CaptureEventActivity.kt
new file mode 100644
index 0000000..4f5caad
--- /dev/null
+++ b/tests/input/src/android/input/cts/CaptureEventActivity.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 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 android.input.cts
+
+import android.app.Activity
+import android.view.InputEvent
+import android.view.KeyEvent
+import android.view.MotionEvent
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.TimeUnit
+
+class CaptureEventActivity : Activity() {
+ private val mEvents = LinkedBlockingQueue<InputEvent>()
+
+ override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean {
+ mEvents.add(MotionEvent.obtain(ev))
+ return true
+ }
+
+ override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
+ mEvents.add(MotionEvent.obtain(ev))
+ return true
+ }
+
+ override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
+ mEvents.add(KeyEvent(event))
+ return true
+ }
+
+ override fun dispatchTrackballEvent(ev: MotionEvent?): Boolean {
+ mEvents.add(MotionEvent.obtain(ev))
+ return true
+ }
+
+ fun getLastInputEvent(): InputEvent? {
+ return mEvents.poll(5, TimeUnit.SECONDS)
+ }
+}
diff --git a/tests/input/src/android/input/cts/InputShellCommandTest.kt b/tests/input/src/android/input/cts/InputShellCommandTest.kt
new file mode 100644
index 0000000..4555b5d
--- /dev/null
+++ b/tests/input/src/android/input/cts/InputShellCommandTest.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 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 android.input.cts
+
+import android.view.MotionEvent
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.rule.ActivityTestRule
+import com.android.compatibility.common.util.PollingCheck
+import com.android.compatibility.common.util.ShellUtils
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private fun getViewCenterOnScreen(v: View): Pair<Int, Int> {
+ val location = IntArray(2)
+ v.getLocationOnScreen(location)
+ val x = location[0] + v.width / 2
+ val y = location[1] + v.height / 2
+ return Pair(x, y)
+}
+
+/**
+ * Tests for the 'adb shell input' command.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class InputShellCommandTest {
+ @get:Rule
+ var mActivityRule: ActivityTestRule<CaptureEventActivity> =
+ ActivityTestRule(CaptureEventActivity::class.java)
+ lateinit var mActivity: CaptureEventActivity
+ val mInstrumentation = InstrumentationRegistry.getInstrumentation()
+
+ @Before
+ fun setUp() {
+ mActivity = mActivityRule.getActivity()
+ PollingCheck.waitFor { mActivity.hasWindowFocus() }
+ }
+
+ /**
+ * Check the tool type set by default by "input tap" command
+ */
+ @Test
+ fun testDefaultToolType() {
+ val (x, y) = getViewCenterOnScreen(mActivity.window.decorView)
+
+ ShellUtils.runShellCommand("input tap $x $y")
+ assertTapToolType(MotionEvent.TOOL_TYPE_FINGER)
+ }
+
+ /**
+ * Check that the tool type of the injected events changes according to the event source.
+ */
+ @Test
+ fun testToolType() {
+ val (x, y) = getViewCenterOnScreen(mActivity.window.decorView)
+
+ ShellUtils.runShellCommand("input touchscreen tap $x $y")
+ assertTapToolType(MotionEvent.TOOL_TYPE_FINGER)
+
+ ShellUtils.runShellCommand("input touchpad tap $x $y")
+ assertTapToolType(MotionEvent.TOOL_TYPE_FINGER)
+
+ ShellUtils.runShellCommand("input touchnavigation tap $x $y")
+ assertTapToolType(MotionEvent.TOOL_TYPE_FINGER)
+
+ ShellUtils.runShellCommand("input stylus tap $x $y")
+ assertTapToolType(MotionEvent.TOOL_TYPE_STYLUS)
+
+ ShellUtils.runShellCommand("input mouse tap $x $y")
+ assertTapToolType(MotionEvent.TOOL_TYPE_MOUSE)
+
+ ShellUtils.runShellCommand("input trackball tap $x $y")
+ assertTapToolType(MotionEvent.TOOL_TYPE_MOUSE)
+
+ ShellUtils.runShellCommand("input joystick tap $x $y")
+ assertTapToolType(MotionEvent.TOOL_TYPE_UNKNOWN)
+ }
+
+ private fun getMotionEvent(): MotionEvent {
+ val event = mActivity.getLastInputEvent()
+ assertThat(event).isNotNull()
+ assertThat(event).isInstanceOf(MotionEvent::class.java)
+ return event as MotionEvent
+ }
+
+ private fun assertToolType(event: MotionEvent, toolType: Int) {
+ val pointerProperties = MotionEvent.PointerProperties()
+ for (i in 0 until event.pointerCount) {
+ event.getPointerProperties(i, pointerProperties)
+ assertThat(toolType).isEqualTo(pointerProperties.toolType)
+ }
+ }
+
+ private fun assertTapToolType(toolType: Int) {
+ var event = getMotionEvent()
+ assertThat(event.action).isEqualTo(MotionEvent.ACTION_DOWN)
+ assertToolType(event, toolType)
+
+ event = getMotionEvent()
+ assertThat(event.action).isEqualTo(MotionEvent.ACTION_UP)
+ assertToolType(event, toolType)
+ }
+}