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;
+};