Allow touch if some pointers are MT_TOOL_PALM

We would cancel all touch when we recevied MT_TOOL_PALM because it
becomes accidental touch, and that would break user gesture when
another non-palm pointer is still active.

- Send ACTION_POINTER_UP with AKEY_EVENT_FLAG_CANCELED when recevie
  MT_TOOL_PALM if at least 2 pointers are active.
- Send ACTION_CANEL when receive MT_TOOL_PALM if only one pointer.
- Add test caseis for touch cancel scenarios, to test it would keep
  dispatching non-palm event.

Test: atest inputflinger_tests
Bug: 137221719
Change-Id: Ife35a692c71e45159bfcc771317dff47fa764dda
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index fae7e64..b2c16d0 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -45,16 +45,24 @@
 static const nsecs_t ARBITRARY_TIME = 1234;
 
 // Arbitrary display properties.
-static const int32_t DISPLAY_ID = 0;
-static const int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
-static const int32_t DISPLAY_WIDTH = 480;
-static const int32_t DISPLAY_HEIGHT = 800;
-static const int32_t VIRTUAL_DISPLAY_ID = 1;
-static const int32_t VIRTUAL_DISPLAY_WIDTH = 400;
-static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
+static constexpr int32_t DISPLAY_ID = 0;
+static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
+static constexpr int32_t DISPLAY_WIDTH = 480;
+static constexpr int32_t DISPLAY_HEIGHT = 800;
+static constexpr int32_t VIRTUAL_DISPLAY_ID = 1;
+static constexpr int32_t VIRTUAL_DISPLAY_WIDTH = 400;
+static constexpr int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
 static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
 static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
 
+static constexpr int32_t FIRST_SLOT = 0;
+static constexpr int32_t SECOND_SLOT = 1;
+static constexpr int32_t THIRD_SLOT = 2;
+static constexpr int32_t INVALID_TRACKING_ID = -1;
+static constexpr int32_t FIRST_TRACKING_ID = 0;
+static constexpr int32_t SECOND_TRACKING_ID = 1;
+static constexpr int32_t THIRD_TRACKING_ID = 2;
+
 // Error tolerance for floating point assertions.
 static const float EPSILON = 0.001f;
 
@@ -1880,10 +1888,6 @@
 // --- 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 {
@@ -1954,9 +1958,9 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
     // ACTION_POINTER_UP (Second slot)
-    mDevice->sendUp();
+    mDevice->sendPointerUp();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
               args.action);
 
     // ACTION_UP
@@ -1971,11 +1975,13 @@
     const Point centerPoint = mDevice->getCenterPoint();
 
     // ACTION_DOWN
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
     mDevice->sendDown(centerPoint);
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
 
-    // ACTION_POINTER_DOWN (Second slot)
+    // ACTION_POINTER_DOWN (second slot)
     const Point secondPoint = centerPoint + Point(100, 100);
     mDevice->sendSlot(SECOND_SLOT);
     mDevice->sendTrackingId(SECOND_TRACKING_ID);
@@ -1984,26 +1990,31 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
               args.action);
 
-    // ACTION_MOVE (Second slot)
+    // 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.
+    // Send MT_TOOL_PALM (second slot), which indicates that the touch IC has determined this to be
+    // a palm event.
+    // Expect to receive the ACTION_POINTER_UP with cancel flag.
     mDevice->sendToolType(MT_TOOL_PALM);
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, args.action);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              args.action);
+    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, args.flags);
 
-    // ACTION_POINTER_UP (Second slot)
-    mDevice->sendUp();
+    // Send up to second slot, expect first slot send moving.
+    mDevice->sendPointerUp();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
-    // ACTION_UP
+    // Send ACTION_UP (first slot)
     mDevice->sendSlot(FIRST_SLOT);
     mDevice->sendUp();
 
-    // Expect no event received after abort the entire gesture.
-    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 }
 
 // --- InputDeviceTest ---
@@ -7064,10 +7075,10 @@
 }
 
 /**
- * Test touch should be canceled when received the MT_TOOL_PALM event, and the following MOVE and
- * UP events should be ignored.
+ * Test single touch should be canceled when received the MT_TOOL_PALM event, and the following
+ * MOVE and UP events should be ignored.
  */
-TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) {
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_SinglePointer) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
@@ -7077,7 +7088,7 @@
 
     // default tool type is finger
     constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
-    processId(mapper, 1);
+    processId(mapper, FIRST_TRACKING_ID);
     processPosition(mapper, x1, y1);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7091,19 +7102,19 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
 
     // Ignore the following MOVE and UP events if had detect a palm event.
