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