blob: a36d5269134b8e2c4b2d9d94931eaf4df980325b [file] [log] [blame]
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -08001/*
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 <gtest/gtest.h>
18#include "../PreferStylusOverTouchBlocker.h"
19
20namespace android {
21
22constexpr int32_t TOUCH_DEVICE_ID = 3;
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -080023constexpr int32_t SECOND_TOUCH_DEVICE_ID = 4;
24constexpr int32_t STYLUS_DEVICE_ID = 5;
25constexpr int32_t SECOND_STYLUS_DEVICE_ID = 6;
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -080026
27constexpr int DOWN = AMOTION_EVENT_ACTION_DOWN;
28constexpr int MOVE = AMOTION_EVENT_ACTION_MOVE;
29constexpr int UP = AMOTION_EVENT_ACTION_UP;
30constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL;
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -080031static constexpr int32_t POINTER_1_DOWN =
32 AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -080033constexpr int32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
34constexpr int32_t STYLUS = AINPUT_SOURCE_STYLUS;
35
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -080036static NotifyMotionArgs generateMotionArgs(nsecs_t downTime, nsecs_t eventTime, int32_t action,
Siarhei Vishniakoub8fd3ef2022-10-10 08:03:54 -070037 const std::vector<Point>& points, uint32_t source) {
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -080038 size_t pointerCount = points.size();
39 if (action == DOWN || action == UP) {
40 EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer";
41 }
42
43 PointerProperties pointerProperties[pointerCount];
44 PointerCoords pointerCoords[pointerCount];
45
46 const int32_t deviceId = isFromSource(source, TOUCHSCREEN) ? TOUCH_DEVICE_ID : STYLUS_DEVICE_ID;
Siarhei Vishniakou09a8fe42022-07-21 17:27:03 -070047 const ToolType toolType =
48 isFromSource(source, TOUCHSCREEN) ? ToolType::FINGER : ToolType::STYLUS;
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -080049 for (size_t i = 0; i < pointerCount; i++) {
50 pointerProperties[i].clear();
51 pointerProperties[i].id = i;
52 pointerProperties[i].toolType = toolType;
53
54 pointerCoords[i].clear();
55 pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x);
56 pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y);
57 }
58
59 // Currently, can't have STYLUS source without it also being a TOUCH source. Update the source
60 // accordingly.
61 if (isFromSource(source, STYLUS)) {
62 source |= TOUCHSCREEN;
63 }
64
65 // Define a valid motion event.
Linnan Li13bf76a2024-05-05 19:18:02 +080066 NotifyMotionArgs args(/*id=*/0, eventTime, /*readTime=*/0, deviceId, source,
Siarhei Vishniakoucfbee532024-05-10 13:41:35 -070067 ui::LogicalDisplayId::DEFAULT, POLICY_FLAG_PASS_TO_USER, action,
Linnan Li13bf76a2024-05-05 19:18:02 +080068 /* actionButton */ 0,
Harry Cutts33476232023-01-30 19:57:29 +000069 /*flags=*/0, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
70 AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
71 pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0,
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -080072 AMOTION_EVENT_INVALID_CURSOR_POSITION,
Harry Cutts33476232023-01-30 19:57:29 +000073 AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /*videoFrames=*/{});
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -080074
75 return args;
76}
77
78class PreferStylusOverTouchTest : public testing::Test {
79protected:
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -080080 void assertNotBlocked(const NotifyMotionArgs& args) { assertResponse(args, {args}); }
81
82 void assertDropped(const NotifyMotionArgs& args) { assertResponse(args, {}); }
83
84 void assertResponse(const NotifyMotionArgs& args,
85 const std::vector<NotifyMotionArgs>& expected) {
86 std::vector<NotifyMotionArgs> receivedArgs = mBlocker.processMotion(args);
87 ASSERT_EQ(expected.size(), receivedArgs.size());
88 for (size_t i = 0; i < expected.size(); i++) {
89 // The 'eventTime' of CANCEL events is dynamically generated. Don't check this field.
90 if (expected[i].action == CANCEL && receivedArgs[i].action == CANCEL) {
91 receivedArgs[i].eventTime = expected[i].eventTime;
92 }
93
94 ASSERT_EQ(expected[i], receivedArgs[i])
95 << expected[i].dump() << " vs " << receivedArgs[i].dump();
96 }
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -080097 }
98
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -080099 void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& devices) {
100 mBlocker.notifyInputDevicesChanged(devices);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800101 }
102
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800103 void dump() const { ALOGI("Blocker: \n%s\n", mBlocker.dump().c_str()); }
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800104
105private:
106 PreferStylusOverTouchBlocker mBlocker;
107};
108
109TEST_F(PreferStylusOverTouchTest, TouchGestureIsNotBlocked) {
110 NotifyMotionArgs args;
111
Harry Cutts33476232023-01-30 19:57:29 +0000112 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800113 assertNotBlocked(args);
114
Harry Cutts33476232023-01-30 19:57:29 +0000115 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{1, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800116 assertNotBlocked(args);
117
Harry Cutts33476232023-01-30 19:57:29 +0000118 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, UP, {{1, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800119 assertNotBlocked(args);
120}
121
122TEST_F(PreferStylusOverTouchTest, StylusGestureIsNotBlocked) {
123 NotifyMotionArgs args;
124
Harry Cutts33476232023-01-30 19:57:29 +0000125 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2}}, STYLUS);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800126 assertNotBlocked(args);
127
Harry Cutts33476232023-01-30 19:57:29 +0000128 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{1, 3}}, STYLUS);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800129 assertNotBlocked(args);
130
Harry Cutts33476232023-01-30 19:57:29 +0000131 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, UP, {{1, 3}}, STYLUS);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800132 assertNotBlocked(args);
133}
134
135/**
136 * Existing touch gesture should be canceled when stylus goes down. There should be an ACTION_CANCEL
137 * event generated.
138 */
139TEST_F(PreferStylusOverTouchTest, TouchIsCanceledWhenStylusGoesDown) {
140 NotifyMotionArgs args;
141
Harry Cutts33476232023-01-30 19:57:29 +0000142 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800143 assertNotBlocked(args);
144
Harry Cutts33476232023-01-30 19:57:29 +0000145 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{1, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800146 assertNotBlocked(args);
147
Harry Cutts33476232023-01-30 19:57:29 +0000148 args = generateMotionArgs(/*downTime=*/3, /*eventTime=*/3, DOWN, {{10, 30}}, STYLUS);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800149 NotifyMotionArgs cancelArgs =
Harry Cutts33476232023-01-30 19:57:29 +0000150 generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, CANCEL, {{1, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800151 cancelArgs.flags |= AMOTION_EVENT_FLAG_CANCELED;
152 assertResponse(args, {cancelArgs, args});
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800153
154 // Both stylus and touch events continue. Stylus should be not blocked, and touch should be
155 // blocked
Harry Cutts33476232023-01-30 19:57:29 +0000156 args = generateMotionArgs(/*downTime=*/3, /*eventTime=*/4, MOVE, {{10, 31}}, STYLUS);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800157 assertNotBlocked(args);
158
Harry Cutts33476232023-01-30 19:57:29 +0000159 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/5, MOVE, {{1, 4}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800160 assertDropped(args);
161}
162
163/**
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800164 * Stylus goes down after touch gesture.
165 */
166TEST_F(PreferStylusOverTouchTest, StylusDownAfterTouch) {
167 NotifyMotionArgs args;
168
Harry Cutts33476232023-01-30 19:57:29 +0000169 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800170 assertNotBlocked(args);
171
Harry Cutts33476232023-01-30 19:57:29 +0000172 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/1, MOVE, {{1, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800173 assertNotBlocked(args);
174
Harry Cutts33476232023-01-30 19:57:29 +0000175 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/2, UP, {{1, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800176 assertNotBlocked(args);
177
178 // Stylus goes down
Harry Cutts33476232023-01-30 19:57:29 +0000179 args = generateMotionArgs(/*downTime=*/3, /*eventTime=*/3, DOWN, {{10, 30}}, STYLUS);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800180 assertNotBlocked(args);
181}
182
183/**
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800184 * New touch events should be simply blocked (dropped) when stylus is down. No CANCEL event should
185 * be generated.
186 */
187TEST_F(PreferStylusOverTouchTest, NewTouchIsBlockedWhenStylusIsDown) {
188 NotifyMotionArgs args;
189 constexpr nsecs_t stylusDownTime = 0;
190 constexpr nsecs_t touchDownTime = 1;
191
Harry Cutts33476232023-01-30 19:57:29 +0000192 args = generateMotionArgs(stylusDownTime, /*eventTime=*/0, DOWN, {{10, 30}}, STYLUS);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800193 assertNotBlocked(args);
194
Harry Cutts33476232023-01-30 19:57:29 +0000195 args = generateMotionArgs(touchDownTime, /*eventTime=*/1, DOWN, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800196 assertDropped(args);
197
198 // Stylus should continue to work
Harry Cutts33476232023-01-30 19:57:29 +0000199 args = generateMotionArgs(stylusDownTime, /*eventTime=*/2, MOVE, {{10, 31}}, STYLUS);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800200 assertNotBlocked(args);
201
202 // Touch should continue to be blocked
Harry Cutts33476232023-01-30 19:57:29 +0000203 args = generateMotionArgs(touchDownTime, /*eventTime=*/1, MOVE, {{1, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800204 assertDropped(args);
205
Harry Cutts33476232023-01-30 19:57:29 +0000206 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/5, MOVE, {{1, 4}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800207 assertDropped(args);
208}
209
210/**
211 * New touch events should be simply blocked (dropped) when stylus is down. No CANCEL event should
212 * be generated.
213 */
214TEST_F(PreferStylusOverTouchTest, NewTouchWorksAfterStylusIsLifted) {
215 NotifyMotionArgs args;
216 constexpr nsecs_t stylusDownTime = 0;
217 constexpr nsecs_t touchDownTime = 4;
218
219 // Stylus goes down and up
Harry Cutts33476232023-01-30 19:57:29 +0000220 args = generateMotionArgs(stylusDownTime, /*eventTime=*/0, DOWN, {{10, 30}}, STYLUS);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800221 assertNotBlocked(args);
222
Harry Cutts33476232023-01-30 19:57:29 +0000223 args = generateMotionArgs(stylusDownTime, /*eventTime=*/2, MOVE, {{10, 31}}, STYLUS);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800224 assertNotBlocked(args);
225
Harry Cutts33476232023-01-30 19:57:29 +0000226 args = generateMotionArgs(stylusDownTime, /*eventTime=*/3, UP, {{10, 31}}, STYLUS);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800227 assertNotBlocked(args);
228
229 // New touch goes down. It should not be blocked
230 args = generateMotionArgs(touchDownTime, touchDownTime, DOWN, {{1, 2}}, TOUCHSCREEN);
231 assertNotBlocked(args);
232
Harry Cutts33476232023-01-30 19:57:29 +0000233 args = generateMotionArgs(touchDownTime, /*eventTime=*/5, MOVE, {{1, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800234 assertNotBlocked(args);
235
Harry Cutts33476232023-01-30 19:57:29 +0000236 args = generateMotionArgs(touchDownTime, /*eventTime=*/6, UP, {{1, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800237 assertNotBlocked(args);
238}
239
240/**
241 * Once a touch gesture is canceled, it should continue to be canceled, even if the stylus has been
242 * lifted.
243 */
244TEST_F(PreferStylusOverTouchTest, AfterStylusIsLiftedCurrentTouchIsBlocked) {
245 NotifyMotionArgs args;
246 constexpr nsecs_t stylusDownTime = 0;
247 constexpr nsecs_t touchDownTime = 1;
248
Harry Cutts33476232023-01-30 19:57:29 +0000249 assertNotBlocked(generateMotionArgs(stylusDownTime, /*eventTime=*/0, DOWN, {{10, 30}}, STYLUS));
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800250
Harry Cutts33476232023-01-30 19:57:29 +0000251 args = generateMotionArgs(touchDownTime, /*eventTime=*/1, DOWN, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800252 assertDropped(args);
253
254 // Lift the stylus
Harry Cutts33476232023-01-30 19:57:29 +0000255 args = generateMotionArgs(stylusDownTime, /*eventTime=*/2, UP, {{10, 30}}, STYLUS);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800256 assertNotBlocked(args);
257
258 // Touch should continue to be blocked
Harry Cutts33476232023-01-30 19:57:29 +0000259 args = generateMotionArgs(touchDownTime, /*eventTime=*/3, MOVE, {{1, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800260 assertDropped(args);
261
Harry Cutts33476232023-01-30 19:57:29 +0000262 args = generateMotionArgs(touchDownTime, /*eventTime=*/4, UP, {{1, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800263 assertDropped(args);
264
265 // New touch should go through, though.
266 constexpr nsecs_t newTouchDownTime = 5;
Harry Cutts33476232023-01-30 19:57:29 +0000267 args = generateMotionArgs(newTouchDownTime, /*eventTime=*/5, DOWN, {{10, 20}}, TOUCHSCREEN);
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800268 assertNotBlocked(args);
269}
270
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800271/**
272 * If an event with mixed stylus and touch pointers is encountered, it should be ignored. Touches
273 * from such should pass, even if stylus from the same device goes down.
274 */
275TEST_F(PreferStylusOverTouchTest, MixedStylusAndTouchPointersAreIgnored) {
276 NotifyMotionArgs args;
277
278 // Event from a stylus device, but with finger tool type
Harry Cutts33476232023-01-30 19:57:29 +0000279 args = generateMotionArgs(/*downTime=*/1, /*eventTime=*/1, DOWN, {{1, 2}}, STYLUS);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800280 // Keep source stylus, but make the tool type touch
Siarhei Vishniakou09a8fe42022-07-21 17:27:03 -0700281 args.pointerProperties[0].toolType = ToolType::FINGER;
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800282 assertNotBlocked(args);
283
284 // Second pointer (stylus pointer) goes down, from the same device
Harry Cutts33476232023-01-30 19:57:29 +0000285 args = generateMotionArgs(/*downTime=*/1, /*eventTime=*/2, POINTER_1_DOWN, {{1, 2}, {10, 20}},
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800286 STYLUS);
287 // Keep source stylus, but make the tool type touch
Siarhei Vishniakou09a8fe42022-07-21 17:27:03 -0700288 args.pointerProperties[0].toolType = ToolType::STYLUS;
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800289 assertNotBlocked(args);
290
291 // Second pointer (stylus pointer) goes down, from the same device
Harry Cutts33476232023-01-30 19:57:29 +0000292 args = generateMotionArgs(/*downTime=*/1, /*eventTime=*/3, MOVE, {{2, 3}, {11, 21}}, STYLUS);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800293 // Keep source stylus, but make the tool type touch
Siarhei Vishniakou09a8fe42022-07-21 17:27:03 -0700294 args.pointerProperties[0].toolType = ToolType::FINGER;
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800295 assertNotBlocked(args);
296}
297
298/**
299 * When there are two touch devices, stylus down should cancel all current touch streams.
300 */
301TEST_F(PreferStylusOverTouchTest, TouchFromTwoDevicesAndStylus) {
302 NotifyMotionArgs touch1Down =
Harry Cutts33476232023-01-30 19:57:29 +0000303 generateMotionArgs(/*downTime=*/1, /*eventTime=*/1, DOWN, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800304 assertNotBlocked(touch1Down);
305
306 NotifyMotionArgs touch2Down =
Harry Cutts33476232023-01-30 19:57:29 +0000307 generateMotionArgs(/*downTime=*/2, /*eventTime=*/2, DOWN, {{3, 4}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800308 touch2Down.deviceId = SECOND_TOUCH_DEVICE_ID;
309 assertNotBlocked(touch2Down);
310
311 NotifyMotionArgs stylusDown =
Harry Cutts33476232023-01-30 19:57:29 +0000312 generateMotionArgs(/*downTime=*/3, /*eventTime=*/3, DOWN, {{10, 30}}, STYLUS);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800313 NotifyMotionArgs cancelArgs1 = touch1Down;
314 cancelArgs1.action = CANCEL;
315 cancelArgs1.flags |= AMOTION_EVENT_FLAG_CANCELED;
316 NotifyMotionArgs cancelArgs2 = touch2Down;
317 cancelArgs2.action = CANCEL;
318 cancelArgs2.flags |= AMOTION_EVENT_FLAG_CANCELED;
319 assertResponse(stylusDown, {cancelArgs1, cancelArgs2, stylusDown});
320}
321
322/**
323 * Touch should be canceled when stylus goes down. After the stylus lifts up, the touch from that
324 * device should continue to be canceled.
325 * If one of the devices is already canceled, it should remain canceled, but new touches from a
326 * different device should go through.
327 */
328TEST_F(PreferStylusOverTouchTest, AllTouchMustLiftAfterCanceledByStylus) {
329 // First device touches down
330 NotifyMotionArgs touch1Down =
Harry Cutts33476232023-01-30 19:57:29 +0000331 generateMotionArgs(/*downTime=*/1, /*eventTime=*/1, DOWN, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800332 assertNotBlocked(touch1Down);
333
334 // Stylus goes down - touch should be canceled
335 NotifyMotionArgs stylusDown =
Harry Cutts33476232023-01-30 19:57:29 +0000336 generateMotionArgs(/*downTime=*/2, /*eventTime=*/2, DOWN, {{10, 30}}, STYLUS);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800337 NotifyMotionArgs cancelArgs1 = touch1Down;
338 cancelArgs1.action = CANCEL;
339 cancelArgs1.flags |= AMOTION_EVENT_FLAG_CANCELED;
340 assertResponse(stylusDown, {cancelArgs1, stylusDown});
341
342 // Stylus goes up
343 NotifyMotionArgs stylusUp =
Harry Cutts33476232023-01-30 19:57:29 +0000344 generateMotionArgs(/*downTime=*/2, /*eventTime=*/3, UP, {{10, 30}}, STYLUS);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800345 assertNotBlocked(stylusUp);
346
347 // Touch from the first device remains blocked
348 NotifyMotionArgs touch1Move =
Harry Cutts33476232023-01-30 19:57:29 +0000349 generateMotionArgs(/*downTime=*/1, /*eventTime=*/4, MOVE, {{2, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800350 assertDropped(touch1Move);
351
352 // Second touch goes down. It should not be blocked because stylus has already lifted.
353 NotifyMotionArgs touch2Down =
Harry Cutts33476232023-01-30 19:57:29 +0000354 generateMotionArgs(/*downTime=*/5, /*eventTime=*/5, DOWN, {{31, 32}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800355 touch2Down.deviceId = SECOND_TOUCH_DEVICE_ID;
356 assertNotBlocked(touch2Down);
357
358 // First device is lifted up. It's already been canceled, so the UP event should be dropped.
359 NotifyMotionArgs touch1Up =
Harry Cutts33476232023-01-30 19:57:29 +0000360 generateMotionArgs(/*downTime=*/1, /*eventTime=*/6, UP, {{2, 3}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800361 assertDropped(touch1Up);
362
363 // Touch from second device touch should continue to work
364 NotifyMotionArgs touch2Move =
Harry Cutts33476232023-01-30 19:57:29 +0000365 generateMotionArgs(/*downTime=*/5, /*eventTime=*/7, MOVE, {{32, 33}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800366 touch2Move.deviceId = SECOND_TOUCH_DEVICE_ID;
367 assertNotBlocked(touch2Move);
368
369 // Second touch lifts up
370 NotifyMotionArgs touch2Up =
Harry Cutts33476232023-01-30 19:57:29 +0000371 generateMotionArgs(/*downTime=*/5, /*eventTime=*/8, UP, {{32, 33}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800372 touch2Up.deviceId = SECOND_TOUCH_DEVICE_ID;
373 assertNotBlocked(touch2Up);
374
375 // Now that all touch has been lifted, new touch from either first or second device should work
376 NotifyMotionArgs touch3Down =
Harry Cutts33476232023-01-30 19:57:29 +0000377 generateMotionArgs(/*downTime=*/9, /*eventTime=*/9, DOWN, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800378 assertNotBlocked(touch3Down);
379
380 NotifyMotionArgs touch4Down =
Harry Cutts33476232023-01-30 19:57:29 +0000381 generateMotionArgs(/*downTime=*/10, /*eventTime=*/10, DOWN, {{100, 200}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800382 touch4Down.deviceId = SECOND_TOUCH_DEVICE_ID;
383 assertNotBlocked(touch4Down);
384}
385
386/**
387 * When we don't know that a specific device does both stylus and touch, and we only see touch
388 * pointers from it, we should treat it as a touch device. That means, the device events should be
389 * canceled when stylus from another device goes down. When we detect simultaneous touch and stylus
390 * from this device though, we should just pass this device through without canceling anything.
391 *
392 * In this test:
393 * 1. Start by touching down with device 1
394 * 2. Device 2 has stylus going down
395 * 3. Device 1 should be canceled.
396 * 4. When we add stylus pointers to the device 1, they should continue to be canceled.
397 * 5. Device 1 lifts up.
398 * 6. Subsequent events from device 1 should not be canceled even if stylus is down.
399 * 7. If a reset happens, and such device is no longer there, then we should
400 * Therefore, the device 1 is "ignored" and does not participate into "prefer stylus over touch"
401 * behaviour.
402 */
403TEST_F(PreferStylusOverTouchTest, MixedStylusAndTouchDeviceIsCanceledAtFirst) {
404 // Touch from device 1 goes down
405 NotifyMotionArgs touchDown =
Harry Cutts33476232023-01-30 19:57:29 +0000406 generateMotionArgs(/*downTime=*/1, /*eventTime=*/1, DOWN, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800407 touchDown.source = STYLUS;
408 assertNotBlocked(touchDown);
409
410 // Stylus from device 2 goes down. Touch should be canceled.
411 NotifyMotionArgs args =
Harry Cutts33476232023-01-30 19:57:29 +0000412 generateMotionArgs(/*downTime=*/2, /*eventTime=*/2, DOWN, {{10, 20}}, STYLUS);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800413 NotifyMotionArgs cancelTouchArgs = touchDown;
414 cancelTouchArgs.action = CANCEL;
415 cancelTouchArgs.flags |= AMOTION_EVENT_FLAG_CANCELED;
416 assertResponse(args, {cancelTouchArgs, args});
417
418 // Introduce a stylus pointer into the device 1 stream. It should be ignored.
Harry Cutts33476232023-01-30 19:57:29 +0000419 args = generateMotionArgs(/*downTime=*/1, /*eventTime=*/3, POINTER_1_DOWN, {{1, 2}, {3, 4}},
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800420 TOUCHSCREEN);
Siarhei Vishniakou09a8fe42022-07-21 17:27:03 -0700421 args.pointerProperties[1].toolType = ToolType::STYLUS;
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800422 args.source = STYLUS;
423 assertDropped(args);
424
425 // Lift up touch from the mixed touch/stylus device
Harry Cutts33476232023-01-30 19:57:29 +0000426 args = generateMotionArgs(/*downTime=*/1, /*eventTime=*/4, CANCEL, {{1, 2}, {3, 4}},
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800427 TOUCHSCREEN);
Siarhei Vishniakou09a8fe42022-07-21 17:27:03 -0700428 args.pointerProperties[1].toolType = ToolType::STYLUS;
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800429 args.source = STYLUS;
430 assertDropped(args);
431
432 // Stylus from device 2 is still down. Since the device 1 is now identified as a mixed
433 // touch/stylus device, its events should go through, even if they are touch.
Harry Cutts33476232023-01-30 19:57:29 +0000434 args = generateMotionArgs(/*downTime=*/5, /*eventTime=*/5, DOWN, {{21, 22}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800435 touchDown.source = STYLUS;
436 assertResponse(args, {args});
437
438 // Reconfigure such that only the stylus device remains
439 InputDeviceInfo stylusDevice;
Harry Cutts33476232023-01-30 19:57:29 +0000440 stylusDevice.initialize(STYLUS_DEVICE_ID, /*generation=*/1, /*controllerNumber=*/1,
441 /*identifier=*/{}, "stylus device", /*external=*/false,
Siarhei Vishniakoucfbee532024-05-10 13:41:35 -0700442 /*hasMic=*/false, ui::LogicalDisplayId::INVALID);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800443 notifyInputDevicesChanged({stylusDevice});
444 // The touchscreen device was removed, so we no longer remember anything about it. We should
445 // again start blocking touch events from it.
Harry Cutts33476232023-01-30 19:57:29 +0000446 args = generateMotionArgs(/*downTime=*/6, /*eventTime=*/6, DOWN, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800447 args.source = STYLUS;
448 assertDropped(args);
449}
450
451/**
452 * If two styli are active at the same time, touch should be blocked until both of them are lifted.
453 * If one of them lifts, touch should continue to be blocked.
454 */
455TEST_F(PreferStylusOverTouchTest, TouchIsBlockedWhenTwoStyliAreUsed) {
456 NotifyMotionArgs args;
457
458 // First stylus is down
Harry Cutts33476232023-01-30 19:57:29 +0000459 assertNotBlocked(generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{10, 30}}, STYLUS));
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800460
461 // Second stylus is down
Harry Cutts33476232023-01-30 19:57:29 +0000462 args = generateMotionArgs(/*downTime=*/1, /*eventTime=*/1, DOWN, {{20, 40}}, STYLUS);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800463 args.deviceId = SECOND_STYLUS_DEVICE_ID;
464 assertNotBlocked(args);
465
466 // Touch goes down. It should be ignored.
Harry Cutts33476232023-01-30 19:57:29 +0000467 args = generateMotionArgs(/*downTime=*/2, /*eventTime=*/2, DOWN, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800468 assertDropped(args);
469
470 // Lift the first stylus
Harry Cutts33476232023-01-30 19:57:29 +0000471 args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/3, UP, {{10, 30}}, STYLUS);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800472 assertNotBlocked(args);
473
474 // Touch should continue to be blocked
Harry Cutts33476232023-01-30 19:57:29 +0000475 args = generateMotionArgs(/*downTime=*/2, /*eventTime=*/4, UP, {{1, 2}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800476 assertDropped(args);
477
478 // New touch should be blocked because second stylus is still down
Harry Cutts33476232023-01-30 19:57:29 +0000479 args = generateMotionArgs(/*downTime=*/5, /*eventTime=*/5, DOWN, {{5, 6}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800480 assertDropped(args);
481
482 // Second stylus goes up
Harry Cutts33476232023-01-30 19:57:29 +0000483 args = generateMotionArgs(/*downTime=*/1, /*eventTime=*/6, UP, {{20, 40}}, STYLUS);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800484 args.deviceId = SECOND_STYLUS_DEVICE_ID;
485 assertNotBlocked(args);
486
487 // Current touch gesture should continue to be blocked
488 // Touch should continue to be blocked
Harry Cutts33476232023-01-30 19:57:29 +0000489 args = generateMotionArgs(/*downTime=*/5, /*eventTime=*/7, UP, {{5, 6}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800490 assertDropped(args);
491
492 // Now that all styli were lifted, new touch should go through
Harry Cutts33476232023-01-30 19:57:29 +0000493 args = generateMotionArgs(/*downTime=*/8, /*eventTime=*/8, DOWN, {{7, 8}}, TOUCHSCREEN);
Siarhei Vishniakoua6a660f2022-03-04 15:12:16 -0800494 assertNotBlocked(args);
495}
496
Siarhei Vishniakoua3c8e512022-02-10 19:46:34 -0800497} // namespace android