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/reader/Android.bp b/services/inputflinger/reader/Android.bp
index f3b680b..d29692c 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -64,6 +64,7 @@
"mapper/gestures/GestureConverter.cpp",
"mapper/gestures/GesturesLogging.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 3b51be8..89ba07e 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -96,11 +96,12 @@
mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
// Even though we don't explicitly delete copy/move semantics, it's safe to
- // give away a pointer to TouchpadInputMapper here because
+ // give away pointers to TouchpadInputMapper and its members here because
// 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and
// 2) TouchpadInputMapper is stored as a unique_ptr and not moved.
+ mGestureInterpreter->SetPropProvider(const_cast<GesturesPropProvider*>(&gesturePropProvider),
+ &mPropertyProvider);
mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
- // TODO(b/251196347): set a property provider, so we can change gesture properties.
// TODO(b/251196347): set a timer provider, so the library can use timers.
}
@@ -108,6 +109,15 @@
if (mPointerController != nullptr) {
mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
}
+
+ // The gesture interpreter's destructor will call its property provider's free function for all
+ // gesture properties, in this case calling PropertyProvider::freeProperty using a raw pointer
+ // to mPropertyProvider. Depending on the declaration order in TouchpadInputMapper.h, this may
+ // happen after mPropertyProvider has been destructed, causing allocation errors. Depending on
+ // declaration order to avoid crashes seems rather fragile, so explicitly clear the property
+ // provider here to ensure all the freeProperty calls happen before mPropertyProvider is
+ // destructed.
+ mGestureInterpreter->SetPropProvider(nullptr, nullptr);
}
uint32_t TouchpadInputMapper::getSources() const {
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 3a92211..8ffc8a0 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -29,6 +29,7 @@
#include "NotifyArgs.h"
#include "gestures/GestureConverter.h"
#include "gestures/HardwareStateConverter.h"
+#include "gestures/PropertyProvider.h"
#include "include/gestures.h"
@@ -57,6 +58,8 @@
mGestureInterpreter;
std::shared_ptr<PointerControllerInterface> mPointerController;
+ PropertyProvider mPropertyProvider;
+
HardwareStateConverter mStateConverter;
GestureConverter mGestureConverter;
diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
new file mode 100644
index 0000000..2e12854
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 "../Macros.h"
+
+#include "gestures/PropertyProvider.h"
+
+#include <algorithm>
+#include <utility>
+
+#include <ftl/enum.h>
+#include <log/log_main.h>
+
+namespace android {
+
+namespace {
+
+GesturesProp* createInt(void* data, const char* name, int* loc, size_t count, const int* init) {
+ return static_cast<PropertyProvider*>(data)->createIntArrayProperty(name, loc, count, init);
+}
+
+GesturesProp* createBool(void* data, const char* name, GesturesPropBool* loc, size_t count,
+ const GesturesPropBool* init) {
+ return static_cast<PropertyProvider*>(data)->createBoolArrayProperty(name, loc, count, init);
+}
+
+GesturesProp* createString(void* data, const char* name, const char** loc, const char* const init) {
+ return static_cast<PropertyProvider*>(data)->createStringProperty(name, loc, init);
+}
+
+GesturesProp* createReal(void* data, const char* name, double* loc, size_t count,
+ const double* init) {
+ return static_cast<PropertyProvider*>(data)->createRealArrayProperty(name, loc, count, init);
+}
+
+void registerHandlers(void* data, GesturesProp* prop, void* handlerData,
+ GesturesPropGetHandler getter, GesturesPropSetHandler setter) {
+ prop->registerHandlers(handlerData, getter, setter);
+}
+
+void freeProperty(void* data, GesturesProp* prop) {
+ static_cast<PropertyProvider*>(data)->freeProperty(prop);
+}
+
+} // namespace
+
+const GesturesPropProvider gesturePropProvider = {
+ .create_int_fn = createInt,
+ .create_bool_fn = createBool,
+ .create_string_fn = createString,
+ .create_real_fn = createReal,
+ .register_handlers_fn = registerHandlers,
+ .free_fn = freeProperty,
+};
+
+bool PropertyProvider::hasProperty(const std::string name) const {
+ return mProperties.find(name) != mProperties.end();
+}
+
+GesturesProp& PropertyProvider::getProperty(const std::string name) {
+ return mProperties.at(name);
+}
+
+GesturesProp* PropertyProvider::createIntArrayProperty(const std::string name, int* loc,
+ size_t count, const int* init) {
+ const auto [it, inserted] =
+ mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
+ LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str());
+ return &it->second;
+}
+
+GesturesProp* PropertyProvider::createBoolArrayProperty(const std::string name,
+ GesturesPropBool* loc, size_t count,
+ const GesturesPropBool* init) {
+ const auto [it, inserted] =
+ mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
+ LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str());
+ return &it->second;
+}
+
+GesturesProp* PropertyProvider::createRealArrayProperty(const std::string name, double* loc,
+ size_t count, const double* init) {
+ const auto [it, inserted] =
+ mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
+ LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str());
+ return &it->second;
+}
+
+GesturesProp* PropertyProvider::createStringProperty(const std::string name, const char** loc,
+ const char* const init) {
+ const auto [it, inserted] = mProperties.insert(std::pair{name, GesturesProp(name, loc, init)});
+ LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str());
+ return &it->second;
+}
+
+void PropertyProvider::freeProperty(GesturesProp* prop) {
+ mProperties.erase(prop->getName());
+}
+
+} // namespace android
+
+template <typename T>
+GesturesProp::GesturesProp(std::string name, T* dataPointer, size_t count, const T* initialValues)
+ : mName(name), mCount(count), mDataPointer(dataPointer) {
+ std::copy_n(initialValues, count, dataPointer);
+}
+
+GesturesProp::GesturesProp(std::string name, const char** dataPointer,
+ const char* const initialValue)
+ : mName(name), mCount(1), mDataPointer(dataPointer) {
+ *(std::get<const char**>(mDataPointer)) = initialValue;
+}
+
+void GesturesProp::registerHandlers(void* handlerData, GesturesPropGetHandler getter,
+ GesturesPropSetHandler setter) {
+ mHandlerData = handlerData;
+ mGetter = getter;
+ mSetter = setter;
+}
+
+std::vector<int> GesturesProp::getIntValues() const {
+ LOG_ALWAYS_FATAL_IF(!std::holds_alternative<int*>(mDataPointer),
+ "Attempt to read ints from \"%s\" gesture property.", mName.c_str());
+ return getValues<int, int>(std::get<int*>(mDataPointer));
+}
+
+std::vector<bool> GesturesProp::getBoolValues() const {
+ LOG_ALWAYS_FATAL_IF(!std::holds_alternative<GesturesPropBool*>(mDataPointer),
+ "Attempt to read bools from \"%s\" gesture property.", mName.c_str());
+ return getValues<bool, GesturesPropBool>(std::get<GesturesPropBool*>(mDataPointer));
+}
+
+std::vector<double> GesturesProp::getRealValues() const {
+ LOG_ALWAYS_FATAL_IF(!std::holds_alternative<double*>(mDataPointer),
+ "Attempt to read reals from \"%s\" gesture property.", mName.c_str());
+ return getValues<double, double>(std::get<double*>(mDataPointer));
+}
+
+std::string GesturesProp::getStringValue() const {
+ LOG_ALWAYS_FATAL_IF(!std::holds_alternative<const char**>(mDataPointer),
+ "Attempt to read a string from \"%s\" gesture property.", mName.c_str());
+ if (mGetter != nullptr) {
+ mGetter(mHandlerData);
+ }
+ return std::string(*std::get<const char**>(mDataPointer));
+}
+
+void GesturesProp::setBoolValues(const std::vector<bool>& values) {
+ LOG_ALWAYS_FATAL_IF(!std::holds_alternative<GesturesPropBool*>(mDataPointer),
+ "Attempt to write bools to \"%s\" gesture property.", mName.c_str());
+ setValues(std::get<GesturesPropBool*>(mDataPointer), values);
+}
+
+void GesturesProp::setIntValues(const std::vector<int>& values) {
+ LOG_ALWAYS_FATAL_IF(!std::holds_alternative<int*>(mDataPointer),
+ "Attempt to write ints to \"%s\" gesture property.", mName.c_str());
+ setValues(std::get<int*>(mDataPointer), values);
+}
+
+void GesturesProp::setRealValues(const std::vector<double>& values) {
+ LOG_ALWAYS_FATAL_IF(!std::holds_alternative<double*>(mDataPointer),
+ "Attempt to write reals to \"%s\" gesture property.", mName.c_str());
+ setValues(std::get<double*>(mDataPointer), values);
+}
+
+template <typename T, typename U>
+const std::vector<T> GesturesProp::getValues(U* dataPointer) const {
+ if (mGetter != nullptr) {
+ mGetter(mHandlerData);
+ }
+ std::vector<T> values;
+ values.reserve(mCount);
+ for (size_t i = 0; i < mCount; i++) {
+ values.push_back(dataPointer[i]);
+ }
+ return values;
+}
+
+template <typename T, typename U>
+void GesturesProp::setValues(T* dataPointer, const std::vector<U>& values) {
+ LOG_ALWAYS_FATAL_IF(values.size() != mCount,
+ "Attempt to write %zu values to \"%s\" gesture property, which holds %zu.",
+ values.size(), mName.c_str(), mCount);
+ std::copy(values.begin(), values.end(), dataPointer);
+ if (mSetter != nullptr) {
+ mSetter(mHandlerData);
+ }
+}
diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.h b/services/inputflinger/reader/mapper/gestures/PropertyProvider.h
new file mode 100644
index 0000000..4bebd46
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.h
@@ -0,0 +1,97 @@
+/*
+ * 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 <map>
+#include <string>
+#include <variant>
+#include <vector>
+
+#include "include/gestures.h"
+
+namespace android {
+
+// Struct containing functions that wrap PropertyProvider in a C-compatible interface.
+extern const GesturesPropProvider gesturePropProvider;
+
+// Implementation of a gestures library property provider, which provides configuration parameters.
+class PropertyProvider {
+public:
+ bool hasProperty(const std::string name) const;
+ GesturesProp& getProperty(const std::string name);
+
+ // Methods to be called by the gestures library:
+ GesturesProp* createIntArrayProperty(const std::string name, int* loc, size_t count,
+ const int* init);
+ GesturesProp* createBoolArrayProperty(const std::string name, GesturesPropBool* loc,
+ size_t count, const GesturesPropBool* init);
+ GesturesProp* createRealArrayProperty(const std::string name, double* loc, size_t count,
+ const double* init);
+ GesturesProp* createStringProperty(const std::string name, const char** loc,
+ const char* const init);
+
+ void freeProperty(GesturesProp* prop);
+
+private:
+ std::map<std::string, GesturesProp> mProperties;
+};
+
+} // namespace android
+
+// Represents a single gesture property.
+//
+// Pointers to this struct will be used by the gestures library (though it can never deference
+// them). The library's API requires this to be in the top-level namespace.
+struct GesturesProp {
+public:
+ template <typename T>
+ GesturesProp(std::string name, T* dataPointer, size_t count, const T* initialValues);
+ GesturesProp(std::string name, const char** dataPointer, const char* const initialValue);
+
+ std::string getName() const { return mName; }
+
+ size_t getCount() const { return mCount; }
+
+ void registerHandlers(void* handlerData, GesturesPropGetHandler getter,
+ GesturesPropSetHandler setter);
+
+ std::vector<int> getIntValues() const;
+ std::vector<bool> getBoolValues() const;
+ std::vector<double> getRealValues() const;
+ std::string getStringValue() const;
+
+ void setIntValues(const std::vector<int>& values);
+ void setBoolValues(const std::vector<bool>& values);
+ void setRealValues(const std::vector<double>& values);
+ // Setting string values isn't supported since we don't have a use case yet and the memory
+ // management adds additional complexity.
+
+private:
+ // Two type parameters are required for these methods, rather than one, due to the gestures
+ // library using its own bool type.
+ template <typename T, typename U>
+ const std::vector<T> getValues(U* dataPointer) const;
+ template <typename T, typename U>
+ void setValues(T* dataPointer, const std::vector<U>& values);
+
+ std::string mName;
+ size_t mCount;
+ std::variant<int*, GesturesPropBool*, const char**, double*> mDataPointer;
+ void* mHandlerData = nullptr;
+ GesturesPropGetHandler mGetter = nullptr;
+ GesturesPropSetHandler mSetter = nullptr;
+};