Merge "Add TouchIntegrationTest tests" into rvc-dev
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 3ae8b56..618aefc 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1748,7 +1748,7 @@
 
     virtual void SetUp() override {
         mFakePolicy = new FakeInputReaderPolicy();
-        mTestListener = new TestInputListener();
+        mTestListener = new TestInputListener(50ms);
 
         mReader = new InputReader(std::make_shared<EventHub>(), mFakePolicy, mTestListener);
         ASSERT_EQ(mReader->start(), OK);
@@ -1847,6 +1847,135 @@
     ASSERT_LE(prevTimestamp, keyArgs.eventTime);
 }
 
+// --- TouchProcessTest ---
+class TouchIntegrationTest : public InputReaderIntegrationTest {
+protected:
+    static const int32_t FIRST_SLOT = 0;
+    static const int32_t SECOND_SLOT = 1;
+    static const int32_t FIRST_TRACKING_ID = 0;
+    static const int32_t SECOND_TRACKING_ID = 1;
+    const std::string UNIQUE_ID = "local:0";
+
+    virtual void SetUp() override {
+        InputReaderIntegrationTest::SetUp();
+        // At least add an internal display.
+        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                     DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
+                                     ViewportType::VIEWPORT_INTERNAL);
+
+        mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    }
+
+    void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+                                      int32_t orientation, const std::string& uniqueId,
+                                      std::optional<uint8_t> physicalPort,
+                                      ViewportType viewportType) {
+        mFakePolicy->addDisplayViewport(displayId, width, height, orientation, uniqueId,
+                                        physicalPort, viewportType);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    }
+
+    std::unique_ptr<UinputTouchScreen> mDevice;
+};
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendDown(centerPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+    // ACTION_MOVE
+    mDevice->sendMove(centerPoint + Point(1, 1));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+    // ACTION_UP
+    mDevice->sendUp();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+}
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendDown(centerPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+    // ACTION_POINTER_DOWN (Second slot)
+    const Point secondPoint = centerPoint + Point(100, 100);
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendTrackingId(SECOND_TRACKING_ID);
+    mDevice->sendDown(secondPoint + Point(1, 1));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              args.action);
+
+    // ACTION_MOVE (Second slot)
+    mDevice->sendMove(secondPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+    // ACTION_POINTER_UP (Second slot)
+    mDevice->sendUp();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              args.action);
+
+    // ACTION_UP
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendUp();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+}
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendDown(centerPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+    // ACTION_POINTER_DOWN (Second slot)
+    const Point secondPoint = centerPoint + Point(100, 100);
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendTrackingId(SECOND_TRACKING_ID);
+    mDevice->sendDown(secondPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              args.action);
+
+    // ACTION_MOVE (Second slot)
+    mDevice->sendMove(secondPoint + Point(1, 1));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+    // Send MT_TOOL_PALM, which indicates that the touch IC has determined this to be a grip event.
+    // Expect to receive ACTION_CANCEL, to abort the entire gesture.
+    mDevice->sendToolType(MT_TOOL_PALM);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, args.action);
+
+    // ACTION_POINTER_UP (Second slot)
+    mDevice->sendUp();
+
+    // ACTION_UP
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendUp();
+
+    // Expect no event received after abort the entire gesture.
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+}
+
 // --- InputDeviceTest ---
 class InputDeviceTest : public testing::Test {
 protected:
@@ -7032,5 +7161,4 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
 }
-
 } // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 06b05ac..86ff3b1 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -19,20 +19,11 @@
 
 #include "TestInputListener.h"
 
