summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/companion/java/com/android/server/companion/virtual/InputController.java109
-rw-r--r--services/core/jni/com_android_server_companion_virtual_InputController.cpp470
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java5
3 files changed, 146 insertions, 438 deletions
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 df5e37c947cc..21b51b1acef0 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 @@ 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));
}
}
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 dd757bc91415..4898d95b8e4b 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 @@ 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 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 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 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 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 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 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 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 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 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 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 void nativeCloseUinput(JNIEnv* env, jobject thiz, jlong ptr) {
+ VirtualInputDevice* virtualInputDevice = reinterpret_cast<VirtualInputDevice*>(ptr);
+ delete virtualInputDevice;
}
-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,
- 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;
-}
-
-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;
+// 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);
}
-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);
+// Native methods for VirtualMouse
+static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jlong ptr, jint buttonCode,
+ jint action) {
+ VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
+ return virtualMouse->writeButtonEvent(buttonCode, action);
}
-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 51bd5b0f76c9..3cc6b0103322 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 @@ 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;
}
}