blob: 6d075501d088dc31ab5830756af4bdb5079ec78e [file] [log] [blame]
/*
* Copyright (C) 2022 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.Instrumentation
import android.content.Context
import android.graphics.Point
import android.graphics.PointF
import android.hardware.input.InputManager
import android.view.Display
import android.view.InputDevice
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.cts.input.UinputDevice
import org.json.JSONArray
import org.json.JSONObject
import java.io.Closeable
class UinputTouchScreen(instrumentation: Instrumentation, display: Display) : Closeable {
private val uinputDevice: UinputDevice
private lateinit var port: String
private val inputManager: InputManager
init {
uinputDevice = createDevice(instrumentation, display)
inputManager = instrumentation.targetContext.getSystemService(InputManager::class.java)!!
associateWith(display)
}
private fun injectEvent(events: IntArray) {
uinputDevice.injectEvents(events.joinToString(prefix = "[", postfix = "]",
separator = ","))
}
fun sendBtnTouch(isDown: Boolean) {
injectEvent(intArrayOf(EV_KEY, BTN_TOUCH, if (isDown) 1 else 0))
}
fun sendDown(id: Int, location: PointF) {
injectEvent(intArrayOf(EV_ABS, ABS_MT_SLOT, id))
injectEvent(intArrayOf(EV_ABS, ABS_MT_TRACKING_ID, id))
injectEvent(intArrayOf(EV_ABS, ABS_MT_POSITION_X, location.x.toInt()))
injectEvent(intArrayOf(EV_ABS, ABS_MT_POSITION_Y, location.y.toInt()))
injectEvent(intArrayOf(EV_SYN, SYN_REPORT, 0))
}
fun sendMove(id: Int, location: PointF) {
// Use same events of down.
sendDown(id, location)
}
fun sendUp(id: Int) {
injectEvent(intArrayOf(EV_ABS, ABS_MT_SLOT, id))
injectEvent(intArrayOf(EV_ABS, ABS_MT_TRACKING_ID, INVALID_TRACKING_ID))
injectEvent(intArrayOf(EV_SYN, SYN_REPORT, 0))
}
fun sendToolType(id: Int, toolType: Int) {
injectEvent(intArrayOf(EV_ABS, ABS_MT_SLOT, id))
injectEvent(intArrayOf(EV_ABS, ABS_MT_TOOL_TYPE, toolType))
injectEvent(intArrayOf(EV_SYN, SYN_REPORT, 0))
}
private fun readRawResource(context: Context): String? {
return context.resources
.openRawResource(R.raw.test_touchscreen_register)
.bufferedReader().use { it.readText() }
}
private fun createDevice(instrumentation: Instrumentation, display: Display): UinputDevice {
val json = JSONObject(readRawResource(instrumentation.targetContext))
val resourceDeviceId: Int = json.getInt("id")
val vendorId = json.getInt("vid")
val productId = json.getInt("pid")
port = json.getString("port")
// Replace maximum value to real display size.
val displaySize = Point()
display.getRealSize(displaySize)
val abs_info: JSONArray = json.getJSONArray("abs_info")
for (i in 0 until abs_info.length()) {
val item = abs_info.getJSONObject(i)
if (item.get("code") == ABS_MT_POSITION_X) {
item.getJSONObject("info").put("maximum", displaySize.x - 1)
}
if (item.get("code") == ABS_MT_POSITION_Y) {
item.getJSONObject("info").put("maximum", displaySize.y - 1)
}
}
// Create the uinput device.
val registerCommand = json.toString()
return UinputDevice(instrumentation, resourceDeviceId,
vendorId, productId, InputDevice.SOURCE_TOUCHSCREEN, registerCommand)
}
private fun associateWith(display: Display) {
runWithShellPermissionIdentity(
{ inputManager.addUniqueIdAssociation(port, display.uniqueId!!) },
"android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY")
}
override fun close() {
runWithShellPermissionIdentity(
{ inputManager.removeUniqueIdAssociation(port) },
"android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY")
uinputDevice.close()
}
companion object {
const val EV_SYN = 0
const val EV_KEY = 1
const val EV_ABS = 3
const val ABS_MT_SLOT = 0x2f
const val ABS_MT_POSITION_X = 0x35
const val ABS_MT_POSITION_Y = 0x36
const val ABS_MT_TOOL_TYPE = 0x37
const val ABS_MT_TRACKING_ID = 0x39
const val BTN_TOUCH = 0x14a
const val SYN_REPORT = 0
const val MT_TOOL_FINGER = 0
const val MT_TOOL_PALM = 2
const val INVALID_TRACKING_ID = -1
}
}