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

Commit 018faea1 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

InputReader: Add API to get the last used input device

This will be used by the InputMethodManagerService to notify the IME of
the last input device type that was used to interact with Android when
the IME is shown. This API eliminates the need for IMMS to add another
global spy window to solve this problem.

Bug: 336615195
Test: atest inputflinger_tests
Change-Id: If594bd07bfd0a3cb542fc300854f1dd5717aeab2
parent 7b3c2125
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -394,6 +394,12 @@ public:

    /* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */
    virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;

    /* Get the ID of the InputDevice that was used most recently.
     *
     * Returns ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID if no device has been used since boot.
     */
    virtual DeviceId getLastUsedInputDeviceId() = 0;
};

// --- TouchAffineTransformation ---
+37 −3
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ using android::base::StringPrintf;

namespace android {

namespace {

/**
 * Determines if the identifiers passed are a sub-devices. Sub-devices are physical devices
 * that expose multiple input device paths such a keyboard that also has a touchpad input.
@@ -49,7 +51,7 @@ namespace android {
 *    inputs versus the same device plugged into multiple ports.
 */

static bool isSubDevice(const InputDeviceIdentifier& identifier1,
bool isSubDevice(const InputDeviceIdentifier& identifier1,
                 const InputDeviceIdentifier& identifier2) {
    return (identifier1.vendor == identifier2.vendor &&
            identifier1.product == identifier2.product && identifier1.bus == identifier2.bus &&
@@ -58,7 +60,7 @@ static bool isSubDevice(const InputDeviceIdentifier& identifier1,
            identifier1.location == identifier2.location);
}

static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
    const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action);
    if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER &&
        actionMasked != AMOTION_EVENT_ACTION_DOWN &&
@@ -69,6 +71,28 @@ static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
    return isStylusToolType(motionArgs.pointerProperties[actionIndex].toolType);
}

bool isNewGestureStart(const NotifyMotionArgs& motion) {
    return motion.action == AMOTION_EVENT_ACTION_DOWN ||
            motion.action == AMOTION_EVENT_ACTION_HOVER_ENTER;
}

bool isNewGestureStart(const NotifyKeyArgs& key) {
    return key.action == AKEY_EVENT_ACTION_DOWN;
}

// Return the event's device ID if it marks the start of a new gesture.
std::optional<DeviceId> getDeviceIdOfNewGesture(const NotifyArgs& args) {
    if (const auto* motion = std::get_if<NotifyMotionArgs>(&args); motion != nullptr) {
        return isNewGestureStart(*motion) ? std::make_optional(motion->deviceId) : std::nullopt;
    }
    if (const auto* key = std::get_if<NotifyKeyArgs>(&args); key != nullptr) {
        return isNewGestureStart(*key) ? std::make_optional(key->deviceId) : std::nullopt;
    }
    return std::nullopt;
}

} // namespace

// --- InputReader ---

InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -162,6 +186,11 @@ void InputReader::loopOnce() {
        }

        std::swap(notifyArgs, mPendingArgs);

        // Keep track of the last used device
        for (const NotifyArgs& args : notifyArgs) {
            mLastUsedDeviceId = getDeviceIdOfNewGesture(args).value_or(mLastUsedDeviceId);
        }
    } // release lock

    // Flush queued events out to the listener.
@@ -883,6 +912,11 @@ void InputReader::sysfsNodeChanged(const std::string& sysfsNodePath) {
    mEventHub->sysfsNodeChanged(sysfsNodePath);
}

DeviceId InputReader::getLastUsedInputDeviceId() {
    std::scoped_lock _l(mLock);
    return mLastUsedDeviceId;
}

