xref: /aosp_15_r20/frameworks/native/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "../UnwantedInteractionBlocker.h"
18 #include <android-base/silent_death_test.h>
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
21 #include <linux/input.h>
22 #include <thread>
23 #include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
24 
25 #include "TestEventMatchers.h"
26 #include "TestInputListener.h"
27 
28 using ::testing::AllOf;
29 
30 namespace android {
31 
32 constexpr int32_t DEVICE_ID = 3;
33 constexpr int32_t X_RESOLUTION = 11;
34 constexpr int32_t Y_RESOLUTION = 11;
35 constexpr int32_t MAJOR_RESOLUTION = 1;
36 
37 const nsecs_t RESAMPLE_PERIOD = ::ui::kResamplePeriod.InNanoseconds();
38 
39 constexpr int POINTER_0_DOWN =
40         AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
41 constexpr int POINTER_1_DOWN =
42         AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
43 constexpr int POINTER_2_DOWN =
44         AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
45 constexpr int POINTER_0_UP =
46         AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
47 constexpr int POINTER_1_UP =
48         AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
49 constexpr int POINTER_2_UP =
50         AMOTION_EVENT_ACTION_POINTER_UP | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
51 constexpr int DOWN = AMOTION_EVENT_ACTION_DOWN;
52 constexpr int MOVE = AMOTION_EVENT_ACTION_MOVE;
53 constexpr int UP = AMOTION_EVENT_ACTION_UP;
54 constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL;
55 
56 constexpr int32_t FLAG_CANCELED = AMOTION_EVENT_FLAG_CANCELED;
57 
toNs(std::chrono::nanoseconds duration)58 static nsecs_t toNs(std::chrono::nanoseconds duration) {
59     return duration.count();
60 }
61 
62 struct PointerData {
63     float x;
64     float y;
65     float major;
66 };
67 
generateMotionArgs(nsecs_t downTime,nsecs_t eventTime,int32_t action,const std::vector<PointerData> & points)68 static NotifyMotionArgs generateMotionArgs(nsecs_t downTime, nsecs_t eventTime, int32_t action,
69                                            const std::vector<PointerData>& points) {
70     size_t pointerCount = points.size();
71     if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) {
72         EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer";
73     }
74 
75     PointerProperties pointerProperties[pointerCount];
76     PointerCoords pointerCoords[pointerCount];
77 
78     for (size_t i = 0; i < pointerCount; i++) {
79         pointerProperties[i].clear();
80         pointerProperties[i].id = i;
81         pointerProperties[i].toolType = ToolType::FINGER;
82 
83         pointerCoords[i].clear();
84         pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x);
85         pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y);
86         pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, points[i].major);
87     }
88 
89     // Define a valid motion event.
90     NotifyMotionArgs args(/*id=*/0, eventTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
91                           ui::LogicalDisplayId::DEFAULT, POLICY_FLAG_PASS_TO_USER, action,
92                           /*actionButton=*/0,
93                           /*flags=*/0, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
94                           AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
95                           pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0,
96                           AMOTION_EVENT_INVALID_CURSOR_POSITION,
97                           AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /*videoFrames=*/{});
98 
99     return args;
100 }
101 
generateTestDeviceInfo()102 static InputDeviceInfo generateTestDeviceInfo() {
103     InputDeviceIdentifier identifier;
104 
105     auto info = InputDeviceInfo();
106     info.initialize(DEVICE_ID, /*generation=*/1, /*controllerNumber=*/1, identifier, "alias",
107                     /*isExternal=*/false, /*hasMic=*/false, ui::LogicalDisplayId::INVALID);
108     info.addSource(AINPUT_SOURCE_TOUCHSCREEN);
109     info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat=*/0,
110                         /*fuzz=*/0, X_RESOLUTION);
111     info.addMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN, 0, 2559, /*flat=*/0,
112                         /*fuzz=*/0, Y_RESOLUTION);
113     info.addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN, 0, 255,
114                         /*flat=*/0, /*fuzz=*/0, MAJOR_RESOLUTION);
115 
116     return info;
117 }
118 
generatePalmFilterDeviceInfo()119 static AndroidPalmFilterDeviceInfo generatePalmFilterDeviceInfo() {
120     InputDeviceInfo androidInfo = generateTestDeviceInfo();
121     std::optional<AndroidPalmFilterDeviceInfo> info = createPalmFilterDeviceInfo(androidInfo);
122     if (!info) {
123         ADD_FAILURE() << "Could not convert android device info to ::ui version";
124         return {};
125     }
126     return *info;
127 }
128 
TEST(DeviceInfoConversionTest,TabletDeviceTest)129 TEST(DeviceInfoConversionTest, TabletDeviceTest) {
130     AndroidPalmFilterDeviceInfo info = generatePalmFilterDeviceInfo();
131     ASSERT_EQ(X_RESOLUTION, info.x_res);
132     ASSERT_EQ(Y_RESOLUTION, info.y_res);
133     ASSERT_EQ(MAJOR_RESOLUTION, info.touch_major_res);
134     ASSERT_EQ(1599, info.max_x);
135     ASSERT_EQ(2559, info.max_y);
136 }
137 
assertArgs(const NotifyMotionArgs & args,int32_t action,const std::vector<std::pair<int32_t,PointerData>> & pointers)138 static void assertArgs(const NotifyMotionArgs& args, int32_t action,
139                        const std::vector<std::pair<int32_t /*pointerId*/, PointerData>>& pointers) {
140     ASSERT_EQ(action, args.action)
141             << "Expected " << MotionEvent::actionToString(action) << " but got " << args.action;
142     ASSERT_EQ(pointers.size(), args.getPointerCount());
143     for (size_t i = 0; i < args.getPointerCount(); i++) {
144         const auto& [pointerId, pointerData] = pointers[i];
145         ASSERT_EQ(pointerId, args.pointerProperties[i].id);
146         ASSERT_EQ(pointerData.x, args.pointerCoords[i].getX());
147         ASSERT_EQ(pointerData.y, args.pointerCoords[i].getY());
148         ASSERT_EQ(pointerData.major,
149                   args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR));
150     }
151 }
152 
TEST(RemovePointerIdsTest,RemoveOnePointer)153 TEST(RemovePointerIdsTest, RemoveOnePointer) {
154     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0,
155                                                AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}});
156 
157     NotifyMotionArgs pointer1Only = removePointerIds(args, {0});
158     assertArgs(pointer1Only, AMOTION_EVENT_ACTION_MOVE, {{1, {4, 5, 6}}});
159 
160     NotifyMotionArgs pointer0Only = removePointerIds(args, {1});
161     assertArgs(pointer0Only, AMOTION_EVENT_ACTION_MOVE, {{0, {1, 2, 3}}});
162 }
163 
164 /**
165  * Remove 2 out of 3 pointers during a MOVE event.
166  */
TEST(RemovePointerIdsTest,RemoveTwoPointers)167 TEST(RemovePointerIdsTest, RemoveTwoPointers) {
168     NotifyMotionArgs args =
169             generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, AMOTION_EVENT_ACTION_MOVE,
170                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
171 
172     NotifyMotionArgs pointer1Only = removePointerIds(args, {0, 2});
173     assertArgs(pointer1Only, AMOTION_EVENT_ACTION_MOVE, {{1, {4, 5, 6}}});
174 }
175 
176 /**
177  * Remove an active pointer during a POINTER_DOWN event, and also remove a non-active
178  * pointer during a POINTER_DOWN event.
179  */
TEST(RemovePointerIdsTest,ActionPointerDown)180 TEST(RemovePointerIdsTest, ActionPointerDown) {
181     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
182                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
183 
184     NotifyMotionArgs pointers0And2 = removePointerIds(args, {1});
185     assertArgs(pointers0And2, ACTION_UNKNOWN, {{0, {1, 2, 3}}, {2, {7, 8, 9}}});
186 
187     NotifyMotionArgs pointers1And2 = removePointerIds(args, {0});
188     assertArgs(pointers1And2, POINTER_0_DOWN, {{1, {4, 5, 6}}, {2, {7, 8, 9}}});
189 }
190 
191 /**
192  * Remove all pointers during a MOVE event.
193  */
TEST(RemovePointerIdsTest,RemoveAllPointersDuringMove)194 TEST(RemovePointerIdsTest, RemoveAllPointersDuringMove) {
195     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0,
196                                                AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}});
197 
198     NotifyMotionArgs noPointers = removePointerIds(args, {0, 1});
199     ASSERT_EQ(0u, noPointers.getPointerCount());
200 }
201 
202 /**
203  * If we have ACTION_POINTER_DOWN, and we remove all pointers except for the active pointer,
204  * then we should just have ACTION_DOWN. Likewise, a POINTER_UP event should become an UP event.
205  */
TEST(RemovePointerIdsTest,PointerDownBecomesDown)206 TEST(RemovePointerIdsTest, PointerDownBecomesDown) {
207     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
208                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
209 
210     NotifyMotionArgs pointer1 = removePointerIds(args, {0, 2});
211     assertArgs(pointer1, DOWN, {{1, {4, 5, 6}}});
212 
213     args.action = POINTER_1_UP;
214     pointer1 = removePointerIds(args, {0, 2});
215     assertArgs(pointer1, UP, {{1, {4, 5, 6}}});
216 }
217 
218 /**
219  * If a pointer that is now going down is canceled, then we can just drop the POINTER_DOWN event.
220  */
TEST(CancelSuppressedPointersTest,CanceledPointerDownIsDropped)221 TEST(CancelSuppressedPointersTest, CanceledPointerDownIsDropped) {
222     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
223                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
224     std::vector<NotifyMotionArgs> result =
225             cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
226                                      /*newSuppressedPointerIds=*/{1});
227     ASSERT_TRUE(result.empty());
228 }
229 
230 /**
231  * If a pointer is already suppressed, the POINTER_UP event for this pointer should be dropped
232  */
TEST(CancelSuppressedPointersTest,SuppressedPointerUpIsDropped)233 TEST(CancelSuppressedPointersTest, SuppressedPointerUpIsDropped) {
234     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
235                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
236     std::vector<NotifyMotionArgs> result =
237             cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
238                                      /*newSuppressedPointerIds=*/{1});
239     ASSERT_TRUE(result.empty());
240 }
241 
242 /**
243  * If a pointer is already suppressed, it should be removed from a MOVE event.
244  */
TEST(CancelSuppressedPointersTest,SuppressedPointerIsRemovedDuringMove)245 TEST(CancelSuppressedPointersTest, SuppressedPointerIsRemovedDuringMove) {
246     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE,
247                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
248     std::vector<NotifyMotionArgs> result =
249             cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
250                                      /*newSuppressedPointerIds=*/{1});
251     ASSERT_EQ(1u, result.size());
252     assertArgs(result[0], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}});
253 }
254 
255 /**
256  * If a pointer just got canceled during a MOVE event, we should see two events:
257  * 1) ACTION_POINTER_UP with FLAG_CANCELED so that this pointer is lifted
258  * 2) A MOVE event without this pointer
259  */
TEST(CancelSuppressedPointersTest,NewlySuppressedPointerIsCanceled)260 TEST(CancelSuppressedPointersTest, NewlySuppressedPointerIsCanceled) {
261     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE,
262                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
263     std::vector<NotifyMotionArgs> result =
264             cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
265                                      /*newSuppressedPointerIds=*/{1});
266     ASSERT_EQ(2u, result.size());
267     assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
268     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
269     assertArgs(result[1], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}});
270 }
271 
272 /**
273  * If we have a single pointer that gets canceled during a MOVE, the entire gesture
274  * should be canceled with ACTION_CANCEL.
275  */
TEST(CancelSuppressedPointersTest,SingleSuppressedPointerIsCanceled)276 TEST(CancelSuppressedPointersTest, SingleSuppressedPointerIsCanceled) {
277     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE, {{1, 2, 3}});
278     std::vector<NotifyMotionArgs> result =
279             cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
280                                      /*newSuppressedPointerIds=*/{0});
281     ASSERT_EQ(1u, result.size());
282     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
283     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
284 }
285 
286 /**
287  * If one of 3 pointers gets canceled during a POINTER_UP event, we should proceed with POINTER_UP,
288  * but this event should also have FLAG_CANCELED to indicate that this pointer was unintentional.
289  */
TEST(CancelSuppressedPointersTest,SuppressedPointer1GoingUpIsCanceled)290 TEST(CancelSuppressedPointersTest, SuppressedPointer1GoingUpIsCanceled) {
291     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
292                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
293     std::vector<NotifyMotionArgs> result =
294             cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
295                                      /*newSuppressedPointerIds=*/{1});
296     ASSERT_EQ(1u, result.size());
297     assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
298     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
299 }
300 
301 /**
302  * Same test as above, but we change the pointer's index to 0 instead of 1. This helps detect
303  * errors with handling pointer index inside the action.
304  */
TEST(CancelSuppressedPointersTest,SuppressedPointer0GoingUpIsCanceled)305 TEST(CancelSuppressedPointersTest, SuppressedPointer0GoingUpIsCanceled) {
306     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_0_UP,
307                                                {{1, 2, 3}, {4, 5, 6}});
308     std::vector<NotifyMotionArgs> result =
309             cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
310                                      /*newSuppressedPointerIds=*/{0});
311     ASSERT_EQ(1u, result.size());
312     assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
313     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
314 }
315 
316 /**
317  * If two pointers are canceled simultaneously during MOVE, we should see a single ACTION_CANCEL
318  * event. This event would cancel the entire gesture.
319  */
TEST(CancelSuppressedPointersTest,TwoNewlySuppressedPointersAreBothCanceled)320 TEST(CancelSuppressedPointersTest, TwoNewlySuppressedPointersAreBothCanceled) {
321     NotifyMotionArgs args =
322             generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE, {{1, 2, 3}, {4, 5, 6}});
323     std::vector<NotifyMotionArgs> result =
324             cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
325                                      /*newSuppressedPointerIds=*/{0, 1});
326     ASSERT_EQ(1u, result.size());
327     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
328     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
329 }
330 
331 /**
332  * Similar test to above. During a POINTER_UP event, both pointers are detected as 'palm' and
333  * therefore should be removed. In this case, we should send a single ACTION_CANCEL that
334  * would undo the entire gesture.
335  */
TEST(CancelSuppressedPointersTest,TwoPointersAreCanceledDuringPointerUp)336 TEST(CancelSuppressedPointersTest, TwoPointersAreCanceledDuringPointerUp) {
337     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
338                                                {{1, 2, 3}, {4, 5, 6}});
339     std::vector<NotifyMotionArgs> result =
340             cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
341                                      /*newSuppressedPointerIds=*/{0, 1});
342     ASSERT_EQ(1u, result.size());
343     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
344     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
345 }
346 
347 /**
348  * When all pointers have been removed from the touch stream, and we have a new POINTER_DOWN,
349  * this should become a regular DOWN event because it's the only pointer that will be valid now.
350  */
TEST(CancelSuppressedPointersTest,NewPointerDownBecomesDown)351 TEST(CancelSuppressedPointersTest, NewPointerDownBecomesDown) {
352     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_2_DOWN,
353                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
354     std::vector<NotifyMotionArgs> result =
355             cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{0, 1},
356                                      /*newSuppressedPointerIds=*/{0, 1});
357     ASSERT_EQ(1u, result.size());
358     assertArgs(result[0], DOWN, {{2, {7, 8, 9}}});
359     ASSERT_EQ(0, result[0].flags);
360 }
361 
362 /**
363  * Call 'getTouches' for a DOWN event and check that the resulting 'InProgressTouchEvdev'
364  * struct is populated as expected.
365  */
TEST(GetTouchesTest,ConvertDownEvent)366 TEST(GetTouchesTest, ConvertDownEvent) {
367     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
368     AndroidPalmFilterDeviceInfo deviceInfo = generatePalmFilterDeviceInfo();
369     SlotState slotState;
370     SlotState oldSlotState = slotState;
371     slotState.update(args);
372     std::vector<::ui::InProgressTouchEvdev> touches =
373             getTouches(args, deviceInfo, oldSlotState, slotState);
374     ASSERT_EQ(1u, touches.size());
375     ::ui::InProgressTouchEvdev expected;
376 
377     expected.major = 3;
378     expected.minor = 0;
379     expected.tool_type = MT_TOOL_FINGER;
380     expected.altered = true;
381     expected.was_cancelled = false;
382     expected.cancelled = false;
383     expected.delayed = false;
384     expected.was_delayed = false;
385     expected.held = false;
386     expected.was_held = false;
387     expected.was_touching = false;
388     expected.touching = true;
389     expected.x = 1;
390     expected.y = 2;
391     expected.tracking_id = 0;
392     std::optional<size_t> slot = slotState.getSlotForPointerId(0);
393     ASSERT_TRUE(slot);
394     expected.slot = *slot;
395     expected.pressure = 0;
396     expected.tool_code = BTN_TOOL_FINGER;
397     expected.reported_tool_type = ::ui::EventPointerType::kTouch;
398     expected.stylus_button = false;
399 
400     ASSERT_EQ(expected, touches[0]) << touches[0];
401 }
402 
403 // --- UnwantedInteractionBlockerTest ---
404 
405 class UnwantedInteractionBlockerTest : public testing::Test {
406 protected:
407     TestInputListener mTestListener;
408     std::unique_ptr<UnwantedInteractionBlockerInterface> mBlocker;
409 
SetUp()410     void SetUp() override {
411         mBlocker = std::make_unique<UnwantedInteractionBlocker>(mTestListener,
412                                                                 /*enablePalmRejection=*/true);
413     }
414 };
415 
416 /**
417  * Keys are not handled in 'UnwantedInteractionBlocker' and should be passed
418  * to next stage unmodified.
419  */
TEST_F(UnwantedInteractionBlockerTest,KeyIsPassedToNextListener)420 TEST_F(UnwantedInteractionBlockerTest, KeyIsPassedToNextListener) {
421     // Create a basic key event and send to blocker
422     NotifyKeyArgs args(/*sequenceNum=*/1, /*eventTime=*/2, /*readTime=*/21, /*deviceId=*/3,
423                        AINPUT_SOURCE_KEYBOARD, ui::LogicalDisplayId::DEFAULT, /*policyFlags=*/0,
424                        AKEY_EVENT_ACTION_DOWN, /*flags=*/4, AKEYCODE_HOME, /*scanCode=*/5,
425                        AMETA_NONE, /*downTime=*/6);
426 
427     mBlocker->notifyKey(args);
428     ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(testing::Eq(args)));
429 }
430 
431 /**
432  * Create a basic motion event. Since it's just a DOWN event, it should not
433  * be detected as palm and should be sent to the next listener stage
434  * unmodified.
435  */
TEST_F(UnwantedInteractionBlockerTest,DownEventIsPassedToNextListener)436 TEST_F(UnwantedInteractionBlockerTest, DownEventIsPassedToNextListener) {
437     NotifyMotionArgs motionArgs =
438             generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
439     mBlocker->notifyMotion(motionArgs);
440     ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(testing::Eq(motionArgs)));
441 }
442 
443 /**
444  * Create a basic switch event and send it to the UnwantedInteractionBlocker.
445  * Expect that the event is received by the next input stage, unmodified.
446  */
TEST_F(UnwantedInteractionBlockerTest,SwitchIsPassedToNextListener)447 TEST_F(UnwantedInteractionBlockerTest, SwitchIsPassedToNextListener) {
448     NotifySwitchArgs args(/*sequenceNum=*/1, /*eventTime=*/2, /*policyFlags=*/3,
449                           /*switchValues=*/4, /*switchMask=*/5);
450 
451     mBlocker->notifySwitch(args);
452     NotifySwitchArgs outArgs;
453     ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs));
454     ASSERT_EQ(args, outArgs);
455 }
456 
457 /**
458  * Create a basic device reset event and send it to UnwantedInteractionBlocker.
459  * Expect that the event is received by the next input stage, unmodified.
460  */
TEST_F(UnwantedInteractionBlockerTest,DeviceResetIsPassedToNextListener)461 TEST_F(UnwantedInteractionBlockerTest, DeviceResetIsPassedToNextListener) {
462     NotifyDeviceResetArgs args(/*sequenceNum=*/1, /*eventTime=*/2, DEVICE_ID);
463 
464     mBlocker->notifyDeviceReset(args);
465     NotifyDeviceResetArgs outArgs;
466     ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs));
467     ASSERT_EQ(args, outArgs);
468 }
469 
470 /**
471  * The state should be reset when device reset happens. That means, we can reset in the middle of a
472  * gesture, and start a new stream. There should be no crash. If the state wasn't reset correctly,
473  * a crash due to inconsistent event stream could have occurred.
474  */
TEST_F(UnwantedInteractionBlockerTest,NoCrashWhenResetHappens)475 TEST_F(UnwantedInteractionBlockerTest, NoCrashWhenResetHappens) {
476     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
477     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, DOWN, {{1, 2, 3}}));
478     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, MOVE, {{4, 5, 6}}));
479     mBlocker->notifyDeviceReset({/*sequenceNum=*/1, /*eventTime=*/3, DEVICE_ID});
480     // Start a new gesture with a DOWN event, even though the previous event stream was incomplete.
481     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/4, DOWN, {{7, 8, 9}}));
482 }
483 
TEST_F(UnwantedInteractionBlockerTest,NoCrashWhenStylusSourceWithFingerToolIsReceived)484 TEST_F(UnwantedInteractionBlockerTest, NoCrashWhenStylusSourceWithFingerToolIsReceived) {
485     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
486     NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, DOWN, {{1, 2, 3}});
487     args.pointerProperties[0].toolType = ToolType::FINGER;
488     args.source = AINPUT_SOURCE_STYLUS;
489     mBlocker->notifyMotion(args);
490 }
491 
492 /**
493  * If input devices have changed, but the important device info that's used by the
494  * UnwantedInteractionBlocker has not changed, there should not be a reset.
495  */
TEST_F(UnwantedInteractionBlockerTest,NoResetIfDeviceInfoChanges)496 TEST_F(UnwantedInteractionBlockerTest, NoResetIfDeviceInfoChanges) {
497     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
498     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, DOWN, {{1, 2, 3}}));
499     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, MOVE, {{4, 5, 6}}));
500 
501     // Now pretend the device changed, even though nothing is different for DEVICE_ID in practice.
502     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
503 
504     // The MOVE event continues the gesture that started before 'devices changed', so it should not
505     // cause a crash.
506     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/4, MOVE, {{7, 8, 9}}));
507 }
508 
509 /**
510  * Send a touch event, and then a stylus event. Make sure that both work.
511  */
TEST_F(UnwantedInteractionBlockerTest,StylusAfterTouchWorks)512 TEST_F(UnwantedInteractionBlockerTest, StylusAfterTouchWorks) {
513     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
514     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}}));
515     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{4, 5, 6}}));
516     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, UP, {{4, 5, 6}}));
517 
518     // Now touch down stylus
519     NotifyMotionArgs args;
520     args = generateMotionArgs(/*downTime=*/3, /*eventTime=*/3, DOWN, {{10, 20, 30}});
521     args.pointerProperties[0].toolType = ToolType::STYLUS;
522     args.source |= AINPUT_SOURCE_STYLUS;
523     mBlocker->notifyMotion(args);
524     args = generateMotionArgs(/*downTime=*/3, /*eventTime=*/4, MOVE, {{40, 50, 60}});
525     args.pointerProperties[0].toolType = ToolType::STYLUS;
526     args.source |= AINPUT_SOURCE_STYLUS;
527     mBlocker->notifyMotion(args);
528     args = generateMotionArgs(/*downTime=*/3, /*eventTime=*/5, UP, {{40, 50, 60}});
529     args.pointerProperties[0].toolType = ToolType::STYLUS;
530     args.source |= AINPUT_SOURCE_STYLUS;
531     mBlocker->notifyMotion(args);
532 }
533 
534 /**
535  * Call dump, and on another thread, try to send some motions. The blocker should
536  * not crash. On 2022 hardware, this test requires ~ 13K executions (about 20 seconds) to reproduce
537  * the original bug. This is meant to be run with "--gtest_repeat=100000 --gtest_break_on_failure"
538  * options
539  */
TEST_F(UnwantedInteractionBlockerTest,DumpCanBeAccessedOnAnotherThread)540 TEST_F(UnwantedInteractionBlockerTest, DumpCanBeAccessedOnAnotherThread) {
541     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
542     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}}));
543     std::thread dumpThread([this]() {
544         std::string dump;
545         mBlocker->dump(dump);
546     });
547     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{4, 5, 6}}));
548     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, UP, {{4, 5, 6}}));
549     dumpThread.join();
550 }
551 
552 /**
553  * Heuristic filter that's present in the palm rejection model blocks touches early if the size
554  * of the touch is large. This is an integration test that checks that this filter kicks in.
555  */
TEST_F(UnwantedInteractionBlockerTest,HeuristicFilterWorks)556 TEST_F(UnwantedInteractionBlockerTest, HeuristicFilterWorks) {
557     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
558     // Small touch down
559     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}}));
560     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
561 
562     // Large touch oval on the next move
563     mBlocker->notifyMotion(
564             generateMotionArgs(/*downTime=*/0, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}}));
565     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
566 
567     // Lift up the touch to force the model to decide on whether it's a palm
568     mBlocker->notifyMotion(
569             generateMotionArgs(/*downTime=*/0, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}}));
570     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(CANCEL));
571 }
572 
573 /**
574  * Send a stylus event that would have triggered the heuristic palm detector if it were a touch
575  * event. However, since it's a stylus event, it should propagate without being canceled through
576  * the blocker.
577  * This is similar to `HeuristicFilterWorks` test, but for stylus tool.
578  */
TEST_F(UnwantedInteractionBlockerTest,StylusIsNotBlocked)579 TEST_F(UnwantedInteractionBlockerTest, StylusIsNotBlocked) {
580     NotifyInputDevicesChangedArgs deviceChangedArgs = {/*id=*/0, {generateTestDeviceInfo()}};
581     deviceChangedArgs.inputDeviceInfos[0].addSource(AINPUT_SOURCE_STYLUS);
582     mBlocker->notifyInputDevicesChanged(deviceChangedArgs);
583     NotifyMotionArgs args1 = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
584     args1.pointerProperties[0].toolType = ToolType::STYLUS;
585     mBlocker->notifyMotion(args1);
586     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
587 
588     // Move the stylus, setting large TOUCH_MAJOR/TOUCH_MINOR dimensions
589     NotifyMotionArgs args2 =
590             generateMotionArgs(/*downTime=*/0, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
591     args2.pointerProperties[0].toolType = ToolType::STYLUS;
592     mBlocker->notifyMotion(args2);
593     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
594 
595     // Lift up the stylus. If it were a touch event, this would force the model to decide on whether
596     // it's a palm.
597     NotifyMotionArgs args3 =
598             generateMotionArgs(/*downTime=*/0, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
599     args3.pointerProperties[0].toolType = ToolType::STYLUS;
600     mBlocker->notifyMotion(args3);
601     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(UP));
602 }
603 
604 /**
605  * Send a mixed touch and stylus event.
606  * The touch event goes first, and is a palm. The stylus event goes down after.
607  * Stylus event should continue to work even after touch is detected as a palm.
608  */
TEST_F(UnwantedInteractionBlockerTest,TouchIsBlockedWhenMixedWithStylus)609 TEST_F(UnwantedInteractionBlockerTest, TouchIsBlockedWhenMixedWithStylus) {
610     NotifyInputDevicesChangedArgs deviceChangedArgs = {/*id=*/0, {generateTestDeviceInfo()}};
611     deviceChangedArgs.inputDeviceInfos[0].addSource(AINPUT_SOURCE_STYLUS);
612     mBlocker->notifyInputDevicesChanged(deviceChangedArgs);
613 
614     // Touch down
615     NotifyMotionArgs args1 = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
616     mBlocker->notifyMotion(args1);
617     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
618 
619     // Stylus pointer down
620     NotifyMotionArgs args2 = generateMotionArgs(/*downTime=*/0, RESAMPLE_PERIOD, POINTER_1_DOWN,
621                                                 {{1, 2, 3}, {10, 20, 30}});
622     args2.pointerProperties[1].toolType = ToolType::STYLUS;
623     mBlocker->notifyMotion(args2);
624     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(POINTER_1_DOWN));
625 
626     // Large touch oval on the next finger move
627     NotifyMotionArgs args3 = generateMotionArgs(/*downTime=*/0, 2 * RESAMPLE_PERIOD, MOVE,
628                                                 {{1, 2, 300}, {11, 21, 30}});
629     args3.pointerProperties[1].toolType = ToolType::STYLUS;
630     mBlocker->notifyMotion(args3);
631     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
632 
633     // Lift up the finger pointer. It should be canceled due to the heuristic filter.
634     NotifyMotionArgs args4 = generateMotionArgs(/*downTime=*/0, 3 * RESAMPLE_PERIOD, POINTER_0_UP,
635                                                 {{1, 2, 300}, {11, 21, 30}});
636     args4.pointerProperties[1].toolType = ToolType::STYLUS;
637     mBlocker->notifyMotion(args4);
638     mTestListener.assertNotifyMotionWasCalled(
639             AllOf(WithMotionAction(POINTER_0_UP), WithFlags(FLAG_CANCELED)));
640 
641     NotifyMotionArgs args5 =
642             generateMotionArgs(/*downTime=*/0, 4 * RESAMPLE_PERIOD, MOVE, {{12, 22, 30}});
643     args5.pointerProperties[0].id = args4.pointerProperties[1].id;
644     args5.pointerProperties[0].toolType = ToolType::STYLUS;
645     mBlocker->notifyMotion(args5);
646     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
647 
648     // Lift up the stylus pointer
649     NotifyMotionArgs args6 =
650             generateMotionArgs(/*downTime=*/0, 5 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
651     args6.pointerProperties[0].id = args4.pointerProperties[1].id;
652     args6.pointerProperties[0].toolType = ToolType::STYLUS;
653     mBlocker->notifyMotion(args6);
654     mTestListener.assertNotifyMotionWasCalled(WithMotionAction(UP));
655 }
656 
657 using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest;
658 
659 /**
660  * The state should be reset when device reset happens. If we receive an inconsistent event after
661  * the reset happens, crash should occur.
662  */
TEST_F(UnwantedInteractionBlockerTestDeathTest,InconsistentEventAfterResetCausesACrash)663 TEST_F(UnwantedInteractionBlockerTestDeathTest, InconsistentEventAfterResetCausesACrash) {
664     ScopedSilentDeath _silentDeath;
665     NotifyMotionArgs args;
666     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
667     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, DOWN, {{1, 2, 3}}));
668     mBlocker->notifyMotion(generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, MOVE, {{4, 5, 6}}));
669     NotifyDeviceResetArgs resetArgs(/*sequenceNum=*/1, /*eventTime=*/3, DEVICE_ID);
670     mBlocker->notifyDeviceReset(resetArgs);
671     // Sending MOVE without a DOWN -> should crash!
672     ASSERT_DEATH(
673             {
674                 mBlocker->notifyMotion(
675                         generateMotionArgs(/*downTime=*/0, /*eventTime=*/4, MOVE, {{7, 8, 9}}));
676             },
677             "Could not find slot");
678 }
679 
680 /**
681  * There should be a crash when an inconsistent event is received.
682  */
TEST_F(UnwantedInteractionBlockerTestDeathTest,WhenMoveWithoutDownCausesACrash)683 TEST_F(UnwantedInteractionBlockerTestDeathTest, WhenMoveWithoutDownCausesACrash) {
684     ScopedSilentDeath _silentDeath;
685     mBlocker->notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
686     ASSERT_DEATH(
687             {
688                 mBlocker->notifyMotion(
689                         generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{1, 2, 3}}));
690             },
691             "Could not find slot");
692 }
693 
694 class PalmRejectorTest : public testing::Test {
695 protected:
696     std::unique_ptr<PalmRejector> mPalmRejector;
697 
SetUp()698     void SetUp() override {
699         AndroidPalmFilterDeviceInfo info = generatePalmFilterDeviceInfo();
700         mPalmRejector = std::make_unique<PalmRejector>(info);
701     }
702 };
703 
704 using PalmRejectorTestDeathTest = PalmRejectorTest;
705 
TEST_F(PalmRejectorTestDeathTest,InconsistentEventCausesACrash)706 TEST_F(PalmRejectorTestDeathTest, InconsistentEventCausesACrash) {
707     ScopedSilentDeath _silentDeath;
708     constexpr nsecs_t downTime = 0;
709     NotifyMotionArgs args =
710             generateMotionArgs(downTime, /*eventTime=*/2, MOVE, {{1406.0, 650.0, 52.0}});
711     ASSERT_DEATH({ mPalmRejector->processMotion(args); }, "Could not find slot");
712 }
713 
714 /**
715  * Use PalmRejector with actual touchscreen data and real model.
716  * Two pointers that should both be classified as palms.
717  */
TEST_F(PalmRejectorTest,TwoPointersAreCanceled)718 TEST_F(PalmRejectorTest, TwoPointersAreCanceled) {
719     std::vector<NotifyMotionArgs> argsList;
720     const nsecs_t downTime = toNs(0ms);
721 
722     mPalmRejector->processMotion(
723             generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
724     mPalmRejector->processMotion(
725             generateMotionArgs(downTime, toNs(8ms), MOVE, {{1406.0, 650.0, 52.0}}));
726     mPalmRejector->processMotion(
727             generateMotionArgs(downTime, toNs(16ms), MOVE, {{1429.0, 672.0, 46.0}}));
728     mPalmRejector->processMotion(
729             generateMotionArgs(downTime, toNs(24ms), MOVE, {{1417.0, 685.0, 41.0}}));
730     mPalmRejector->processMotion(
731             generateMotionArgs(downTime, toNs(32ms), POINTER_1_DOWN,
732                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
733     mPalmRejector->processMotion(
734             generateMotionArgs(downTime, toNs(40ms), MOVE,
735                                {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}}));
736     mPalmRejector->processMotion(
737             generateMotionArgs(downTime, toNs(48ms), MOVE,
738                                {{1415.0, 719.0, 44.0}, {1060.0, 760.0, 11.0}}));
739     mPalmRejector->processMotion(
740             generateMotionArgs(downTime, toNs(56ms), MOVE,
741                                {{1421.0, 733.0, 42.0}, {1065.0, 769.0, 13.0}}));
742     mPalmRejector->processMotion(
743             generateMotionArgs(downTime, toNs(64ms), MOVE,
744                                {{1426.0, 742.0, 43.0}, {1068.0, 771.0, 13.0}}));
745     mPalmRejector->processMotion(
746             generateMotionArgs(downTime, toNs(72ms), MOVE,
747                                {{1430.0, 748.0, 45.0}, {1069.0, 772.0, 13.0}}));
748     argsList = mPalmRejector->processMotion(
749             generateMotionArgs(downTime, toNs(80ms), MOVE,
750                                {{1432.0, 750.0, 44.0}, {1069.0, 772.0, 13.0}}));
751     ASSERT_EQ(1u, argsList.size());
752     ASSERT_EQ(0 /* No FLAG_CANCELED */, argsList[0].flags);
753     argsList = mPalmRejector->processMotion(
754             generateMotionArgs(downTime, toNs(88ms), MOVE,
755                                {{1433.0, 751.0, 44.0}, {1070.0, 771.0, 13.0}}));
756     ASSERT_EQ(2u, argsList.size());
757     ASSERT_EQ(POINTER_0_UP, argsList[0].action);
758     ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
759     ASSERT_EQ(MOVE, argsList[1].action);
760     ASSERT_EQ(1u, argsList[1].getPointerCount());
761     ASSERT_EQ(0, argsList[1].flags);
762 
763     mPalmRejector->processMotion(
764             generateMotionArgs(downTime, toNs(96ms), MOVE,
765                                {{1433.0, 751.0, 42.0}, {1071.0, 770.0, 13.0}}));
766     mPalmRejector->processMotion(
767             generateMotionArgs(downTime, toNs(104ms), MOVE,
768                                {{1433.0, 751.0, 45.0}, {1072.0, 769.0, 13.0}}));
769     mPalmRejector->processMotion(
770             generateMotionArgs(downTime, toNs(112ms), MOVE,
771                                {{1433.0, 751.0, 43.0}, {1072.0, 768.0, 13.0}}));
772     argsList = mPalmRejector->processMotion(
773             generateMotionArgs(downTime, toNs(120ms), MOVE,
774                                {{1433.0, 751.0, 45.0}, {1072.0, 767.0, 13.0}}));
775     ASSERT_EQ(1u, argsList.size());
776     ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, argsList[0].action);
777     mPalmRejector->processMotion(
778             generateMotionArgs(downTime, toNs(128ms), MOVE,
779                                {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}}));
780     mPalmRejector->processMotion(
781             generateMotionArgs(downTime, toNs(136ms), MOVE,
782                                {{1433.0, 750.0, 44.0}, {1072.0, 765.0, 13.0}}));
783     mPalmRejector->processMotion(
784             generateMotionArgs(downTime, toNs(144ms), MOVE,
785                                {{1433.0, 750.0, 42.0}, {1072.0, 763.0, 14.0}}));
786     mPalmRejector->processMotion(
787             generateMotionArgs(downTime, toNs(152ms), MOVE,
788                                {{1434.0, 750.0, 44.0}, {1073.0, 761.0, 14.0}}));
789     mPalmRejector->processMotion(
790             generateMotionArgs(downTime, toNs(160ms), MOVE,
791                                {{1435.0, 750.0, 43.0}, {1073.0, 759.0, 15.0}}));
792     mPalmRejector->processMotion(
793             generateMotionArgs(downTime, toNs(168ms), MOVE,
794                                {{1436.0, 750.0, 45.0}, {1074.0, 757.0, 15.0}}));
795     mPalmRejector->processMotion(
796             generateMotionArgs(downTime, toNs(176ms), MOVE,
797                                {{1436.0, 750.0, 44.0}, {1074.0, 755.0, 15.0}}));
798     mPalmRejector->processMotion(
799             generateMotionArgs(downTime, toNs(184ms), MOVE,
800                                {{1436.0, 750.0, 45.0}, {1074.0, 753.0, 15.0}}));
801     mPalmRejector->processMotion(
802             generateMotionArgs(downTime, toNs(192ms), MOVE,
803                                {{1436.0, 749.0, 44.0}, {1074.0, 751.0, 15.0}}));
804     mPalmRejector->processMotion(
805             generateMotionArgs(downTime, toNs(200ms), MOVE,
806                                {{1435.0, 748.0, 45.0}, {1074.0, 749.0, 15.0}}));
807     mPalmRejector->processMotion(
808             generateMotionArgs(downTime, toNs(208ms), MOVE,
809                                {{1434.0, 746.0, 44.0}, {1074.0, 747.0, 14.0}}));
810     mPalmRejector->processMotion(
811             generateMotionArgs(downTime, toNs(216ms), MOVE,
812                                {{1433.0, 744.0, 44.0}, {1075.0, 745.0, 14.0}}));
813     mPalmRejector->processMotion(
814             generateMotionArgs(downTime, toNs(224ms), MOVE,
815                                {{1431.0, 741.0, 43.0}, {1075.0, 742.0, 13.0}}));
816     mPalmRejector->processMotion(
817             generateMotionArgs(downTime, toNs(232ms), MOVE,
818                                {{1428.0, 738.0, 43.0}, {1076.0, 739.0, 12.0}}));
819     mPalmRejector->processMotion(
820             generateMotionArgs(downTime, toNs(240ms), MOVE,
821                                {{1400.0, 726.0, 54.0}, {1076.0, 739.0, 13.0}}));
822     argsList = mPalmRejector->processMotion(
823             generateMotionArgs(downTime, toNs(248ms), POINTER_1_UP,
824                                {{1362.0, 716.0, 55.0}, {1076.0, 739.0, 13.0}}));
825     ASSERT_TRUE(argsList.empty());
826     mPalmRejector->processMotion(
827             generateMotionArgs(downTime, toNs(256ms), MOVE, {{1362.0, 716.0, 55.0}}));
828     mPalmRejector->processMotion(
829             generateMotionArgs(downTime, toNs(264ms), MOVE, {{1347.0, 707.0, 54.0}}));
830     mPalmRejector->processMotion(
831             generateMotionArgs(downTime, toNs(272ms), MOVE, {{1340.0, 698.0, 54.0}}));
832     mPalmRejector->processMotion(
833             generateMotionArgs(downTime, toNs(280ms), MOVE, {{1338.0, 694.0, 55.0}}));
834     mPalmRejector->processMotion(
835             generateMotionArgs(downTime, toNs(288ms), MOVE, {{1336.0, 690.0, 53.0}}));
836     mPalmRejector->processMotion(
837             generateMotionArgs(downTime, toNs(296ms), MOVE, {{1334.0, 685.0, 47.0}}));
838     mPalmRejector->processMotion(
839             generateMotionArgs(downTime, toNs(304ms), MOVE, {{1333.0, 679.0, 46.0}}));
840     mPalmRejector->processMotion(
841             generateMotionArgs(downTime, toNs(312ms), MOVE, {{1332.0, 672.0, 45.0}}));
842     mPalmRejector->processMotion(
843             generateMotionArgs(downTime, toNs(320ms), MOVE, {{1333.0, 666.0, 40.0}}));
844     mPalmRejector->processMotion(
845             generateMotionArgs(downTime, toNs(328ms), MOVE, {{1336.0, 661.0, 24.0}}));
846     mPalmRejector->processMotion(
847             generateMotionArgs(downTime, toNs(336ms), MOVE, {{1338.0, 656.0, 16.0}}));
848     mPalmRejector->processMotion(
849             generateMotionArgs(downTime, toNs(344ms), MOVE, {{1341.0, 649.0, 1.0}}));
850     argsList = mPalmRejector->processMotion(
851             generateMotionArgs(downTime, toNs(352ms), UP, {{1341.0, 649.0, 1.0}}));
852     ASSERT_TRUE(argsList.empty());
853 }
854 
855 /**
856  * A test implementation of PalmDetectionFilter that allows you to specify which pointer you want
857  * the model to consider 'suppressed'. The pointer is specified using its position (x, y).
858  * Current limitation:
859  *      Pointers may not cross each other in space during motion. Otherwise, any pointer with the
860  *      position matching the suppressed position will be considered "palm".
861  */
862 class TestFilter : public ::ui::PalmDetectionFilter {
863 public:
TestFilter(::ui::SharedPalmDetectionFilterState * state,std::vector<std::pair<float,float>> & suppressedPointers)864     TestFilter(::ui::SharedPalmDetectionFilterState* state,
865                std::vector<std::pair<float, float>>& suppressedPointers)
866           : ::ui::PalmDetectionFilter(state), mSuppressedPointers(suppressedPointers) {}
867 
Filter(const std::vector<::ui::InProgressTouchEvdev> & touches,::base::TimeTicks time,std::bitset<::ui::kNumTouchEvdevSlots> * slots_to_hold,std::bitset<::ui::kNumTouchEvdevSlots> * slots_to_suppress)868     void Filter(const std::vector<::ui::InProgressTouchEvdev>& touches, ::base::TimeTicks time,
869                 std::bitset<::ui::kNumTouchEvdevSlots>* slots_to_hold,
870                 std::bitset<::ui::kNumTouchEvdevSlots>* slots_to_suppress) override {
871         updateSuppressedSlots(touches);
872         *slots_to_suppress = mSuppressedSlots;
873     }
874 
FilterNameForTesting() const875     std::string FilterNameForTesting() const override { return "test filter"; }
876 
877 private:
updateSuppressedSlots(const std::vector<::ui::InProgressTouchEvdev> & touches)878     void updateSuppressedSlots(const std::vector<::ui::InProgressTouchEvdev>& touches) {
879         for (::ui::InProgressTouchEvdev touch : touches) {
880             for (const auto& [x, y] : mSuppressedPointers) {
881                 const float dx = (touch.x - x);
882                 const float dy = (touch.y - y);
883                 const float distanceSquared = dx * dx + dy * dy;
884                 if (distanceSquared < 1) {
885                     mSuppressedSlots.set(touch.slot, true);
886                 }
887             }
888         }
889     }
890 
891     std::bitset<::ui::kNumTouchEvdevSlots> mSuppressedSlots;
892     std::vector<std::pair<float, float>>& mSuppressedPointers;
893 };
894 
895 class PalmRejectorFakeFilterTest : public testing::Test {
896 protected:
897     std::unique_ptr<PalmRejector> mPalmRejector;
898 
SetUp()899     void SetUp() override {
900         std::unique_ptr<::ui::PalmDetectionFilter> filter =
901                 std::make_unique<TestFilter>(&mSharedPalmState, /*byref*/ mSuppressedPointers);
902         mPalmRejector =
903                 std::make_unique<PalmRejector>(generatePalmFilterDeviceInfo(), std::move(filter));
904     }
905 
suppressPointerAtPosition(float x,float y)906     void suppressPointerAtPosition(float x, float y) { mSuppressedPointers.push_back({x, y}); }
907 
908 private:
909     std::vector<std::pair<float, float>> mSuppressedPointers;
910     ::ui::SharedPalmDetectionFilterState mSharedPalmState; // unused, but we must retain ownership
911 };
912 
913 /**
914  * When a MOVE event happens, the model identifies the pointer as palm. At that time, the palm
915  * rejector should send a POINTER_UP event for this pointer with FLAG_CANCELED, and subsequent
916  * events should have this pointer removed.
917  */
TEST_F(PalmRejectorFakeFilterTest,OneOfTwoPointersIsCanceled)918 TEST_F(PalmRejectorFakeFilterTest, OneOfTwoPointersIsCanceled) {
919     std::vector<NotifyMotionArgs> argsList;
920     constexpr nsecs_t downTime = 0;
921 
922     mPalmRejector->processMotion(
923             generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
924     mPalmRejector->processMotion(
925             generateMotionArgs(downTime, 1, POINTER_1_DOWN,
926                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
927     // Cancel the second pointer
928     suppressPointerAtPosition(1059, 731);
929     argsList = mPalmRejector->processMotion(
930             generateMotionArgs(downTime, 255955783039000, MOVE,
931                                {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}}));
932     ASSERT_EQ(2u, argsList.size());
933     // First event - cancel pointer 1
934     ASSERT_EQ(POINTER_1_UP, argsList[0].action);
935     ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
936     // Second event - send MOVE for the remaining pointer
937     ASSERT_EQ(MOVE, argsList[1].action);
938     ASSERT_EQ(0, argsList[1].flags);
939 
940     // Future move events only contain 1 pointer, because the second pointer will continue
941     // to be suppressed
942     argsList = mPalmRejector->processMotion(
943             generateMotionArgs(downTime, 255955783039000, MOVE,
944                                {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}}));
945     ASSERT_EQ(1u, argsList.size());
946     ASSERT_EQ(MOVE, argsList[0].action);
947     ASSERT_EQ(1u, argsList[0].getPointerCount());
948     ASSERT_EQ(1433, argsList[0].pointerCoords[0].getX());
949     ASSERT_EQ(751, argsList[0].pointerCoords[0].getY());
950 }
951 
952 /**
953  * Send two pointers, and suppress both of them. Check that ACTION_CANCEL is generated.
954  * Afterwards:
955  *  1) Future MOVE events are ignored.
956  *  2) When a new pointer goes down, ACTION_DOWN is generated
957  */
TEST_F(PalmRejectorFakeFilterTest,NewDownEventAfterCancel)958 TEST_F(PalmRejectorFakeFilterTest, NewDownEventAfterCancel) {
959     std::vector<NotifyMotionArgs> argsList;
960     constexpr nsecs_t downTime = 0;
961 
962     mPalmRejector->processMotion(
963             generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
964     mPalmRejector->processMotion(
965             generateMotionArgs(downTime, 1, POINTER_1_DOWN,
966                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
967     // Cancel both pointers
968     suppressPointerAtPosition(1059, 731);
969     suppressPointerAtPosition(1400, 680);
970     argsList = mPalmRejector->processMotion(
971             generateMotionArgs(downTime, 1, MOVE, {{1400, 680, 41}, {1059, 731, 10}}));
972     ASSERT_EQ(1u, argsList.size());
973     // Cancel all
974     ASSERT_EQ(CANCEL, argsList[0].action);
975     ASSERT_EQ(2u, argsList[0].getPointerCount());
976     ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
977 
978     // Future move events are ignored
979     argsList = mPalmRejector->processMotion(
980             generateMotionArgs(downTime, 255955783039000, MOVE,
981                                {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}}));
982     ASSERT_EQ(0u, argsList.size());
983 
984     // When a new pointer goes down, a new DOWN event is generated
985     argsList = mPalmRejector->processMotion(
986             generateMotionArgs(downTime, 255955783039000, POINTER_2_DOWN,
987                                {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}, {1000, 700, 10}}));
988     ASSERT_EQ(1u, argsList.size());
989     ASSERT_EQ(DOWN, argsList[0].action);
990     ASSERT_EQ(1u, argsList[0].getPointerCount());
991     ASSERT_EQ(2, argsList[0].pointerProperties[0].id);
992 }
993 
994 /**
995  * 2 pointers are classified as palm simultaneously. When they are later
996  * released by Android, make sure that we drop both of these POINTER_UP events.
997  * Since they are classified as palm at the same time, we just need to receive a single CANCEL
998  * event. From MotionEvent docs: """A pointer id remains valid until the pointer eventually goes up
999  * (indicated by ACTION_UP or ACTION_POINTER_UP) or when the gesture is canceled (indicated by
1000  *  ACTION_CANCEL)."""
1001  * This means that generating additional POINTER_UP events is not necessary.
1002  * The risk here is that "oldSuppressedPointerIds" will not be correct, because it will update after
1003  * each motion, but pointers are canceled one at a time by Android.
1004  */
TEST_F(PalmRejectorFakeFilterTest,TwoPointersCanceledWhenOnePointerGoesUp)1005 TEST_F(PalmRejectorFakeFilterTest, TwoPointersCanceledWhenOnePointerGoesUp) {
1006     std::vector<NotifyMotionArgs> argsList;
1007     constexpr nsecs_t downTime = 0;
1008 
1009     mPalmRejector->processMotion(
1010             generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
1011     mPalmRejector->processMotion(
1012             generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_DOWN,
1013                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
1014     // Suppress both pointers!!
1015     suppressPointerAtPosition(1414, 702);
1016     suppressPointerAtPosition(1059, 731);
1017     argsList = mPalmRejector->processMotion(
1018             generateMotionArgs(downTime, 255955783039000, POINTER_1_UP,
1019                                {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}}));
1020     ASSERT_EQ(1u, argsList.size());
1021     ASSERT_EQ(CANCEL, argsList[0].action) << MotionEvent::actionToString(argsList[0].action);
1022     ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
1023 
1024     // Future move events should not go to the listener.
1025     argsList = mPalmRejector->processMotion(
1026             generateMotionArgs(downTime, 255955783049000, MOVE, {{1435.0, 755.0, 43.0}}));
1027     ASSERT_EQ(0u, argsList.size());
1028 
1029     argsList = mPalmRejector->processMotion(
1030             generateMotionArgs(downTime, 255955783059000, UP, {{1436.0, 756.0, 43.0}}));
1031     ASSERT_EQ(0u, argsList.size());
1032 }
1033 
1034 /**
1035  * Send 3 pointers, and then cancel one of them during a MOVE event. We should see ACTION_POINTER_UP
1036  * generated for that. Next, another pointer is canceled during ACTION_POINTER_DOWN. For that
1037  * pointer, we simply shouldn't send the event.
1038  */
TEST_F(PalmRejectorFakeFilterTest,CancelTwoPointers)1039 TEST_F(PalmRejectorFakeFilterTest, CancelTwoPointers) {
1040     std::vector<NotifyMotionArgs> argsList;
1041     constexpr nsecs_t downTime = 0;
1042 
1043     mPalmRejector->processMotion(
1044             generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
1045     mPalmRejector->processMotion(
1046             generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_DOWN,
1047                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
1048 
1049     // Suppress second pointer (pointer 1)
1050     suppressPointerAtPosition(1060, 700);
1051     argsList = mPalmRejector->processMotion(
1052             generateMotionArgs(downTime, /*eventTime=*/1, MOVE,
1053                                {{1417.0, 685.0, 41.0}, {1060, 700, 10.0}}));
1054     ASSERT_EQ(2u, argsList.size());
1055     ASSERT_EQ(POINTER_1_UP, argsList[0].action);
1056     ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
1057 
1058     ASSERT_EQ(MOVE, argsList[1].action) << MotionEvent::actionToString(argsList[1].action);
1059     ASSERT_EQ(0, argsList[1].flags);
1060 
1061     // A new pointer goes down and gets suppressed right away. It should just be dropped
1062     suppressPointerAtPosition(1001, 601);
1063     argsList = mPalmRejector->processMotion(
1064             generateMotionArgs(downTime, /*eventTime=*/1, POINTER_2_DOWN,
1065                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}}));
1066 
1067     ASSERT_EQ(0u, argsList.size());
1068     // Likewise, pointer that's already canceled should be ignored
1069     argsList = mPalmRejector->processMotion(
1070             generateMotionArgs(downTime, /*eventTime=*/1, POINTER_2_UP,
1071                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}}));
1072     ASSERT_EQ(0u, argsList.size());
1073 
1074     // Cancel all pointers when pointer 1 goes up. Pointer 1 was already canceled earlier.
1075     suppressPointerAtPosition(1417, 685);
1076     argsList = mPalmRejector->processMotion(
1077             generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_UP,
1078                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
1079     ASSERT_EQ(1u, argsList.size());
1080     ASSERT_EQ(CANCEL, argsList[0].action);
1081 }
1082 
1083 } // namespace android
1084