TouchpadInputMapper: Add gesture property provider
Gesture properties are configurable parameters used by the gestures
library [0]. These will be used for per-device tuning and to implement
various touchpad settings. The property provider gives Android an
interface with which to get and set property values.
[0]: https://chromium.googlesource.com/chromiumos/platform/gestures/+/refs/heads/main/docs/gesture_properties.md
Bug: 251196347
Test: atest inputflinger_tests
Test: on device with some hacky test code
Change-Id: I7723dcbabf29e13c8ab0907c71e5dd1faa766b45
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 58a5c31..af40fed 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -55,6 +55,7 @@
"LatencyTracker_test.cpp",
"NotifyArgs_test.cpp",
"PreferStylusOverTouch_test.cpp",
+ "PropertyProvider_test.cpp",
"TestInputListener.cpp",
"UinputDevice.cpp",
"UnwantedInteractionBlocker_test.cpp",
diff --git a/services/inputflinger/tests/PropertyProvider_test.cpp b/services/inputflinger/tests/PropertyProvider_test.cpp
new file mode 100644
index 0000000..42a6a9f
--- /dev/null
+++ b/services/inputflinger/tests/PropertyProvider_test.cpp
@@ -0,0 +1,286 @@
+/*
+ * 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/PropertyProvider.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "include/gestures.h"
+
+namespace android {
+
+using testing::ElementsAre;
+
+class PropertyProviderTest : public testing::Test {
+protected:
+ PropertyProvider mProvider;
+};
+
+TEST_F(PropertyProviderTest, Int_Create) {
+ const size_t COUNT = 4;
+ int intData[COUNT] = {0, 0, 0, 0};
+ int initialValues[COUNT] = {1, 2, 3, 4};
+ gesturePropProvider.create_int_fn(&mProvider, "Some Integers", intData, COUNT, initialValues);
+
+ ASSERT_TRUE(mProvider.hasProperty("Some Integers"));
+ GesturesProp& prop = mProvider.getProperty("Some Integers");
+ EXPECT_EQ(prop.getName(), "Some Integers");
+ EXPECT_EQ(prop.getCount(), COUNT);
+ EXPECT_THAT(intData, ElementsAre(1, 2, 3, 4));
+}
+
+TEST_F(PropertyProviderTest, Int_Get) {
+ const size_t COUNT = 4;
+ int intData[COUNT] = {0, 0, 0, 0};
+ int initialValues[COUNT] = {9, 9, 9, 9};
+ GesturesProp* propPtr = gesturePropProvider.create_int_fn(&mProvider, "Some Integers", intData,
+ COUNT, initialValues);
+
+ // Get handlers are supposed to be called before the property's data is accessed, so they can
+ // update it if necessary. This getter updates the values, so that the ordering can be checked.
+ GesturesPropGetHandler getter{[](void* handlerData) -> GesturesPropBool {
+ int* array = static_cast<int*>(handlerData);
+ array[0] = 1;
+ array[1] = 2;
+ array[2] = 3;
+ array[3] = 4;
+ return true;
+ }};
+ gesturePropProvider.register_handlers_fn(&mProvider, propPtr, /* handler_data= */ intData,
+ getter, nullptr);
+
+ ASSERT_TRUE(mProvider.hasProperty("Some Integers"));
+ GesturesProp& prop = mProvider.getProperty("Some Integers");
+ EXPECT_THAT(prop.getIntValues(), ElementsAre(1, 2, 3, 4));
+}
+
+TEST_F(PropertyProviderTest, Int_Set) {
+ const size_t COUNT = 4;
+ int intData[COUNT] = {0, 0, 0, 0};
+ int initialValues[COUNT] = {9, 9, 9, 9};
+ GesturesProp* propPtr = gesturePropProvider.create_int_fn(&mProvider, "Some Integers", intData,
+ COUNT, initialValues);
+
+ struct SetterData {
+ bool setterCalled;
+ int* propertyData;
+ };
+ SetterData setterData = {false, intData};
+ GesturesPropSetHandler setter{[](void* handlerData) {
+ SetterData* data = static_cast<SetterData*>(handlerData);
+ // Set handlers should be called after the property's data has changed, so check the data.
+ EXPECT_EQ(data->propertyData[0], 1);
+ EXPECT_EQ(data->propertyData[1], 2);
+ EXPECT_EQ(data->propertyData[2], 3);
+ EXPECT_EQ(data->propertyData[3], 4);
+ data->setterCalled = true;
+ }};
+ gesturePropProvider.register_handlers_fn(&mProvider, propPtr, /* handler_data= */ &setterData,
+ nullptr, setter);
+
+ ASSERT_TRUE(mProvider.hasProperty("Some Integers"));
+ GesturesProp& prop = mProvider.getProperty("Some Integers");
+ prop.setIntValues({1, 2, 3, 4});
+ EXPECT_THAT(intData, ElementsAre(1, 2, 3, 4));
+ EXPECT_TRUE(setterData.setterCalled);
+ EXPECT_THAT(prop.getIntValues(), ElementsAre(1, 2, 3, 4));
+}
+
+TEST_F(PropertyProviderTest, Bool_Create) {
+ const size_t COUNT = 3;
+ GesturesPropBool boolData[COUNT] = {false, false, false};
+ GesturesPropBool initialValues[COUNT] = {true, false, false};
+ gesturePropProvider.create_bool_fn(&mProvider, "Some Booleans", boolData, COUNT, initialValues);
+
+ ASSERT_TRUE(mProvider.hasProperty("Some Booleans"));
+ GesturesProp& prop = mProvider.getProperty("Some Booleans");
+ EXPECT_EQ(prop.getName(), "Some Booleans");
+ EXPECT_EQ(prop.getCount(), COUNT);
+ EXPECT_THAT(boolData, ElementsAre(true, false, false));
+}
+
+TEST_F(PropertyProviderTest, Bool_Get) {
+ const size_t COUNT = 3;
+ GesturesPropBool boolData[COUNT] = {false, false, false};
+ GesturesPropBool initialValues[COUNT] = {true, false, false};
+ GesturesProp* propPtr = gesturePropProvider.create_bool_fn(&mProvider, "Some Booleans",
+ boolData, COUNT, initialValues);
+
+ // Get handlers are supposed to be called before the property's data is accessed, so they can
+ // update it if necessary. This getter updates the values, so that the ordering can be checked.
+ GesturesPropGetHandler getter{[](void* handlerData) -> GesturesPropBool {
+ GesturesPropBool* array = static_cast<GesturesPropBool*>(handlerData);
+ array[0] = false;
+ array[1] = true;
+ array[2] = true;
+ return true;
+ }};
+ gesturePropProvider.register_handlers_fn(&mProvider, propPtr, /* handler_data= */ boolData,
+ getter, nullptr);
+
+ ASSERT_TRUE(mProvider.hasProperty("Some Booleans"));
+ GesturesProp& prop = mProvider.getProperty("Some Booleans");
+ EXPECT_THAT(prop.getBoolValues(), ElementsAre(false, true, true));
+}
+
+TEST_F(PropertyProviderTest, Bool_Set) {
+ const size_t COUNT = 3;
+ GesturesPropBool boolData[COUNT] = {false, false, false};
+ GesturesPropBool initialValues[COUNT] = {true, false, false};
+ GesturesProp* propPtr = gesturePropProvider.create_bool_fn(&mProvider, "Some Booleans",
+ boolData, COUNT, initialValues);
+
+ struct SetterData {
+ bool setterCalled;
+ GesturesPropBool* propertyData;
+ };
+ SetterData setterData = {false, boolData};
+ GesturesPropSetHandler setter{[](void* handlerData) {
+ SetterData* data = static_cast<SetterData*>(handlerData);
+ // Set handlers should be called after the property's data has changed, so check the data.
+ EXPECT_EQ(data->propertyData[0], false);
+ EXPECT_EQ(data->propertyData[1], true);
+ EXPECT_EQ(data->propertyData[2], true);
+ data->setterCalled = true;
+ }};
+ gesturePropProvider.register_handlers_fn(&mProvider, propPtr, /* handler_data= */ &setterData,
+ nullptr, setter);
+
+ ASSERT_TRUE(mProvider.hasProperty("Some Booleans"));
+ GesturesProp& prop = mProvider.getProperty("Some Booleans");
+ prop.setBoolValues({false, true, true});
+ EXPECT_THAT(boolData, ElementsAre(false, true, true));
+ EXPECT_TRUE(setterData.setterCalled);
+ EXPECT_THAT(prop.getBoolValues(), ElementsAre(false, true, true));
+}
+
+TEST_F(PropertyProviderTest, Real_Create) {
+ const size_t COUNT = 3;
+ double realData[COUNT] = {0.0, 0.0, 0.0};
+ double initialValues[COUNT] = {3.14, 0.7, -5.0};
+ gesturePropProvider.create_real_fn(&mProvider, "Some Reals", realData, COUNT, initialValues);
+
+ ASSERT_TRUE(mProvider.hasProperty("Some Reals"));
+ GesturesProp& prop = mProvider.getProperty("Some Reals");
+ EXPECT_EQ(prop.getName(), "Some Reals");
+ EXPECT_EQ(prop.getCount(), COUNT);
+ EXPECT_THAT(realData, ElementsAre(3.14, 0.7, -5.0));
+}
+
+TEST_F(PropertyProviderTest, Real_Get) {
+ const size_t COUNT = 3;
+ double realData[COUNT] = {0.0, 0.0, 0.0};
+ double initialValues[COUNT] = {-1.0, -1.0, -1.0};
+ GesturesProp* propPtr = gesturePropProvider.create_real_fn(&mProvider, "Some Reals", realData,
+ COUNT, initialValues);
+
+ // Get handlers are supposed to be called before the property's data is accessed, so they can
+ // update it if necessary. This getter updates the values, so that the ordering can be checked.
+ GesturesPropGetHandler getter{[](void* handlerData) -> GesturesPropBool {
+ double* array = static_cast<double*>(handlerData);
+ array[0] = 3.14;
+ array[1] = 0.7;
+ array[2] = -5.0;
+ return true;
+ }};
+ gesturePropProvider.register_handlers_fn(&mProvider, propPtr, /* handler_data= */ realData,
+ getter, nullptr);
+
+ ASSERT_TRUE(mProvider.hasProperty("Some Reals"));
+ GesturesProp& prop = mProvider.getProperty("Some Reals");
+ EXPECT_THAT(prop.getRealValues(), ElementsAre(3.14, 0.7, -5.0));
+}
+
+TEST_F(PropertyProviderTest, Real_Set) {
+ const size_t COUNT = 3;
+ double realData[COUNT] = {0.0, 0.0, 0.0};
+ double initialValues[COUNT] = {-1.0, -1.0, -1.0};
+ GesturesProp* propPtr = gesturePropProvider.create_real_fn(&mProvider, "Some Reals", realData,
+ COUNT, initialValues);
+
+ struct SetterData {
+ bool setterCalled;
+ double* propertyData;
+ };
+ SetterData setterData = {false, realData};
+ GesturesPropSetHandler setter{[](void* handlerData) {
+ SetterData* data = static_cast<SetterData*>(handlerData);
+ // Set handlers should be called after the property's data has changed, so check the data.
+ EXPECT_EQ(data->propertyData[0], 3.14);
+ EXPECT_EQ(data->propertyData[1], 0.7);
+ EXPECT_EQ(data->propertyData[2], -5.0);
+ data->setterCalled = true;
+ }};
+ gesturePropProvider.register_handlers_fn(&mProvider, propPtr, /* handler_data= */ &setterData,
+ nullptr, setter);
+
+ ASSERT_TRUE(mProvider.hasProperty("Some Reals"));
+ GesturesProp& prop = mProvider.getProperty("Some Reals");
+ prop.setRealValues({3.14, 0.7, -5.0});
+ EXPECT_THAT(realData, ElementsAre(3.14, 0.7, -5.0));
+ EXPECT_TRUE(setterData.setterCalled);
+ EXPECT_THAT(prop.getRealValues(), ElementsAre(3.14, 0.7, -5.0));
+}
+
+TEST_F(PropertyProviderTest, String_Create) {
+ const char* str = nullptr;
+ std::string initialValue = "Foo";
+ gesturePropProvider.create_string_fn(&mProvider, "A String", &str, initialValue.c_str());
+
+ ASSERT_TRUE(mProvider.hasProperty("A String"));
+ GesturesProp& prop = mProvider.getProperty("A String");
+ EXPECT_EQ(prop.getName(), "A String");
+ EXPECT_EQ(prop.getCount(), 1u);
+ EXPECT_STREQ(str, "Foo");
+}
+
+TEST_F(PropertyProviderTest, String_Get) {
+ const char* str = nullptr;
+ std::string initialValue = "Foo";
+ GesturesProp* propPtr = gesturePropProvider.create_string_fn(&mProvider, "A String", &str,
+ initialValue.c_str());
+
+ // Get handlers are supposed to be called before the property's data is accessed, so they can
+ // update it if necessary. This getter updates the values, so that the ordering can be checked.
+ struct GetterData {
+ const char** strPtr;
+ std::string newValue; // Have to store the new value outside getter so it stays allocated.
+ };
+ GetterData getterData = {&str, "Bar"};
+ GesturesPropGetHandler getter{[](void* handlerData) -> GesturesPropBool {
+ GetterData* data = static_cast<GetterData*>(handlerData);
+ *data->strPtr = data->newValue.c_str();
+ return true;
+ }};
+ gesturePropProvider.register_handlers_fn(&mProvider, propPtr, /* handler_data= */ &getterData,
+ getter, nullptr);
+
+ ASSERT_TRUE(mProvider.hasProperty("A String"));
+ GesturesProp& prop = mProvider.getProperty("A String");
+ EXPECT_EQ(prop.getStringValue(), "Bar");
+}
+
+TEST_F(PropertyProviderTest, Free) {
+ int intData = 0;
+ int initialValue = 42;
+ GesturesProp* propPtr =
+ gesturePropProvider.create_int_fn(&mProvider, "Foo", &intData, 1, &initialValue);
+ gesturePropProvider.free_fn(&mProvider, propPtr);
+
+ EXPECT_FALSE(mProvider.hasProperty("Foo"));
+}
+
+} // namespace android