• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2024 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 "MultiTouchInputMapper.h"
18 
19 #include <android-base/logging.h>
20 #include <gtest/gtest.h>
21 #include <list>
22 #include <optional>
23 
24 #include "InputMapperTest.h"
25 #include "InterfaceMocks.h"
26 #include "ScopedFlagOverride.h"
27 #include "TestEventMatchers.h"
28 
29 #define TAG "MultiTouchpadInputMapperUnit_test"
30 
31 namespace android {
32 
33 using testing::_;
34 using testing::AllOf;
35 using testing::IsEmpty;
36 using testing::Return;
37 using testing::SetArgPointee;
38 using testing::VariantWith;
39 
40 namespace {
41 
42 constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
43 constexpr ui::LogicalDisplayId SECOND_DISPLAY_ID = ui::LogicalDisplayId{DISPLAY_ID.val() + 1};
44 constexpr int32_t DISPLAY_WIDTH = 480;
45 constexpr int32_t DISPLAY_HEIGHT = 800;
46 constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
47 constexpr int32_t SLOT_COUNT = 5;
48 
49 constexpr int32_t ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
50 constexpr int32_t ACTION_CANCEL = AMOTION_EVENT_ACTION_CANCEL;
51 constexpr int32_t ACTION_POINTER_0_UP =
52         AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
53 constexpr int32_t ACTION_POINTER_1_DOWN =
54         AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
55 
56 template <typename... Args>
assertNotifyArgs(const std::list<NotifyArgs> & args,Args...matchers)57 void assertNotifyArgs(const std::list<NotifyArgs>& args, Args... matchers) {
58     ASSERT_THAT(args, ElementsAre(matchers...))
59             << "Got instead: " << dumpContainer(args, streamableToString);
60 }
61 
62 } // namespace
63 
64 /**
65  * Unit tests for MultiTouchInputMapper.
66  */
67 class MultiTouchInputMapperUnitTest : public InputMapperUnitTest {
68 protected:
SetUp()69     void SetUp() override { SetUp(/*bus=*/0, /*isExternal=*/false); }
SetUp(int bus,bool isExternal)70     void SetUp(int bus, bool isExternal) override {
71         InputMapperUnitTest::SetUp(bus, isExternal);
72 
73         // Present scan codes
74         expectScanCodes(/*present=*/true,
75                         {BTN_TOUCH, BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP,
76                          BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP});
77 
78         // Missing scan codes that the mapper checks for.
79         expectScanCodes(/*present=*/false,
80                         {BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_BRUSH, BTN_TOOL_PENCIL,
81                          BTN_TOOL_AIRBRUSH});
82 
83         // Current scan code state - all keys are UP by default
84         setScanCodeState(KeyState::UP, {BTN_LEFT,           BTN_RIGHT,        BTN_MIDDLE,
85                                         BTN_BACK,           BTN_SIDE,         BTN_FORWARD,
86                                         BTN_EXTRA,          BTN_TASK,         BTN_TOUCH,
87                                         BTN_STYLUS,         BTN_STYLUS2,      BTN_0,
88                                         BTN_TOOL_FINGER,    BTN_TOOL_PEN,     BTN_TOOL_RUBBER,
89                                         BTN_TOOL_BRUSH,     BTN_TOOL_PENCIL,  BTN_TOOL_AIRBRUSH,
90                                         BTN_TOOL_MOUSE,     BTN_TOOL_LENS,    BTN_TOOL_DOUBLETAP,
91                                         BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP});
92 
93         setKeyCodeState(KeyState::UP,
94                         {AKEYCODE_STYLUS_BUTTON_PRIMARY, AKEYCODE_STYLUS_BUTTON_SECONDARY});
95 
96         // Input properties - only INPUT_PROP_DIRECT for touchscreen
97         EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, _)).WillRepeatedly(Return(false));
98         EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_DIRECT))
99                 .WillRepeatedly(Return(true));
100         // The following EXPECT_CALL lines are not load-bearing, but without them gtest prints
101         // warnings about "uninteresting mocked call", which are distracting when developing the
102         // tests because this text is interleaved with logs of interest.
103         EXPECT_CALL(mMockEventHub, getVirtualKeyDefinitions(EVENTHUB_ID, _))
104                 .WillRepeatedly(Return());
105         EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, _))
106                 .WillRepeatedly(testing::Return(false));
107         EXPECT_CALL(mMockEventHub, getVideoFrames(EVENTHUB_ID))
108                 .WillRepeatedly(testing::Return(std::vector<TouchVideoFrame>{}));
109         EXPECT_CALL(mMockInputReaderContext, getExternalStylusDevices(_)).WillRepeatedly(Return());
110         EXPECT_CALL(mMockInputReaderContext, getGlobalMetaState()).WillRepeatedly(Return(0));
111 
112         // Axes that the device has
113         setupAxis(ABS_MT_SLOT, /*valid=*/true, /*min=*/0, /*max=*/SLOT_COUNT - 1, /*resolution=*/0);
114         setupAxis(ABS_MT_TRACKING_ID, /*valid=*/true, /*min*/ 0, /*max=*/255, /*resolution=*/0);
115         setupAxis(ABS_MT_POSITION_X, /*valid=*/true, /*min=*/0, /*max=*/2000, /*resolution=*/24);
116         setupAxis(ABS_MT_POSITION_Y, /*valid=*/true, /*min=*/0, /*max=*/1000, /*resolution=*/24);
117 
118         // Axes that the device does not have
119         setupAxis(ABS_MT_PRESSURE, /*valid=*/false, /*min*/ 0, /*max=*/255, /*resolution=*/0);
120         setupAxis(ABS_MT_ORIENTATION, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
121         setupAxis(ABS_MT_DISTANCE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
122         setupAxis(ABS_MT_TOUCH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
123         setupAxis(ABS_MT_TOUCH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
124         setupAxis(ABS_MT_WIDTH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
125         setupAxis(ABS_MT_WIDTH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
126         setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
127 
128         // reset current slot at the beginning
129         EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT))
130                 .WillRepeatedly(Return(0));
131 
132         // mark all slots not in use
133         mockSlotValues({});
134 
135         mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
136         DisplayViewport internalViewport =
137                 createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
138                                /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
139         mFakePolicy->addDisplayViewport(internalViewport);
140         mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext,
141                                                            mFakePolicy->getReaderConfiguration());
142     }
143 
144     // Mocks position and tracking Ids for the provided slots. Remaining slots will be marked
145     // unused.
mockSlotValues(const std::unordered_map<int32_t,std::pair<Point,int32_t>> & slotValues)146     void mockSlotValues(
147             const std::unordered_map<int32_t /*slotIndex*/,
148                                      std::pair<Point /*position*/, int32_t /*trackingId*/>>&
149                     slotValues) {
150         EXPECT_CALL(mMockEventHub, getMtSlotValues(EVENTHUB_ID, _, SLOT_COUNT))
151                 .WillRepeatedly([=](int32_t, int32_t axis,
152                                     size_t slotCount) -> base::Result<std::vector<int32_t>> {
153                     // tracking Id for the unused slots must set to be < 0
154                     std::vector<int32_t> outMtSlotValues(slotCount + 1, -1);
155                     outMtSlotValues[0] = axis;
156                     switch (axis) {
157                         case ABS_MT_POSITION_X:
158                             for (const auto& [slotIndex, valuePair] : slotValues) {
159                                 outMtSlotValues[slotIndex] = valuePair.first.x;
160                             }
161                             return outMtSlotValues;
162                         case ABS_MT_POSITION_Y:
163                             for (const auto& [slotIndex, valuePair] : slotValues) {
164                                 outMtSlotValues[slotIndex] = valuePair.first.y;
165                             }
166                             return outMtSlotValues;
167                         case ABS_MT_TRACKING_ID:
168                             for (const auto& [slotIndex, valuePair] : slotValues) {
169                                 outMtSlotValues[slotIndex] = valuePair.second;
170                             }
171                             return outMtSlotValues;
172                         default:
173                             return base::ResultError("Axis not supported", NAME_NOT_FOUND);
174                     }
175                 });
176     }
177 
processPosition(int32_t x,int32_t y)178     [[nodiscard]] std::list<NotifyArgs> processPosition(int32_t x, int32_t y) {
179         std::list<NotifyArgs> args;
180         args += process(EV_ABS, ABS_MT_POSITION_X, x);
181         args += process(EV_ABS, ABS_MT_POSITION_Y, y);
182         return args;
183     }
184 
processId(int32_t id)185     [[nodiscard]] std::list<NotifyArgs> processId(int32_t id) {
186         return process(EV_ABS, ABS_MT_TRACKING_ID, id);
187     }
188 
processKey(int32_t code,int32_t value)189     [[nodiscard]] std::list<NotifyArgs> processKey(int32_t code, int32_t value) {
190         return process(EV_KEY, code, value);
191     }
192 
processSlot(int32_t slot)193     [[nodiscard]] std::list<NotifyArgs> processSlot(int32_t slot) {
194         return process(EV_ABS, ABS_MT_SLOT, slot);
195     }
196 
processSync()197     [[nodiscard]] std::list<NotifyArgs> processSync() { return process(EV_SYN, SYN_REPORT, 0); }
198 };
199 
200 /**
201  * While a gesture is active, change the display that the device is associated with. Make sure that
202  * the CANCEL event that's generated has the display id of the original DOWN event, rather than the
203  * new display id.
204  */
TEST_F(MultiTouchInputMapperUnitTest,ChangeAssociatedDisplayIdWhenTouchIsActive)205 TEST_F(MultiTouchInputMapperUnitTest, ChangeAssociatedDisplayIdWhenTouchIsActive) {
206     std::list<NotifyArgs> args;
207 
208     // Add a second viewport that later will be associated with our device.
209     DisplayViewport secondViewport =
210             createViewport(SECOND_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
211                            /*isActive=*/true, "local:1", NO_PORT, ViewportType::EXTERNAL);
212     mFakePolicy->addDisplayViewport(secondViewport);
213     std::optional<DisplayViewport> firstViewport =
214             mFakePolicy->getDisplayViewportByUniqueId("local:0");
215 
216     // InputReaderConfiguration contains information about how devices are associated with displays.
217     // The mapper receives this information. However, it doesn't actually parse it - that's done by
218     // InputDevice. The mapper asks InputDevice about the associated viewport, so that's what we
219     // need to mock here to simulate association. This abstraction is confusing and should be
220     // refactored.
221 
222     // Start with the first viewport
223     ON_CALL((*mDevice), getAssociatedViewport).WillByDefault(Return(firstViewport));
224     args += mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration,
225                                  InputReaderConfiguration::Change::DISPLAY_INFO);
226 
227     int32_t x1 = 100, y1 = 125;
228     args += processKey(BTN_TOUCH, 1);
229     args += processPosition(x1, y1);
230     args += processId(1);
231     args += processSync();
232     ASSERT_THAT(args,
233                 ElementsAre(VariantWith<NotifyMotionArgs>(
234                         AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(DISPLAY_ID)))));
235     args.clear();
236 
237     // Now associate with the second viewport, and reconfigure.
238     ON_CALL((*mDevice), getAssociatedViewport).WillByDefault(Return(secondViewport));
239     args += mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration,
240                                  InputReaderConfiguration::Change::DISPLAY_INFO);
241     assertNotifyArgs(args,
242                      VariantWith<NotifyMotionArgs>(
243                              AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(DISPLAY_ID))),
244                      VariantWith<NotifyDeviceResetArgs>(WithDeviceId(DEVICE_ID)));
245 
246     // The remainder of the gesture is ignored
247     // Move.
248     x1 += 10;
249     y1 += 15;
250     args = processPosition(x1, y1);
251     args += processSync();
252     // Up
253     args += processKey(BTN_TOUCH, 0);
254     args += processId(-1);
255     args += processSync();
256 
257     ASSERT_THAT(args, IsEmpty());
258 
259     // New touch is delivered with the new display id.
260     args += processId(2);
261     args += processKey(BTN_TOUCH, 1);
262     args += processPosition(x1 + 20, y1 + 40);
263     args += processSync();
264     assertNotifyArgs(args,
265                      VariantWith<NotifyMotionArgs>(AllOf(WithMotionAction(ACTION_DOWN),
266                                                          WithDisplayId(SECOND_DISPLAY_ID))));
267 }
268 
269 // This test simulates a multi-finger gesture with unexpected reset in between. This might happen
270 // due to buffer overflow and device with report a SYN_DROPPED. In this case we expect mapper to be
271 // reset, MT slot state to be re-populated and the gesture should be cancelled and restarted.
TEST_F(MultiTouchInputMapperUnitTest,MultiFingerGestureWithUnexpectedReset)272 TEST_F(MultiTouchInputMapperUnitTest, MultiFingerGestureWithUnexpectedReset) {
273     std::list<NotifyArgs> args;
274 
275     // Two fingers down at once.
276     constexpr int32_t FIRST_TRACKING_ID = 1, SECOND_TRACKING_ID = 2;
277     int32_t x1 = 100, y1 = 125, x2 = 200, y2 = 225;
278     args += processKey(BTN_TOUCH, 1);
279     args += processPosition(x1, y1);
280     args += processId(FIRST_TRACKING_ID);
281     args += processSlot(1);
282     args += processPosition(x2, y2);
283     args += processId(SECOND_TRACKING_ID);
284     ASSERT_THAT(args, IsEmpty());
285 
286     args += processSync();
287     ASSERT_THAT(args,
288                 ElementsAre(VariantWith<NotifyMotionArgs>(
289                                     WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
290                             VariantWith<NotifyMotionArgs>(
291                                     WithMotionAction(ACTION_POINTER_1_DOWN))));
292 
293     // Move.
294     x1 += 10;
295     y1 += 15;
296     x2 += 5;
297     y2 -= 10;
298     args = processSlot(0);
299     args += processPosition(x1, y1);
300     args += processSlot(1);
301     args += processPosition(x2, y2);
302     ASSERT_THAT(args, IsEmpty());
303 
304     args = processSync();
305     ASSERT_THAT(args,
306                 ElementsAre(VariantWith<NotifyMotionArgs>(
307                         WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
308     const auto pointerCoordsBeforeReset = std::get<NotifyMotionArgs>(args.back()).pointerCoords;
309 
310     // On buffer overflow mapper will be reset and MT slots data will be repopulated
311     EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT))
312             .WillRepeatedly(Return(1));
313 
314     mockSlotValues(
315             {{1, {Point{x1, y1}, FIRST_TRACKING_ID}}, {2, {Point{x2, y2}, SECOND_TRACKING_ID}}});
316 
317     setScanCodeState(KeyState::DOWN, {BTN_TOUCH});
318 
319     args = mMapper->reset(systemTime(SYSTEM_TIME_MONOTONIC));
320     ASSERT_THAT(args,
321                 ElementsAre(VariantWith<NotifyMotionArgs>(
322                         WithMotionAction(AMOTION_EVENT_ACTION_CANCEL))));
323 
324     // SYN_REPORT should restart the gesture again
325     args = processSync();
326     ASSERT_THAT(args,
327                 ElementsAre(VariantWith<NotifyMotionArgs>(
328                                     WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
329                             VariantWith<NotifyMotionArgs>(
330                                     WithMotionAction(ACTION_POINTER_1_DOWN))));
331     ASSERT_EQ(std::get<NotifyMotionArgs>(args.back()).pointerCoords, pointerCoordsBeforeReset);
332 
333     // Move.
334     x1 += 10;
335     y1 += 15;
336     x2 += 5;
337     y2 -= 10;
338     args = processSlot(0);
339     args += processPosition(x1, y1);
340     args += processSlot(1);
341     args += processPosition(x2, y2);
342     ASSERT_THAT(args, IsEmpty());
343 
344     args = processSync();
345     ASSERT_THAT(args,
346                 ElementsAre(VariantWith<NotifyMotionArgs>(
347                         WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
348 
349     // First finger up.
350     args = processSlot(0);
351     args += processId(-1);
352     ASSERT_THAT(args, IsEmpty());
353 
354     args = processSync();
355     ASSERT_THAT(args,
356                 ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_POINTER_0_UP))));
357 
358     // Second finger up.
359     args = processKey(BTN_TOUCH, 0);
360     args += processSlot(1);
361     args += processId(-1);
362     ASSERT_THAT(args, IsEmpty());
363 
364     args = processSync();
365     ASSERT_THAT(args,
366                 ElementsAre(
367                         VariantWith<NotifyMotionArgs>(WithMotionAction(AMOTION_EVENT_ACTION_UP))));
368 }
369 
370 class ExternalMultiTouchInputMapperTest : public MultiTouchInputMapperUnitTest {
371 protected:
SetUp()372     void SetUp() override { MultiTouchInputMapperUnitTest::SetUp(/*bus=*/0, /*isExternal=*/true); }
373 };
374 
375 /**
376  * Expect fallback to internal viewport if device is external and external viewport is not present.
377  */
TEST_F(ExternalMultiTouchInputMapperTest,Viewports_Fallback)378 TEST_F(ExternalMultiTouchInputMapperTest, Viewports_Fallback) {
379     std::list<NotifyArgs> args;
380 
381     // Expect the event to be sent to the internal viewport,
382     // because an external viewport is not present.
383     args += processKey(BTN_TOUCH, 1);
384     args += processId(1);
385     args += processPosition(100, 200);
386     args += processSync();
387 
388     assertNotifyArgs(args,
389                      VariantWith<NotifyMotionArgs>(
390                              AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(DISPLAY_ID))));
391 
392     // Expect the event to be sent to the external viewport if it is present.
393     DisplayViewport externalViewport =
394             createViewport(SECOND_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
395                            /*isActive=*/true, "local:1", NO_PORT, ViewportType::EXTERNAL);
396     mFakePolicy->addDisplayViewport(externalViewport);
397     std::optional<DisplayViewport> internalViewport =
398             mFakePolicy->getDisplayViewportByUniqueId("local:0");
399     mReaderConfiguration.setDisplayViewports({*internalViewport, externalViewport});
400     args = mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration,
401                                 InputReaderConfiguration::Change::DISPLAY_INFO);
402 
403     assertNotifyArgs(args,
404                      VariantWith<NotifyMotionArgs>(
405                              AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(DISPLAY_ID))),
406                      VariantWith<NotifyDeviceResetArgs>(WithDeviceId(DEVICE_ID)));
407     // Lift up the old pointer.
408     args = processKey(BTN_TOUCH, 0);
409     args += processId(-1);
410     args += processSync();
411 
412     // Send new pointer
413     args += processKey(BTN_TOUCH, 1);
414     args += processId(2);
415     args += processPosition(111, 211);
416     args += processSync();
417     assertNotifyArgs(args,
418                      VariantWith<NotifyMotionArgs>(AllOf(WithMotionAction(ACTION_DOWN),
419                                                          WithDisplayId(SECOND_DISPLAY_ID))));
420 }
421 
422 class MultiTouchInputMapperPointerModeUnitTest : public MultiTouchInputMapperUnitTest {
423 protected:
SetUp()424     void SetUp() override {
425         MultiTouchInputMapperUnitTest::SetUp();
426 
427         // TouchInputMapper goes into POINTER mode whenever INPUT_PROP_DIRECT is not set.
428         EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_DIRECT))
429                 .WillRepeatedly(Return(false));
430 
431         mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext,
432                                                            mFakePolicy->getReaderConfiguration());
433     }
434 };
435 
TEST_F(MultiTouchInputMapperPointerModeUnitTest,MouseToolOnlyDownWhenMouseButtonsAreDown)436 TEST_F(MultiTouchInputMapperPointerModeUnitTest, MouseToolOnlyDownWhenMouseButtonsAreDown) {
437     SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, true);
438 
439     std::list<NotifyArgs> args;
440 
441     // Set the tool type to mouse.
442     args += processKey(BTN_TOOL_MOUSE, 1);
443 
444     args += processPosition(100, 100);
445     args += processId(1);
446     ASSERT_THAT(args, IsEmpty());
447 
448     args = processSync();
449     ASSERT_THAT(args,
450                 ElementsAre(VariantWith<NotifyMotionArgs>(
451                                     AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
452                                           WithToolType(ToolType::MOUSE))),
453                             VariantWith<NotifyMotionArgs>(
454                                     AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
455                                           WithToolType(ToolType::MOUSE)))));
456 
457     // Setting BTN_TOUCH does not make a mouse pointer go down.
458     args = processKey(BTN_TOUCH, 1);
459     args += processSync();
460     ASSERT_THAT(args,
461                 ElementsAre(VariantWith<NotifyMotionArgs>(
462                         WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))));
463 
464     // The mouse button is pressed, so the mouse goes down.
465     args = processKey(BTN_MOUSE, 1);
466     args += processSync();
467     ASSERT_THAT(args,
468                 ElementsAre(VariantWith<NotifyMotionArgs>(
469                                     AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
470                                           WithToolType(ToolType::MOUSE))),
471                             VariantWith<NotifyMotionArgs>(
472                                     AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
473                                           WithToolType(ToolType::MOUSE),
474                                           WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))),
475                             VariantWith<NotifyMotionArgs>(
476                                     AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
477                                           WithToolType(ToolType::MOUSE),
478                                           WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
479                                           WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY)))));
480 
481     // The mouse button is released, so the mouse starts hovering.
482     args = processKey(BTN_MOUSE, 0);
483     args += processSync();
484     ASSERT_THAT(args,
485                 ElementsAre(VariantWith<NotifyMotionArgs>(
486                                     AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
487                                           WithButtonState(0), WithToolType(ToolType::MOUSE),
488                                           WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))),
489                             VariantWith<NotifyMotionArgs>(
490                                     AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
491                                           WithToolType(ToolType::MOUSE), WithButtonState(0))),
492                             VariantWith<NotifyMotionArgs>(
493                                     AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
494                                           WithToolType(ToolType::MOUSE))),
495                             VariantWith<NotifyMotionArgs>(
496                                     AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
497                                           WithToolType(ToolType::MOUSE)))));
498 
499     // Change the tool type so that it is no longer a mouse.
500     // The default tool type is finger, and the finger is already down.
501     args = processKey(BTN_TOOL_MOUSE, 0);
502     args += processSync();
503     ASSERT_THAT(args,
504                 ElementsAre(VariantWith<NotifyMotionArgs>(
505                                     AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
506                                           WithToolType(ToolType::MOUSE))),
507                             VariantWith<NotifyMotionArgs>(
508                                     AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
509                                           WithToolType(ToolType::FINGER)))));
510 }
511 
512 } // namespace android
513