1 /*
2  * Copyright 2023 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 "../InputDeviceMetricsSource.h"
18 
19 #include <NotifyArgsBuilders.h>
20 
21 #include <android/input.h>
22 #include <ftl/enum.h>
23 #include <gtest/gtest.h>
24 #include <input/Input.h>
25 #include <input/InputEventBuilders.h>
26 #include <linux/input.h>
27 
28 #include <set>
29 
30 namespace android {
31 
32 namespace {
33 
34 constexpr auto ALL_USAGE_SOURCES = ftl::enum_range<InputDeviceUsageSource>();
35 constexpr uint32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
36 constexpr uint32_t STYLUS = AINPUT_SOURCE_STYLUS;
37 constexpr uint32_t KEY_SOURCES =
38         AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD;
39 constexpr int32_t POINTER_1_DOWN =
40         AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
41 
42 } // namespace
43 
44 // --- InputDeviceMetricsSourceDeviceClassificationTest ---
45 
46 class DeviceClassificationFixture : public ::testing::Test,
47                                     public ::testing::WithParamInterface<InputDeviceUsageSource> {};
48 
TEST_P(DeviceClassificationFixture,ValidClassifications)49 TEST_P(DeviceClassificationFixture, ValidClassifications) {
50     const InputDeviceUsageSource usageSource = GetParam();
51 
52     // Use a switch to ensure a test is added for all source classifications.
53     switch (usageSource) {
54         case InputDeviceUsageSource::UNKNOWN: {
55             ASSERT_EQ(InputDeviceUsageSource::UNKNOWN,
56                       getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NONE,
57                                                KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, TOUCHSCREEN)
58                                                        .build()));
59 
60             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::UNKNOWN};
61             ASSERT_EQ(srcs,
62                       getUsageSourcesForMotionArgs(
63                               MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_KEYBOARD)
64                                       .pointer(PointerBuilder(/*id=*/1, ToolType::PALM)
65                                                        .x(100)
66                                                        .y(200))
67                                       .build()));
68             break;
69         }
70 
71         case InputDeviceUsageSource::BUTTONS: {
72             ASSERT_EQ(InputDeviceUsageSource::BUTTONS,
73                       getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
74                                                KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
75                                                        .keyCode(AKEYCODE_STYLUS_BUTTON_TAIL)
76                                                        .build()));
77             break;
78         }
79 
80         case InputDeviceUsageSource::KEYBOARD: {
81             ASSERT_EQ(InputDeviceUsageSource::KEYBOARD,
82                       getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
83                                                KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
84                                                        .build()));
85             break;
86         }
87 
88         case InputDeviceUsageSource::DPAD: {
89             ASSERT_EQ(InputDeviceUsageSource::DPAD,
90                       getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
91                                                KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
92                                                        .keyCode(AKEYCODE_DPAD_CENTER)
93                                                        .build()));
94 
95             ASSERT_EQ(InputDeviceUsageSource::DPAD,
96                       getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
97                                                KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
98                                                        .keyCode(AKEYCODE_DPAD_CENTER)
99                                                        .build()));
100             break;
101         }
102 
103         case InputDeviceUsageSource::GAMEPAD: {
104             ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
105                       getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
106                                                KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
107                                                        .keyCode(AKEYCODE_BUTTON_A)
108                                                        .build()));
109 
110             ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
111                       getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
112                                                KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
113                                                        .keyCode(AKEYCODE_BUTTON_A)
114                                                        .build()));
115             break;
116         }
117 
118         case InputDeviceUsageSource::JOYSTICK: {
119             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::JOYSTICK};
120             ASSERT_EQ(srcs,
121                       getUsageSourcesForMotionArgs(
122                               MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_JOYSTICK)
123                                       .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
124                                                        .axis(AMOTION_EVENT_AXIS_GAS, 1.f))
125                                       .build()));
126             break;
127         }
128 
129         case InputDeviceUsageSource::MOUSE: {
130             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE};
131             ASSERT_EQ(srcs,
132                       getUsageSourcesForMotionArgs(
133                               MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
134                                                 AINPUT_SOURCE_MOUSE)
135                                       .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
136                                                        .x(100)
137                                                        .y(200))
138                                       .build()));
139             break;
140         }
141 
142         case InputDeviceUsageSource::MOUSE_CAPTURED: {
143             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE_CAPTURED};
144             ASSERT_EQ(srcs,
145                       getUsageSourcesForMotionArgs(
146                               MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
147                                                 AINPUT_SOURCE_MOUSE_RELATIVE)
148                                       .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
149                                                        .x(100)
150                                                        .y(200)
151                                                        .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 100)
152                                                        .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 200))
153                                       .build()));
154             break;
155         }
156 
157         case InputDeviceUsageSource::TOUCHPAD: {
158             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD};
159             ASSERT_EQ(srcs,
160                       getUsageSourcesForMotionArgs(
161                               MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
162                                       .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
163                                                        .x(100)
164                                                        .y(200))
165                                       .build()));
166             break;
167         }
168 
169         case InputDeviceUsageSource::TOUCHPAD_CAPTURED: {
170             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD_CAPTURED};
171             ASSERT_EQ(srcs,
172                       getUsageSourcesForMotionArgs(
173                               MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHPAD)
174                                       .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
175                                                        .x(100)
176                                                        .y(200)
177                                                        .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 1)
178                                                        .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 2))
179                                       .build()));
180             break;
181         }
182 
183         case InputDeviceUsageSource::ROTARY_ENCODER: {
184             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::ROTARY_ENCODER};
185             ASSERT_EQ(srcs,
186                       getUsageSourcesForMotionArgs(
187                               MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
188                                                 AINPUT_SOURCE_ROTARY_ENCODER)
189                                       .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
190                                                        .axis(AMOTION_EVENT_AXIS_SCROLL, 10)
191                                                        .axis(AMOTION_EVENT_AXIS_VSCROLL, 10))
192                                       .build()));
193             break;
194         }
195 
196         case InputDeviceUsageSource::STYLUS_DIRECT: {
197             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_DIRECT};
198             ASSERT_EQ(srcs,
199                       getUsageSourcesForMotionArgs(
200                               MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
201                                                 STYLUS | TOUCHSCREEN)
202                                       .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
203                                                        .x(100)
204                                                        .y(200))
205                                       .build()));
206             break;
207         }
208 
209         case InputDeviceUsageSource::STYLUS_INDIRECT: {
210             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_INDIRECT};
211             ASSERT_EQ(srcs,
212                       getUsageSourcesForMotionArgs(
213                               MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
214                                                 STYLUS | TOUCHSCREEN | AINPUT_SOURCE_MOUSE)
215                                       .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
216                                                        .x(100)
217                                                        .y(200))
218                                       .build()));
219             break;
220         }
221 
222         case InputDeviceUsageSource::STYLUS_FUSED: {
223             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_FUSED};
224             ASSERT_EQ(srcs,
225                       getUsageSourcesForMotionArgs(
226                               MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
227                                                 AINPUT_SOURCE_BLUETOOTH_STYLUS | TOUCHSCREEN)
228                                       .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
229                                                        .x(100)
230                                                        .y(200))
231                                       .build()));
232             break;
233         }
234 
235         case InputDeviceUsageSource::TOUCH_NAVIGATION: {
236             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCH_NAVIGATION};
237             ASSERT_EQ(srcs,
238                       getUsageSourcesForMotionArgs(
239                               MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
240                                                 AINPUT_SOURCE_TOUCH_NAVIGATION)
241                                       .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
242                                                        .x(100)
243                                                        .y(200))
244                                       .build()));
245             break;
246         }
247 
248         case InputDeviceUsageSource::TOUCHSCREEN: {
249             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN};
250             ASSERT_EQ(srcs,
251                       getUsageSourcesForMotionArgs(
252                               MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN)
253                                       .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
254                                                        .x(100)
255                                                        .y(200))
256                                       .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER)
257                                                        .x(300)
258                                                        .y(400))
259                                       .build()));
260             break;
261         }
262 
263         case InputDeviceUsageSource::TRACKBALL: {
264             std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TRACKBALL};
265             ASSERT_EQ(srcs,
266                       getUsageSourcesForMotionArgs(
267                               MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
268                                                 AINPUT_SOURCE_TRACKBALL)
269                                       .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
270                                                        .axis(AMOTION_EVENT_AXIS_VSCROLL, 100)
271                                                        .axis(AMOTION_EVENT_AXIS_HSCROLL, 200))
272                                       .build()));
273             break;
274         }
275     }
276 }
277 
278 INSTANTIATE_TEST_SUITE_P(InputDeviceMetricsSourceDeviceClassificationTest,
279                          DeviceClassificationFixture,
280                          ::testing::ValuesIn(ALL_USAGE_SOURCES.begin(), ALL_USAGE_SOURCES.end()),
__anon548648b80202(const testing::TestParamInfo<InputDeviceUsageSource>& testParamInfo) 281                          [](const testing::TestParamInfo<InputDeviceUsageSource>& testParamInfo) {
282                              return ftl::enum_string(testParamInfo.param);
283                          });
284 
TEST(InputDeviceMetricsSourceDeviceClassificationTest,MixedClassificationTouchscreenStylus)285 TEST(InputDeviceMetricsSourceDeviceClassificationTest, MixedClassificationTouchscreenStylus) {
286     std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN,
287                                           InputDeviceUsageSource::STYLUS_DIRECT};
288     ASSERT_EQ(srcs,
289               getUsageSourcesForMotionArgs(
290                       MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN | STYLUS)
291                               .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(100).y(200))
292                               .pointer(PointerBuilder(/*id=*/2, ToolType::STYLUS).x(300).y(400))
293                               .build()));
294 }
295 
296 } // namespace android
297