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

Commit 561109f6 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Track existing pointers on the virtual touchscreen."

parents 9dd6ebe4 fb58ee74
Loading
Loading
Loading
Loading
+91 −9
Original line number Diff line number Diff line
@@ -29,8 +29,17 @@
#include <utils/Log.h>

#include <map>
#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);
}

namespace android {

enum class DeviceType {
@@ -194,6 +203,15 @@ static std::map<int, int> KEY_CODE_MAPPING = {
        {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;

/** 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) {
@@ -366,6 +384,12 @@ static int nativeOpenUinputTouchscreen(JNIEnv* env, jobject thiz, jstring name,

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);
}

@@ -425,6 +449,69 @@ static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jint fd, jint butt
    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;
}

static bool nativeWriteTouchEvent(JNIEnv* env, jobject thiz, jint fd, jint pointerId, jint toolType,
                                  jint action, jfloat locationX, jfloat locationY, jfloat pressure,
                                  jfloat majorAxisSize) {
@@ -446,16 +533,11 @@ static bool nativeWriteTouchEvent(JNIEnv* env, jobject thiz, jint fd, jint point
        return false;
    }
    UinputAction uinputAction = actionIterator->second;
    if (uinputAction == UinputAction::PRESS || uinputAction == UinputAction::RELEASE) {
        if (!writeInputEvent(fd, EV_KEY, BTN_TOUCH, static_cast<int32_t>(uinputAction))) {
    if (uinputAction == UinputAction::PRESS && !handleTouchDown(fd, pointerId)) {
        return false;
        }
        if (!writeInputEvent(fd, EV_ABS, ABS_MT_TRACKING_ID,
                             static_cast<int32_t>(uinputAction == UinputAction::PRESS ? pointerId
                                                                                      : -1))) {
    } else if (uinputAction == UinputAction::RELEASE && !handleTouchUp(fd, pointerId)) {
        return false;
    }
    }
    if (!writeInputEvent(fd, EV_ABS, ABS_MT_POSITION_X, locationX)) {
        return false;
    }