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