Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bedb117e authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Move uinput logic out of jni" into main

parents 664b2219 8a13ae82
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -211,7 +211,6 @@ cc_defaults {
        "android.system.suspend-V1-ndk",
        "server_configurable_flags",
        "service.incremental",
        "android.companion.virtualdevice.flags-aconfig-cc",
    ],

    static_libs: [
+2 −253
Original line number Diff line number Diff line
@@ -19,273 +19,22 @@
#include <android-base/unique_fd.h>
#include <android/input.h>
#include <android/keycodes.h>
#include <android_companion_virtualdevice_flags.h>
#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>
#include <nativehelper/ScopedUtfChars.h>
#include <utils/Log.h>

#include <map>
#include <set>
#include <string>

using android::base::unique_fd;

namespace android {

namespace vd_flags = android::companion::virtualdevice::flags;

static constexpr jlong INVALID_PTR = 0;

enum class DeviceType {
    KEYBOARD,
    MOUSE,
    TOUCHSCREEN,
    DPAD,
    STYLUS,
    ROTARY_ENCODER,
};

static unique_fd invalidFd() {
    return unique_fd(-1);
}

/** Creates a new uinput device and assigns a file descriptor. */
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 invalidFd();
    }

    ioctl(fd, UI_SET_PHYS, phys);

    ioctl(fd, UI_SET_EVBIT, EV_KEY);
    ioctl(fd, UI_SET_EVBIT, EV_SYN);
    switch (deviceType) {
        case DeviceType::DPAD:
            for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) {
                ioctl(fd, UI_SET_KEYBIT, keyCode);
            }
            break;
        case DeviceType::KEYBOARD:
            for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) {
                ioctl(fd, UI_SET_KEYBIT, keyCode);
            }
            break;
        case DeviceType::MOUSE:
            ioctl(fd, UI_SET_EVBIT, EV_REL);
            ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
            ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
            ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
            ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
            ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
            ioctl(fd, UI_SET_RELBIT, REL_X);
            ioctl(fd, UI_SET_RELBIT, REL_Y);
            ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
            ioctl(fd, UI_SET_RELBIT, REL_HWHEEL);
            if (vd_flags::high_resolution_scroll()) {
                ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
                ioctl(fd, UI_SET_RELBIT, REL_HWHEEL_HI_RES);
            }
            break;
        case DeviceType::TOUCHSCREEN:
            ioctl(fd, UI_SET_EVBIT, EV_ABS);
            ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
            ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
            ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
            ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
            ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
            ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
            ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
            ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
            ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
            break;
        case DeviceType::STYLUS:
            ioctl(fd, UI_SET_EVBIT, EV_ABS);
            ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
            ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS);
            ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2);
            ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_PEN);
            ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_RUBBER);
            ioctl(fd, UI_SET_ABSBIT, ABS_X);
            ioctl(fd, UI_SET_ABSBIT, ABS_Y);
            ioctl(fd, UI_SET_ABSBIT, ABS_TILT_X);
            ioctl(fd, UI_SET_ABSBIT, ABS_TILT_Y);
            ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
            ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
            break;
        case DeviceType::ROTARY_ENCODER:
            ioctl(fd, UI_SET_EVBIT, EV_REL);
            ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
            if (vd_flags::high_resolution_scroll()) {
                ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
            }
            break;
        default:
            ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType));
            return invalidFd();
    }

    int version;
    if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) {
        uinput_setup setup;
        memset(&setup, 0, sizeof(setup));
        strlcpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE);
        setup.id.version = 1;
        setup.id.bustype = BUS_VIRTUAL;
        setup.id.vendor = vendorId;
        setup.id.product = productId;
        if (deviceType == DeviceType::TOUCHSCREEN) {
            uinput_abs_setup xAbsSetup;
            xAbsSetup.code = ABS_MT_POSITION_X;
            xAbsSetup.absinfo.maximum = screenWidth - 1;
            xAbsSetup.absinfo.minimum = 0;
            if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
                ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno));
                return invalidFd();
            }
            uinput_abs_setup yAbsSetup;
            yAbsSetup.code = ABS_MT_POSITION_Y;
            yAbsSetup.absinfo.maximum = screenHeight - 1;
            yAbsSetup.absinfo.minimum = 0;
            if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
                ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno));
                return invalidFd();
            }
            uinput_abs_setup majorAbsSetup;
            majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
            majorAbsSetup.absinfo.maximum = screenWidth - 1;
            majorAbsSetup.absinfo.minimum = 0;
            if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) {
                ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno));
                return invalidFd();
            }
            uinput_abs_setup pressureAbsSetup;
            pressureAbsSetup.code = ABS_MT_PRESSURE;
            pressureAbsSetup.absinfo.maximum = 255;
            pressureAbsSetup.absinfo.minimum = 0;
            if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
                ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
                return invalidFd();
            }
            uinput_abs_setup slotAbsSetup;
            slotAbsSetup.code = ABS_MT_SLOT;
            slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
            slotAbsSetup.absinfo.minimum = 0;
            if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
                ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
                return invalidFd();
            }
            uinput_abs_setup trackingIdAbsSetup;
            trackingIdAbsSetup.code = ABS_MT_TRACKING_ID;
            trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
            trackingIdAbsSetup.absinfo.minimum = 0;
            if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) {
                ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno));
                return invalidFd();
            }
        } else if (deviceType == DeviceType::STYLUS) {
            uinput_abs_setup xAbsSetup;
            xAbsSetup.code = ABS_X;
            xAbsSetup.absinfo.maximum = screenWidth - 1;
            xAbsSetup.absinfo.minimum = 0;
            if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
                ALOGE("Error creating stylus uinput x axis: %s", strerror(errno));
                return invalidFd();
            }
            uinput_abs_setup yAbsSetup;
            yAbsSetup.code = ABS_Y;
            yAbsSetup.absinfo.maximum = screenHeight - 1;
            yAbsSetup.absinfo.minimum = 0;
            if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
                ALOGE("Error creating stylus uinput y axis: %s", strerror(errno));
                return invalidFd();
            }
            uinput_abs_setup tiltXAbsSetup;
            tiltXAbsSetup.code = ABS_TILT_X;
            tiltXAbsSetup.absinfo.maximum = 90;
            tiltXAbsSetup.absinfo.minimum = -90;
            if (ioctl(fd, UI_ABS_SETUP, &tiltXAbsSetup) != 0) {
                ALOGE("Error creating stylus uinput tilt x axis: %s", strerror(errno));
                return invalidFd();
            }
            uinput_abs_setup tiltYAbsSetup;
            tiltYAbsSetup.code = ABS_TILT_Y;
            tiltYAbsSetup.absinfo.maximum = 90;
            tiltYAbsSetup.absinfo.minimum = -90;
            if (ioctl(fd, UI_ABS_SETUP, &tiltYAbsSetup) != 0) {
                ALOGE("Error creating stylus uinput tilt y axis: %s", strerror(errno));
                return invalidFd();
            }
            uinput_abs_setup pressureAbsSetup;
            pressureAbsSetup.code = ABS_PRESSURE;
            pressureAbsSetup.absinfo.maximum = 255;
            pressureAbsSetup.absinfo.minimum = 0;
            if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
                ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
                return invalidFd();
            }
        }
        if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
            ALOGE("Error creating uinput device: %s", strerror(errno));
            return invalidFd();
        }
    } else {
        // UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
        ALOGI("Falling back to version %d manual setup", version);
        uinput_user_dev fallback;
        memset(&fallback, 0, sizeof(fallback));
        strlcpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE);
        fallback.id.version = 1;
        fallback.id.bustype = BUS_VIRTUAL;
        fallback.id.vendor = vendorId;
        fallback.id.product = productId;
        if (deviceType == DeviceType::TOUCHSCREEN) {
            fallback.absmin[ABS_MT_POSITION_X] = 0;
            fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1;
            fallback.absmin[ABS_MT_POSITION_Y] = 0;
            fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1;
            fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0;
            fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1;
            fallback.absmin[ABS_MT_PRESSURE] = 0;
            fallback.absmax[ABS_MT_PRESSURE] = 255;
        } else if (deviceType == DeviceType::STYLUS) {
            fallback.absmin[ABS_X] = 0;
            fallback.absmax[ABS_X] = screenWidth - 1;
            fallback.absmin[ABS_Y] = 0;
            fallback.absmax[ABS_Y] = screenHeight - 1;
            fallback.absmin[ABS_TILT_X] = -90;
            fallback.absmax[ABS_TILT_X] = 90;
            fallback.absmin[ABS_TILT_Y] = -90;
            fallback.absmax[ABS_TILT_Y] = 90;
            fallback.absmin[ABS_PRESSURE] = 0;
            fallback.absmax[ABS_PRESSURE] = 255;
        }
        if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
            ALOGE("Error creating uinput device: %s", strerror(errno));
            return invalidFd();
        }
    }

    if (ioctl(fd, UI_DEV_CREATE) != 0) {
        ALOGE("Error creating uinput device: %s", strerror(errno));
        return invalidFd();
    }

    return fd;
}

static unique_fd openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId,
                               jstring phys, DeviceType deviceType, int screenHeight,
                               int screenWidth) {
                               jstring phys, DeviceType deviceType, jint screenHeight,
                               jint screenWidth) {
    ScopedUtfChars readableName(env, name);
    ScopedUtfChars readablePhys(env, phys);
    return openUinput(readableName.c_str(), vendorId, productId, readablePhys.c_str(), deviceType,