Move device specific logic to VirtualInputDevice and its subclasses.
Only keep virtual input device setup code and move device specific logic
to VirtualInputDevice and its subclasses. The benefits are threefold:
1) Better encapsulation: we used to have all input event injection logic
in inputController JNI code, it's hard to tell the capability of each
device type. For example, writeKeyEvent() doesn't have any indication
of which device it's writing into. If a fd representing a mouse is
passed in then the key event might be written to a mouse device.
2) No need to manually close the file descripters. We can now depend on
the destructor of unique_fd.
3) We can get rid of the errorprone static map introduced in ag/21078609
which keeps record of active pointers on touchscreen.
Test: atest VirtualInputTest VirtualMouseTest VirtualKeyboardTest
VirtualTouchscreenTest VirtualDpadTest InputControllerTest
Bug: 267515782
Change-Id: Ic5f246544172a431090f427457265bbf8d63bd70
Merged-In: Ic5f246544172a431090f427457265bbf8d63bd70
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index df5e37c..21b51b1 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -242,7 +242,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
// 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 @@
mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement();
}
- public int getFileDescriptor() {
- return mFd;
+ public long getNativePointer() {
+ return mPtr;
}
public int getType() {
@@ -767,7 +769,7 @@
*/
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 @@
}
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 @@
"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 @@
synchronized (mLock) {
mInputDeviceDescriptors.put(deviceToken,
- new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys,
+ new InputDeviceDescriptor(ptr, binderDeathRecipient, type, displayId, phys,
deviceName, inputDeviceId));
}
}
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index dd757bc..4898d95 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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) {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
index 51bd5b0f..3cc6b01 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
@@ -79,7 +79,7 @@
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 @@
}
// Process the device added notification.
mTestableLooper.processAllMessages();
- return null;
+ // Return a placeholder pointer to the native input device.
+ return 1L;
}
}