void InputReader::dump(std::string& dump) {
    std::scoped_lock _l(mLock);

+5 −0
Original line number Diff line number Diff line
@@ -118,6 +118,8 @@ public:

    void sysfsNodeChanged(const std::string& sysfsNodePath) override;

    DeviceId getLastUsedInputDeviceId() override;

protected:
    // These members are protected so they can be instrumented by test cases.
    virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId,
@@ -200,6 +202,9 @@ private:
    // records timestamp of the last key press on the physical keyboard
    nsecs_t mLastKeyDownTimestamp GUARDED_BY(mLock){0};

    // The input device that produced a new gesture most recently.
    DeviceId mLastUsedDeviceId GUARDED_BY(mLock){ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID};

    // low-level input event decoding and device management
    [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
            REQUIRES(mLock);
+77 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <JoystickInputMapper.h>
#include <KeyboardInputMapper.h>
#include <MultiTouchInputMapper.h>
#include <NotifyArgsBuilders.h>
#include <PeripheralController.h>
#include <SensorInputMapper.h>
#include <SingleTouchInputMapper.h>
@@ -1184,6 +1185,82 @@ TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) {
    mFakeListener->assertNotifyCaptureWasNotCalled();
}

TEST_F(InputReaderTest, GetLastUsedInputDeviceId) {
    constexpr int32_t FIRST_DEVICE_ID = END_RESERVED_ID + 1000;
    constexpr int32_t SECOND_DEVICE_ID = FIRST_DEVICE_ID + 1;
    FakeInputMapper& firstMapper =
            addDeviceWithFakeInputMapper(FIRST_DEVICE_ID, FIRST_DEVICE_ID, "first",
                                         InputDeviceClass::KEYBOARD, AINPUT_SOURCE_KEYBOARD,
                                         /*configuration=*/nullptr);
    FakeInputMapper& secondMapper =
            addDeviceWithFakeInputMapper(SECOND_DEVICE_ID, SECOND_DEVICE_ID, "second",
                                         InputDeviceClass::TOUCH_MT, AINPUT_SOURCE_STYLUS,
                                         /*configuration=*/nullptr);

    ASSERT_EQ(ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID, mReader->getLastUsedInputDeviceId());

    // Start a new key gesture from the first device
    firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
                                          .deviceId(FIRST_DEVICE_ID)
                                          .build()});
    mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0);
    mReader->loopOnce();
    ASSERT_EQ(firstMapper.getDeviceId(), mReader->getLastUsedInputDeviceId());

    // Start a new touch gesture from the second device
    secondMapper.setProcessResult(
            {MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_STYLUS)
                     .deviceId(SECOND_DEVICE_ID)
                     .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER))
                     .build()});
    mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0);
    mReader->loopOnce();
    ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId());

    // Releasing the key is not a new gesture, so it does not update the last used device
    firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD)
                                          .deviceId(FIRST_DEVICE_ID)
                                          .build()});
    mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0);
    mReader->loopOnce();
    ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId());

    // But pressing a new key does start a new gesture
    firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
                                          .deviceId(FIRST_DEVICE_ID)
                                          .build()});
    mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0);
    mReader->loopOnce();
    ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId());

    // Moving or ending a touch gesture does not update the last used device
    secondMapper.setProcessResult(
            {MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
                     .deviceId(SECOND_DEVICE_ID)
                     .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS))
                     .build()});
    mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0);
    mReader->loopOnce();
    ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId());
    secondMapper.setProcessResult({MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS)
                                           .deviceId(SECOND_DEVICE_ID)
                                           .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS))
                                           .build()});
    mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0);
    mReader->loopOnce();
    ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId());

    // Starting a new hover gesture updates the last used device
    secondMapper.setProcessResult(
            {MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
                     .deviceId(SECOND_DEVICE_ID)
                     .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS))
                     .build()});
    mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0);
    mReader->loopOnce();
    ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId());
}

class FakeVibratorInputMapper : public FakeInputMapper {
public:
    FakeVibratorInputMapper(InputDeviceContext& deviceContext,
+2 −0
Original line number Diff line number Diff line
@@ -169,6 +169,8 @@ public:
        reader->sysfsNodeChanged(sysfsNodePath);
    }

    DeviceId getLastUsedInputDeviceId() override { return reader->getLastUsedInputDeviceId(); }

private:
    std::unique_ptr<InputReaderInterface> reader;
};