diff options
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; } } |