TouchpadInputMapper: test createHardwareProperties
Bug: 245989146
Test: atest inputflinger_tests:HardwarePropertiesTest
Change-Id: I9810cec1c458725c4513bc6d8f110a09090b0528
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index c0c6d1f..8fe6411 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -66,6 +66,7 @@
"mapper/accumulator/TouchButtonAccumulator.cpp",
"mapper/gestures/GestureConverter.cpp",
"mapper/gestures/GesturesLogging.cpp",
+ "mapper/gestures/HardwareProperties.cpp",
"mapper/gestures/HardwareStateConverter.cpp",
"mapper/gestures/PropertyProvider.cpp",
],
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index b826ab9..6ea004d 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -33,6 +33,7 @@
#include <statslog.h>
#include "TouchCursorInputMapperCommon.h"
#include "TouchpadInputMapper.h"
+#include "gestures/HardwareProperties.h"
#include "ui/Rotation.h"
namespace android {
@@ -119,61 +120,6 @@
return output;
}
-short getMaxTouchCount(const InputDeviceContext& context) {
- if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5;
- if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4;
- if (context.hasScanCode(BTN_TOOL_TRIPLETAP)) return 3;
- if (context.hasScanCode(BTN_TOOL_DOUBLETAP)) return 2;
- if (context.hasScanCode(BTN_TOOL_FINGER)) return 1;
- return 0;
-}
-
-HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
- HardwareProperties props;
- RawAbsoluteAxisInfo absMtPositionX;
- context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
- props.left = absMtPositionX.minValue;
- props.right = absMtPositionX.maxValue;
- props.res_x = absMtPositionX.resolution;
-
- RawAbsoluteAxisInfo absMtPositionY;
- context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
- props.top = absMtPositionY.minValue;
- props.bottom = absMtPositionY.maxValue;
- props.res_y = absMtPositionY.resolution;
-
- RawAbsoluteAxisInfo absMtOrientation;
- context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
- props.orientation_minimum = absMtOrientation.minValue;
- props.orientation_maximum = absMtOrientation.maxValue;
-
- RawAbsoluteAxisInfo absMtSlot;
- context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
- props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
- props.max_touch_cnt = getMaxTouchCount(context);
-
- // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
- // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads
- // that did this, so assume false.
- props.supports_t5r2 = false;
-
- props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT);
- props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD);
-
- // Mouse-only properties, which will always be false.
- props.has_wheel = false;
- props.wheel_is_hi_res = false;
-
- // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads
- // are haptic.
- props.is_haptic_pad = false;
-
- RawAbsoluteAxisInfo absMtPressure;
- context.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &absMtPressure);
- props.reports_pressure = absMtPressure.valid;
- return props;
-}
-
void gestureInterpreterCallback(void* clientData, const Gesture* gesture) {
TouchpadInputMapper* mapper = static_cast<TouchpadInputMapper*>(clientData);
mapper->consumeGesture(gesture);
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
new file mode 100644
index 0000000..04655dc
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HardwareProperties.h"
+
+namespace android {
+
+namespace {
+
+unsigned short getMaxTouchCount(const InputDeviceContext& context) {
+ if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5;
+ if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4;
+ if (context.hasScanCode(BTN_TOOL_TRIPLETAP)) return 3;
+ if (context.hasScanCode(BTN_TOOL_DOUBLETAP)) return 2;
+ if (context.hasScanCode(BTN_TOOL_FINGER)) return 1;
+ return 0;
+}
+
+} // namespace
+
+HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
+ HardwareProperties props;
+ RawAbsoluteAxisInfo absMtPositionX;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
+ props.left = absMtPositionX.minValue;
+ props.right = absMtPositionX.maxValue;
+ props.res_x = absMtPositionX.resolution;
+
+ RawAbsoluteAxisInfo absMtPositionY;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
+ props.top = absMtPositionY.minValue;
+ props.bottom = absMtPositionY.maxValue;
+ props.res_y = absMtPositionY.resolution;
+
+ RawAbsoluteAxisInfo absMtOrientation;
+ context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
+ props.orientation_minimum = absMtOrientation.minValue;
+ props.orientation_maximum = absMtOrientation.maxValue;
+
+ RawAbsoluteAxisInfo absMtSlot;
+ context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
+ props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
+ props.max_touch_cnt = getMaxTouchCount(context);
+
+ // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
+ // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads
+ // that did this, so assume false.
+ props.supports_t5r2 = false;
+
+ props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT);
+ props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD);
+
+ // Mouse-only properties, which will always be false.
+ props.has_wheel = false;
+ props.wheel_is_hi_res = false;
+
+ // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads
+ // are haptic.
+ props.is_haptic_pad = false;
+
+ RawAbsoluteAxisInfo absMtPressure;
+ context.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &absMtPressure);
+ props.reports_pressure = absMtPressure.valid;
+ return props;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareProperties.h b/services/inputflinger/reader/mapper/gestures/HardwareProperties.h
new file mode 100644
index 0000000..672f8c1
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/HardwareProperties.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "InputDevice.h"
+
+#include "include/gestures.h"
+
+namespace android {
+
+// Creates a Gestures library HardwareProperties struct for the touchpad described by the
+// InputDeviceContext.
+HardwareProperties createHardwareProperties(const InputDeviceContext& context);
+
+} // namespace android
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 3d6df30..f2e8885 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -47,6 +47,7 @@
"FakePointerController.cpp",
"FocusResolver_test.cpp",
"GestureConverter_test.cpp",
+ "HardwareProperties_test.cpp",
"HardwareStateConverter_test.cpp",
"InputDeviceMetricsCollector_test.cpp",
"InputMapperTest.cpp",
diff --git a/services/inputflinger/tests/HardwareProperties_test.cpp b/services/inputflinger/tests/HardwareProperties_test.cpp
new file mode 100644
index 0000000..8dfa8c8
--- /dev/null
+++ b/services/inputflinger/tests/HardwareProperties_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <gestures/HardwareProperties.h>
+
+#include <memory>
+#include <set>
+
+#include <gtest/gtest.h>
+#include <linux/input-event-codes.h>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InterfaceMocks.h"
+#include "TestConstants.h"
+#include "include/gestures.h"
+
+namespace android {
+
+using testing::Return;
+
+class HardwarePropertiesTest : public testing::Test {
+public:
+ HardwarePropertiesTest() {
+ EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub));
+ InputDeviceIdentifier identifier;
+ identifier.name = "device";
+ identifier.location = "USB1";
+ mDevice = std::make_unique<InputDevice>(&mMockInputReaderContext, DEVICE_ID,
+ /*generation=*/2, identifier);
+ mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID);
+ }
+
+protected:
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr int32_t EVENTHUB_ID = 1;
+
+ void setupValidAxis(int axis, int32_t min, int32_t max, int32_t resolution) {
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_))
+ .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) {
+ outAxisInfo->valid = true;
+ outAxisInfo->minValue = min;
+ outAxisInfo->maxValue = max;
+ outAxisInfo->flat = 0;
+ outAxisInfo->fuzz = 0;
+ outAxisInfo->resolution = resolution;
+ return OK;
+ });
+ }
+
+ void setupInvalidAxis(int axis) {
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_))
+ .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) {
+ outAxisInfo->valid = false;
+ return -1;
+ });
+ }
+
+ void setProperty(int property, bool value) {
+ EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, property))
+ .WillRepeatedly(Return(value));
+ }
+
+ void setButtonsPresent(std::set<int> buttonCodes, bool present) {
+ for (const auto& buttonCode : buttonCodes) {
+ EXPECT_CALL(mMockEventHub, hasScanCode(EVENTHUB_ID, buttonCode))
+ .WillRepeatedly(Return(present));
+ }
+ }
+
+ MockEventHubInterface mMockEventHub;
+ MockInputReaderContext mMockInputReaderContext;
+ std::unique_ptr<InputDevice> mDevice;
+ std::unique_ptr<InputDeviceContext> mDeviceContext;
+};
+
+TEST_F(HardwarePropertiesTest, FancyTouchpad) {
+ setupValidAxis(ABS_MT_POSITION_X, 0, 2048, 27);
+ setupValidAxis(ABS_MT_POSITION_Y, 0, 1500, 30);
+ setupValidAxis(ABS_MT_ORIENTATION, -3, 4, 0);
+ setupValidAxis(ABS_MT_SLOT, 0, 15, 0);
+ setupValidAxis(ABS_MT_PRESSURE, 0, 256, 0);
+
+ setProperty(INPUT_PROP_SEMI_MT, false);
+ setProperty(INPUT_PROP_BUTTONPAD, true);
+
+ setButtonsPresent({BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP,
+ BTN_TOOL_QUINTTAP},
+ true);
+
+ HardwareProperties hwprops = createHardwareProperties(*mDeviceContext);
+ EXPECT_NEAR(0, hwprops.left, EPSILON);
+ EXPECT_NEAR(0, hwprops.top, EPSILON);
+ EXPECT_NEAR(2048, hwprops.right, EPSILON);
+ EXPECT_NEAR(1500, hwprops.bottom, EPSILON);
+
+ EXPECT_NEAR(27, hwprops.res_x, EPSILON);
+ EXPECT_NEAR(30, hwprops.res_y, EPSILON);
+
+ EXPECT_NEAR(-3, hwprops.orientation_minimum, EPSILON);
+ EXPECT_NEAR(4, hwprops.orientation_maximum, EPSILON);
+
+ EXPECT_EQ(16, hwprops.max_finger_cnt);
+ EXPECT_EQ(5, hwprops.max_touch_cnt);
+
+ EXPECT_FALSE(hwprops.supports_t5r2);
+ EXPECT_FALSE(hwprops.support_semi_mt);
+ EXPECT_TRUE(hwprops.is_button_pad);
+ EXPECT_FALSE(hwprops.has_wheel);
+ EXPECT_FALSE(hwprops.wheel_is_hi_res);
+ EXPECT_FALSE(hwprops.is_haptic_pad);
+ EXPECT_TRUE(hwprops.reports_pressure);
+}
+
+TEST_F(HardwarePropertiesTest, BasicTouchpad) {
+ setupValidAxis(ABS_MT_POSITION_X, 0, 1024, 0);
+ setupValidAxis(ABS_MT_POSITION_Y, 0, 768, 0);
+ setupValidAxis(ABS_MT_SLOT, 0, 7, 0);
+
+ setupInvalidAxis(ABS_MT_ORIENTATION);
+ setupInvalidAxis(ABS_MT_PRESSURE);
+
+ setProperty(INPUT_PROP_SEMI_MT, false);
+ setProperty(INPUT_PROP_BUTTONPAD, false);
+
+ setButtonsPresent({BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP}, true);
+ setButtonsPresent({BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP}, false);
+
+ HardwareProperties hwprops = createHardwareProperties(*mDeviceContext);
+ EXPECT_NEAR(0, hwprops.left, EPSILON);
+ EXPECT_NEAR(0, hwprops.top, EPSILON);
+ EXPECT_NEAR(1024, hwprops.right, EPSILON);
+ EXPECT_NEAR(768, hwprops.bottom, EPSILON);
+
+ EXPECT_NEAR(0, hwprops.res_x, EPSILON);
+ EXPECT_NEAR(0, hwprops.res_y, EPSILON);
+
+ EXPECT_NEAR(0, hwprops.orientation_minimum, EPSILON);
+ EXPECT_NEAR(0, hwprops.orientation_maximum, EPSILON);
+
+ EXPECT_EQ(8, hwprops.max_finger_cnt);
+ EXPECT_EQ(3, hwprops.max_touch_cnt);
+
+ EXPECT_FALSE(hwprops.supports_t5r2);
+ EXPECT_FALSE(hwprops.support_semi_mt);
+ EXPECT_FALSE(hwprops.is_button_pad);
+ EXPECT_FALSE(hwprops.has_wheel);
+ EXPECT_FALSE(hwprops.wheel_is_hi_res);
+ EXPECT_FALSE(hwprops.is_haptic_pad);
+ EXPECT_FALSE(hwprops.reports_pressure);
+}
+
+} // namespace android