Merge "Move device specific logic to VirtualInputDevice and its subclasses." into udc-dev

This commit is contained in:
Zixuan Qu
2023-02-21 19:08:29 +00:00
committed by Android (Google) Code Review
3 changed files with 147 additions and 439 deletions

View File

@@ -242,7 +242,7 @@ class InputController {
private void closeInputDeviceDescriptorLocked(IBinder token,
InputDeviceDescriptor inputDeviceDescriptor) {
token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
mNativeWrapper.closeUinput(inputDeviceDescriptor.getNativePointer());
String phys = inputDeviceDescriptor.getPhys();
InputManager.getInstance().removeUniqueIdAssociation(phys);
// Type associations are added in the case of navigation touchpads. Those should be removed
@@ -366,7 +366,7 @@ class InputController {
throw new IllegalArgumentException(
"Could not send key event to input device for given token");
}
return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getFileDescriptor(),
return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getNativePointer(),
event.getKeyCode(), event.getAction());
}
}
@@ -379,7 +379,7 @@ class InputController {
throw new IllegalArgumentException(
"Could not send key event to input device for given token");
}
return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getFileDescriptor(),
return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getNativePointer(),
event.getKeyCode(), event.getAction());
}
}
@@ -397,7 +397,7 @@ class InputController {
throw new IllegalStateException(
"Display id associated with this mouse is not currently targetable");
}
return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getFileDescriptor(),
return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getNativePointer(),
event.getButtonCode(), event.getAction());
}
}
@@ -410,7 +410,7 @@ class InputController {
throw new IllegalArgumentException(
"Could not send touch event to input device for given token");
}
return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getFileDescriptor(),
return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getNativePointer(),
event.getPointerId(), event.getToolType(), event.getAction(), event.getX(),
event.getY(), event.getPressure(), event.getMajorAxisSize());
}
@@ -429,7 +429,7 @@ class InputController {
throw new IllegalStateException(
"Display id associated with this mouse is not currently targetable");
}
return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getFileDescriptor(),
return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getNativePointer(),
event.getRelativeX(), event.getRelativeY());
}
}
@@ -447,7 +447,7 @@ class InputController {
throw new IllegalStateException(
"Display id associated with this mouse is not currently targetable");
}
return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getFileDescriptor(),
return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getNativePointer(),
event.getXAxisMovement(), event.getYAxisMovement());
}
}
@@ -474,7 +474,7 @@ class InputController {
synchronized (mLock) {
fout.println(" Active descriptors: ");
for (InputDeviceDescriptor inputDeviceDescriptor : mInputDeviceDescriptors.values()) {
fout.println(" fd: " + inputDeviceDescriptor.getFileDescriptor());
fout.println(" ptr: " + inputDeviceDescriptor.getNativePointer());
fout.println(" displayId: " + inputDeviceDescriptor.getDisplayId());
fout.println(" creationOrder: "
+ inputDeviceDescriptor.getCreationOrderNumber());
@@ -487,10 +487,10 @@ class InputController {
}
@VisibleForTesting
void addDeviceForTesting(IBinder deviceToken, int fd, int type, int displayId, String phys,
void addDeviceForTesting(IBinder deviceToken, long ptr, int type, int displayId, String phys,
String deviceName, int inputDeviceId) {
synchronized (mLock) {
mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(fd, () -> {
mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(ptr, () -> {
}, type, displayId, phys, deviceName, inputDeviceId));
}
}
@@ -504,75 +504,76 @@ class InputController {
return inputDeviceDescriptors;
}
private static native int nativeOpenUinputDpad(String deviceName, int vendorId, int productId,
private static native long nativeOpenUinputDpad(String deviceName, int vendorId, int productId,
String phys);
private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId,
private static native long nativeOpenUinputKeyboard(String deviceName, int vendorId,
int productId, String phys);
private static native int nativeOpenUinputMouse(String deviceName, int vendorId, int productId,
private static native long nativeOpenUinputMouse(String deviceName, int vendorId, int productId,
String phys);
private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId,
private static native long nativeOpenUinputTouchscreen(String deviceName, int vendorId,
int productId, String phys, int height, int width);
private static native boolean nativeCloseUinput(int fd);
private static native boolean nativeWriteDpadKeyEvent(int fd, int androidKeyCode, int action);
private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action);
private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action);
private static native boolean nativeWriteTouchEvent(int fd, int pointerId, int toolType,
private static native void nativeCloseUinput(long ptr);
private static native boolean nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action);
private static native boolean nativeWriteKeyEvent(long ptr, int androidKeyCode, int action);
private static native boolean nativeWriteButtonEvent(long ptr, int buttonCode, int action);
private static native boolean nativeWriteTouchEvent(long ptr, int pointerId, int toolType,
int action, float locationX, float locationY, float pressure, float majorAxisSize);
private static native boolean nativeWriteRelativeEvent(int fd, float relativeX,
private static native boolean nativeWriteRelativeEvent(long ptr, float relativeX,
float relativeY);
private static native boolean nativeWriteScrollEvent(int fd, float xAxisMovement,
private static native boolean nativeWriteScrollEvent(long ptr, float xAxisMovement,
float yAxisMovement);
/** Wrapper around the static native methods for tests. */
@VisibleForTesting
protected static class NativeWrapper {
public int openUinputDpad(String deviceName, int vendorId, int productId, String phys) {
public long openUinputDpad(String deviceName, int vendorId, int productId, String phys) {
return nativeOpenUinputDpad(deviceName, vendorId, productId, phys);
}
public int openUinputKeyboard(String deviceName, int vendorId, int productId, String phys) {
public long openUinputKeyboard(String deviceName, int vendorId, int productId,
String phys) {
return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys);
}
public int openUinputMouse(String deviceName, int vendorId, int productId, String phys) {
public long openUinputMouse(String deviceName, int vendorId, int productId, String phys) {
return nativeOpenUinputMouse(deviceName, vendorId, productId, phys);
}
public int openUinputTouchscreen(String deviceName, int vendorId,
public long openUinputTouchscreen(String deviceName, int vendorId,
int productId, String phys, int height, int width) {
return nativeOpenUinputTouchscreen(deviceName, vendorId, productId, phys, height,
width);
}
public boolean closeUinput(int fd) {
return nativeCloseUinput(fd);
public void closeUinput(long ptr) {
nativeCloseUinput(ptr);
}
public boolean writeDpadKeyEvent(int fd, int androidKeyCode, int action) {
return nativeWriteDpadKeyEvent(fd, androidKeyCode, action);
public boolean writeDpadKeyEvent(long ptr, int androidKeyCode, int action) {
return nativeWriteDpadKeyEvent(ptr, androidKeyCode, action);
}
public boolean writeKeyEvent(int fd, int androidKeyCode, int action) {
return nativeWriteKeyEvent(fd, androidKeyCode, action);
public boolean writeKeyEvent(long ptr, int androidKeyCode, int action) {
return nativeWriteKeyEvent(ptr, androidKeyCode, action);
}
public boolean writeButtonEvent(int fd, int buttonCode, int action) {
return nativeWriteButtonEvent(fd, buttonCode, action);
public boolean writeButtonEvent(long ptr, int buttonCode, int action) {
return nativeWriteButtonEvent(ptr, buttonCode, action);
}
public boolean writeTouchEvent(int fd, int pointerId, int toolType, int action,
public boolean writeTouchEvent(long ptr, int pointerId, int toolType, int action,
float locationX, float locationY, float pressure, float majorAxisSize) {
return nativeWriteTouchEvent(fd, pointerId, toolType,
return nativeWriteTouchEvent(ptr, pointerId, toolType,
action, locationX, locationY,
pressure, majorAxisSize);
}
public boolean writeRelativeEvent(int fd, float relativeX, float relativeY) {
return nativeWriteRelativeEvent(fd, relativeX, relativeY);
public boolean writeRelativeEvent(long ptr, float relativeX, float relativeY) {
return nativeWriteRelativeEvent(ptr, relativeX, relativeY);
}
public boolean writeScrollEvent(int fd, float xAxisMovement, float yAxisMovement) {
return nativeWriteScrollEvent(fd, xAxisMovement,
public boolean writeScrollEvent(long ptr, float xAxisMovement, float yAxisMovement) {
return nativeWriteScrollEvent(ptr, xAxisMovement,
yAxisMovement);
}
}
@@ -597,7 +598,8 @@ class InputController {
private static final AtomicLong sNextCreationOrderNumber = new AtomicLong(1);
private final int mFd;
// Pointer to the native input device object.
private final long mPtr;
private final IBinder.DeathRecipient mDeathRecipient;
private final @Type int mType;
private final int mDisplayId;
@@ -611,9 +613,9 @@ class InputController {
// Monotonically increasing number; devices with lower numbers were created earlier.
private final long mCreationOrderNumber;
InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type,
InputDeviceDescriptor(long ptr, IBinder.DeathRecipient deathRecipient, @Type int type,
int displayId, String phys, String name, int inputDeviceId) {
mFd = fd;
mPtr = ptr;
mDeathRecipient = deathRecipient;
mType = type;
mDisplayId = displayId;
@@ -623,8 +625,8 @@ class InputController {
mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement();
}
public int getFileDescriptor() {
return mFd;
public long getNativePointer() {
return mPtr;
}
public int getType() {
@@ -767,7 +769,7 @@ class InputController {
*/
private void createDeviceInternal(@InputDeviceDescriptor.Type int type, String deviceName,
int vendorId, int productId, IBinder deviceToken, int displayId, String phys,
Supplier<Integer> deviceOpener)
Supplier<Long> deviceOpener)
throws DeviceCreationException {
if (!mThreadVerifier.isValidThread()) {
throw new IllegalStateException(
@@ -776,19 +778,22 @@ class InputController {
}
validateDeviceName(deviceName);
final int fd;
final long ptr;
final BinderDeathRecipient binderDeathRecipient;
final int inputDeviceId;
setUniqueIdAssociation(displayId, phys);
try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId)) {
fd = deviceOpener.get();
if (fd < 0) {
ptr = deviceOpener.get();
// See INVALID_PTR in libs/input/VirtualInputDevice.cpp.
if (ptr == 0) {
throw new DeviceCreationException(
"A native error occurred when creating touchscreen: " + -fd);
"A native error occurred when creating virtual input device: "
+ deviceName);
}
// The fd is valid from here, so ensure that all failures close the fd after this point.
// The pointer to the native input device is valid from here, so ensure that all
// failures close the device after this point.
try {
inputDeviceId = waiter.waitForDeviceCreation();
@@ -800,7 +805,7 @@ class InputController {
"Client died before virtual device could be created.", e);
}
} catch (DeviceCreationException e) {
mNativeWrapper.closeUinput(fd);
mNativeWrapper.closeUinput(ptr);
throw e;
}
} catch (DeviceCreationException e) {
@@ -810,7 +815,7 @@ class InputController {
synchronized (mLock) {
mInputDeviceDescriptors.put(deviceToken,
new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys,
new InputDeviceDescriptor(ptr, binderDeathRecipient, type, displayId, phys,
deviceName, inputDeviceId));
}
}

View File

@@ -22,6 +22,7 @@
#include <errno.h>
#include <fcntl.h>
#include <input/Input.h>
#include <input/VirtualInputDevice.h>
#include <linux/uinput.h>
#include <math.h>
#include <nativehelper/JNIHelp.h>
@@ -32,16 +33,12 @@
#include <set>
#include <string>
/**
* Log debug messages about native virtual input devices.
* Enable this via "adb shell setprop log.tag.InputController DEBUG"
*/
static bool isDebug() {
return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
}
using android::base::unique_fd;
namespace android {
static constexpr jlong INVALID_PTR = 0;
enum class DeviceType {
KEYBOARD,
MOUSE,
@@ -49,176 +46,18 @@ enum class DeviceType {
DPAD,
};
enum class UinputAction {
RELEASE = 0,
PRESS = 1,
MOVE = 2,
CANCEL = 3,
};
static std::map<int, UinputAction> BUTTON_ACTION_MAPPING = {
{AMOTION_EVENT_ACTION_BUTTON_PRESS, UinputAction::PRESS},
{AMOTION_EVENT_ACTION_BUTTON_RELEASE, UinputAction::RELEASE},
};
static std::map<int, UinputAction> KEY_ACTION_MAPPING = {
{AKEY_EVENT_ACTION_DOWN, UinputAction::PRESS},
{AKEY_EVENT_ACTION_UP, UinputAction::RELEASE},
};
static std::map<int, UinputAction> TOUCH_ACTION_MAPPING = {
{AMOTION_EVENT_ACTION_DOWN, UinputAction::PRESS},
{AMOTION_EVENT_ACTION_UP, UinputAction::RELEASE},
{AMOTION_EVENT_ACTION_MOVE, UinputAction::MOVE},
{AMOTION_EVENT_ACTION_CANCEL, UinputAction::CANCEL},
};
// Button code mapping from https://source.android.com/devices/input/touch-devices
static std::map<int, int> BUTTON_CODE_MAPPING = {
{AMOTION_EVENT_BUTTON_PRIMARY, BTN_LEFT}, {AMOTION_EVENT_BUTTON_SECONDARY, BTN_RIGHT},
{AMOTION_EVENT_BUTTON_TERTIARY, BTN_MIDDLE}, {AMOTION_EVENT_BUTTON_BACK, BTN_BACK},
{AMOTION_EVENT_BUTTON_FORWARD, BTN_FORWARD},
};
// Tool type mapping from https://source.android.com/devices/input/touch-devices
static std::map<int, int> TOOL_TYPE_MAPPING = {
{AMOTION_EVENT_TOOL_TYPE_FINGER, MT_TOOL_FINGER},
{AMOTION_EVENT_TOOL_TYPE_PALM, MT_TOOL_PALM},
};
// Dpad keycode mapping from https://source.android.com/devices/input/keyboard-devices
static std::map<int, int> DPAD_KEY_CODE_MAPPING = {
{AKEYCODE_DPAD_DOWN, KEY_DOWN}, {AKEYCODE_DPAD_UP, KEY_UP},
{AKEYCODE_DPAD_LEFT, KEY_LEFT}, {AKEYCODE_DPAD_RIGHT, KEY_RIGHT},
{AKEYCODE_DPAD_CENTER, KEY_SELECT}, {AKEYCODE_BACK, KEY_BACK},
};
// Keycode mapping from https://source.android.com/devices/input/keyboard-devices
static std::map<int, int> KEY_CODE_MAPPING = {
{AKEYCODE_0, KEY_0},
{AKEYCODE_1, KEY_1},
{AKEYCODE_2, KEY_2},
{AKEYCODE_3, KEY_3},
{AKEYCODE_4, KEY_4},
{AKEYCODE_5, KEY_5},
{AKEYCODE_6, KEY_6},
{AKEYCODE_7, KEY_7},
{AKEYCODE_8, KEY_8},
{AKEYCODE_9, KEY_9},
{AKEYCODE_A, KEY_A},
{AKEYCODE_B, KEY_B},
{AKEYCODE_C, KEY_C},
{AKEYCODE_D, KEY_D},
{AKEYCODE_E, KEY_E},
{AKEYCODE_F, KEY_F},
{AKEYCODE_G, KEY_G},
{AKEYCODE_H, KEY_H},
{AKEYCODE_I, KEY_I},
{AKEYCODE_J, KEY_J},
{AKEYCODE_K, KEY_K},
{AKEYCODE_L, KEY_L},
{AKEYCODE_M, KEY_M},
{AKEYCODE_N, KEY_N},
{AKEYCODE_O, KEY_O},
{AKEYCODE_P, KEY_P},
{AKEYCODE_Q, KEY_Q},
{AKEYCODE_R, KEY_R},
{AKEYCODE_S, KEY_S},
{AKEYCODE_T, KEY_T},
{AKEYCODE_U, KEY_U},
{AKEYCODE_V, KEY_V},
{AKEYCODE_W, KEY_W},
{AKEYCODE_X, KEY_X},
{AKEYCODE_Y, KEY_Y},
{AKEYCODE_Z, KEY_Z},
{AKEYCODE_GRAVE, KEY_GRAVE},
{AKEYCODE_MINUS, KEY_MINUS},
{AKEYCODE_EQUALS, KEY_EQUAL},
{AKEYCODE_LEFT_BRACKET, KEY_LEFTBRACE},
{AKEYCODE_RIGHT_BRACKET, KEY_RIGHTBRACE},
{AKEYCODE_BACKSLASH, KEY_BACKSLASH},
{AKEYCODE_SEMICOLON, KEY_SEMICOLON},
{AKEYCODE_APOSTROPHE, KEY_APOSTROPHE},
{AKEYCODE_COMMA, KEY_COMMA},
{AKEYCODE_PERIOD, KEY_DOT},
{AKEYCODE_SLASH, KEY_SLASH},
{AKEYCODE_ALT_LEFT, KEY_LEFTALT},
{AKEYCODE_ALT_RIGHT, KEY_RIGHTALT},
{AKEYCODE_CTRL_LEFT, KEY_LEFTCTRL},
{AKEYCODE_CTRL_RIGHT, KEY_RIGHTCTRL},
{AKEYCODE_SHIFT_LEFT, KEY_LEFTSHIFT},
{AKEYCODE_SHIFT_RIGHT, KEY_RIGHTSHIFT},
{AKEYCODE_META_LEFT, KEY_LEFTMETA},
{AKEYCODE_META_RIGHT, KEY_RIGHTMETA},
{AKEYCODE_CAPS_LOCK, KEY_CAPSLOCK},
{AKEYCODE_SCROLL_LOCK, KEY_SCROLLLOCK},
{AKEYCODE_NUM_LOCK, KEY_NUMLOCK},
{AKEYCODE_ENTER, KEY_ENTER},
{AKEYCODE_TAB, KEY_TAB},
{AKEYCODE_SPACE, KEY_SPACE},
{AKEYCODE_DPAD_DOWN, KEY_DOWN},
{AKEYCODE_DPAD_UP, KEY_UP},
{AKEYCODE_DPAD_LEFT, KEY_LEFT},
{AKEYCODE_DPAD_RIGHT, KEY_RIGHT},
{AKEYCODE_MOVE_END, KEY_END},
{AKEYCODE_MOVE_HOME, KEY_HOME},
{AKEYCODE_PAGE_DOWN, KEY_PAGEDOWN},
{AKEYCODE_PAGE_UP, KEY_PAGEUP},
{AKEYCODE_DEL, KEY_BACKSPACE},
{AKEYCODE_FORWARD_DEL, KEY_DELETE},
{AKEYCODE_INSERT, KEY_INSERT},
{AKEYCODE_ESCAPE, KEY_ESC},
{AKEYCODE_BREAK, KEY_PAUSE},
{AKEYCODE_F1, KEY_F1},
{AKEYCODE_F2, KEY_F2},
{AKEYCODE_F3, KEY_F3},
{AKEYCODE_F4, KEY_F4},
{AKEYCODE_F5, KEY_F5},
{AKEYCODE_F6, KEY_F6},
{AKEYCODE_F7, KEY_F7},
{AKEYCODE_F8, KEY_F8},
{AKEYCODE_F9, KEY_F9},
{AKEYCODE_F10, KEY_F10},
{AKEYCODE_F11, KEY_F11},
{AKEYCODE_F12, KEY_F12},
{AKEYCODE_BACK, KEY_BACK},
{AKEYCODE_FORWARD, KEY_FORWARD},
{AKEYCODE_NUMPAD_1, KEY_KP1},
{AKEYCODE_NUMPAD_2, KEY_KP2},
{AKEYCODE_NUMPAD_3, KEY_KP3},
{AKEYCODE_NUMPAD_4, KEY_KP4},
{AKEYCODE_NUMPAD_5, KEY_KP5},
{AKEYCODE_NUMPAD_6, KEY_KP6},
{AKEYCODE_NUMPAD_7, KEY_KP7},
{AKEYCODE_NUMPAD_8, KEY_KP8},
{AKEYCODE_NUMPAD_9, KEY_KP9},
{AKEYCODE_NUMPAD_0, KEY_KP0},
{AKEYCODE_NUMPAD_ADD, KEY_KPPLUS},
{AKEYCODE_NUMPAD_SUBTRACT, KEY_KPMINUS},
{AKEYCODE_NUMPAD_MULTIPLY, KEY_KPASTERISK},
{AKEYCODE_NUMPAD_DIVIDE, KEY_KPSLASH},
{AKEYCODE_NUMPAD_DOT, KEY_KPDOT},
{AKEYCODE_NUMPAD_ENTER, KEY_KPENTER},
{AKEYCODE_NUMPAD_EQUALS, KEY_KPEQUAL},
{AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA},
};
/*
* Map from the uinput touchscreen fd to the pointers present in the previous touch events that
* hasn't been lifted.
* We only allow pointer id to go up to MAX_POINTERS because the maximum slots of virtual
* touchscreen is set up with MAX_POINTERS. Note that in other cases Android allows pointer id to go
* up to MAX_POINTERS_ID.
*/
static std::map<int32_t, std::bitset<MAX_POINTERS>> unreleasedTouches;
static unique_fd invalidFd() {
return unique_fd(-1);
}
/** Creates a new uinput device and assigns a file descriptor. */
static int openUinput(const char* readableName, jint vendorId, jint productId, const char* phys,
DeviceType deviceType, jint screenHeight, jint screenWidth) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
static unique_fd openUinput(const char* readableName, jint vendorId, jint productId,
const char* phys, DeviceType deviceType, jint screenHeight,
jint screenWidth) {
unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
if (fd < 0) {
ALOGE("Error creating uinput device: %s", strerror(errno));
return -errno;
return invalidFd();
}
ioctl(fd, UI_SET_PHYS, phys);
@@ -227,12 +66,12 @@ static int openUinput(const char* readableName, jint vendorId, jint productId, c
ioctl(fd, UI_SET_EVBIT, EV_SYN);
switch (deviceType) {
case DeviceType::DPAD:
for (const auto& [_, keyCode] : DPAD_KEY_CODE_MAPPING) {
for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) {
ioctl(fd, UI_SET_KEYBIT, keyCode);
}
break;
case DeviceType::KEYBOARD:
for (const auto& [_, keyCode] : KEY_CODE_MAPPING) {
for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) {
ioctl(fd, UI_SET_KEYBIT, keyCode);
}
break;
@@ -277,7 +116,7 @@ static int openUinput(const char* readableName, jint vendorId, jint productId, c
xAbsSetup.absinfo.minimum = 0;
if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno));
return -errno;
return invalidFd();
}
uinput_abs_setup yAbsSetup;
yAbsSetup.code = ABS_MT_POSITION_Y;
@@ -285,7 +124,7 @@ static int openUinput(const char* readableName, jint vendorId, jint productId, c
yAbsSetup.absinfo.minimum = 0;
if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno));
return -errno;
return invalidFd();
}
uinput_abs_setup majorAbsSetup;
majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
@@ -293,7 +132,7 @@ static int openUinput(const char* readableName, jint vendorId, jint productId, c
majorAbsSetup.absinfo.minimum = 0;
if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) {
ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno));
return -errno;
return invalidFd();
}
uinput_abs_setup pressureAbsSetup;
pressureAbsSetup.code = ABS_MT_PRESSURE;
@@ -301,7 +140,7 @@ static int openUinput(const char* readableName, jint vendorId, jint productId, c
pressureAbsSetup.absinfo.minimum = 0;
if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
return -errno;
return invalidFd();
}
uinput_abs_setup slotAbsSetup;
slotAbsSetup.code = ABS_MT_SLOT;
@@ -309,12 +148,12 @@ static int openUinput(const char* readableName, jint vendorId, jint productId, c
slotAbsSetup.absinfo.minimum = 0;
if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
return -errno;
return invalidFd();
}
}
if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
ALOGE("Error creating uinput device: %s", strerror(errno));
return -errno;
return invalidFd();
}
} else {
// UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
@@ -338,255 +177,118 @@ static int openUinput(const char* readableName, jint vendorId, jint productId, c
}
if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
ALOGE("Error creating uinput device: %s", strerror(errno));
return -errno;
return invalidFd();
}
}
if (ioctl(fd, UI_DEV_CREATE) != 0) {
ALOGE("Error creating uinput device: %s", strerror(errno));
return -errno;
return invalidFd();
}
return fd.release();
return fd;
}
static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId, jstring phys,
DeviceType deviceType, int screenHeight, int screenWidth) {
static unique_fd openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId,
jstring phys, DeviceType deviceType, int screenHeight,
int screenWidth) {
ScopedUtfChars readableName(env, name);
ScopedUtfChars readablePhys(env, phys);
return openUinput(readableName.c_str(), vendorId, productId, readablePhys.c_str(), deviceType,
screenHeight, screenWidth);
}
static int nativeOpenUinputDpad(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
jint productId, jstring phys) {
return openUinputJni(env, name, vendorId, productId, phys, DeviceType::DPAD,
/* screenHeight */ 0, /* screenWidth */ 0);
static jlong nativeOpenUinputDpad(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
jint productId, jstring phys) {
auto fd = openUinputJni(env, name, vendorId, productId, phys, DeviceType::DPAD,
/* screenHeight= */ 0, /* screenWidth= */ 0);
return fd.ok() ? reinterpret_cast<jlong>(new VirtualDpad(std::move(fd))) : INVALID_PTR;
}
static int nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
jint productId, jstring phys) {
return openUinputJni(env, name, vendorId, productId, phys, DeviceType::KEYBOARD,
/* screenHeight */ 0, /* screenWidth */ 0);
static jlong nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
jint productId, jstring phys) {
auto fd = openUinputJni(env, name, vendorId, productId, phys, DeviceType::KEYBOARD,
/* screenHeight= */ 0, /* screenWidth= */ 0);
return fd.ok() ? reinterpret_cast<jlong>(new VirtualKeyboard(std::move(fd))) : INVALID_PTR;
}
static int nativeOpenUinputMouse(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
jint productId, jstring phys) {
return openUinputJni(env, name, vendorId, productId, phys, DeviceType::MOUSE,
/* screenHeight */ 0, /* screenWidth */ 0);
static jlong nativeOpenUinputMouse(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
jint productId, jstring phys) {
auto fd = openUinputJni(env, name, vendorId, productId, phys, DeviceType::MOUSE,
/* screenHeight= */ 0, /* screenWidth= */ 0);
return fd.ok() ? reinterpret_cast<jlong>(new VirtualMouse(std::move(fd))) : INVALID_PTR;
}
static int nativeOpenUinputTouchscreen(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
jint productId, jstring phys, jint height, jint width) {
return openUinputJni(env, name, vendorId, productId, phys, DeviceType::TOUCHSCREEN, height,
width);
static jlong nativeOpenUinputTouchscreen(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
jint productId, jstring phys, jint height, jint width) {
auto fd = openUinputJni(env, name, vendorId, productId, phys, DeviceType::TOUCHSCREEN, height,
width);
return fd.ok() ? reinterpret_cast<jlong>(new VirtualTouchscreen(std::move(fd))) : INVALID_PTR;
}
static bool nativeCloseUinput(JNIEnv* env, jobject thiz, jint fd) {
ioctl(fd, UI_DEV_DESTROY);
if (auto touchesOnFd = unreleasedTouches.find(fd); touchesOnFd != unreleasedTouches.end()) {
const size_t remainingPointers = touchesOnFd->second.size();
unreleasedTouches.erase(touchesOnFd);
ALOGW_IF(remainingPointers > 0, "Closing touchscreen %d, erased %zu unreleased pointers.",
fd, remainingPointers);
}
return close(fd);
static void nativeCloseUinput(JNIEnv* env, jobject thiz, jlong ptr) {
VirtualInputDevice* virtualInputDevice = reinterpret_cast<VirtualInputDevice*>(ptr);
delete virtualInputDevice;
}
static bool writeInputEvent(int fd, uint16_t type, uint16_t code, int32_t value) {
struct input_event ev = {.type = type, .code = code, .value = value};
return TEMP_FAILURE_RETRY(write(fd, &ev, sizeof(struct input_event))) == sizeof(ev);
}
static bool writeKeyEvent(jint fd, jint androidKeyCode, jint action,
const std::map<int, int>& keyCodeMapping) {
auto keyCodeIterator = keyCodeMapping.find(androidKeyCode);
if (keyCodeIterator == keyCodeMapping.end()) {
ALOGE("Unsupported native keycode for androidKeyCode %d", androidKeyCode);
return false;
}
auto actionIterator = KEY_ACTION_MAPPING.find(action);
if (actionIterator == KEY_ACTION_MAPPING.end()) {
return false;
}
if (!writeInputEvent(fd, EV_KEY, static_cast<uint16_t>(keyCodeIterator->second),
static_cast<int32_t>(actionIterator->second))) {
return false;
}
if (!writeInputEvent(fd, EV_SYN, SYN_REPORT, 0)) {
return false;
}
return true;
}
static bool nativeWriteDpadKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode,
// Native methods for VirtualDpad
static bool nativeWriteDpadKeyEvent(JNIEnv* env, jobject thiz, jlong ptr, jint androidKeyCode,
jint action) {
return writeKeyEvent(fd, androidKeyCode, action, DPAD_KEY_CODE_MAPPING);
VirtualDpad* virtualDpad = reinterpret_cast<VirtualDpad*>(ptr);
return virtualDpad->writeDpadKeyEvent(androidKeyCode, action);
}
static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode,
// Native methods for VirtualKeyboard
static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jlong ptr, jint androidKeyCode,
jint action) {
return writeKeyEvent(fd, androidKeyCode, action, KEY_CODE_MAPPING);
VirtualKeyboard* virtualKeyboard = reinterpret_cast<VirtualKeyboard*>(ptr);
return virtualKeyboard->writeKeyEvent(androidKeyCode, action);
}
static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jint fd, jint buttonCode,
// Native methods for VirtualTouchscreen
static bool nativeWriteTouchEvent(JNIEnv* env, jobject thiz, jlong ptr, jint pointerId,
jint toolType, jint action, jfloat locationX, jfloat locationY,
jfloat pressure, jfloat majorAxisSize) {
VirtualTouchscreen* virtualTouchscreen = reinterpret_cast<VirtualTouchscreen*>(ptr);
return virtualTouchscreen->writeTouchEvent(pointerId, toolType, action, locationX, locationY,
pressure, majorAxisSize);
}
// Native methods for VirtualMouse
static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jlong ptr, jint buttonCode,
jint action) {
auto buttonCodeIterator = BUTTON_CODE_MAPPING.find(buttonCode);
if (buttonCodeIterator == BUTTON_CODE_MAPPING.end()) {
return false;
}
auto actionIterator = BUTTON_ACTION_MAPPING.find(action);
if (actionIterator == BUTTON_ACTION_MAPPING.end()) {
return false;
}
if (!writeInputEvent(fd, EV_KEY, static_cast<uint16_t>(buttonCodeIterator->second),
static_cast<int32_t>(actionIterator->second))) {
return false;
}
if (!writeInputEvent(fd, EV_SYN, SYN_REPORT, 0)) {
return false;
}
return true;
VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
return virtualMouse->writeButtonEvent(buttonCode, action);
}
static bool handleTouchUp(int fd, int pointerId) {
if (!writeInputEvent(fd, EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(-1))) {
return false;
}
auto touchesOnFd = unreleasedTouches.find(fd);
if (touchesOnFd == unreleasedTouches.end()) {
ALOGE("PointerId %d action UP received with no prior events on touchscreen %d.", pointerId,
fd);
return false;
}
ALOGD_IF(isDebug(), "Unreleased touches found for touchscreen %d in the map", fd);
// When a pointer is no longer in touch, remove the pointer id from the corresponding
// entry in the unreleased touches map.
if (pointerId < 0 || pointerId >= MAX_POINTERS) {
ALOGE("Virtual touch event has invalid pointer id %d; value must be between 0 and %zu",
pointerId, MAX_POINTERS - 1);
return false;
}
if (!touchesOnFd->second.test(pointerId)) {
ALOGE("PointerId %d action UP received with no prior action DOWN on touchscreen %d.",
pointerId, fd);
return false;
}
touchesOnFd->second.reset(pointerId);
ALOGD_IF(isDebug(), "Pointer %d erased from the touchscreen %d", pointerId, fd);
// Only sends the BTN UP event when there's no pointers on the touchscreen.
if (touchesOnFd->second.none()) {
unreleasedTouches.erase(touchesOnFd);
if (!writeInputEvent(fd, EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE))) {
return false;
}
ALOGD_IF(isDebug(), "No pointers on touchscreen %d, BTN UP event sent.", fd);
}
return true;
}
static bool handleTouchDown(int fd, int pointerId) {
// When a new pointer is down on the touchscreen, add the pointer id in the corresponding
// entry in the unreleased touches map.
auto touchesOnFd = unreleasedTouches.find(fd);
if (touchesOnFd == unreleasedTouches.end()) {
// Only sends the BTN Down event when the first pointer on the touchscreen is down.
if (!writeInputEvent(fd, EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS))) {
return false;
}
touchesOnFd = unreleasedTouches.insert({fd, {}}).first;
ALOGD_IF(isDebug(), "New touchscreen with fd %d added in the unreleased touches map.", fd);
}
if (touchesOnFd->second.test(pointerId)) {
ALOGE("Repetitive action DOWN event received on a pointer %d that is already down.",
pointerId);
return false;
}
touchesOnFd->second.set(pointerId);
ALOGD_IF(isDebug(), "Added pointer %d under touchscreen %d in the map", pointerId, fd);
if (!writeInputEvent(fd, EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(pointerId))) {
return false;
}
return true;
}
static bool nativeWriteTouchEvent(JNIEnv* env, jobject thiz, jint fd, jint pointerId, jint toolType,
jint action, jfloat locationX, jfloat locationY, jfloat pressure,
jfloat majorAxisSize) {
if (!writeInputEvent(fd, EV_ABS, ABS_MT_SLOT, pointerId)) {
return false;
}
auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType);
if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) {
return false;
}
if (toolType != -1) {
if (!writeInputEvent(fd, EV_ABS, ABS_MT_TOOL_TYPE,
static_cast<int32_t>(toolTypeIterator->second))) {
return false;
}
}
auto actionIterator = TOUCH_ACTION_MAPPING.find(action);
if (actionIterator == TOUCH_ACTION_MAPPING.end()) {
return false;
}
UinputAction uinputAction = actionIterator->second;
if (uinputAction == UinputAction::PRESS && !handleTouchDown(fd, pointerId)) {
return false;
} else if (uinputAction == UinputAction::RELEASE && !handleTouchUp(fd, pointerId)) {
return false;
}
if (!writeInputEvent(fd, EV_ABS, ABS_MT_POSITION_X, locationX)) {
return false;
}
if (!writeInputEvent(fd, EV_ABS, ABS_MT_POSITION_Y, locationY)) {
return false;
}
if (!isnan(pressure)) {
if (!writeInputEvent(fd, EV_ABS, ABS_MT_PRESSURE, pressure)) {
return false;
}
}
if (!isnan(majorAxisSize)) {
if (!writeInputEvent(fd, EV_ABS, ABS_MT_TOUCH_MAJOR, majorAxisSize)) {
return false;
}
}
return writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
}
static bool nativeWriteRelativeEvent(JNIEnv* env, jobject thiz, jint fd, jfloat relativeX,
static bool nativeWriteRelativeEvent(JNIEnv* env, jobject thiz, jlong ptr, jfloat relativeX,
jfloat relativeY) {
return writeInputEvent(fd, EV_REL, REL_X, relativeX) &&
writeInputEvent(fd, EV_REL, REL_Y, relativeY) &&
writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
return virtualMouse->writeRelativeEvent(relativeX, relativeY);
}
static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jint fd, jfloat xAxisMovement,
static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jlong ptr, jfloat xAxisMovement,
jfloat yAxisMovement) {
return writeInputEvent(fd, EV_REL, REL_HWHEEL, xAxisMovement) &&
writeInputEvent(fd, EV_REL, REL_WHEEL, yAxisMovement) &&
writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
return virtualMouse->writeScrollEvent(xAxisMovement, yAxisMovement);
}
static JNINativeMethod methods[] = {
{"nativeOpenUinputDpad", "(Ljava/lang/String;IILjava/lang/String;)I",
{"nativeOpenUinputDpad", "(Ljava/lang/String;IILjava/lang/String;)J",
(void*)nativeOpenUinputDpad},
{"nativeOpenUinputKeyboard", "(Ljava/lang/String;IILjava/lang/String;)I",
{"nativeOpenUinputKeyboard", "(Ljava/lang/String;IILjava/lang/String;)J",
(void*)nativeOpenUinputKeyboard},
{"nativeOpenUinputMouse", "(Ljava/lang/String;IILjava/lang/String;)I",
{"nativeOpenUinputMouse", "(Ljava/lang/String;IILjava/lang/String;)J",
(void*)nativeOpenUinputMouse},
{"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)I",
{"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)J",
(void*)nativeOpenUinputTouchscreen},
{"nativeCloseUinput", "(I)Z", (void*)nativeCloseUinput},
{"nativeWriteDpadKeyEvent", "(III)Z", (void*)nativeWriteDpadKeyEvent},
{"nativeWriteKeyEvent", "(III)Z", (void*)nativeWriteKeyEvent},
{"nativeWriteButtonEvent", "(III)Z", (void*)nativeWriteButtonEvent},
{"nativeWriteTouchEvent", "(IIIIFFFF)Z", (void*)nativeWriteTouchEvent},
{"nativeWriteRelativeEvent", "(IFF)Z", (void*)nativeWriteRelativeEvent},
{"nativeWriteScrollEvent", "(IFF)Z", (void*)nativeWriteScrollEvent},
{"nativeCloseUinput", "(J)V", (void*)nativeCloseUinput},
{"nativeWriteDpadKeyEvent", "(JII)Z", (void*)nativeWriteDpadKeyEvent},
{"nativeWriteKeyEvent", "(JII)Z", (void*)nativeWriteKeyEvent},
{"nativeWriteButtonEvent", "(JII)Z", (void*)nativeWriteButtonEvent},
{"nativeWriteTouchEvent", "(JIIIFFFF)Z", (void*)nativeWriteTouchEvent},
{"nativeWriteRelativeEvent", "(JFF)Z", (void*)nativeWriteRelativeEvent},
{"nativeWriteScrollEvent", "(JFF)Z", (void*)nativeWriteScrollEvent},
};
int register_android_server_companion_virtual_InputController(JNIEnv* env) {

View File

@@ -79,7 +79,7 @@ class InputManagerMockHelper {
InputManager.resetInstance(mIInputManagerMock);
}
private Void handleNativeOpenInputDevice(InvocationOnMock inv) {
private long handleNativeOpenInputDevice(InvocationOnMock inv) {
Objects.requireNonNull(mDevicesChangedListener,
"InputController did not register an InputDevicesChangedListener.");
@@ -101,6 +101,7 @@ class InputManagerMockHelper {
}
// Process the device added notification.
mTestableLooper.processAllMessages();
return null;
// Return a placeholder pointer to the native input device.
return 1L;
}
}