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 "TouchpadInputMapper.h"
18
19 #include <android-base/logging.h>
20 #include <gtest/gtest.h>
21 #include <input/AccelerationCurve.h>
22
23 #include <log/log.h>
24 #include <thread>
25 #include "InputMapperTest.h"
26 #include "InterfaceMocks.h"
27 #include "TestConstants.h"
28 #include "TestEventMatchers.h"
29
30 #define TAG "TouchpadInputMapper_test"
31
32 namespace android {
33
34 using testing::Return;
35 using testing::VariantWith;
36 constexpr auto ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
37 constexpr auto ACTION_UP = AMOTION_EVENT_ACTION_UP;
38 constexpr auto BUTTON_PRESS = AMOTION_EVENT_ACTION_BUTTON_PRESS;
39 constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE;
40 constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
41 constexpr auto HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER;
42 constexpr auto HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT;
43 constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
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
48 /**
49 * Unit tests for TouchpadInputMapper.
50 */
51 class TouchpadInputMapperTest : public InputMapperUnitTest {
52 protected:
SetUp()53 void SetUp() override {
54 InputMapperUnitTest::SetUp();
55
56 // Present scan codes: BTN_TOUCH and BTN_TOOL_FINGER
57 expectScanCodes(/*present=*/true,
58 {BTN_LEFT, BTN_RIGHT, BTN_TOOL_FINGER, BTN_TOOL_QUINTTAP, BTN_TOUCH,
59 BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP});
60 // Missing scan codes that the mapper checks for.
61 expectScanCodes(/*present=*/false,
62 {BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_BRUSH, BTN_TOOL_PENCIL,
63 BTN_TOOL_AIRBRUSH});
64
65 // Current scan code state - all keys are UP by default
66 setScanCodeState(KeyState::UP, {BTN_TOUCH, BTN_STYLUS,
67 BTN_STYLUS2, BTN_0,
68 BTN_TOOL_FINGER, BTN_TOOL_PEN,
69 BTN_TOOL_RUBBER, BTN_TOOL_BRUSH,
70 BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH,
71 BTN_TOOL_MOUSE, BTN_TOOL_LENS,
72 BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP,
73 BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP,
74 BTN_LEFT, BTN_RIGHT,
75 BTN_MIDDLE, BTN_BACK,
76 BTN_SIDE, BTN_FORWARD,
77 BTN_EXTRA, BTN_TASK});
78
79 setKeyCodeState(KeyState::UP,
80 {AKEYCODE_STYLUS_BUTTON_PRIMARY, AKEYCODE_STYLUS_BUTTON_SECONDARY});
81
82 // Key mappings
83 EXPECT_CALL(mMockEventHub,
84 mapKey(EVENTHUB_ID, BTN_LEFT, /*usageCode=*/0, /*metaState=*/0, testing::_,
85 testing::_, testing::_))
86 .WillRepeatedly(Return(NAME_NOT_FOUND));
87
88 // Input properties - only INPUT_PROP_BUTTONPAD
89 EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_BUTTONPAD))
90 .WillRepeatedly(Return(true));
91 EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_SEMI_MT))
92 .WillRepeatedly(Return(false));
93
94 // Axes that the device has
95 setupAxis(ABS_MT_SLOT, /*valid=*/true, /*min=*/0, /*max=*/4, /*resolution=*/0);
96 setupAxis(ABS_MT_POSITION_X, /*valid=*/true, /*min=*/0, /*max=*/2000, /*resolution=*/24);
97 setupAxis(ABS_MT_POSITION_Y, /*valid=*/true, /*min=*/0, /*max=*/1000, /*resolution=*/24);
98 setupAxis(ABS_MT_PRESSURE, /*valid=*/true, /*min*/ 0, /*max=*/255, /*resolution=*/0);
99 // Axes that the device does not have
100 setupAxis(ABS_MT_ORIENTATION, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
101 setupAxis(ABS_MT_TOUCH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
102 setupAxis(ABS_MT_TOUCH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
103 setupAxis(ABS_MT_WIDTH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
104 setupAxis(ABS_MT_WIDTH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
105 setupAxis(ABS_MT_TRACKING_ID, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
106 setupAxis(ABS_MT_DISTANCE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
107 setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
108
109 EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT))
110 .WillRepeatedly(Return(0));
111 EXPECT_CALL(mMockEventHub, getMtSlotValues(EVENTHUB_ID, testing::_, testing::_))
112 .WillRepeatedly([]() -> base::Result<std::vector<int32_t>> {
113 return base::ResultError("Axis not supported", NAME_NOT_FOUND);
114 });
115 mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration);
116 }
117 };
118
119 /**
120 * Start moving the finger and then click the left touchpad button. Check whether HOVER_EXIT is
121 * generated when hovering stops. Currently, it is not.
122 * In the current implementation, HOVER_MOVE and ACTION_DOWN events are not sent out right away,
123 * but only after the button is released.
124 */
TEST_F(TouchpadInputMapperTest,HoverAndLeftButtonPress)125 TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) {
126 mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
127 DisplayViewport viewport =
128 createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
129 /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
130 mFakePolicy->addDisplayViewport(viewport);
131 std::list<NotifyArgs> args;
132
133 args += mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration,
134 InputReaderConfiguration::Change::DISPLAY_INFO);
135 ASSERT_THAT(args, testing::IsEmpty());
136
137 args += process(EV_ABS, ABS_MT_TRACKING_ID, 1);
138 args += process(EV_KEY, BTN_TOUCH, 1);
139 setScanCodeState(KeyState::DOWN, {BTN_TOOL_FINGER});
140 args += process(EV_KEY, BTN_TOOL_FINGER, 1);
141 args += process(EV_ABS, ABS_MT_POSITION_X, 50);
142 args += process(EV_ABS, ABS_MT_POSITION_Y, 50);
143 args += process(EV_ABS, ABS_MT_PRESSURE, 1);
144 args += process(EV_SYN, SYN_REPORT, 0);
145 ASSERT_THAT(args, testing::IsEmpty());
146
147 // Without this sleep, the test fails.
148 // TODO(b/284133337): Figure out whether this can be removed
149 std::this_thread::sleep_for(std::chrono::milliseconds(20));
150
151 args += process(EV_KEY, BTN_LEFT, 1);
152 setScanCodeState(KeyState::DOWN, {BTN_LEFT});
153 args += process(EV_SYN, SYN_REPORT, 0);
154
155 args += process(EV_KEY, BTN_LEFT, 0);
156 setScanCodeState(KeyState::UP, {BTN_LEFT});
157 args += process(EV_SYN, SYN_REPORT, 0);
158 ASSERT_THAT(args,
159 ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)),
160 VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
161 VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_EXIT)),
162 VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
163 VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)),
164 VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
165 VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
166 VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER))));
167
168 // Liftoff
169 args.clear();
170 args += process(EV_ABS, ABS_MT_PRESSURE, 0);
171 args += process(EV_ABS, ABS_MT_TRACKING_ID, -1);
172 args += process(EV_KEY, BTN_TOUCH, 0);
173 setScanCodeState(KeyState::UP, {BTN_TOOL_FINGER});
174 args += process(EV_KEY, BTN_TOOL_FINGER, 0);
175 args += process(EV_SYN, SYN_REPORT, 0);
176 ASSERT_THAT(args, testing::IsEmpty());
177 }
178
TEST_F(TouchpadInputMapperTest,TouchpadHardwareState)179 TEST_F(TouchpadInputMapperTest, TouchpadHardwareState) {
180 mReaderConfiguration.shouldNotifyTouchpadHardwareState = true;
181 std::list<NotifyArgs> args =
182 mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
183 InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
184
185 args += process(EV_ABS, ABS_MT_TRACKING_ID, 1);
186 args += process(EV_KEY, BTN_TOUCH, 1);
187 setScanCodeState(KeyState::DOWN, {BTN_TOOL_FINGER});
188 args += process(EV_KEY, BTN_TOOL_FINGER, 1);
189 args += process(EV_ABS, ABS_MT_POSITION_X, 50);
190 args += process(EV_ABS, ABS_MT_POSITION_Y, 50);
191 args += process(EV_ABS, ABS_MT_PRESSURE, 1);
192 args += process(EV_SYN, SYN_REPORT, 0);
193
194 mFakePolicy->assertTouchpadHardwareStateNotified();
195 }
196
TEST_F(TouchpadInputMapperTest,TouchpadAccelerationDisabled)197 TEST_F(TouchpadInputMapperTest, TouchpadAccelerationDisabled) {
198 mReaderConfiguration.touchpadAccelerationEnabled = false;
199 mReaderConfiguration.touchpadPointerSpeed = 3;
200
201 std::list<NotifyArgs> args =
202 mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
203 InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
204 auto* touchpadMapper = static_cast<TouchpadInputMapper*>(mMapper.get());
205
206 const auto accelCurvePropsDisabled =
207 touchpadMapper->getGesturePropertyForTesting("Pointer Accel Curve");
208 ASSERT_TRUE(accelCurvePropsDisabled.has_value());
209 std::vector<double> curveValuesDisabled = accelCurvePropsDisabled.value().getRealValues();
210 std::vector<AccelerationCurveSegment> curve =
211 createFlatAccelerationCurve(mReaderConfiguration.touchpadPointerSpeed);
212 double expectedBaseGain = curve[0].baseGain;
213 ASSERT_EQ(curveValuesDisabled[0], std::numeric_limits<double>::infinity());
214 ASSERT_EQ(curveValuesDisabled[1], 0);
215 ASSERT_NEAR(curveValuesDisabled[2], expectedBaseGain, EPSILON);
216 ASSERT_EQ(curveValuesDisabled[3], 0);
217 }
218
TEST_F(TouchpadInputMapperTest,TouchpadAccelerationEnabled)219 TEST_F(TouchpadInputMapperTest, TouchpadAccelerationEnabled) {
220 // Enable touchpad acceleration.
221 mReaderConfiguration.touchpadAccelerationEnabled = true;
222 mReaderConfiguration.touchpadPointerSpeed = 3;
223
224 std::list<NotifyArgs> args =
225 mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
226 InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
227 ASSERT_THAT(args, testing::IsEmpty());
228
229 auto* touchpadMapper = static_cast<TouchpadInputMapper*>(mMapper.get());
230
231 // Get the acceleration curve properties when acceleration is enabled.
232 const auto accelCurvePropsEnabled =
233 touchpadMapper->getGesturePropertyForTesting("Pointer Accel Curve");
234 ASSERT_TRUE(accelCurvePropsEnabled.has_value());
235
236 // Get the curve values.
237 std::vector<double> curveValuesEnabled = accelCurvePropsEnabled.value().getRealValues();
238
239 // Use createAccelerationCurveForPointerSensitivity to get expected curve segments.
240 std::vector<AccelerationCurveSegment> expectedCurveSegments =
241 createAccelerationCurveForPointerSensitivity(mReaderConfiguration.touchpadPointerSpeed);
242
243 // Iterate through the segments and compare the values.
244 for (size_t i = 0; i < expectedCurveSegments.size(); ++i) {
245 // Check max speed.
246 if (std::isinf(expectedCurveSegments[i].maxPointerSpeedMmPerS)) {
247 ASSERT_TRUE(std::isinf(curveValuesEnabled[i * 4 + 0]));
248 } else {
249 ASSERT_NEAR(curveValuesEnabled[i * 4 + 0],
250 expectedCurveSegments[i].maxPointerSpeedMmPerS, EPSILON);
251 }
252
253 // Check that the x^2 term is zero.
254 ASSERT_NEAR(curveValuesEnabled[i * 4 + 1], 0, EPSILON);
255 ASSERT_NEAR(curveValuesEnabled[i * 4 + 2], expectedCurveSegments[i].baseGain, EPSILON);
256 ASSERT_NEAR(curveValuesEnabled[i * 4 + 3], expectedCurveSegments[i].reciprocal, EPSILON);
257 }
258 }
259
260 } // namespace android
261