-namespace {
-
-using std::chrono_literals::operator""ms;
-
-// Timeout for waiting for an expected event
-static constexpr std::chrono::duration WAIT_TIMEOUT = 5ms;
-
-} // namespace
-
 namespace android {
 
 // --- TestInputListener ---
 
-TestInputListener::TestInputListener() { }
+TestInputListener::TestInputListener(const std::chrono::milliseconds timeout) : mTimeout(timeout) {}
 
 TestInputListener::~TestInputListener() { }
 
@@ -95,9 +86,9 @@
 
     std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
     if (queue.empty()) {
-        const bool eventReceived =
-                mCondition.wait_for(lock, WAIT_TIMEOUT,
-                                    [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+        const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) {
+            return !queue.empty();
+        });
         if (!eventReceived) {
             FAIL() << "Timed out waiting for event: " << message.c_str();
         }
@@ -114,7 +105,7 @@
     base::ScopedLockAssertion assumeLocked(mLock);
 
     std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
-    const bool eventReceived = mCondition.wait_for(lock, WAIT_TIMEOUT, [&queue]() REQUIRES(mLock) {
+    const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) {
         return !queue.empty();
     });
     if (eventReceived) {
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 945e2ea..4262f5a 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -21,6 +21,8 @@
 #include <gtest/gtest.h>
 #include "InputListener.h"
 
+using std::chrono_literals::operator""ms;
+
 namespace android {
 
 // --- TestInputListener ---
@@ -30,7 +32,7 @@
     virtual ~TestInputListener();
 
 public:
-    TestInputListener();
+    TestInputListener(const std::chrono::milliseconds timeout = 5ms);
 
     void assertNotifyConfigurationChangedWasCalled(
             NotifyConfigurationChangedArgs* outEventArgs = nullptr);
@@ -73,6 +75,7 @@
 
     std::mutex mLock;
     std::condition_variable mCondition;
+    const std::chrono::milliseconds mTimeout;
 
     std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
                std::vector<NotifyDeviceResetArgs>,          //
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 2775d21..99480b7 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -119,7 +119,7 @@
     EXPECT_NO_FATAL_FAILURE(releaseKey(key));
 }
 
-// --- UinputHomeKey---
+// --- UinputHomeKey ---
 
 UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {}
 
@@ -127,4 +127,71 @@
     EXPECT_NO_FATAL_FAILURE(pressAndReleaseKey(KEY_HOME));
 }
 
+// --- UinputTouchScreen ---
+UinputTouchScreen::UinputTouchScreen(const Rect* size)
+      : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {}
+
+void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
+    // Setup the touch screen device
+    ioctl(fd, UI_SET_EVBIT, EV_KEY);
+    ioctl(fd, UI_SET_EVBIT, EV_REL);
+    ioctl(fd, UI_SET_EVBIT, EV_ABS);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
+    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_PROPBIT, INPUT_PROP_DIRECT);
+    ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+
+    device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
+    device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
+    device->absmin[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MIN;
+    device->absmax[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MAX;
+    device->absmin[ABS_MT_POSITION_X] = mSize.left;
+    device->absmax[ABS_MT_POSITION_X] = mSize.right - 1;
+    device->absmin[ABS_MT_POSITION_Y] = mSize.top;
+    device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1;
+    device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN;
+    device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
+}
+
+void UinputTouchScreen::sendSlot(int32_t slot) {
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_SLOT, slot));
+}
+
+void UinputTouchScreen::sendTrackingId(int32_t trackingId) {
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TRACKING_ID, trackingId));
+}
+
+void UinputTouchScreen::sendDown(const Point& point) {
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 1));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputTouchScreen::sendMove(const Point& point) {
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputTouchScreen::sendUp() {
+    sendTrackingId(0xffffffff);
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 0));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputTouchScreen::sendToolType(int32_t toolType) {
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TOOL_TYPE, toolType));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+// Get the center x, y base on the range definition.
+const Point UinputTouchScreen::getCenterPoint() {
+    return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2);
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index 57d9011..ec3cd9f 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -22,6 +22,8 @@
 #include <inttypes.h>
 #include <linux/uinput.h>
 #include <log/log.h>
+#include <ui/Point.h>
+#include <ui/Rect.h>
 
 #include <memory>
 
@@ -106,6 +108,40 @@
     UinputHomeKey();
 };
 
+// --- UinputTouchScreen ---
+// A touch screen device with specific size.
+class UinputTouchScreen : public UinputDevice {
+public:
+    static constexpr const char* DEVICE_NAME = "Test Touch Screen";
+    static const int32_t RAW_TOUCH_MIN = 0;
+    static const int32_t RAW_TOUCH_MAX = 31;
+    static const int32_t RAW_ID_MIN = 0;
+    static const int32_t RAW_ID_MAX = 9;
+    static const int32_t RAW_SLOT_MIN = 0;
+    static const int32_t RAW_SLOT_MAX = 9;
+    static const int32_t RAW_PRESSURE_MIN = 0;
+    static const int32_t RAW_PRESSURE_MAX = 255;
+
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+    void sendSlot(int32_t slot);
+    void sendTrackingId(int32_t trackingId);
+    void sendDown(const Point& point);
+    void sendMove(const Point& point);
+    void sendUp();
+    void sendToolType(int32_t toolType);
+
+    const Point getCenterPoint();
+
+protected:
+    UinputTouchScreen(const Rect* size);
+
+private:
+    void configureDevice(int fd, uinput_user_dev* device) override;
+    const Rect mSize;
+};
+
 } // namespace android
 
 #endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H