/* * 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 #include #include #include #include #include #include namespace android { namespace { GesturesProp* createInt(void* data, const char* name, int* loc, size_t count, const int* init) { return static_cast(data)->createIntArrayProperty(name, loc, count, init); } GesturesProp* createBool(void* data, const char* name, GesturesPropBool* loc, size_t count, const GesturesPropBool* init) { return static_cast(data)->createBoolArrayProperty(name, loc, count, init); } GesturesProp* createString(void* data, const char* name, const char** loc, const char* const init) { return static_cast(data)->createStringProperty(name, loc, init); } GesturesProp* createReal(void* data, const char* name, double* loc, size_t count, const double* init) { return static_cast(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(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); } std::string PropertyProvider::dump() const { std::string dump; for (const auto& [name, property] : mProperties) { dump += property.dump() + "\n"; } return dump; } void PropertyProvider::loadPropertiesFromIdcFile(const PropertyMap& idcProperties) { // For compatibility with the configuration file syntax, gesture property names in IDC files are // prefixed with "gestureProp." and have spaces replaced by underscores. So, for example, the // configuration key "gestureProp.Palm_Width" refers to the "Palm Width" property. const std::string gesturePropPrefix = "gestureProp."; for (const std::string& key : idcProperties.getKeysWithPrefix(gesturePropPrefix)) { std::string propertyName = key.substr(gesturePropPrefix.length()); for (size_t i = 0; i < propertyName.length(); i++) { if (propertyName[i] == '_') { propertyName[i] = ' '; } } auto it = mProperties.find(propertyName); if (it != mProperties.end()) { it->second.trySetFromIdcProperty(idcProperties, key); } else { ALOGE("Gesture property \"%s\" specified in IDC file does not exist for this device.", propertyName.c_str()); } } } 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 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(mDataPointer)) = initialValue; } std::string GesturesProp::dump() const { using android::base::StringPrintf; std::string type, values; switch (mDataPointer.index()) { case 0: type = "integer"; values = android::dumpVector(getIntValues()); break; case 1: type = "boolean"; values = android::dumpVector(getBoolValues()); break; case 2: type = "string"; values = getStringValue(); break; case 3: type = "real"; values = android::dumpVector(getRealValues()); break; } std::string typeAndSize = mCount == 1 ? type : std::to_string(mCount) + " " + type + "s"; return StringPrintf("%s (%s): %s", mName.c_str(), typeAndSize.c_str(), values.c_str()); } void GesturesProp::registerHandlers(void* handlerData, GesturesPropGetHandler getter, GesturesPropSetHandler setter) { mHandlerData = handlerData; mGetter = getter; mSetter = setter; } std::vector GesturesProp::getIntValues() const { LOG_ALWAYS_FATAL_IF(!std::holds_alternative(mDataPointer), "Attempt to read ints from \"%s\" gesture property.", mName.c_str()); return getValues(std::get(mDataPointer)); } std::vector GesturesProp::getBoolValues() const { LOG_ALWAYS_FATAL_IF(!std::holds_alternative(mDataPointer), "Attempt to read bools from \"%s\" gesture property.", mName.c_str()); return getValues(std::get(mDataPointer)); } std::vector GesturesProp::getRealValues() const { LOG_ALWAYS_FATAL_IF(!std::holds_alternative(mDataPointer), "Attempt to read reals from \"%s\" gesture property.", mName.c_str()); return getValues(std::get(mDataPointer)); } std::string GesturesProp::getStringValue() const { LOG_ALWAYS_FATAL_IF(!std::holds_alternative(mDataPointer), "Attempt to read a string from \"%s\" gesture property.", mName.c_str()); if (mGetter != nullptr) { mGetter(mHandlerData); } return std::string(*std::get(mDataPointer)); } void GesturesProp::setBoolValues(const std::vector& values) { LOG_ALWAYS_FATAL_IF(!std::holds_alternative(mDataPointer), "Attempt to write bools to \"%s\" gesture property.", mName.c_str()); setValues(std::get(mDataPointer), values); } void GesturesProp::setIntValues(const std::vector& values) { LOG_ALWAYS_FATAL_IF(!std::holds_alternative(mDataPointer), "Attempt to write ints to \"%s\" gesture property.", mName.c_str()); setValues(std::get(mDataPointer), values); } void GesturesProp::setRealValues(const std::vector& values) { LOG_ALWAYS_FATAL_IF(!std::holds_alternative(mDataPointer), "Attempt to write reals to \"%s\" gesture property.", mName.c_str()); setValues(std::get(mDataPointer), values); } namespace { // Helper to std::visit with lambdas. template struct Visitor : V... { using V::operator()...; }; // explicit deduction guide (not needed as of C++20) template Visitor(V...) -> Visitor; } // namespace void GesturesProp::trySetFromIdcProperty(const android::PropertyMap& idcProperties, const std::string& propertyName) { if (mCount != 1) { ALOGE("Gesture property \"%s\" is an array, and so cannot be set in an IDC file.", mName.c_str()); return; } bool parsedSuccessfully = false; Visitor setVisitor{ [&](int*) { if (std::optional value = idcProperties.getInt(propertyName); value) { parsedSuccessfully = true; setIntValues({*value}); } }, [&](GesturesPropBool*) { if (std::optional value = idcProperties.getBool(propertyName); value) { parsedSuccessfully = true; setBoolValues({*value}); } }, [&](double*) { if (std::optional value = idcProperties.getDouble(propertyName); value) { parsedSuccessfully = true; setRealValues({*value}); } }, [&](const char**) { ALOGE("Gesture property \"%s\" is a string, and so cannot be set in an IDC file.", mName.c_str()); // We've already reported the type mismatch, so set parsedSuccessfully. parsedSuccessfully = true; }, }; std::visit(setVisitor, mDataPointer); ALOGE_IF(!parsedSuccessfully, "Gesture property \"%s\" couldn't be set due to a type mismatch.", mName.c_str()); } template const std::vector GesturesProp::getValues(U* dataPointer) const { if (mGetter != nullptr) { mGetter(mHandlerData); } std::vector values; values.reserve(mCount); for (size_t i = 0; i < mCount; i++) { values.push_back(dataPointer[i]); } return values; } template void GesturesProp::setValues(T* dataPointer, const std::vector& 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); } }