-    processId(mapper, 1);
+    processId(mapper, FIRST_TRACKING_ID);
     processPosition(mapper, x2, y2);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 
     // finger up.
-    processId(mapper, -1);
+    processId(mapper, INVALID_TRACKING_ID);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 
     // new finger down
+    processId(mapper, FIRST_TRACKING_ID);
     processToolType(mapper, MT_TOOL_FINGER);
-    processId(mapper, 1);
     processPosition(mapper, x3, y3);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7112,11 +7123,10 @@
 }
 
 /**
- * Test multi-touch should be canceled when received the MT_TOOL_PALM event from some finger,
- * and could be allowed again after all non-MT_TOOL_PALM is release and the new point is
- * MT_TOOL_FINGER.
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event from some finger,
+ * and the rest active fingers could still be allowed to receive the events
  */
-TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType2) {
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_TwoPointers) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
@@ -7125,8 +7135,8 @@
     NotifyMotionArgs motionArgs;
 
     // default tool type is finger
-    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
-    processId(mapper, 1);
+    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220;
+    processId(mapper, FIRST_TRACKING_ID);
     processPosition(mapper, x1, y1);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7134,51 +7144,232 @@
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
 
     // Second finger down.
-    processSlot(mapper, 1);
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
     processPosition(mapper, x2, y2);
-    processId(mapper, 2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
+
+    // If the tool type of the first finger changes to MT_TOOL_PALM,
+    // we expect to receive ACTION_POINTER_UP with cancel flag.
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, FIRST_TRACKING_ID);
+    processToolType(mapper, MT_TOOL_PALM);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+    // The following MOVE events of second finger should be processed.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2 + 1, y2 + 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // First finger up. It used to be in palm mode, and we already generated ACTION_POINTER_UP for
+    // it. Second finger receive move.
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // Second finger keeps moving.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2 + 2, y2 + 2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // Second finger up.
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+}
+
+/**
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event, if only 1 finger
+ * is active, it should send CANCEL after receiving the MT_TOOL_PALM event.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_ShouldCancelWhenAllTouchIsPalm) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
+    // First finger down.
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1, y1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // Second finger down.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2, y2);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
               motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
 
-    // If the tool type of the first pointer changes to MT_TOOL_PALM,
-    // the entire gesture should be aborted, so we expect to receive ACTION_CANCEL.
-    processSlot(mapper, 0);
-    processId(mapper, 1);
+    // If the tool type of the first finger changes to MT_TOOL_PALM,
+    // we expect to receive ACTION_POINTER_UP with cancel flag.
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, FIRST_TRACKING_ID);
+    processToolType(mapper, MT_TOOL_PALM);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+    // Second finger keeps moving.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2 + 1, y2 + 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // second finger becomes palm, receive cancel due to only 1 finger is active.
+    processId(mapper, SECOND_TRACKING_ID);
     processToolType(mapper, MT_TOOL_PALM);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
 
-    // Ignore the following MOVE and UP events if had detect a palm event.
-    processSlot(mapper, 1);
-    processId(mapper, 2);
+    // third finger down.
+    processSlot(mapper, THIRD_SLOT);
+    processId(mapper, THIRD_TRACKING_ID);
+    processToolType(mapper, MT_TOOL_FINGER);
     processPosition(mapper, x3, y3);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
-    // second finger up.
-    processId(mapper, -1);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
-    // first finger move, but still in palm
-    processSlot(mapper, 0);
-    processId(mapper, 1);
-    processPosition(mapper, x1 - 1, y1 - 1);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
-    // second finger down, expect as new finger down.
-    processSlot(mapper, 1);
-    processId(mapper, 2);
-    processPosition(mapper, x2, y2);
-    processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // third finger move
+    processId(mapper, THIRD_TRACKING_ID);
+    processPosition(mapper, x3 + 1, y3 + 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // first finger up, third finger receive move.
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // second finger up, third finger receive move.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // third finger up.
+    processSlot(mapper, THIRD_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+}
+
+/**
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event from some finger,
+ * and the active finger could still be allowed to receive the events
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPointer) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    // default tool type is finger
+    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220;
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1, y1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // Second finger down.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2, y2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // If the tool type of the second finger changes to MT_TOOL_PALM,
+    // we expect to receive ACTION_POINTER_UP with cancel flag.
+    processId(mapper, SECOND_TRACKING_ID);
+    processToolType(mapper, MT_TOOL_PALM);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+    // The following MOVE event should be processed.
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1 + 1, y1 + 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // second finger up.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // first finger keep moving
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1 + 2, y1 + 2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // first finger up.
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
 }
 
 // --- MultiTouchInputMapperTest_ExternalDevice ---