From 3673167161a903721107cb05127d8a45b2950316 Mon Sep 17 00:00:00 2001 From: chasewu Date: Thu, 6 Feb 2020 12:06:49 +0800 Subject: [PATCH] vibrator: Modify some delta and apply calibration fix Reference the delta from the leading project. Bug: 148918900 Test: manual haptics function check and logs Change-Id: I777d3295a7a92e2ce8547b85886038cfbe32ac5a Signed-off-by: chasewu --- device-redfin.mk | 14 + init.insmod.redfin.cfg | 2 +- vibrator/Android.bp | 67 +-- vibrator/AndroidTest.xml | 28 - vibrator/Hardware.cpp | 209 -------- vibrator/Hardware.h | 134 ----- vibrator/Vibrator.cpp | 415 --------------- vibrator/Vibrator.h | 166 ------ ...id.hardware.vibrator@1.3-service.redfin.rc | 38 -- vibrator/common/Android.bp | 33 ++ vibrator/common/HardwareBase.cpp | 134 +++++ vibrator/common/HardwareBase.h | 212 ++++++++ vibrator/common/TEST_MAPPING | 13 + vibrator/common/bench/Android.bp | 32 ++ vibrator/common/bench/benchmark.cpp | 274 ++++++++++ vibrator/common/utils.h | 162 ++++++ vibrator/drv2624/Android.bp | 55 ++ vibrator/drv2624/Hardware.h | 175 +++++++ vibrator/drv2624/TEST_MAPPING | 15 + vibrator/drv2624/Vibrator.cpp | 485 ++++++++++++++++++ vibrator/drv2624/Vibrator.h | 201 ++++++++ ...id.hardware.vibrator@1.3-service.redfin.rc | 15 + ...d.hardware.vibrator@1.3-service.redfin.xml | 0 vibrator/drv2624/bench/Android.bp | 32 ++ vibrator/drv2624/bench/benchmark.cpp | 203 ++++++++ vibrator/drv2624/drv2624.bin | Bin 0 -> 45 bytes vibrator/{ => drv2624}/service.cpp | 15 +- vibrator/drv2624/tests/Android.bp | 31 ++ vibrator/drv2624/tests/mocks.h | 68 +++ vibrator/{ => drv2624}/tests/test-hwapi.cpp | 321 +++++++----- vibrator/drv2624/tests/test-hwcal.cpp | 396 ++++++++++++++ vibrator/drv2624/tests/test-vibrator.cpp | 449 ++++++++++++++++ vibrator/{ => drv2624}/tests/types.h | 8 +- .../{tests/main.cpp => drv2624/tests/utils.h} | 13 +- vibrator/tests/mocks.h | 60 --- vibrator/tests/test-hwcal.cpp | 302 ----------- vibrator/tests/test-vibrator.cpp | 464 ----------------- vibrator/tests/utils.h | 39 -- vibrator/utils.h | 35 -- 39 files changed, 3230 insertions(+), 2085 deletions(-) delete mode 100644 vibrator/AndroidTest.xml delete mode 100644 vibrator/Hardware.cpp delete mode 100644 vibrator/Hardware.h delete mode 100644 vibrator/Vibrator.cpp delete mode 100644 vibrator/Vibrator.h delete mode 100644 vibrator/android.hardware.vibrator@1.3-service.redfin.rc create mode 100644 vibrator/common/Android.bp create mode 100644 vibrator/common/HardwareBase.cpp create mode 100644 vibrator/common/HardwareBase.h create mode 100644 vibrator/common/TEST_MAPPING create mode 100644 vibrator/common/bench/Android.bp create mode 100644 vibrator/common/bench/benchmark.cpp create mode 100644 vibrator/common/utils.h create mode 100644 vibrator/drv2624/Android.bp create mode 100644 vibrator/drv2624/Hardware.h create mode 100644 vibrator/drv2624/TEST_MAPPING create mode 100644 vibrator/drv2624/Vibrator.cpp create mode 100644 vibrator/drv2624/Vibrator.h create mode 100644 vibrator/drv2624/android.hardware.vibrator@1.3-service.redfin.rc rename vibrator/{ => drv2624}/android.hardware.vibrator@1.3-service.redfin.xml (100%) create mode 100644 vibrator/drv2624/bench/Android.bp create mode 100644 vibrator/drv2624/bench/benchmark.cpp create mode 100644 vibrator/drv2624/drv2624.bin rename vibrator/{ => drv2624}/service.cpp (80%) create mode 100644 vibrator/drv2624/tests/Android.bp create mode 100644 vibrator/drv2624/tests/mocks.h rename vibrator/{ => drv2624}/tests/test-hwapi.cpp (50%) create mode 100644 vibrator/drv2624/tests/test-hwcal.cpp create mode 100644 vibrator/drv2624/tests/test-vibrator.cpp rename vibrator/{ => drv2624}/tests/types.h (81%) rename vibrator/{tests/main.cpp => drv2624/tests/utils.h} (67%) delete mode 100644 vibrator/tests/mocks.h delete mode 100644 vibrator/tests/test-hwcal.cpp delete mode 100644 vibrator/tests/test-vibrator.cpp delete mode 100644 vibrator/tests/utils.h delete mode 100644 vibrator/utils.h diff --git a/device-redfin.mk b/device-redfin.mk index 2a6d20a..90c8b94 100644 --- a/device-redfin.mk +++ b/device-redfin.mk @@ -115,6 +115,20 @@ PRODUCT_PACKAGES += \ PRODUCT_PACKAGES += \ android.hardware.vibrator@1.3-service.redfin +# DRV2624 Haptics Waveform +PRODUCT_COPY_FILES += \ + device/google/redfin/vibrator/drv2624/drv2624.bin:$(TARGET_COPY_OUT_VENDOR)/firmware/drv2624.bin + +# Vibrator HAL +PRODUCT_PRODUCT_PROPERTIES +=\ + ro.vibrator.hal.config.dynamic=1 \ + ro.vibrator.hal.click.duration=8 \ + ro.vibrator.hal.tick.duration=8 \ + ro.vibrator.hal.heavyclick.duration=8 \ + ro.vibrator.hal.long.voltage=161 \ + ro.vibrator.hal.long.frequency.shift=0 \ + ro.vibrator.hal.steady.shape=1 + # Dumpstate HAL PRODUCT_PACKAGES += \ android.hardware.dumpstate@1.0-service.redfin diff --git a/init.insmod.redfin.cfg b/init.insmod.redfin.cfg index f57ad6c..ad77b2d 100644 --- a/init.insmod.redfin.cfg +++ b/init.insmod.redfin.cfg @@ -5,7 +5,7 @@ ############################################# # Load kernel modules -modprobe|adsp_loader_dlkm.ko apr_dlkm.ko atomic64_test.ko bolero_cdc_dlkm.ko br_netfilter.ko hdmi_dlkm.ko lcd.ko lkdtm.ko llcc_perfmon.ko locktorture.ko machine_dlkm.ko mbhc_dlkm.ko native_dlkm.ko pinctrl_lpi_dlkm.ko platform_dlkm.ko q6_dlkm.ko q6_notifier_dlkm.ko q6_pdr_dlkm.ko rcutorture.ko rx_macro_dlkm.ko snd_event_dlkm.ko stub_dlkm.ko swr_ctrl_dlkm.ko swr_dlkm.ko test_user_copy.ko torture.ko tx_macro_dlkm.ko usf_dlkm.ko va_macro_dlkm.ko wcd938x_dlkm.ko wcd938x_slave_dlkm.ko wcd9xxx_dlkm.ko wcd_core_dlkm.ko wlan.ko wsa881x_dlkm.ko wsa_macro_dlkm.ko heatmap.ko sec_touch.ko +modprobe|adsp_loader_dlkm.ko apr_dlkm.ko atomic64_test.ko bolero_cdc_dlkm.ko br_netfilter.ko hdmi_dlkm.ko lcd.ko lkdtm.ko llcc_perfmon.ko locktorture.ko machine_dlkm.ko mbhc_dlkm.ko native_dlkm.ko pinctrl_lpi_dlkm.ko platform_dlkm.ko q6_dlkm.ko q6_notifier_dlkm.ko q6_pdr_dlkm.ko rcutorture.ko rx_macro_dlkm.ko snd_event_dlkm.ko stub_dlkm.ko swr_ctrl_dlkm.ko swr_dlkm.ko test_user_copy.ko torture.ko tx_macro_dlkm.ko usf_dlkm.ko va_macro_dlkm.ko wcd938x_dlkm.ko wcd938x_slave_dlkm.ko wcd9xxx_dlkm.ko wcd_core_dlkm.ko wlan.ko wsa881x_dlkm.ko wsa_macro_dlkm.ko heatmap.ko sec_touch.ko drv2624.ko # All modules loaded setprop|vendor.all.modules.ready diff --git a/vibrator/Android.bp b/vibrator/Android.bp index 261a26c..c19ddbd 100644 --- a/vibrator/Android.bp +++ b/vibrator/Android.bp @@ -1,5 +1,5 @@ // -// Copyright (C) 2017 The Android Open Source Project +// Copyright (C) 2019 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. @@ -14,69 +14,18 @@ // limitations under the License. cc_defaults { - name: "android.hardware.vibrator@1.3-defaults.redfin", + name: "PixelVibratorDefaultsRedfin", defaults: ["hidl_defaults"], relative_install_path: "hw", - shared_libs: [ - "libhidlbase", - "libcutils", - "libhidltransport", - "liblog", - "libhwbinder", - "libutils", - "libhardware", - "android.hardware.vibrator@1.0", - "android.hardware.vibrator@1.1", - "android.hardware.vibrator@1.2", - "android.hardware.vibrator@1.3", - ], - proprietary: true, -} - -cc_library { - name: "android.hardware.vibrator@1.3-impl.redfin", - defaults: ["android.hardware.vibrator@1.3-defaults.redfin"], - srcs: [ - "Hardware.cpp", - "Vibrator.cpp", - ], -} - -cc_binary { - name: "android.hardware.vibrator@1.3-service.redfin", - defaults: ["android.hardware.vibrator@1.3-defaults.redfin"], - init_rc: ["android.hardware.vibrator@1.3-service.redfin.rc"], - vintf_fragments: ["android.hardware.vibrator@1.3-service.redfin.xml"], - srcs: ["service.cpp"], - static_libs: ["android.hardware.vibrator@1.3-impl.redfin"], -} - -cc_test { - name: "PtsVibratorHalRedfinTestSuite", - defaults: ["android.hardware.vibrator@1.3-defaults.redfin"], - srcs: [ - "tests/main.cpp", - "tests/test-hwapi.cpp", - "tests/test-hwcal.cpp", - "tests/test-vibrator.cpp", - ], static_libs: [ - "android.hardware.vibrator@1.3-impl.redfin", - "libgmock", + "PixelVibratorCommonRedfin", ], shared_libs: [ "libbase", + "libhidlbase", + "libcutils", + "liblog", + "libutils", + "libhardware", ], - test_suites: [ - "general-tests", - "pts", - ], - multilib: { - lib32: { - suffix: "32", - }, - lib64: { - suffix: "64", - }, - }, } diff --git a/vibrator/AndroidTest.xml b/vibrator/AndroidTest.xml deleted file mode 100644 index a6ddf6d..0000000 --- a/vibrator/AndroidTest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - diff --git a/vibrator/Hardware.cpp b/vibrator/Hardware.cpp deleted file mode 100644 index 6f1d01d..0000000 --- a/vibrator/Hardware.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -#define LOG_TAG "android.hardware.vibrator@1.3-service.redfin" - -#include "Hardware.h" - -#include - -#include - -namespace android { -namespace hardware { -namespace vibrator { -namespace V1_3 { -namespace implementation { - -template -static void fileFromEnv(const char *env, T *outStream, std::string *outName = nullptr) { - auto file = std::getenv(env); - auto mode = std::is_base_of_v ? std::ios_base::out : std::ios_base::in; - - if (file == nullptr) { - ALOGE("Failed get env %s", env); - return; - } - - if (outName != nullptr) { - *outName = std::string(file); - } - - // Force 'in' mode to prevent file creation - outStream->open(file, mode | std::ios_base::in); - if (!*outStream) { - ALOGE("Failed to open %s:%s (%d): %s", env, file, errno, strerror(errno)); - } -} - -static auto pathsFromEnv(const char *env) { - std::map ret; - auto value = std::getenv(env); - - if (value == nullptr) { - return ret; - } - - std::istringstream paths{value}; - std::string path; - - while (paths >> path) { - ret[path].open(path); - } - - return ret; -} - -static std::string trim(const std::string &str, const std::string &whitespace = " \t") { - const auto str_begin = str.find_first_not_of(whitespace); - if (str_begin == std::string::npos) { - return ""; - } - - const auto str_end = str.find_last_not_of(whitespace); - const auto str_range = str_end - str_begin + 1; - - return str.substr(str_begin, str_range); -} - -template -static Enable_If_Iterable unpack(std::istream &stream, T *value) { - for (auto &entry : *value) { - stream >> entry; - } -} - -template -static Enable_If_Iterable unpack(std::istream &stream, T *value) { - stream >> *value; -} - -HwApi::HwApi() { - // ostreams below are required - fileFromEnv("F0_FILEPATH", &mF0, &mNames[&mF0]); - fileFromEnv("REDC_FILEPATH", &mRedc, &mNames[&mRedc]); - fileFromEnv("Q_FILEPATH", &mQ, &mNames[&mQ]); - fileFromEnv("ACTIVATE_PATH", &mActivate, &mNames[&mActivate]); - fileFromEnv("DURATION_PATH", &mDuration, &mNames[&mDuration]); - fileFromEnv("STATE_PATH", &mState, &mNames[&mState]); - fileFromEnv("EFFECT_DURATION_PATH", &mEffectDuration, &mNames[&mEffectDuration]); - fileFromEnv("EFFECT_INDEX_PATH", &mEffectIndex, &mNames[&mEffectIndex]); - fileFromEnv("EFFECT_QUEUE_PATH", &mEffectQueue, &mNames[&mEffectQueue]); - fileFromEnv("EFFECT_SCALE_PATH", &mEffectScale, &mNames[&mEffectScale]); - fileFromEnv("GLOBAL_SCALE_PATH", &mGlobalScale, &mNames[&mGlobalScale]); - fileFromEnv("ASP_ENABLE_PATH", &mAspEnable, &mNames[&mAspEnable]); - fileFromEnv("GPIO_FALL_INDEX", &mGpioFallIndex, &mNames[&mGpioFallIndex]); - fileFromEnv("GPIO_FALL_SCALE", &mGpioFallScale, &mNames[&mGpioFallScale]); - fileFromEnv("GPIO_RISE_INDEX", &mGpioRiseIndex, &mNames[&mGpioRiseIndex]); - fileFromEnv("GPIO_RISE_SCALE", &mGpioRiseScale, &mNames[&mGpioRiseScale]); -} - -template -bool HwApi::has(T &stream) { - return !!stream; -} - -template -bool HwApi::get(T *value, U &stream) { - bool ret; - stream.seekg(0); - stream >> *value; - if (!(ret = !!stream)) { - ALOGE("Failed to read %s (%d): %s", mNames[&stream].c_str(), errno, strerror(errno)); - } - stream.clear(); - return ret; -} - -template -bool HwApi::set(const T &value, U &stream) { - bool ret; - stream << value << std::endl; - if (!(ret = !!stream)) { - ALOGE("Failed to write %s (%d): %s", mNames[&stream].c_str(), errno, strerror(errno)); - stream.clear(); - } - return ret; -} - -void HwApi::debug(int fd) { - dprintf(fd, "Kernel:\n"); - - for (auto &entry : pathsFromEnv("HWAPI_DEBUG_PATHS")) { - auto &path = entry.first; - auto &stream = entry.second; - std::string line; - - dprintf(fd, " %s:\n", path.c_str()); - while (std::getline(stream, line)) { - dprintf(fd, " %s\n", line.c_str()); - } - } -} - -HwCal::HwCal() { - std::ifstream calfile; - - fileFromEnv("CALIBRATION_FILEPATH", &calfile); - - for (std::string line; std::getline(calfile, line);) { - if (line.empty() || line[0] == '#') { - continue; - } - std::istringstream is_line(line); - std::string key, value; - if (std::getline(is_line, key, ':') && std::getline(is_line, value)) { - mCalData[trim(key)] = trim(value); - } - } -} - -template -bool HwCal::get(const char *key, T *value) { - auto it = mCalData.find(key); - if (it == mCalData.end()) { - ALOGE("Missing %s config!", key); - return false; - } - std::stringstream stream{it->second}; - unpack(stream, value); - if (!stream || !stream.eof()) { - ALOGE("Invalid %s config!", key); - return false; - } - return true; -} - -void HwCal::debug(int fd) { - std::ifstream stream; - std::string path; - std::string line; - - dprintf(fd, "Persist:\n"); - - fileFromEnv("CALIBRATION_FILEPATH", &stream, &path); - - dprintf(fd, " %s:\n", path.c_str()); - while (std::getline(stream, line)) { - dprintf(fd, " %s\n", line.c_str()); - } -} - -} // namespace implementation -} // namespace V1_3 -} // namespace vibrator -} // namespace hardware -} // namespace android diff --git a/vibrator/Hardware.h b/vibrator/Hardware.h deleted file mode 100644 index 4b65f24..0000000 --- a/vibrator/Hardware.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ -#ifndef ANDROID_HARDWARE_VIBRATOR_HARDWARE_H -#define ANDROID_HARDWARE_VIBRATOR_HARDWARE_H - -#include "Vibrator.h" -#include "utils.h" - -namespace android { -namespace hardware { -namespace vibrator { -namespace V1_3 { -namespace implementation { - -class HwApi : public Vibrator::HwApi { - public: - HwApi(); - bool setF0(uint32_t value) override { return set(value, mF0); } - bool setRedc(uint32_t value) override { return set(value, mRedc); } - bool setQ(uint32_t value) override { return set(value, mQ); } - bool setActivate(bool value) override { return set(value, mActivate); } - bool setDuration(uint32_t value) override { return set(value, mDuration); } - bool getEffectDuration(uint32_t *value) override { return get(value, mEffectDuration); } - bool setEffectIndex(uint32_t value) override { return set(value, mEffectIndex); } - bool setEffectQueue(std::string value) override { return set(value, mEffectQueue); } - bool hasEffectScale() override { return has(mEffectScale); } - bool setEffectScale(uint32_t value) override { return set(value, mEffectScale); } - bool setGlobalScale(uint32_t value) override { return set(value, mGlobalScale); } - bool setState(bool value) override { return set(value, mState); } - bool hasAspEnable() override { return has(mAspEnable); } - bool getAspEnable(bool *value) override { return get(value, mAspEnable); } - bool setAspEnable(bool value) override { return set(value, mAspEnable); } - bool setGpioFallIndex(uint32_t value) override { return set(value, mGpioFallIndex); } - bool setGpioFallScale(uint32_t value) override { return set(value, mGpioFallScale); } - bool setGpioRiseIndex(uint32_t value) override { return set(value, mGpioRiseIndex); } - bool setGpioRiseScale(uint32_t value) override { return set(value, mGpioRiseScale); } - void debug(int fd) override; - - private: - template - bool has(T &stream); - template - bool get(T *value, U &stream); - template - bool set(const T &value, U &stream); - - private: - std::map mNames; - std::ofstream mF0; - std::ofstream mRedc; - std::ofstream mQ; - std::ofstream mActivate; - std::ofstream mDuration; - std::ifstream mEffectDuration; - std::ofstream mEffectIndex; - std::ofstream mEffectQueue; - std::ofstream mEffectScale; - std::ofstream mGlobalScale; - std::ofstream mState; - std::fstream mAspEnable; - std::ofstream mGpioFallIndex; - std::ofstream mGpioFallScale; - std::ofstream mGpioRiseIndex; - std::ofstream mGpioRiseScale; -}; - -class HwCal : public Vibrator::HwCal { - private: - static constexpr char F0_CONFIG[] = "f0_measured"; - static constexpr char REDC_CONFIG[] = "redc_measured"; - static constexpr char Q_CONFIG[] = "q_measured"; - static constexpr char Q_INDEX[] = "q_index"; - static constexpr char VOLTAGES_CONFIG[] = "v_levels"; - - static constexpr uint32_t Q_FLOAT_TO_FIXED = 1 << 16; - static constexpr float Q_INDEX_TO_FLOAT = 1.5f; - static constexpr uint32_t Q_INDEX_TO_FIXED = Q_INDEX_TO_FLOAT * Q_FLOAT_TO_FIXED; - static constexpr uint32_t Q_INDEX_OFFSET = 2.0f * Q_FLOAT_TO_FIXED; - - static constexpr uint32_t Q_DEFAULT = 15.5 * Q_FLOAT_TO_FIXED; - static constexpr std::array V_LEVELS_DEFAULT = {60, 70, 80, 90, 100, 76}; - - public: - HwCal(); - bool getF0(uint32_t *value) override { return get(F0_CONFIG, value); } - bool getRedc(uint32_t *value) override { return get(REDC_CONFIG, value); } - bool getQ(uint32_t *value) override { - if (get(Q_CONFIG, value)) { - return true; - } - if (get(Q_INDEX, value)) { - *value = *value * Q_INDEX_TO_FIXED + Q_INDEX_OFFSET; - return true; - } - *value = Q_DEFAULT; - return true; - } - bool getVolLevels(std::array *value) override { - if (get(VOLTAGES_CONFIG, value)) { - return true; - } - *value = V_LEVELS_DEFAULT; - return true; - } - void debug(int fd) override; - - private: - template - bool get(const char *key, T *value); - - private: - std::map mCalData; -}; - -} // namespace implementation -} // namespace V1_3 -} // namespace vibrator -} // namespace hardware -} // namespace android - -#endif // ANDROID_HARDWARE_VIBRATOR_HARDWARE_H diff --git a/vibrator/Vibrator.cpp b/vibrator/Vibrator.cpp deleted file mode 100644 index 8bce7cc..0000000 --- a/vibrator/Vibrator.cpp +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#define LOG_TAG "android.hardware.vibrator@1.3-service.redfin" - -#include - -#include -#include -#include - -#include "Vibrator.h" - -#include -#include -#include -#include - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0])) -#endif - -namespace android { -namespace hardware { -namespace vibrator { -namespace V1_3 { -namespace implementation { - -using Status = ::android::hardware::vibrator::V1_0::Status; -using EffectStrength = ::android::hardware::vibrator::V1_0::EffectStrength; - -static constexpr uint32_t WAVEFORM_SIMPLE_EFFECT_INDEX = 2; - -static constexpr uint32_t WAVEFORM_TEXTURE_TICK_EFFECT_LEVEL = 0; - -static constexpr uint32_t WAVEFORM_TICK_EFFECT_LEVEL = 1; - -static constexpr uint32_t WAVEFORM_CLICK_EFFECT_LEVEL = 2; - -static constexpr uint32_t WAVEFORM_HEAVY_CLICK_EFFECT_LEVEL = 3; - -static constexpr uint32_t WAVEFORM_DOUBLE_CLICK_SILENCE_MS = 100; - -static constexpr uint32_t WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0; - -static constexpr uint32_t WAVEFORM_TRIGGER_QUEUE_INDEX = 65534; - -static constexpr uint32_t VOLTAGE_GLOBAL_SCALE_LEVEL = 5; -static constexpr uint8_t VOLTAGE_SCALE_MAX = 100; - -static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6; // I2C Transaction + DSP Return-From-Standby -static constexpr int8_t MAX_PAUSE_TIMING_ERROR_MS = 1; // ALERT Irq Handling - -static constexpr float AMP_ATTENUATE_STEP_SIZE = 0.125f; -static constexpr float EFFECT_FREQUENCY_KHZ = 48.0f; - -static uint8_t amplitudeToScale(uint8_t amplitude, uint8_t maximum) { - return std::round((-20 * std::log10(amplitude / static_cast(maximum))) / - (AMP_ATTENUATE_STEP_SIZE)); -} - -Vibrator::Vibrator(std::unique_ptr hwapi, std::unique_ptr hwcal) - : mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)) { - uint32_t caldata; - uint32_t effectDuration; - - if (!mHwApi->setState(true)) { - ALOGE("Failed to set state (%d): %s", errno, strerror(errno)); - } - - if (mHwCal->getF0(&caldata)) { - mHwApi->setF0(caldata); - } - if (mHwCal->getRedc(&caldata)) { - mHwApi->setRedc(caldata); - } - if (mHwCal->getQ(&caldata)) { - mHwApi->setQ(caldata); - } - mHwCal->getVolLevels(&mVolLevels); - - mHwApi->setEffectIndex(WAVEFORM_SIMPLE_EFFECT_INDEX); - mHwApi->getEffectDuration(&effectDuration); - - mSimpleEffectDuration = std::ceil(effectDuration / EFFECT_FREQUENCY_KHZ); - - const uint32_t scaleFall = - amplitudeToScale(mVolLevels[WAVEFORM_CLICK_EFFECT_LEVEL], VOLTAGE_SCALE_MAX); - const uint32_t scaleRise = - amplitudeToScale(mVolLevels[WAVEFORM_HEAVY_CLICK_EFFECT_LEVEL], VOLTAGE_SCALE_MAX); - - mHwApi->setGpioFallIndex(WAVEFORM_SIMPLE_EFFECT_INDEX); - mHwApi->setGpioFallScale(scaleFall); - mHwApi->setGpioRiseIndex(WAVEFORM_SIMPLE_EFFECT_INDEX); - mHwApi->setGpioRiseScale(scaleRise); -} - -Return Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex) { - mHwApi->setEffectIndex(effectIndex); - mHwApi->setDuration(timeoutMs); - mHwApi->setActivate(1); - - return Status::OK; -} - -// Methods from ::android::hardware::vibrator::V1_1::IVibrator follow. -Return Vibrator::on(uint32_t timeoutMs) { - if (MAX_COLD_START_LATENCY_MS <= UINT32_MAX - timeoutMs) { - timeoutMs += MAX_COLD_START_LATENCY_MS; - } - setGlobalAmplitude(true); - return on(timeoutMs, WAVEFORM_LONG_VIBRATION_EFFECT_INDEX); -} - -Return Vibrator::off() { - setGlobalAmplitude(false); - if (!mHwApi->setActivate(0)) { - ALOGE("Failed to turn vibrator off (%d): %s", errno, strerror(errno)); - return Status::UNKNOWN_ERROR; - } - return Status::OK; -} - -Return Vibrator::supportsAmplitudeControl() { - return !isUnderExternalControl() && mHwApi->hasEffectScale(); -} - -Return Vibrator::setAmplitude(uint8_t amplitude) { - if (!amplitude) { - return Status::BAD_VALUE; - } - - if (!isUnderExternalControl()) { - return setEffectAmplitude(amplitude, UINT8_MAX); - } else { - return Status::UNSUPPORTED_OPERATION; - } -} - -Return Vibrator::setEffectAmplitude(uint8_t amplitude, uint8_t maximum) { - int32_t scale = amplitudeToScale(amplitude, maximum); - - if (!mHwApi->setEffectScale(scale)) { - ALOGE("Failed to set effect amplitude (%d): %s", errno, strerror(errno)); - return Status::UNKNOWN_ERROR; - } - - return Status::OK; -} - -Return Vibrator::setGlobalAmplitude(bool set) { - uint8_t amplitude = set ? mVolLevels[VOLTAGE_GLOBAL_SCALE_LEVEL] : VOLTAGE_SCALE_MAX; - int32_t scale = amplitudeToScale(amplitude, VOLTAGE_SCALE_MAX); - - if (!mHwApi->setGlobalScale(scale)) { - ALOGE("Failed to set global amplitude (%d): %s", errno, strerror(errno)); - return Status::UNKNOWN_ERROR; - } - - return Status::OK; -} - -// Methods from ::android::hardware::vibrator::V1_3::IVibrator follow. - -Return Vibrator::supportsExternalControl() { - return (mHwApi->hasAspEnable() ? true : false); -} - -Return Vibrator::setExternalControl(bool enabled) { - setGlobalAmplitude(enabled); - - if (!mHwApi->setAspEnable(enabled)) { - ALOGE("Failed to set external control (%d): %s", errno, strerror(errno)); - return Status::UNKNOWN_ERROR; - } - return Status::OK; -} - -bool Vibrator::isUnderExternalControl() { - bool isAspEnabled; - mHwApi->getAspEnable(&isAspEnabled); - return isAspEnabled; -} - -// Methods from ::android.hidl.base::V1_0::IBase follow. - -Return Vibrator::debug(const hidl_handle &handle, - const hidl_vec & /* options */) { - if (handle == nullptr || handle->numFds < 1 || handle->data[0] < 0) { - ALOGE("Called debug() with invalid fd."); - return Void(); - } - - int fd = handle->data[0]; - - dprintf(fd, "HIDL:\n"); - - dprintf(fd, " Voltage Levels:"); - for (auto v : mVolLevels) { - dprintf(fd, " %" PRIu32, v); - } - dprintf(fd, "\n"); - - dprintf(fd, " Effect Duration: %" PRIu32 "\n", mSimpleEffectDuration); - - dprintf(fd, "\n"); - - mHwApi->debug(fd); - - dprintf(fd, "\n"); - - mHwCal->debug(fd); - - fsync(fd); - return Void(); -} - -template -Return Vibrator::performWrapper(T effect, EffectStrength strength, perform_cb _hidl_cb) { - auto validRange = hidl_enum_range(); - if (effect < *validRange.begin() || effect > *std::prev(validRange.end())) { - _hidl_cb(Status::UNSUPPORTED_OPERATION, 0); - return Void(); - } - return performEffect(static_cast(effect), strength, _hidl_cb); -} - -Return Vibrator::perform(V1_0::Effect effect, EffectStrength strength, perform_cb _hidl_cb) { - return performWrapper(effect, strength, _hidl_cb); -} - -Return Vibrator::perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength, - perform_cb _hidl_cb) { - return performWrapper(effect, strength, _hidl_cb); -} - -Return Vibrator::perform_1_2(V1_2::Effect effect, EffectStrength strength, - perform_cb _hidl_cb) { - return performWrapper(effect, strength, _hidl_cb); -} - -Return Vibrator::perform_1_3(Effect effect, EffectStrength strength, perform_cb _hidl_cb) { - return performWrapper(effect, strength, _hidl_cb); -} - -Return Vibrator::getSimpleDetails(Effect effect, EffectStrength strength, - uint32_t *outTimeMs, uint32_t *outVolLevel) { - uint32_t timeMs; - uint32_t volLevel; - uint32_t volIndex; - int8_t volOffset; - - switch (strength) { - case EffectStrength::LIGHT: - volOffset = -1; - break; - case EffectStrength::MEDIUM: - volOffset = 0; - break; - case EffectStrength::STRONG: - volOffset = 1; - break; - default: - return Status::UNSUPPORTED_OPERATION; - } - - switch (effect) { - case Effect::TEXTURE_TICK: - volIndex = WAVEFORM_TEXTURE_TICK_EFFECT_LEVEL; - volOffset = 0; - break; - case Effect::TICK: - volIndex = WAVEFORM_TICK_EFFECT_LEVEL; - volOffset = 0; - break; - case Effect::CLICK: - volIndex = WAVEFORM_CLICK_EFFECT_LEVEL; - break; - case Effect::HEAVY_CLICK: - volIndex = WAVEFORM_HEAVY_CLICK_EFFECT_LEVEL; - break; - default: - return Status::UNSUPPORTED_OPERATION; - } - - volLevel = mVolLevels[volIndex + volOffset]; - timeMs = mSimpleEffectDuration + MAX_COLD_START_LATENCY_MS; - - *outTimeMs = timeMs; - *outVolLevel = volLevel; - - return Status::OK; -} - -Return Vibrator::getCompoundDetails(Effect effect, EffectStrength strength, - uint32_t *outTimeMs, uint32_t * /*outVolLevel*/, - std::string *outEffectQueue) { - Status status; - uint32_t timeMs; - std::ostringstream effectBuilder; - uint32_t thisTimeMs; - uint32_t thisVolLevel; - - switch (effect) { - case Effect::DOUBLE_CLICK: - timeMs = 0; - - status = getSimpleDetails(Effect::CLICK, strength, &thisTimeMs, &thisVolLevel); - if (status != Status::OK) { - return status; - } - effectBuilder << WAVEFORM_SIMPLE_EFFECT_INDEX << "." << thisVolLevel; - timeMs += thisTimeMs; - - effectBuilder << ","; - - effectBuilder << WAVEFORM_DOUBLE_CLICK_SILENCE_MS; - timeMs += WAVEFORM_DOUBLE_CLICK_SILENCE_MS + MAX_PAUSE_TIMING_ERROR_MS; - - effectBuilder << ","; - - status = getSimpleDetails(Effect::HEAVY_CLICK, strength, &thisTimeMs, &thisVolLevel); - if (status != Status::OK) { - return status; - } - effectBuilder << WAVEFORM_SIMPLE_EFFECT_INDEX << "." << thisVolLevel; - timeMs += thisTimeMs; - - break; - default: - return Status::UNSUPPORTED_OPERATION; - } - - *outTimeMs = timeMs; - *outEffectQueue = effectBuilder.str(); - - return Status::OK; -} - -Return Vibrator::setEffectQueue(const std::string &effectQueue) { - if (!mHwApi->setEffectQueue(effectQueue)) { - ALOGE("Failed to write \"%s\" to effect queue (%d): %s", effectQueue.c_str(), errno, - strerror(errno)); - return Status::UNKNOWN_ERROR; - } - - return Status::OK; -} - -Return Vibrator::performEffect(Effect effect, EffectStrength strength, perform_cb _hidl_cb) { - Status status = Status::OK; - uint32_t timeMs = 0; - uint32_t effectIndex; - uint32_t volLevel; - std::string effectQueue; - - switch (effect) { - case Effect::TEXTURE_TICK: - // fall-through - case Effect::TICK: - // fall-through - case Effect::CLICK: - // fall-through - case Effect::HEAVY_CLICK: - status = getSimpleDetails(effect, strength, &timeMs, &volLevel); - break; - case Effect::DOUBLE_CLICK: - status = getCompoundDetails(effect, strength, &timeMs, &volLevel, &effectQueue); - break; - default: - status = Status::UNSUPPORTED_OPERATION; - break; - } - if (status != Status::OK) { - goto exit; - } - - if (!effectQueue.empty()) { - status = setEffectQueue(effectQueue); - if (status != Status::OK) { - goto exit; - } - effectIndex = WAVEFORM_TRIGGER_QUEUE_INDEX; - } else { - setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX); - effectIndex = WAVEFORM_SIMPLE_EFFECT_INDEX; - } - - on(timeMs, effectIndex); - -exit: - - _hidl_cb(status, timeMs); - - return Void(); -} - -} // namespace implementation -} // namespace V1_3 -} // namespace vibrator -} // namespace hardware -} // namespace android diff --git a/vibrator/Vibrator.h b/vibrator/Vibrator.h deleted file mode 100644 index b204dae..0000000 --- a/vibrator/Vibrator.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ -#ifndef ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H -#define ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H - -#include -#include - -#include - -namespace android { -namespace hardware { -namespace vibrator { -namespace V1_3 { -namespace implementation { - -class Vibrator : public IVibrator { - public: - // APIs for interfacing with the kernel driver. - class HwApi { - public: - virtual ~HwApi() = default; - // Stores the LRA resonant frequency to be used for PWLE playback - // and click compensation. - virtual bool setF0(uint32_t value) = 0; - // Stores the LRA series resistance to be used for click - // compensation. - virtual bool setRedc(uint32_t value) = 0; - // Stores the LRA Q factor to be used for Q-dependent waveform - // selection. - virtual bool setQ(uint32_t value) = 0; - // Activates/deactivates the vibrator for durations specified by - // setDuration(). - virtual bool setActivate(bool value) = 0; - // Specifies the vibration duration in milliseconds. - virtual bool setDuration(uint32_t value) = 0; - // Reports the duration of the waveform selected by - // setEffectIndex(), measured in 48-kHz periods. - virtual bool getEffectDuration(uint32_t *value) = 0; - // Selects the waveform associated with vibration calls from - // the Android vibrator HAL. - virtual bool setEffectIndex(uint32_t value) = 0; - // Specifies an array of waveforms, delays, and repetition markers to - // generate complex waveforms. - virtual bool setEffectQueue(std::string value) = 0; - // Reports whether setEffectScale() is supported. - virtual bool hasEffectScale() = 0; - // Indicates the number of 0.125-dB steps of attenuation to apply to - // waveforms triggered in response to vibration calls from the - // Android vibrator HAL. - virtual bool setEffectScale(uint32_t value) = 0; - // Indicates the number of 0.125-dB steps of attenuation to apply to - // any output waveform (additive to all other set*Scale() - // controls). - virtual bool setGlobalScale(uint32_t value) = 0; - // Specifies the active state of the vibrator - // (true = enabled, false= disabled). - virtual bool setState(bool value) = 0; - // Reports whether getAspEnable()/setAspEnable() is supported. - virtual bool hasAspEnable() = 0; - // Enables/disables ASP playback. - virtual bool getAspEnable(bool *value) = 0; - // Reports enabled/disabled state of ASP playback. - virtual bool setAspEnable(bool value) = 0; - // Selects the waveform associated with a GPIO1 falling edge. - virtual bool setGpioFallIndex(uint32_t value) = 0; - // Indicates the number of 0.125-dB steps of attenuation to apply to - // waveforms triggered in response to a GPIO1 falling edge. - virtual bool setGpioFallScale(uint32_t value) = 0; - // Selects the waveform associated with a GPIO1 rising edge. - virtual bool setGpioRiseIndex(uint32_t value) = 0; - // Indicates the number of 0.125-dB steps of attenuation to apply to - // waveforms triggered in response to a GPIO1 rising edge. - virtual bool setGpioRiseScale(uint32_t value) = 0; - // Emit diagnostic information to the given file. - virtual void debug(int fd) = 0; - }; - - // APIs for obtaining calibration/configuration data from persistent memory. - class HwCal { - public: - virtual ~HwCal() = default; - // Obtains the LRA resonant frequency to be used for PWLE playback - // and click compensation. - virtual bool getF0(uint32_t *value) = 0; - // Obtains the LRA series resistance to be used for click - // compensation. - virtual bool getRedc(uint32_t *value) = 0; - // Obtains the LRA Q factor to be used for Q-dependent waveform - // selection. - virtual bool getQ(uint32_t *value) = 0; - // Obtains the discreet voltage levels to be applied for the various - // waveforms, in units of 1%. - virtual bool getVolLevels(std::array *value) = 0; - // Emit diagnostic information to the given file. - virtual void debug(int fd) = 0; - }; - - public: - Vibrator(std::unique_ptr hwapi, std::unique_ptr hwcal); - - // Methods from ::android::hardware::vibrator::V1_0::IVibrator follow. - using Status = ::android::hardware::vibrator::V1_0::Status; - Return on(uint32_t timeoutMs) override; - Return off() override; - Return supportsAmplitudeControl() override; - Return setAmplitude(uint8_t amplitude) override; - - // Methods from ::android::hardware::vibrator::V1_3::IVibrator follow. - Return supportsExternalControl() override; - Return setExternalControl(bool enabled) override; - - using EffectStrength = ::android::hardware::vibrator::V1_0::EffectStrength; - Return perform(V1_0::Effect effect, EffectStrength strength, - perform_cb _hidl_cb) override; - Return perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength, - perform_cb _hidl_cb) override; - Return perform_1_2(V1_2::Effect effect, EffectStrength strength, - perform_cb _hidl_cb) override; - Return perform_1_3(Effect effect, EffectStrength strength, perform_cb _hidl_cb) override; - - // Methods from ::android.hidl.base::V1_0::IBase follow. - Return debug(const hidl_handle &handle, const hidl_vec &options) override; - - private: - Return on(uint32_t timeoutMs, uint32_t effectIndex); - template - Return performWrapper(T effect, EffectStrength strength, perform_cb _hidl_cb); - // set 'amplitude' based on an arbitrary scale determined by 'maximum' - Return setEffectAmplitude(uint8_t amplitude, uint8_t maximum); - Return setGlobalAmplitude(bool set); - // 'simple' effects are those precompiled and loaded into the controller - Return getSimpleDetails(Effect effect, EffectStrength strength, uint32_t *outTimeMs, - uint32_t *outVolLevel); - // 'compound' effects are those composed by stringing multiple 'simple' effects - Return getCompoundDetails(Effect effect, EffectStrength strength, uint32_t *outTimeMs, - uint32_t *outVolLevel, std::string *outEffectQueue); - Return setEffectQueue(const std::string &effectQueue); - Return performEffect(Effect effect, EffectStrength strength, perform_cb _hidl_cb); - bool isUnderExternalControl(); - std::unique_ptr mHwApi; - std::unique_ptr mHwCal; - std::array mVolLevels; - uint32_t mSimpleEffectDuration; -}; - -} // namespace implementation -} // namespace V1_3 -} // namespace vibrator -} // namespace hardware -} // namespace android - -#endif // ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H diff --git a/vibrator/android.hardware.vibrator@1.3-service.redfin.rc b/vibrator/android.hardware.vibrator@1.3-service.redfin.rc deleted file mode 100644 index a609ab0..0000000 --- a/vibrator/android.hardware.vibrator@1.3-service.redfin.rc +++ /dev/null @@ -1,38 +0,0 @@ -service vendor.vibrator-1-3 /vendor/bin/hw/android.hardware.vibrator@1.3-service.redfin - class hal - user system - group system - - setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l25a.cal - - setenv F0_FILEPATH /sys/class/leds/vibrator/device/f0_stored - setenv REDC_FILEPATH /sys/class/leds/vibrator/device/redc_stored - setenv Q_FILEPATH /sys/class/leds/vibrator/device/q_stored - setenv ACTIVATE_PATH /sys/class/leds/vibrator/activate - setenv DURATION_PATH /sys/class/leds/vibrator/duration - setenv STATE_PATH /sys/class/leds/vibrator/state - setenv EFFECT_DURATION_PATH /sys/class/leds/vibrator/device/cp_trigger_duration - setenv EFFECT_INDEX_PATH /sys/class/leds/vibrator/device/cp_trigger_index - setenv EFFECT_QUEUE_PATH /sys/class/leds/vibrator/device/cp_trigger_queue - setenv EFFECT_SCALE_PATH /sys/class/leds/vibrator/device/cp_dig_scale - setenv GLOBAL_SCALE_PATH /sys/class/leds/vibrator/device/dig_scale - setenv ASP_ENABLE_PATH /sys/class/leds/vibrator/device/asp_enable - setenv GPIO_FALL_INDEX /sys/class/leds/vibrator/device/gpio1_fall_index - setenv GPIO_FALL_SCALE /sys/class/leds/vibrator/device/gpio1_fall_dig_scale - setenv GPIO_RISE_INDEX /sys/class/leds/vibrator/device/gpio1_rise_index - setenv GPIO_RISE_SCALE /sys/class/leds/vibrator/device/gpio1_rise_dig_scale - - setenv HWAPI_DEBUG_PATHS " - /sys/class/leds/vibrator/device/asp_enable - /sys/class/leds/vibrator/device/f0_stored - /sys/class/leds/vibrator/device/fw_rev - /sys/class/leds/vibrator/device/gpio1_fall_dig_scale - /sys/class/leds/vibrator/device/gpio1_fall_index - /sys/class/leds/vibrator/device/gpio1_rise_dig_scale - /sys/class/leds/vibrator/device/gpio1_rise_index - /sys/class/leds/vibrator/device/heartbeat - /sys/class/leds/vibrator/device/num_waves - /sys/class/leds/vibrator/device/q_stored - /sys/class/leds/vibrator/device/redc_stored - /sys/class/leds/vibrator/state - " diff --git a/vibrator/common/Android.bp b/vibrator/common/Android.bp new file mode 100644 index 0000000..477aa05 --- /dev/null +++ b/vibrator/common/Android.bp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2019 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. + +cc_library { + name: "PixelVibratorCommonRedfin", + srcs: [ + "HardwareBase.cpp", + ], + shared_libs: [ + "libbase", + "libcutils", + "liblog", + "libutils", + ], + cflags: [ + "-DATRACE_TAG=(ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)", + "-DLOG_TAG=\"android.hardware.vibrator@1.x-common\"", + ], + export_include_dirs: ["."], + vendor_available: true, +} diff --git a/vibrator/common/HardwareBase.cpp b/vibrator/common/HardwareBase.cpp new file mode 100644 index 0000000..f868b6a --- /dev/null +++ b/vibrator/common/HardwareBase.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2019 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 "HardwareBase.h" + +#include +#include + +#include +#include + +#include "utils.h" + +namespace android { +namespace hardware { +namespace vibrator { +namespace common { +namespace implementation { + +HwApiBase::HwApiBase() { + mPathPrefix = std::getenv("HWAPI_PATH_PREFIX") ?: ""; + if (mPathPrefix.empty()) { + ALOGE("Failed get HWAPI path prefix!"); + } +} + +bool HwApiBase::has(const std::ios &stream) { + return !!stream; +} + +void HwApiBase::debug(int fd) { + dprintf(fd, "Kernel:\n"); + + for (auto &entry : utils::pathsFromEnv("HWAPI_DEBUG_PATHS", mPathPrefix)) { + auto &path = entry.first; + auto &stream = entry.second; + std::string line; + + dprintf(fd, " %s:\n", path.c_str()); + while (std::getline(stream, line)) { + dprintf(fd, " %s\n", line.c_str()); + } + } + + mRecordsMutex.lock(); + dprintf(fd, " Records:\n"); + for (auto &r : mRecords) { + if (r == nullptr) { + continue; + } + dprintf(fd, " %s\n", r->toString(mNames).c_str()); + } + mRecordsMutex.unlock(); +} + +HwCalBase::HwCalBase() { + std::ifstream calfile; + auto propertyPrefix = std::getenv("PROPERTY_PREFIX"); + + if (propertyPrefix != NULL) { + mPropertyPrefix = std::string(propertyPrefix); + } else { + ALOGE("Failed get property prefix!"); + } + + utils::fileFromEnv("CALIBRATION_FILEPATH", &calfile); + + for (std::string line; std::getline(calfile, line);) { + if (line.empty() || line[0] == '#') { + continue; + } + std::istringstream is_line(line); + std::string key, value; + if (std::getline(is_line, key, ':') && std::getline(is_line, value)) { + mCalData[utils::trim(key)] = utils::trim(value); + } + } +} + +void HwCalBase::debug(int fd) { + std::ifstream stream; + std::string path; + std::string line; + struct context { + HwCalBase *obj; + int fd; + } context{this, fd}; + + dprintf(fd, "Properties:\n"); + + property_list( + [](const char *key, const char *value, void *cookie) { + struct context *context = static_cast(cookie); + HwCalBase *obj = context->obj; + int fd = context->fd; + const std::string expect{obj->mPropertyPrefix}; + const std::string actual{key, std::min(strlen(key), expect.size())}; + if (actual == expect) { + dprintf(fd, " %s:\n", key); + dprintf(fd, " %s\n", value); + } + }, + &context); + + dprintf(fd, "\n"); + + dprintf(fd, "Persist:\n"); + + utils::fileFromEnv("CALIBRATION_FILEPATH", &stream, &path); + + dprintf(fd, " %s:\n", path.c_str()); + while (std::getline(stream, line)) { + dprintf(fd, " %s\n", line.c_str()); + } +} + +} // namespace implementation +} // namespace common +} // namespace vibrator +} // namespace hardware +} // namespace android diff --git a/vibrator/common/HardwareBase.h b/vibrator/common/HardwareBase.h new file mode 100644 index 0000000..46aa839 --- /dev/null +++ b/vibrator/common/HardwareBase.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef ANDROID_HARDWARE_VIBRATOR_HARDWARE_BASE_H +#define ANDROID_HARDWARE_VIBRATOR_HARDWARE_BASE_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "utils.h" + +namespace android { +namespace hardware { +namespace vibrator { +namespace common { +namespace implementation { + +using base::unique_fd; + +class HwApiBase { + private: + using NamesMap = std::map; + + class RecordInterface { + public: + virtual std::string toString(const NamesMap &names) = 0; + virtual ~RecordInterface() {} + }; + template + class Record : public RecordInterface { + public: + Record(const char *func, const T &value, const std::ios *stream) + : mFunc(func), mValue(value), mStream(stream) {} + std::string toString(const NamesMap &names) override; + + private: + const char *mFunc; + const T mValue; + const std::ios *mStream; + }; + using Records = std::list>; + + static constexpr uint32_t RECORDS_SIZE = 32; + + public: + HwApiBase(); + void debug(int fd); + + protected: + template + void open(const std::string &name, T *stream); + bool has(const std::ios &stream); + template + bool get(T *value, std::istream *stream); + template + bool set(const T &value, std::ostream *stream); + template + bool poll(const T &value, std::istream *stream); + template + void record(const char *func, const T &value, const std::ios *stream); + + private: + std::string mPathPrefix; + NamesMap mNames; + Records mRecords{RECORDS_SIZE}; + std::mutex mRecordsMutex; +}; + +#define HWAPI_RECORD(args...) HwApiBase::record(__FUNCTION__, ##args) + +template +void HwApiBase::open(const std::string &name, T *stream) { + mNames[stream] = name; + utils::openNoCreate(mPathPrefix + name, stream); +} + +template +bool HwApiBase::get(T *value, std::istream *stream) { + ATRACE_NAME("HwApi::get"); + bool ret; + stream->seekg(0); + *stream >> *value; + if (!(ret = !!*stream)) { + ALOGE("Failed to read %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno)); + } + stream->clear(); + HWAPI_RECORD(*value, stream); + return ret; +} + +template +bool HwApiBase::set(const T &value, std::ostream *stream) { + ATRACE_NAME("HwApi::set"); + using utils::operator<<; + bool ret; + *stream << value << std::endl; + if (!(ret = !!*stream)) { + ALOGE("Failed to write %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno)); + stream->clear(); + } + HWAPI_RECORD(value, stream); + return ret; +} + +template +bool HwApiBase::poll(const T &value, std::istream *stream) { + ATRACE_NAME("HwApi::poll"); + auto path = mPathPrefix + mNames[stream]; + unique_fd fileFd{::open(path.c_str(), O_RDONLY)}; + unique_fd epollFd{epoll_create(1)}; + epoll_event event = { + .events = EPOLLPRI | EPOLLET, + }; + T actual; + bool ret; + + if (epoll_ctl(epollFd, EPOLL_CTL_ADD, fileFd, &event)) { + ALOGE("Failed to poll %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno)); + return false; + } + + while ((ret = get(&actual, stream)) && (actual != value)) { + epoll_wait(epollFd, &event, 1, -1); + } + + HWAPI_RECORD(value, stream); + return ret; +} + +template +void HwApiBase::record(const char *func, const T &value, const std::ios *stream) { + std::lock_guard lock(mRecordsMutex); + mRecords.emplace_back(std::make_unique>(func, value, stream)); + mRecords.pop_front(); +} + +template +std::string HwApiBase::Record::toString(const NamesMap &names) { + using utils::operator<<; + std::stringstream ret; + + ret << mFunc << " '" << names.at(mStream) << "' = '" << mValue << "'"; + + return ret.str(); +} + +class HwCalBase { + public: + HwCalBase(); + void debug(int fd); + + protected: + template + bool getProperty(const char *key, T *value, const T defval); + template + bool getPersist(const char *key, T *value); + + private: + std::string mPropertyPrefix; + std::map mCalData; +}; + +template +bool HwCalBase::getProperty(const char *key, T *outval, const T defval) { + ATRACE_NAME("HwCal::getProperty"); + *outval = utils::getProperty(mPropertyPrefix + key, defval); + return true; +} + +template +bool HwCalBase::getPersist(const char *key, T *value) { + ATRACE_NAME("HwCal::getPersist"); + auto it = mCalData.find(key); + if (it == mCalData.end()) { + ALOGE("Missing %s config!", key); + return false; + } + std::stringstream stream{it->second}; + utils::unpack(stream, value); + if (!stream || !stream.eof()) { + ALOGE("Invalid %s config!", key); + return false; + } + return true; +} + +} // namespace implementation +} // namespace common +} // namespace vibrator +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_VIBRATOR_HARDWARE_BASE_H diff --git a/vibrator/common/TEST_MAPPING b/vibrator/common/TEST_MAPPING new file mode 100644 index 0000000..c0e8e98 --- /dev/null +++ b/vibrator/common/TEST_MAPPING @@ -0,0 +1,13 @@ +{ + "postsubmit": [ + { + "name": "VibratorHalIntegrationBenchmark" + }, + { + "name": "VibratorHalIntegrationBenchmark", + "keywords": [ + "primary-device" + ] + } + ] +} diff --git a/vibrator/common/bench/Android.bp b/vibrator/common/bench/Android.bp new file mode 100644 index 0000000..8df1d8d --- /dev/null +++ b/vibrator/common/bench/Android.bp @@ -0,0 +1,32 @@ +// +// Copyright (C) 2019 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. + +cc_benchmark { + name: "VibratorHalIntegrationBenchmarkRedfin", + defaults: ["hidl_defaults"], + srcs: [ + "benchmark.cpp", + ], + shared_libs: [ + "android.hardware.vibrator@1.0", + "android.hardware.vibrator@1.1", + "android.hardware.vibrator@1.2", + "android.hardware.vibrator@1.3", + "libhardware", + "libhidlbase", + "libutils", + ], + test_suites: ["device-tests"], +} diff --git a/vibrator/common/bench/benchmark.cpp b/vibrator/common/bench/benchmark.cpp new file mode 100644 index 0000000..06013f9 --- /dev/null +++ b/vibrator/common/bench/benchmark.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2019 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 "benchmark/benchmark.h" + +#include + +using ::android::sp; +using ::android::hardware::hidl_enum_range; +using ::android::hardware::Return; +using ::android::hardware::details::hidl_enum_values; +using ::benchmark::Counter; +using ::benchmark::Fixture; +using ::benchmark::kMicrosecond; +using ::benchmark::State; +using ::benchmark::internal::Benchmark; +using ::std::chrono::duration; +using ::std::chrono::duration_cast; +using ::std::chrono::high_resolution_clock; + +namespace V1_0 = ::android::hardware::vibrator::V1_0; +namespace V1_1 = ::android::hardware::vibrator::V1_1; +namespace V1_2 = ::android::hardware::vibrator::V1_2; +namespace V1_3 = ::android::hardware::vibrator::V1_3; + +template +class VibratorBench : public Fixture { + public: + void SetUp(State & /*state*/) override { mVibrator = I::getService(); } + + void TearDown(State & /*state*/) override { + if (!mVibrator) { + return; + } + mVibrator->off(); + } + + static void DefaultConfig(Benchmark *b) { b->Unit(kMicrosecond); } + + static void DefaultArgs(Benchmark * /*b*/) { + // none + } + + protected: + auto getOtherArg(const State &state, std::size_t index) const { + return state.range(index + 0); + } + + protected: + sp mVibrator; +}; + +enum class EmptyEnum : uint32_t; +template <> +inline constexpr std::array hidl_enum_values = {}; + +template +std::set difference(const hidl_enum_range &t, const hidl_enum_range &u) { + class Compare { + public: + bool operator()(const T &a, const U &b) { return a < static_cast(b); } + bool operator()(const U &a, const T &b) { return static_cast(a) < b; } + }; + std::set ret; + + std::set_difference(t.begin(), t.end(), u.begin(), u.end(), + std::insert_iterator(ret, ret.begin()), Compare()); + + return ret; +} + +template +class VibratorEffectsBench : public VibratorBench { + public: + using Effect = E1; + using EffectStrength = V1_0::EffectStrength; + using Status = V1_0::Status; + + public: + static void DefaultArgs(Benchmark *b) { + b->ArgNames({"Effect", "Strength"}); + for (const auto &effect : difference(hidl_enum_range(), hidl_enum_range())) { + for (const auto &strength : hidl_enum_range()) { + b->Args({static_cast(effect), static_cast(strength)}); + } + } + } + + void performBench(State *state, Return (I::*performApi)(Effect, EffectStrength, + typename I::perform_cb)) { + auto effect = getEffect(*state); + auto strength = getStrength(*state); + bool supported = true; + + (*this->mVibrator.*performApi)(effect, strength, [&](Status status, uint32_t /*lengthMs*/) { + if (status == Status::UNSUPPORTED_OPERATION) { + supported = false; + } + }); + + if (!supported) { + return; + } + + for (auto _ : *state) { + state->ResumeTiming(); + (*this->mVibrator.*performApi)(effect, strength, + [](Status /*status*/, uint32_t /*lengthMs*/) {}); + state->PauseTiming(); + this->mVibrator->off(); + } + } + + protected: + auto getEffect(const State &state) const { + return static_cast(this->getOtherArg(state, 0)); + } + + auto getStrength(const State &state) const { + return static_cast(this->getOtherArg(state, 1)); + } +}; + +#define BENCHMARK_WRAPPER(fixt, test, code) \ + BENCHMARK_DEFINE_F(fixt, test) \ + /* NOLINTNEXTLINE */ \ + (State & state) { \ + if (!mVibrator) { \ + return; \ + } \ + \ + code \ + } \ + BENCHMARK_REGISTER_F(fixt, test)->Apply(fixt::DefaultConfig)->Apply(fixt::DefaultArgs) + +using VibratorBench_V1_0 = VibratorBench; + +BENCHMARK_WRAPPER(VibratorBench_V1_0, on, { + uint32_t ms = UINT32_MAX; + + for (auto _ : state) { + state.ResumeTiming(); + mVibrator->on(ms); + state.PauseTiming(); + mVibrator->off(); + } +}); + +BENCHMARK_WRAPPER(VibratorBench_V1_0, off, { + uint32_t ms = UINT32_MAX; + + for (auto _ : state) { + state.PauseTiming(); + mVibrator->on(ms); + state.ResumeTiming(); + mVibrator->off(); + } +}); + +BENCHMARK_WRAPPER(VibratorBench_V1_0, supportsAmplitudeControl, { + for (auto _ : state) { + mVibrator->supportsAmplitudeControl(); + } +}); + +BENCHMARK_WRAPPER(VibratorBench_V1_0, setAmplitude, { + uint8_t amplitude = UINT8_MAX; + + if (!mVibrator->supportsAmplitudeControl()) { + return; + } + + mVibrator->on(UINT32_MAX); + + for (auto _ : state) { + mVibrator->setAmplitude(amplitude); + } + + mVibrator->off(); +}); + +using VibratorEffectsBench_V1_0 = VibratorEffectsBench; + +BENCHMARK_WRAPPER(VibratorEffectsBench_V1_0, perform, + { performBench(&state, &V1_0::IVibrator::perform); }); + +using VibratorEffectsBench_V1_1 = + VibratorEffectsBench; + +BENCHMARK_WRAPPER(VibratorEffectsBench_V1_1, perform_1_1, + { performBench(&state, &V1_1::IVibrator::perform_1_1); }); + +using VibratorEffectsBench_V1_2 = + VibratorEffectsBench; + +BENCHMARK_WRAPPER(VibratorEffectsBench_V1_2, perform_1_2, + { performBench(&state, &V1_2::IVibrator::perform_1_2); }); + +using VibratorBench_V1_3 = VibratorBench; + +BENCHMARK_WRAPPER(VibratorBench_V1_3, supportsExternalControl, { + for (auto _ : state) { + mVibrator->supportsExternalControl(); + } +}); + +BENCHMARK_WRAPPER(VibratorBench_V1_3, setExternalControl, { + bool enable = true; + + if (!mVibrator->supportsExternalControl()) { + return; + } + + for (auto _ : state) { + state.ResumeTiming(); + mVibrator->setExternalControl(enable); + state.PauseTiming(); + mVibrator->setExternalControl(false); + } +}); + +BENCHMARK_WRAPPER(VibratorBench_V1_3, supportsExternalAmplitudeControl, { + if (!mVibrator->supportsExternalControl()) { + return; + } + + mVibrator->setExternalControl(true); + + for (auto _ : state) { + mVibrator->supportsAmplitudeControl(); + } + + mVibrator->setExternalControl(false); +}); + +BENCHMARK_WRAPPER(VibratorBench_V1_3, setExternalAmplitude, { + uint8_t amplitude = UINT8_MAX; + + if (!mVibrator->supportsExternalControl()) { + return; + } + + mVibrator->setExternalControl(true); + + if (!mVibrator->supportsAmplitudeControl()) { + return; + } + + for (auto _ : state) { + mVibrator->setAmplitude(amplitude); + } + + mVibrator->setExternalControl(false); +}); + +using VibratorEffectsBench_V1_3 = VibratorEffectsBench; + +BENCHMARK_WRAPPER(VibratorEffectsBench_V1_3, perform_1_3, + { performBench(&state, &V1_3::IVibrator::perform_1_3); }); + +BENCHMARK_MAIN(); diff --git a/vibrator/common/utils.h b/vibrator/common/utils.h new file mode 100644 index 0000000..2079256 --- /dev/null +++ b/vibrator/common/utils.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef ANDROID_HARDWARE_VIBRATOR_UTILS_H +#define ANDROID_HARDWARE_VIBRATOR_UTILS_H + +#include +#include +#include + +#include + +namespace android { +namespace hardware { +namespace vibrator { +namespace utils { + +template +class Is_Iterable { + private: + template + static std::true_type test(typename U::iterator *u); + + template + static std::false_type test(U *u); + + public: + static const bool value = decltype(test(0))::value; +}; + +template +using Enable_If_Iterable = std::enable_if_t::value == B>; + +template +using Enable_If_Signed = std::enable_if_t, U>; + +template +using Enable_If_Unsigned = std::enable_if_t, U>; + +// override for default behavior of printing as a character +inline std::ostream &operator<<(std::ostream &stream, const int8_t value) { + return stream << +value; +} +// override for default behavior of printing as a character +inline std::ostream &operator<<(std::ostream &stream, const uint8_t value) { + return stream << +value; +} + +template +inline auto toUnderlying(const T value) { + return static_cast>(value); +} + +template +inline Enable_If_Iterable unpack(std::istream &stream, T *value) { + for (auto &entry : *value) { + stream >> entry; + } +} + +template +inline Enable_If_Iterable unpack(std::istream &stream, T *value) { + stream >> *value; +} + +template <> +inline void unpack(std::istream &stream, std::string *value) { + *value = std::string(std::istreambuf_iterator(stream), {}); + stream.setstate(std::istream::eofbit); +} + +template +inline Enable_If_Signed getProperty(const std::string &key, const T def) { + return base::GetIntProperty(key, def); +} + +template +inline Enable_If_Unsigned getProperty(const std::string &key, const T def) { + return base::GetUintProperty(key, def); +} + +template <> +inline bool getProperty(const std::string &key, const bool def) { + return base::GetBoolProperty(key, def); +} + +template +static void openNoCreate(const std::string &file, T *outStream) { + auto mode = std::is_base_of_v ? std::ios_base::out : std::ios_base::in; + + // Force 'in' mode to prevent file creation + outStream->open(file, mode | std::ios_base::in); + if (!*outStream) { + ALOGE("Failed to open %s (%d): %s", file.c_str(), errno, strerror(errno)); + } +} + +template +static void fileFromEnv(const char *env, T *outStream, std::string *outName = nullptr) { + auto file = std::getenv(env); + + if (file == nullptr) { + ALOGE("Failed get env %s", env); + return; + } + + if (outName != nullptr) { + *outName = std::string(file); + } + + openNoCreate(file, outStream); +} + +static ATTRIBUTE_UNUSED auto pathsFromEnv(const char *env, const std::string &prefix = "") { + std::map ret; + auto value = std::getenv(env); + + if (value == nullptr) { + return ret; + } + + std::istringstream paths{value}; + std::string path; + + while (paths >> path) { + ret[path].open(prefix + path); + } + + return ret; +} + +static ATTRIBUTE_UNUSED std::string trim(const std::string &str, + const std::string &whitespace = " \t") { + const auto str_begin = str.find_first_not_of(whitespace); + if (str_begin == std::string::npos) { + return ""; + } + + const auto str_end = str.find_last_not_of(whitespace); + const auto str_range = str_end - str_begin + 1; + + return str.substr(str_begin, str_range); +} + +} // namespace utils +} // namespace vibrator +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_VIBRATOR_UTILS_H diff --git a/vibrator/drv2624/Android.bp b/vibrator/drv2624/Android.bp new file mode 100644 index 0000000..cf75650 --- /dev/null +++ b/vibrator/drv2624/Android.bp @@ -0,0 +1,55 @@ +// +// Copyright (C) 2017 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. + +cc_defaults { + name: "android.hardware.vibrator@1.3-defaults.redfin", + defaults: ["PixelVibratorDefaultsRedfin"], + shared_libs: [ + "android.hardware.vibrator@1.0", + "android.hardware.vibrator@1.1", + "android.hardware.vibrator@1.2", + "android.hardware.vibrator@1.3", + ], + cflags: [ + "-DATRACE_TAG=(ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)", + "-DLOG_TAG=\"android.hardware.vibrator@1.3-redfin\"", + ], +} + +cc_defaults { + name: "VibratorHalDrv2624TestDefaultsRedfin", + defaults: ["android.hardware.vibrator@1.3-defaults.redfin"], + static_libs: ["android.hardware.vibrator@1.3-impl.redfin"], + test_suites: ["device-tests"], + require_root: true, +} + +cc_library { + name: "android.hardware.vibrator@1.3-impl.redfin", + defaults: ["android.hardware.vibrator@1.3-defaults.redfin"], + srcs: ["Vibrator.cpp"], + export_include_dirs: ["."], + vendor_available: true, +} + +cc_binary { + name: "android.hardware.vibrator@1.3-service.redfin", + defaults: ["android.hardware.vibrator@1.3-defaults.redfin"], + init_rc: ["android.hardware.vibrator@1.3-service.redfin.rc"], + vintf_fragments: ["android.hardware.vibrator@1.3-service.redfin.xml"], + srcs: ["service.cpp"], + static_libs: ["android.hardware.vibrator@1.3-impl.redfin"], + proprietary: true, +} diff --git a/vibrator/drv2624/Hardware.h b/vibrator/drv2624/Hardware.h new file mode 100644 index 0000000..d43fa6d --- /dev/null +++ b/vibrator/drv2624/Hardware.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef ANDROID_HARDWARE_VIBRATOR_HARDWARE_H +#define ANDROID_HARDWARE_VIBRATOR_HARDWARE_H + +#include "HardwareBase.h" +#include "Vibrator.h" + +namespace android { +namespace hardware { +namespace vibrator { +namespace V1_3 { +namespace implementation { + +using common::implementation::HwApiBase; +using common::implementation::HwCalBase; + +class HwApi : public Vibrator::HwApi, private HwApiBase { + public: + static std::unique_ptr Create() { + auto hwapi = std::unique_ptr(new HwApi()); + // the following streams are required + if (!hwapi->mActivate.is_open() || !hwapi->mDuration.is_open() || + !hwapi->mState.is_open()) { + return nullptr; + } + return hwapi; + } + + bool setAutocal(std::string value) override { return set(value, &mAutocal); } + bool setOlLraPeriod(uint32_t value) override { return set(value, &mOlLraPeriod); } + bool setActivate(bool value) override { return set(value, &mActivate); } + bool setDuration(uint32_t value) override { return set(value, &mDuration); } + bool setState(bool value) override { return set(value, &mState); } + bool hasRtpInput() override { return has(mRtpInput); } + bool setRtpInput(int8_t value) override { return set(value, &mRtpInput); } + bool setMode(std::string value) override { return set(value, &mMode); } + bool setSequencer(std::string value) override { return set(value, &mSequencer); } + bool setScale(uint8_t value) override { return set(value, &mScale); } + bool setCtrlLoop(bool value) override { return set(value, &mCtrlLoop); } + bool setLpTriggerEffect(uint32_t value) override { return set(value, &mLpTrigger); } + bool setLraWaveShape(uint32_t value) override { return set(value, &mLraWaveShape); } + bool setOdClamp(uint32_t value) override { return set(value, &mOdClamp); } + void debug(int fd) override { HwApiBase::debug(fd); } + + private: + HwApi() { + open("device/autocal", &mAutocal); + open("device/ol_lra_period", &mOlLraPeriod); + open("activate", &mActivate); + open("duration", &mDuration); + open("state", &mState); + open("device/rtp_input", &mRtpInput); + open("device/mode", &mMode); + open("device/set_sequencer", &mSequencer); + open("device/scale", &mScale); + open("device/ctrl_loop", &mCtrlLoop); + open("device/lp_trigger_effect", &mLpTrigger); + open("device/lra_wave_shape", &mLraWaveShape); + open("device/od_clamp", &mOdClamp); + } + + private: + std::ofstream mAutocal; + std::ofstream mOlLraPeriod; + std::ofstream mActivate; + std::ofstream mDuration; + std::ofstream mState; + std::ofstream mRtpInput; + std::ofstream mMode; + std::ofstream mSequencer; + std::ofstream mScale; + std::ofstream mCtrlLoop; + std::ofstream mLpTrigger; + std::ofstream mLraWaveShape; + std::ofstream mOdClamp; +}; + +class HwCal : public Vibrator::HwCal, private HwCalBase { + private: + static constexpr char AUTOCAL_CONFIG[] = "autocal"; + static constexpr char LRA_PERIOD_CONFIG[] = "lra_period"; + static constexpr char EFFECT_COEFF_CONFIG[] = "haptic_coefficient"; + static constexpr char STEADY_AMP_MAX_CONFIG[] = "vibration_amp_max"; + + static constexpr uint32_t WAVEFORM_CLICK_EFFECT_MS = 6; + static constexpr uint32_t WAVEFORM_TICK_EFFECT_MS = 2; + static constexpr uint32_t WAVEFORM_DOUBLE_CLICK_EFFECT_MS = 144; + static constexpr uint32_t WAVEFORM_HEAVY_CLICK_EFFECT_MS = 8; + + static constexpr uint32_t DEFAULT_LRA_PERIOD = 262; + static constexpr uint32_t DEFAULT_FREQUENCY_SHIFT = 10; + static constexpr uint32_t DEFAULT_VOLTAGE_MAX = 107; // 2.15V; + + public: + HwCal() {} + + bool getAutocal(std::string *value) override { return getPersist(AUTOCAL_CONFIG, value); } + bool getLraPeriod(uint32_t *value) override { + if (getPersist(LRA_PERIOD_CONFIG, value)) { + return true; + } + *value = DEFAULT_LRA_PERIOD; + return true; + } + bool getEffectCoeffs(std::array *value) override { + if (getPersist(EFFECT_COEFF_CONFIG, value)) { + return true; + } + return false; + } + bool getSteadyAmpMax(float *value) override { + if (getPersist(STEADY_AMP_MAX_CONFIG, value)) { + return true; + } + return false; + } + bool getCloseLoopThreshold(uint32_t *value) override { + return getProperty("closeloop.threshold", value, UINT32_MAX); + return true; + } + bool getDynamicConfig(bool *value) override { + return getProperty("config.dynamic", value, false); + } + bool getLongFrequencyShift(uint32_t *value) override { + return getProperty("long.frequency.shift", value, DEFAULT_FREQUENCY_SHIFT); + } + bool getShortVoltageMax(uint32_t *value) override { + return getProperty("short.voltage", value, DEFAULT_VOLTAGE_MAX); + } + bool getLongVoltageMax(uint32_t *value) override { + return getProperty("long.voltage", value, DEFAULT_VOLTAGE_MAX); + } + bool getClickDuration(uint32_t *value) override { + return getProperty("click.duration", value, WAVEFORM_CLICK_EFFECT_MS); + } + bool getTickDuration(uint32_t *value) override { + return getProperty("tick.duration", value, WAVEFORM_TICK_EFFECT_MS); + } + bool getDoubleClickDuration(uint32_t *value) override { + *value = WAVEFORM_DOUBLE_CLICK_EFFECT_MS; + return true; + } + bool getHeavyClickDuration(uint32_t *value) override { + return getProperty("heavyclick.duration", value, WAVEFORM_HEAVY_CLICK_EFFECT_MS); + } + bool getEffectShape(uint32_t *value) override { + return getProperty("effect.shape", value, UINT32_MAX); + } + bool getSteadyShape(uint32_t *value) override { + return getProperty("steady.shape", value, UINT32_MAX); + } + void debug(int fd) override { HwCalBase::debug(fd); } +}; + +} // namespace implementation +} // namespace V1_3 +} // namespace vibrator +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_VIBRATOR_HARDWARE_H diff --git a/vibrator/drv2624/TEST_MAPPING b/vibrator/drv2624/TEST_MAPPING new file mode 100644 index 0000000..ec34f66 --- /dev/null +++ b/vibrator/drv2624/TEST_MAPPING @@ -0,0 +1,15 @@ +{ + "presubmit": [ + { + "name": "VibratorHalDrv2624TestSuite" + } + ], + "postsubmit": [ + { + "name": "VibratorHalDrv2624Benchmark", + "keywords": [ + "primary-device" + ] + } + ] +} diff --git a/vibrator/drv2624/Vibrator.cpp b/vibrator/drv2624/Vibrator.cpp new file mode 100644 index 0000000..02d786f --- /dev/null +++ b/vibrator/drv2624/Vibrator.cpp @@ -0,0 +1,485 @@ +/* + * Copyright (C) 2017 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 "Vibrator.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "utils.h" + +namespace android { +namespace hardware { +namespace vibrator { +namespace V1_3 { +namespace implementation { + +static constexpr int8_t MAX_RTP_INPUT = 127; +static constexpr int8_t MIN_RTP_INPUT = 0; + +static constexpr char RTP_MODE[] = "rtp"; +static constexpr char WAVEFORM_MODE[] = "waveform"; + +// Use effect #1 in the waveform library for CLICK effect +static constexpr char WAVEFORM_CLICK_EFFECT_SEQ[] = "1 0"; + +// Use effect #2 in the waveform library for TICK effect +static constexpr char WAVEFORM_TICK_EFFECT_SEQ[] = "2 0"; + +// Use effect #3 in the waveform library for DOUBLE_CLICK effect +static constexpr char WAVEFORM_DOUBLE_CLICK_EFFECT_SEQ[] = "3 0"; + +// Use effect #4 in the waveform library for HEAVY_CLICK effect +static constexpr char WAVEFORM_HEAVY_CLICK_EFFECT_SEQ[] = "4 0"; + +// UT team design those target G values +static constexpr std::array EFFECT_TARGET_G = {0.175, 0.325, 0.37, 0.475, 0.6}; +static constexpr std::array STEADY_TARGET_G = {1.38, 1.145, 0.905}; + +#define FLOAT_EPS 1e-6 + +static std::uint32_t freqPeriodFormula(std::uint32_t in) { + return 1000000000 / (24615 * in); +} + +static std::uint32_t convertLevelsToOdClamp(float voltageLevel, uint32_t lraPeriod) { + float odClamp; + + odClamp = voltageLevel / + ((21.32 / 1000.0) * + sqrt(1.0 - (static_cast(freqPeriodFormula(lraPeriod)) * 8.0 / 10000.0))); + + return round(odClamp); +} + +static float targetGToVlevelsUnderLinearEquation(std::array inputCoeffs, float targetG) { + // Implement linear equation to get voltage levels, f(x) = ax + b + // 0 to 3.2 is our valid output + float outPutVal = 0.0f; + outPutVal = (targetG - inputCoeffs[1]) / inputCoeffs[0]; + if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) { + return outPutVal; + } else { + return 0.0f; + } +} + +static float targetGToVlevelsUnderCubicEquation(std::array inputCoeffs, float targetG) { + // Implement cubic equation to get voltage levels, f(x) = ax^3 + bx^2 + cx + d + // 0 to 3.2 is our valid output + float AA = 0.0f, BB = 0.0f, CC = 0.0f, Delta = 0.0f; + float Y1 = 0.0f, Y2 = 0.0f, K = 0.0f, T = 0.0f, sita = 0.0f; + float outPutVal = 0.0f; + float oneHalf = 1.0 / 2.0, oneThird = 1.0 / 3.0; + float cosSita = 0.0f, sinSitaSqrt3 = 0.0f, sqrtA = 0.0f; + + AA = inputCoeffs[1] * inputCoeffs[1] - 3.0 * inputCoeffs[0] * inputCoeffs[2]; + BB = inputCoeffs[1] * inputCoeffs[2] - 9.0 * inputCoeffs[0] * (inputCoeffs[3] - targetG); + CC = inputCoeffs[2] * inputCoeffs[2] - 3.0 * inputCoeffs[1] * (inputCoeffs[3] - targetG); + + Delta = BB * BB - 4.0 * AA * CC; + + // There are four discriminants in Shengjin formula. + // https://zh.wikipedia.org/wiki/%E4%B8%89%E6%AC%A1%E6%96%B9%E7%A8%8B#%E7%9B%9B%E9%87%91%E5%85%AC%E5%BC%8F%E6%B3%95 + if ((fabs(AA) <= FLOAT_EPS) && (fabs(BB) <= FLOAT_EPS)) { + // Case 1: A = B = 0 + outPutVal = -inputCoeffs[1] / (3 * inputCoeffs[0]); + if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) { + return outPutVal; + } + return 0.0f; + } else if (Delta > FLOAT_EPS) { + // Case 2: Delta > 0 + Y1 = AA * inputCoeffs[1] + 3.0 * inputCoeffs[0] * (-BB + pow(Delta, oneHalf)) / 2.0; + Y2 = AA * inputCoeffs[1] + 3.0 * inputCoeffs[0] * (-BB - pow(Delta, oneHalf)) / 2.0; + + if ((Y1 < -FLOAT_EPS) && (Y2 > FLOAT_EPS)) { + return (-inputCoeffs[1] + pow(-Y1, oneThird) - pow(Y2, oneThird)) / + (3.0 * inputCoeffs[0]); + } else if ((Y1 > FLOAT_EPS) && (Y2 < -FLOAT_EPS)) { + return (-inputCoeffs[1] - pow(Y1, oneThird) + pow(-Y2, oneThird)) / + (3.0 * inputCoeffs[0]); + } else if ((Y1 < -FLOAT_EPS) && (Y2 < -FLOAT_EPS)) { + return (-inputCoeffs[1] + pow(-Y1, oneThird) + pow(-Y2, oneThird)) / + (3.0 * inputCoeffs[0]); + } else { + return (-inputCoeffs[1] - pow(Y1, oneThird) - pow(Y2, oneThird)) / + (3.0 * inputCoeffs[0]); + } + return 0.0f; + } else if (Delta < -FLOAT_EPS) { + // Case 3: Delta < 0 + T = (2 * AA * inputCoeffs[1] - 3 * inputCoeffs[0] * BB) / (2 * AA * sqrt(AA)); + sita = acos(T); + cosSita = cos(sita / 3); + sinSitaSqrt3 = sqrt(3.0) * sin(sita / 3); + sqrtA = sqrt(AA); + + outPutVal = (-inputCoeffs[1] - 2 * sqrtA * cosSita) / (3 * inputCoeffs[0]); + if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) { + return outPutVal; + } + outPutVal = (-inputCoeffs[1] + sqrtA * (cosSita + sinSitaSqrt3)) / (3 * inputCoeffs[0]); + if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) { + return outPutVal; + } + outPutVal = (-inputCoeffs[1] + sqrtA * (cosSita - sinSitaSqrt3)) / (3 * inputCoeffs[0]); + if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) { + return outPutVal; + } + return 0.0f; + } else if (Delta <= FLOAT_EPS) { + // Case 4: Delta = 0 + K = BB / AA; + outPutVal = (-inputCoeffs[1] / inputCoeffs[0] + K); + if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) { + return outPutVal; + } + outPutVal = (-K / 2); + if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) { + return outPutVal; + } + return 0.0f; + } else { + // Exception handling + return 0.0f; + } +} + +using utils::toUnderlying; + +using Status = ::android::hardware::vibrator::V1_0::Status; +using EffectStrength = ::android::hardware::vibrator::V1_0::EffectStrength; + +Vibrator::Vibrator(std::unique_ptr hwapi, std::unique_ptr hwcal) + : mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)) { + std::string autocal; + uint32_t lraPeriod = 0; + bool dynamicConfig = false; + bool hasEffectCoeffs = false; + std::array effectCoeffs = {0}; + + if (!mHwApi->setState(true)) { + ALOGE("Failed to set state (%d): %s", errno, strerror(errno)); + } + + if (mHwCal->getAutocal(&autocal)) { + mHwApi->setAutocal(autocal); + } + mHwCal->getLraPeriod(&lraPeriod); + + mHwCal->getCloseLoopThreshold(&mCloseLoopThreshold); + mHwCal->getDynamicConfig(&dynamicConfig); + + if (dynamicConfig) { + uint8_t i = 0; + float tempVolLevel = 0.0f; + float tempAmpMax = 0.0f; + uint32_t longFreqencyShift = 0; + uint32_t shortVoltageMax = 0, longVoltageMax = 0; + uint32_t shape = 0; + + mHwCal->getLongFrequencyShift(&longFreqencyShift); + mHwCal->getShortVoltageMax(&shortVoltageMax); + mHwCal->getLongVoltageMax(&longVoltageMax); + + hasEffectCoeffs = mHwCal->getEffectCoeffs(&effectCoeffs); + for (i = 0; i < 5; i++) { + if (hasEffectCoeffs) { + // Use linear approach to get the target voltage levels + if ((effectCoeffs[2] == 0) && (effectCoeffs[3] == 0)) { + tempVolLevel = + targetGToVlevelsUnderLinearEquation(effectCoeffs, EFFECT_TARGET_G[i]); + mEffectTargetOdClamp[i] = convertLevelsToOdClamp(tempVolLevel, lraPeriod); + } else { + // Use cubic approach to get the target voltage levels + tempVolLevel = + targetGToVlevelsUnderCubicEquation(effectCoeffs, EFFECT_TARGET_G[i]); + mEffectTargetOdClamp[i] = convertLevelsToOdClamp(tempVolLevel, lraPeriod); + } + } else { + mEffectTargetOdClamp[i] = shortVoltageMax; + } + } + // Add a boundary protection for level 5 only, since + // some devices might not be able to reach the maximum target G + if ((mEffectTargetOdClamp[4] <= 0) || (mEffectTargetOdClamp[4] > 161)) { + mEffectTargetOdClamp[4] = shortVoltageMax; + } + + mHwCal->getEffectShape(&shape); + mEffectConfig.reset(new VibrationConfig({ + .shape = (shape == UINT32_MAX) ? WaveShape::SINE : static_cast(shape), + .odClamp = &mEffectTargetOdClamp[0], + .olLraPeriod = lraPeriod, + })); + + mSteadyTargetOdClamp = mHwCal->getSteadyAmpMax(&tempAmpMax) + ? round((STEADY_TARGET_G[0] / tempAmpMax) * longVoltageMax) + : longVoltageMax; + mHwCal->getSteadyShape(&shape); + mSteadyConfig.reset(new VibrationConfig({ + .shape = (shape == UINT32_MAX) ? WaveShape::SQUARE : static_cast(shape), + .odClamp = &mSteadyTargetOdClamp, + // 1. Change long lra period to frequency + // 2. Get frequency': subtract the frequency shift from the frequency + // 3. Get final long lra period after put the frequency' to formula + .olLraPeriod = freqPeriodFormula(freqPeriodFormula(lraPeriod) - longFreqencyShift), + })); + } else { + mHwApi->setOlLraPeriod(lraPeriod); + } + + mHwCal->getClickDuration(&mClickDuration); + mHwCal->getTickDuration(&mTickDuration); + mHwCal->getDoubleClickDuration(&mDoubleClickDuration); + mHwCal->getHeavyClickDuration(&mHeavyClickDuration); + + // This enables effect #1 from the waveform library to be triggered by SLPI + // while the AP is in suspend mode + if (!mHwApi->setLpTriggerEffect(1)) { + ALOGW("Failed to set LP trigger mode (%d): %s", errno, strerror(errno)); + } +} + +Return Vibrator::on(uint32_t timeoutMs, const char mode[], + const std::unique_ptr &config, + const int8_t volOffset) { + LoopControl loopMode = LoopControl::OPEN; + + // Open-loop mode is used for short click for over-drive + // Close-loop mode is used for long notification for stability + if (mode == RTP_MODE && timeoutMs > mCloseLoopThreshold) { + loopMode = LoopControl::CLOSE; + } + + mHwApi->setCtrlLoop(toUnderlying(loopMode)); + if (!mHwApi->setDuration(timeoutMs)) { + ALOGE("Failed to set duration (%d): %s", errno, strerror(errno)); + return Status::UNKNOWN_ERROR; + } + + mHwApi->setMode(mode); + if (config != nullptr) { + mHwApi->setLraWaveShape(toUnderlying(config->shape)); + mHwApi->setOdClamp(config->odClamp[volOffset]); + mHwApi->setOlLraPeriod(config->olLraPeriod); + } + + if (!mHwApi->setActivate(1)) { + ALOGE("Failed to activate (%d): %s", errno, strerror(errno)); + return Status::UNKNOWN_ERROR; + } + + return Status::OK; +} + +// Methods from ::android::hardware::vibrator::V1_2::IVibrator follow. +Return Vibrator::on(uint32_t timeoutMs) { + ATRACE_NAME("Vibrator::on"); + return on(timeoutMs, RTP_MODE, mSteadyConfig, 0); +} + +Return Vibrator::off() { + ATRACE_NAME("Vibrator::off"); + if (!mHwApi->setActivate(0)) { + ALOGE("Failed to turn vibrator off (%d): %s", errno, strerror(errno)); + return Status::UNKNOWN_ERROR; + } + return Status::OK; +} + +Return Vibrator::supportsAmplitudeControl() { + ATRACE_NAME("Vibrator::supportsAmplitudeControl"); + return (mHwApi->hasRtpInput() ? true : false); +} + +Return Vibrator::setAmplitude(uint8_t amplitude) { + ATRACE_NAME("Vibrator::setAmplitude"); + if (amplitude == 0) { + return Status::BAD_VALUE; + } + + int32_t rtp_input = + std::round((amplitude - 1) / 254.0 * (MAX_RTP_INPUT - MIN_RTP_INPUT) + MIN_RTP_INPUT); + + if (!mHwApi->setRtpInput(rtp_input)) { + ALOGE("Failed to set amplitude (%d): %s", errno, strerror(errno)); + return Status::UNKNOWN_ERROR; + } + + return Status::OK; +} + +// Methods from ::android::hardware::vibrator::V1_3::IVibrator follow. + +Return Vibrator::supportsExternalControl() { + ATRACE_NAME("Vibrator::supportsExternalControl"); + return false; +} + +Return Vibrator::setExternalControl(bool enabled) { + ATRACE_NAME("Vibrator::setExternalControl"); + ALOGE("Not support in DRV2624 solution, %d", enabled); + return Status::UNSUPPORTED_OPERATION; +} + +// Methods from ::android.hidl.base::V1_0::IBase follow. + +Return Vibrator::debug(const hidl_handle &handle, + const hidl_vec & /* options */) { + if (handle == nullptr || handle->numFds < 1 || handle->data[0] < 0) { + ALOGE("Called debug() with invalid fd."); + return Void(); + } + + int fd = handle->data[0]; + + dprintf(fd, "HIDL:\n"); + + dprintf(fd, " Close Loop Thresh: %" PRIu32 "\n", mCloseLoopThreshold); + if (mSteadyConfig) { + dprintf(fd, " Steady Shape: %" PRIu32 "\n", mSteadyConfig->shape); + dprintf(fd, " Steady OD Clamp: %" PRIu32 "\n", mSteadyConfig->odClamp[0]); + dprintf(fd, " Steady OL LRA Period: %" PRIu32 "\n", mSteadyConfig->olLraPeriod); + } + if (mEffectConfig) { + dprintf(fd, " Effect Shape: %" PRIu32 "\n", mEffectConfig->shape); + dprintf(fd, + " Effect OD Clamp: %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 "\n", + mEffectConfig->odClamp[0], mEffectConfig->odClamp[1], mEffectConfig->odClamp[2], + mEffectConfig->odClamp[3], mEffectConfig->odClamp[4]); + dprintf(fd, " Effect OL LRA Period: %" PRIu32 "\n", mEffectConfig->olLraPeriod); + } + dprintf(fd, " Click Duration: %" PRIu32 "\n", mClickDuration); + dprintf(fd, " Tick Duration: %" PRIu32 "\n", mTickDuration); + dprintf(fd, " Double Click Duration: %" PRIu32 "\n", mDoubleClickDuration); + dprintf(fd, " Heavy Click Duration: %" PRIu32 "\n", mHeavyClickDuration); + + dprintf(fd, "\n"); + + mHwApi->debug(fd); + + dprintf(fd, "\n"); + + mHwCal->debug(fd); + + fsync(fd); + return Void(); +} + +Return Vibrator::perform(V1_0::Effect effect, EffectStrength strength, perform_cb _hidl_cb) { + return performWrapper(effect, strength, _hidl_cb); +} + +Return Vibrator::perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength, + perform_cb _hidl_cb) { + return performWrapper(effect, strength, _hidl_cb); +} + +Return Vibrator::perform_1_2(V1_2::Effect effect, EffectStrength strength, + perform_cb _hidl_cb) { + return performWrapper(effect, strength, _hidl_cb); +} + +Return Vibrator::perform_1_3(Effect effect, EffectStrength strength, perform_cb _hidl_cb) { + return performWrapper(effect, strength, _hidl_cb); +} + +template +Return Vibrator::performWrapper(T effect, EffectStrength strength, perform_cb _hidl_cb) { + ATRACE_NAME("Vibrator::performWrapper"); + auto validEffectRange = hidl_enum_range(); + if (effect < *validEffectRange.begin() || effect > *std::prev(validEffectRange.end())) { + _hidl_cb(Status::UNSUPPORTED_OPERATION, 0); + return Void(); + } + auto validStrengthRange = hidl_enum_range(); + if (strength < *validStrengthRange.begin() || strength > *std::prev(validStrengthRange.end())) { + _hidl_cb(Status::UNSUPPORTED_OPERATION, 0); + return Void(); + } + return performEffect(static_cast(effect), strength, _hidl_cb); +} + +Return Vibrator::performEffect(Effect effect, EffectStrength strength, perform_cb _hidl_cb) { + Status status = Status::OK; + uint32_t timeMS; + int8_t volOffset; + + switch (strength) { + case EffectStrength::LIGHT: + volOffset = 0; + break; + case EffectStrength::MEDIUM: + volOffset = 1; + break; + case EffectStrength::STRONG: + volOffset = 1; + break; + default: + status = Status::UNSUPPORTED_OPERATION; + break; + } + + switch (effect) { + case Effect::TEXTURE_TICK: + mHwApi->setSequencer(WAVEFORM_TICK_EFFECT_SEQ); + timeMS = mTickDuration; + volOffset = TEXTURE_TICK; + break; + case Effect::CLICK: + mHwApi->setSequencer(WAVEFORM_CLICK_EFFECT_SEQ); + timeMS = mClickDuration; + volOffset += CLICK; + break; + case Effect::DOUBLE_CLICK: + mHwApi->setSequencer(WAVEFORM_DOUBLE_CLICK_EFFECT_SEQ); + timeMS = mDoubleClickDuration; + volOffset += CLICK; + break; + case Effect::TICK: + mHwApi->setSequencer(WAVEFORM_TICK_EFFECT_SEQ); + timeMS = mTickDuration; + volOffset += TICK; + break; + case Effect::HEAVY_CLICK: + mHwApi->setSequencer(WAVEFORM_HEAVY_CLICK_EFFECT_SEQ); + timeMS = mHeavyClickDuration; + volOffset += HEAVY_CLICK; + break; + default: + _hidl_cb(Status::UNSUPPORTED_OPERATION, 0); + return Void(); + } + on(timeMS, WAVEFORM_MODE, mEffectConfig, volOffset); + _hidl_cb(status, timeMS); + return Void(); +} + +} // namespace implementation +} // namespace V1_3 +} // namespace vibrator +} // namespace hardware +} // namespace android diff --git a/vibrator/drv2624/Vibrator.h b/vibrator/drv2624/Vibrator.h new file mode 100644 index 0000000..16cd8e8 --- /dev/null +++ b/vibrator/drv2624/Vibrator.h @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2017 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. + */ +#ifndef ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H +#define ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H + +#include +#include + +#include + +namespace android { +namespace hardware { +namespace vibrator { +namespace V1_3 { +namespace implementation { + +class Vibrator : public IVibrator { + public: + // APIs for interfacing with the kernel driver. + class HwApi { + public: + virtual ~HwApi() = default; + // Stores the COMP, BEMF, and GAIN calibration values to use. + // + virtual bool setAutocal(std::string value) = 0; + // Stores the open-loop LRA frequency to be used. + virtual bool setOlLraPeriod(uint32_t value) = 0; + // Activates/deactivates the vibrator for durations specified by + // setDuration(). + virtual bool setActivate(bool value) = 0; + // Specifies the vibration duration in milliseconds. + virtual bool setDuration(uint32_t value) = 0; + // Specifies the active state of the vibrator + // (true = enabled, false = disabled). + virtual bool setState(bool value) = 0; + // Reports whether setRtpInput() is supported. + virtual bool hasRtpInput() = 0; + // Specifies the playback amplitude of the haptic waveforms in RTP mode. + // Negative numbers indicates braking. + virtual bool setRtpInput(int8_t value) = 0; + // Specifies the mode of operation. + // rtp - RTP Mode + // waveform - Waveform Sequencer Mode + // diag - Diagnostics Routine + // autocal - Automatic Level Calibration Routine + virtual bool setMode(std::string value) = 0; + // Specifies a waveform sequence in index-count pairs. + // [ ...] + virtual bool setSequencer(std::string value) = 0; + // Specifies the scaling of effects in Waveform mode. + // 0 - 100% + // 1 - 75% + // 2 - 50% + // 3 - 25% + virtual bool setScale(uint8_t value) = 0; + // Selects either closed loop or open loop mode. + // (true = open, false = closed). + virtual bool setCtrlLoop(bool value) = 0; + // Specifies waveform index to be played in low-power trigger mode. + // 0 - Disabled + // 1+ - Waveform Index + virtual bool setLpTriggerEffect(uint32_t value) = 0; + // Specifies which shape to use for driving the LRA when in open loop + // mode. + // 0 - Square Wave + // 1 - Sine Wave + virtual bool setLraWaveShape(uint32_t value) = 0; + // Specifies the maximum voltage for automatic overdrive and automatic + // braking periods. + virtual bool setOdClamp(uint32_t value) = 0; + // Emit diagnostic information to the given file. + virtual void debug(int fd) = 0; + }; + + // APIs for obtaining calibration/configuration data from persistent memory. + class HwCal { + public: + virtual ~HwCal() = default; + // Obtains the COMP, BEMF, and GAIN calibration values to use. + virtual bool getAutocal(std::string *value) = 0; + // Obtains the open-loop LRA frequency to be used. + virtual bool getLraPeriod(uint32_t *value) = 0; + // Obtains the effect coeffs to calculate the target voltage + virtual bool getEffectCoeffs(std::array *value) = 0; + // Obtain the max steady G value + virtual bool getSteadyAmpMax(float *value) = 0; + // Obtains threshold in ms, above which close-loop should be used. + virtual bool getCloseLoopThreshold(uint32_t *value) = 0; + // Obtains dynamic/static configuration choice. + virtual bool getDynamicConfig(bool *value) = 0; + // Obtains LRA frequency shift for long (steady) vibrations. + virtual bool getLongFrequencyShift(uint32_t *value) = 0; + // Obtains maximum voltage for short (effect) vibrations + virtual bool getShortVoltageMax(uint32_t *value) = 0; + // Obtains maximum voltage for long (steady) vibrations + virtual bool getLongVoltageMax(uint32_t *value) = 0; + // Obtains the duration for the click effect + virtual bool getClickDuration(uint32_t *value) = 0; + // Obtains the duration for the tick effect + virtual bool getTickDuration(uint32_t *value) = 0; + // Obtains the duration for the double-click effect + virtual bool getDoubleClickDuration(uint32_t *value) = 0; + // Obtains the duration for the heavy-click effect + virtual bool getHeavyClickDuration(uint32_t *value) = 0; + // Obtains the wave shape for effect haptics + virtual bool getEffectShape(uint32_t *value) = 0; + // Obtains the wave shape for steady vibration + virtual bool getSteadyShape(uint32_t *value) = 0; + // Emit diagnostic information to the given file. + virtual void debug(int fd) = 0; + }; + + private: + enum class LoopControl : bool { + CLOSE = false, + OPEN = true, + }; + + enum class WaveShape : uint32_t { + SQUARE = 0, + SINE = 1, + }; + + struct VibrationConfig { + WaveShape shape; + uint32_t *odClamp; + uint32_t olLraPeriod; + }; + + enum OdClampOffset : uint32_t { + TEXTURE_TICK, + TICK, + CLICK, + HEAVY_CLICK, + }; + + public: + Vibrator(std::unique_ptr hwapi, std::unique_ptr hwcal); + + // Methods from ::android::hardware::vibrator::V1_0::IVibrator follow. + using Status = ::android::hardware::vibrator::V1_0::Status; + Return on(uint32_t timeoutMs) override; + Return off() override; + Return supportsAmplitudeControl() override; + Return setAmplitude(uint8_t amplitude) override; + + // Methods from ::android::hardware::vibrator::V1_3::IVibrator follow. + Return supportsExternalControl() override; + Return setExternalControl(bool enabled) override; + + using EffectStrength = ::android::hardware::vibrator::V1_0::EffectStrength; + Return perform(V1_0::Effect effect, EffectStrength strength, + perform_cb _hidl_cb) override; + Return perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength, + perform_cb _hidl_cb) override; + Return perform_1_2(V1_2::Effect effect, EffectStrength strength, + perform_cb _hidl_cb) override; + Return perform_1_3(Effect effect, EffectStrength strength, perform_cb _hidl_cb) override; + + // Methods from ::android.hidl.base::V1_0::IBase follow. + Return debug(const hidl_handle &handle, const hidl_vec &options) override; + + private: + Return on(uint32_t timeoutMs, const char mode[], + const std::unique_ptr &config, const int8_t volOffset); + template + Return performWrapper(T effect, EffectStrength strength, perform_cb _hidl_cb); + Return performEffect(Effect effect, EffectStrength strength, perform_cb _hidl_cb); + std::unique_ptr mHwApi; + std::unique_ptr mHwCal; + uint32_t mCloseLoopThreshold; + std::unique_ptr mSteadyConfig; + std::unique_ptr mEffectConfig; + uint32_t mClickDuration; + uint32_t mTickDuration; + uint32_t mDoubleClickDuration; + uint32_t mHeavyClickDuration; + std::array mEffectTargetOdClamp; + uint32_t mSteadyTargetOdClamp; +}; + +} // namespace implementation +} // namespace V1_3 +} // namespace vibrator +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H diff --git a/vibrator/drv2624/android.hardware.vibrator@1.3-service.redfin.rc b/vibrator/drv2624/android.hardware.vibrator@1.3-service.redfin.rc new file mode 100644 index 0000000..02d6ea4 --- /dev/null +++ b/vibrator/drv2624/android.hardware.vibrator@1.3-service.redfin.rc @@ -0,0 +1,15 @@ +service vendor.vibrator-1-3 /vendor/bin/hw/android.hardware.vibrator@1.3-service.redfin + class hal + user system + group system + + setenv PROPERTY_PREFIX ro.vibrator.hal. + setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/drv2624.cal + + setenv HWAPI_PATH_PREFIX /sys/class/leds/vibrator/ + setenv HWAPI_DEBUG_PATHS " + device/autocal + device/lp_trigger_effect + device/ol_lra_period + state + " diff --git a/vibrator/android.hardware.vibrator@1.3-service.redfin.xml b/vibrator/drv2624/android.hardware.vibrator@1.3-service.redfin.xml similarity index 100% rename from vibrator/android.hardware.vibrator@1.3-service.redfin.xml rename to vibrator/drv2624/android.hardware.vibrator@1.3-service.redfin.xml diff --git a/vibrator/drv2624/bench/Android.bp b/vibrator/drv2624/bench/Android.bp new file mode 100644 index 0000000..d7b63ef --- /dev/null +++ b/vibrator/drv2624/bench/Android.bp @@ -0,0 +1,32 @@ +// +// Copyright (C) 2019 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. + +cc_benchmark { + name: "VibratorHalDrv2624BenchmarkRedfin", + defaults: ["VibratorHalDrv2624TestDefaultsRedfin"], + srcs: [ + "benchmark.cpp", + ], + static_libs: [ + "libc++fs", + ], + shared_libs: [ + "libbase", + ], + // TODO(b/135767253): Remove when fixed. + test_suites: ["device-tests"], + // TODO(b/142024316): Remove when fixed. + require_root: true, +} diff --git a/vibrator/drv2624/bench/benchmark.cpp b/vibrator/drv2624/bench/benchmark.cpp new file mode 100644 index 0000000..e63da43 --- /dev/null +++ b/vibrator/drv2624/bench/benchmark.cpp @@ -0,0 +1,203 @@ +/* * Copyright (C) 2019 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 "benchmark/benchmark.h" + +#include +#include +#include + +#include "Hardware.h" +#include "Vibrator.h" + +using ::android::sp; +using ::android::hardware::hidl_enum_range; + +namespace android { +namespace hardware { +namespace vibrator { +namespace V1_3 { +namespace implementation { + +using ::android::base::SetProperty; +using ::android::hardware::vibrator::V1_0::EffectStrength; +using ::android::hardware::vibrator::V1_0::Status; + +class VibratorBench : public benchmark::Fixture { + private: + static constexpr const char *FILE_NAMES[]{ + "device/autocal", + "device/ol_lra_period", + "activate", + "duration", + "state", + "device/rtp_input", + "device/mode", + "device/set_sequencer", + "device/scale", + "device/ctrl_loop", + "device/lp_trigger_effect", + "device/lra_wave_shape", + "device/od_clamp", + }; + static constexpr char PROPERTY_PREFIX[] = "test.vibrator.hal."; + + public: + void SetUp(::benchmark::State &state) override { + auto prefix = std::filesystem::path(mFilesDir.path) / ""; + + setenv("HWAPI_PATH_PREFIX", prefix.c_str(), true); + + for (auto n : FILE_NAMES) { + const auto name = std::filesystem::path(n); + const auto path = std::filesystem::path(mFilesDir.path) / name; + + fs_mkdirs(path.c_str(), S_IRWXU); + symlink("/dev/null", path.c_str()); + } + + setenv("PROPERTY_PREFIX", PROPERTY_PREFIX, true); + + SetProperty(std::string() + PROPERTY_PREFIX + "config.dynamic", getDynamicConfig(state)); + + mVibrator = new Vibrator(HwApi::Create(), std::make_unique()); + } + + static void DefaultConfig(benchmark::internal::Benchmark *b) { + b->Unit(benchmark::kMicrosecond); + } + + static void DefaultArgs(benchmark::internal::Benchmark *b) { + b->ArgNames({"DynamicConfig"}); + b->Args({false}); + b->Args({true}); + } + + protected: + std::string getDynamicConfig(const ::benchmark::State &state) const { + return std::to_string(state.range(0)); + } + + auto getOtherArg(const ::benchmark::State &state, std::size_t index) const { + return state.range(index + 1); + } + + protected: + TemporaryDir mFilesDir; + sp mVibrator; +}; + +#define BENCHMARK_WRAPPER(fixt, test, code) \ + BENCHMARK_DEFINE_F(fixt, test) \ + /* NOLINTNEXTLINE */ \ + (benchmark::State & state){code} BENCHMARK_REGISTER_F(fixt, test) \ + ->Apply(fixt::DefaultConfig) \ + ->Apply(fixt::DefaultArgs) + +BENCHMARK_WRAPPER(VibratorBench, on, { + uint32_t duration = std::rand() ?: 1; + + for (auto _ : state) { + mVibrator->on(duration); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, off, { + for (auto _ : state) { + mVibrator->off(); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, supportsAmplitudeControl, { + for (auto _ : state) { + mVibrator->supportsAmplitudeControl(); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, setAmplitude, { + uint8_t amplitude = std::rand() ?: 1; + + for (auto _ : state) { + mVibrator->setAmplitude(amplitude); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, supportsExternalControl, { + for (auto _ : state) { + mVibrator->supportsExternalControl(); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, setExternalControl_enable, { + for (auto _ : state) { + mVibrator->setExternalControl(true); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, setExternalControl_disable, { + for (auto _ : state) { + mVibrator->setExternalControl(false); + } +}); + +class VibratorEffectsBench : public VibratorBench { + public: + static void DefaultArgs(benchmark::internal::Benchmark *b) { + b->ArgNames({"DynamicConfig", "Effect", "Strength"}); + for (const auto &dynamic : {false, true}) { + for (const auto &effect : hidl_enum_range()) { + for (const auto &strength : hidl_enum_range()) { + b->Args({dynamic, static_cast(effect), static_cast(strength)}); + } + } + } + } + + protected: + auto getEffect(const ::benchmark::State &state) const { + return static_cast(getOtherArg(state, 0)); + } + + auto getStrength(const ::benchmark::State &state) const { + return static_cast(getOtherArg(state, 1)); + } +}; + +BENCHMARK_WRAPPER(VibratorEffectsBench, perform_1_3, { + Effect effect = getEffect(state); + EffectStrength strength = getStrength(state); + bool supported = true; + + mVibrator->perform_1_3(effect, strength, [&](Status status, uint32_t /*lengthMs*/) { + if (status == Status::UNSUPPORTED_OPERATION) { + supported = false; + } + }); + + if (!supported) { + return; + } + + for (auto _ : state) { + mVibrator->perform_1_3(effect, strength, [](Status /*status*/, uint32_t /*lengthMs*/) {}); + } +}); + +} // namespace implementation +} // namespace V1_3 +} // namespace vibrator +} // namespace hardware +} // namespace android + +BENCHMARK_MAIN(); diff --git a/vibrator/drv2624/drv2624.bin b/vibrator/drv2624/drv2624.bin new file mode 100644 index 0000000000000000000000000000000000000000..e6f896db9f11d4a5c6564201aab9c9fa3bd2dd9f GIT binary patch literal 45 xcmY#UV_?u_U|?WnQcz%EW?*0evKbh7nHcz)7zEiE#F^~bfRKZs*Ph3o4FE8(0|Wp7 literal 0 HcmV?d00001 diff --git a/vibrator/service.cpp b/vibrator/drv2624/service.cpp similarity index 80% rename from vibrator/service.cpp rename to vibrator/drv2624/service.cpp index 32eec89..c0447a7 100644 --- a/vibrator/service.cpp +++ b/vibrator/drv2624/service.cpp @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define LOG_TAG "android.hardware.vibrator@1.3-service.redfin" +#include #include #include #include @@ -23,15 +23,24 @@ #include "Hardware.h" #include "Vibrator.h" +using android::OK; +using android::sp; +using android::status_t; +using android::UNKNOWN_ERROR; using android::hardware::configureRpcThreadpool; using android::hardware::joinRpcThreadpool; using android::hardware::vibrator::V1_3::implementation::HwApi; using android::hardware::vibrator::V1_3::implementation::HwCal; using android::hardware::vibrator::V1_3::implementation::Vibrator; -using namespace android; status_t registerVibratorService() { - sp vibrator = new Vibrator(std::make_unique(), std::make_unique()); + auto hwapi = HwApi::Create(); + + if (!hwapi) { + return UNKNOWN_ERROR; + } + + sp vibrator = new Vibrator(std::move(hwapi), std::make_unique()); return vibrator->registerAsService(); } diff --git a/vibrator/drv2624/tests/Android.bp b/vibrator/drv2624/tests/Android.bp new file mode 100644 index 0000000..2adfc84 --- /dev/null +++ b/vibrator/drv2624/tests/Android.bp @@ -0,0 +1,31 @@ +// +// Copyright (C) 2019 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. + +cc_test { + name: "VibratorHalDrv2624TestSuiteRedfin", + defaults: ["VibratorHalDrv2624TestDefaultsRedfin"], + srcs: [ + "test-hwapi.cpp", + "test-hwcal.cpp", + "test-vibrator.cpp", + ], + static_libs: [ + "libc++fs", + "libgmock", + ], + shared_libs: [ + "libbase", + ], +} diff --git a/vibrator/drv2624/tests/mocks.h b/vibrator/drv2624/tests/mocks.h new file mode 100644 index 0000000..d4867df --- /dev/null +++ b/vibrator/drv2624/tests/mocks.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H +#define ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H + +#include "Vibrator.h" + +class MockApi : public ::android::hardware::vibrator::V1_3::implementation::Vibrator::HwApi { + public: + MOCK_METHOD0(destructor, void()); + MOCK_METHOD1(setAutocal, bool(std::string value)); + MOCK_METHOD1(setOlLraPeriod, bool(uint32_t value)); + MOCK_METHOD1(setActivate, bool(bool value)); + MOCK_METHOD1(setDuration, bool(uint32_t value)); + MOCK_METHOD1(setState, bool(bool value)); + MOCK_METHOD0(hasRtpInput, bool()); + MOCK_METHOD1(setRtpInput, bool(int8_t value)); + MOCK_METHOD1(setMode, bool(std::string value)); + MOCK_METHOD1(setSequencer, bool(std::string value)); + MOCK_METHOD1(setScale, bool(uint8_t value)); + MOCK_METHOD1(setCtrlLoop, bool(bool value)); + MOCK_METHOD1(setLpTriggerEffect, bool(uint32_t value)); + MOCK_METHOD1(setLraWaveShape, bool(uint32_t value)); + MOCK_METHOD1(setOdClamp, bool(uint32_t value)); + MOCK_METHOD1(debug, void(int fd)); + + ~MockApi() override { destructor(); }; +}; + +class MockCal : public ::android::hardware::vibrator::V1_3::implementation::Vibrator::HwCal { + public: + MOCK_METHOD0(destructor, void()); + MOCK_METHOD1(getAutocal, bool(std::string &value)); // NOLINT + MOCK_METHOD1(getLraPeriod, bool(uint32_t *value)); + MOCK_METHOD1(getEffectCoeffs, bool(std::array *value)); + MOCK_METHOD1(getSteadyAmpMax, bool(float *value)); + MOCK_METHOD1(getCloseLoopThreshold, bool(uint32_t *value)); + MOCK_METHOD1(getDynamicConfig, bool(bool *value)); + MOCK_METHOD1(getLongFrequencyShift, bool(uint32_t *value)); + MOCK_METHOD1(getShortVoltageMax, bool(uint32_t *value)); + MOCK_METHOD1(getLongVoltageMax, bool(uint32_t *value)); + MOCK_METHOD1(getClickDuration, bool(uint32_t *value)); + MOCK_METHOD1(getTickDuration, bool(uint32_t *value)); + MOCK_METHOD1(getDoubleClickDuration, bool(uint32_t *value)); + MOCK_METHOD1(getHeavyClickDuration, bool(uint32_t *value)); + MOCK_METHOD1(getEffectShape, bool(uint32_t *value)); + MOCK_METHOD1(getSteadyShape, bool(uint32_t *value)); + MOCK_METHOD1(debug, void(int fd)); + + ~MockCal() override { destructor(); }; + // b/132668253: Workaround gMock Compilation Issue + bool getAutocal(std::string *value) { return getAutocal(*value); } +}; + +#endif // ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H diff --git a/vibrator/tests/test-hwapi.cpp b/vibrator/drv2624/tests/test-hwapi.cpp similarity index 50% rename from vibrator/tests/test-hwapi.cpp rename to vibrator/drv2624/tests/test-hwapi.cpp index 87fdae2..fc57f85 100644 --- a/vibrator/tests/test-hwapi.cpp +++ b/vibrator/drv2624/tests/test-hwapi.cpp @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define LOG_TAG "android.hardware.vibrator@1.3-tests.redfin" #include +#include #include #include @@ -23,40 +23,64 @@ #include "Hardware.h" -using namespace ::testing; - namespace android { namespace hardware { namespace vibrator { namespace V1_3 { namespace implementation { +using ::testing::Test; +using ::testing::TestParamInfo; +using ::testing::ValuesIn; +using ::testing::WithParamInterface; + class HwApiTest : public Test { - private: + protected: static constexpr const char *FILE_NAMES[]{ - "F0_FILEPATH", "REDC_FILEPATH", "Q_FILEPATH", "ACTIVATE_PATH", - "DURATION_PATH", "STATE_PATH", "EFFECT_DURATION_PATH", "EFFECT_INDEX_PATH", - "EFFECT_QUEUE_PATH", "EFFECT_SCALE_PATH", "GLOBAL_SCALE_PATH", "ASP_ENABLE_PATH", - "GPIO_FALL_INDEX", "GPIO_FALL_SCALE", "GPIO_RISE_INDEX", "GPIO_RISE_SCALE", + "device/autocal", + "device/ol_lra_period", + "activate", + "duration", + "state", + "device/rtp_input", + "device/mode", + "device/set_sequencer", + "device/scale", + "device/ctrl_loop", + "device/lp_trigger_effect", + "device/lra_wave_shape", + "device/od_clamp", + }; + + static constexpr const char *REQUIRED[]{ + "activate", + "duration", + "state", }; public: void SetUp() override { + std::string prefix; for (auto n : FILE_NAMES) { - auto name = std::string(n); - auto path = std::string(mFilesDir.path) + "/" + name; + auto name = std::filesystem::path(n); + auto path = std::filesystem::path(mFilesDir.path) / name; + fs_mkdirs(path.c_str(), S_IRWXU); std::ofstream touch{path}; - setenv(name.c_str(), path.c_str(), true); mFileMap[name] = path; } - mHwApi = std::make_unique(); + prefix = std::filesystem::path(mFilesDir.path) / ""; + setenv("HWAPI_PATH_PREFIX", prefix.c_str(), true); + mHwApi = HwApi::Create(); - for (auto n : FILE_NAMES) { - auto name = std::string(n); - auto path = std::string(mEmptyDir.path) + "/" + name; - setenv(name.c_str(), path.c_str(), true); + for (auto n : REQUIRED) { + auto name = std::filesystem::path(n); + auto path = std::filesystem::path(mEmptyDir.path) / name; + fs_mkdirs(path.c_str(), S_IRWXU); + std::ofstream touch{path}; } - mNoApi = std::make_unique(); + prefix = std::filesystem::path(mEmptyDir.path) / ""; + setenv("HWAPI_PATH_PREFIX", prefix.c_str(), true); + mNoApi = HwApi::Create(); } void TearDown() override { verifyContents(); } @@ -91,6 +115,21 @@ class HwApiTest : public Test { } } + // TODO(eliptus): Determine how to induce errors in required files + static bool isRequired(const std::string &name) { + for (auto n : REQUIRED) { + if (std::string(n) == name) { + return true; + } + } + return false; + } + + static auto ParamNameFixup(std::string str) { + std::replace(str.begin(), str.end(), '/', '_'); + return str; + } + protected: std::unique_ptr mHwApi; std::unique_ptr mNoApi; @@ -100,14 +139,53 @@ class HwApiTest : public Test { std::map mExpectedContent; }; -template -class HwApiTypedTest - : public HwApiTest, - public testing::WithParamInterface>> { +class CreateTest : public HwApiTest, public WithParamInterface { public: - static auto PrintParam(const testing::TestParamInfo &info) { - return std::get<0>(info.param); - }; + void SetUp() override{}; + void TearDown() override{}; + + static auto PrintParam(const TestParamInfo &info) { + return ParamNameFixup(info.param); + } + static auto &AllParams() { return FILE_NAMES; } +}; + +TEST_P(CreateTest, file_missing) { + auto skip = std::string(GetParam()); + TemporaryDir dir; + std::unique_ptr hwapi; + std::string prefix; + + for (auto n : FILE_NAMES) { + auto name = std::string(n); + auto path = std::string(dir.path) + "/" + name; + if (name == skip) { + continue; + } + fs_mkdirs(path.c_str(), S_IRWXU); + std::ofstream touch{path}; + } + + prefix = std::filesystem::path(dir.path) / ""; + setenv("HWAPI_PATH_PREFIX", prefix.c_str(), true); + hwapi = HwApi::Create(); + if (isRequired(skip)) { + EXPECT_EQ(nullptr, hwapi); + } else { + EXPECT_NE(nullptr, hwapi); + } +} + +INSTANTIATE_TEST_CASE_P(HwApiTests, CreateTest, ValuesIn(CreateTest::AllParams()), + CreateTest::PrintParam); + +template +class HwApiTypedTest : public HwApiTest, + public WithParamInterface>> { + public: + static auto PrintParam(const TestParamInfo &info) { + return ParamNameFixup(std::get<0>(info.param)); + } static auto MakeParam(std::string name, std::function func) { return std::make_tuple(name, func); } @@ -131,85 +209,10 @@ TEST_P(HasTest, success_returnsFalse) { INSTANTIATE_TEST_CASE_P(HwApiTests, HasTest, ValuesIn({ - HasTest::MakeParam("EFFECT_SCALE_PATH", - &Vibrator::HwApi::hasEffectScale), - HasTest::MakeParam("ASP_ENABLE_PATH", &Vibrator::HwApi::hasAspEnable), + HasTest::MakeParam("device/rtp_input", &Vibrator::HwApi::hasRtpInput), }), HasTest::PrintParam); -using GetBoolTest = HwApiTypedTest; - -TEST_P(GetBoolTest, success_returnsTrue) { - auto param = GetParam(); - auto name = std::get<0>(param); - auto func = std::get<1>(param); - bool expect = true; - bool actual = !expect; - - expectAndUpdateContent(name, "1"); - - EXPECT_TRUE(func(*mHwApi, &actual)); - EXPECT_EQ(expect, actual); -} - -TEST_P(GetBoolTest, success_returnsFalse) { - auto param = GetParam(); - auto name = std::get<0>(param); - auto func = std::get<1>(param); - bool expect = false; - bool actual = !expect; - - expectAndUpdateContent(name, "0"); - - EXPECT_TRUE(func(*mHwApi, &actual)); - EXPECT_EQ(expect, actual); -} - -TEST_P(GetBoolTest, failure) { - auto param = GetParam(); - auto func = std::get<1>(param); - bool value; - - EXPECT_FALSE(func(*mNoApi, &value)); -} - -INSTANTIATE_TEST_CASE_P(HwApiTests, GetBoolTest, - ValuesIn({ - GetBoolTest::MakeParam("ASP_ENABLE_PATH", - &Vibrator::HwApi::getAspEnable), - }), - GetBoolTest::PrintParam); - -using GetUint32Test = HwApiTypedTest; - -TEST_P(GetUint32Test, success) { - auto param = GetParam(); - auto name = std::get<0>(param); - auto func = std::get<1>(param); - uint32_t expect = std::rand(); - uint32_t actual = ~expect; - - expectAndUpdateContent(name, expect); - - EXPECT_TRUE(func(*mHwApi, &actual)); - EXPECT_EQ(expect, actual); -} - -TEST_P(GetUint32Test, failure) { - auto param = GetParam(); - auto func = std::get<1>(param); - uint32_t value; - - EXPECT_FALSE(func(*mNoApi, &value)); -} - -INSTANTIATE_TEST_CASE_P(HwApiTests, GetUint32Test, - ValuesIn({ - GetUint32Test::MakeParam("EFFECT_DURATION_PATH", - &Vibrator::HwApi::getEffectDuration), - }), - GetUint32Test::PrintParam); - using SetBoolTest = HwApiTypedTest; TEST_P(SetBoolTest, success_returnsTrue) { @@ -234,21 +237,91 @@ TEST_P(SetBoolTest, success_returnsFalse) { TEST_P(SetBoolTest, failure) { auto param = GetParam(); + auto name = std::get<0>(param); auto func = std::get<1>(param); + if (isRequired(name)) { + GTEST_SKIP(); + } + EXPECT_FALSE(func(*mNoApi, true)); EXPECT_FALSE(func(*mNoApi, false)); } INSTANTIATE_TEST_CASE_P(HwApiTests, SetBoolTest, ValuesIn({ - SetBoolTest::MakeParam("ACTIVATE_PATH", &Vibrator::HwApi::setActivate), - SetBoolTest::MakeParam("STATE_PATH", &Vibrator::HwApi::setState), - SetBoolTest::MakeParam("ASP_ENABLE_PATH", - &Vibrator::HwApi::setAspEnable), + SetBoolTest::MakeParam("activate", &Vibrator::HwApi::setActivate), + SetBoolTest::MakeParam("state", &Vibrator::HwApi::setState), + SetBoolTest::MakeParam("device/ctrl_loop", + &Vibrator::HwApi::setCtrlLoop), }), SetBoolTest::PrintParam); +using SetInt8Test = HwApiTypedTest; + +TEST_P(SetInt8Test, success) { + auto param = GetParam(); + auto name = std::get<0>(param); + auto func = std::get<1>(param); + int8_t value = std::rand(); + + expectContent(name, +value); + + EXPECT_TRUE(func(*mHwApi, value)); +} + +TEST_P(SetInt8Test, failure) { + auto param = GetParam(); + auto name = std::get<0>(param); + auto func = std::get<1>(param); + int8_t value = std::rand(); + + if (isRequired(name)) { + GTEST_SKIP(); + } + + EXPECT_FALSE(func(*mNoApi, value)); +} + +INSTANTIATE_TEST_CASE_P(HwApiTests, SetInt8Test, + ValuesIn({ + SetInt8Test::MakeParam("device/rtp_input", + &Vibrator::HwApi::setRtpInput), + }), + SetInt8Test::PrintParam); + +using SetUint8Test = HwApiTypedTest; + +TEST_P(SetUint8Test, success) { + auto param = GetParam(); + auto name = std::get<0>(param); + auto func = std::get<1>(param); + uint8_t value = std::rand(); + + expectContent(name, +value); + + EXPECT_TRUE(func(*mHwApi, value)); +} + +TEST_P(SetUint8Test, failure) { + auto param = GetParam(); + auto name = std::get<0>(param); + auto func = std::get<1>(param); + uint8_t value = std::rand(); + + if (isRequired(name)) { + GTEST_SKIP(); + } + + EXPECT_FALSE(func(*mNoApi, value)); +} + +INSTANTIATE_TEST_CASE_P(HwApiTests, SetUint8Test, + ValuesIn({ + SetUint8Test::MakeParam("device/scale", &Vibrator::HwApi::setScale), + }), + SetUint8Test::PrintParam); + using SetUint32Test = HwApiTypedTest; TEST_P(SetUint32Test, success) { @@ -264,26 +337,25 @@ TEST_P(SetUint32Test, success) { TEST_P(SetUint32Test, failure) { auto param = GetParam(); + auto name = std::get<0>(param); auto func = std::get<1>(param); uint32_t value = std::rand(); + if (isRequired(name)) { + GTEST_SKIP(); + } + EXPECT_FALSE(func(*mNoApi, value)); } INSTANTIATE_TEST_CASE_P( HwApiTests, SetUint32Test, ValuesIn({ - SetUint32Test::MakeParam("F0_FILEPATH", &Vibrator::HwApi::setF0), - SetUint32Test::MakeParam("REDC_FILEPATH", &Vibrator::HwApi::setRedc), - SetUint32Test::MakeParam("Q_FILEPATH", &Vibrator::HwApi::setQ), - SetUint32Test::MakeParam("DURATION_PATH", &Vibrator::HwApi::setDuration), - SetUint32Test::MakeParam("EFFECT_INDEX_PATH", &Vibrator::HwApi::setEffectIndex), - SetUint32Test::MakeParam("EFFECT_SCALE_PATH", &Vibrator::HwApi::setEffectScale), - SetUint32Test::MakeParam("GLOBAL_SCALE_PATH", &Vibrator::HwApi::setGlobalScale), - SetUint32Test::MakeParam("GPIO_FALL_INDEX", &Vibrator::HwApi::setGpioFallIndex), - SetUint32Test::MakeParam("GPIO_FALL_SCALE", &Vibrator::HwApi::setGpioFallScale), - SetUint32Test::MakeParam("GPIO_RISE_INDEX", &Vibrator::HwApi::setGpioRiseIndex), - SetUint32Test::MakeParam("GPIO_RISE_SCALE", &Vibrator::HwApi::setGpioRiseScale), + SetUint32Test::MakeParam("device/ol_lra_period", &Vibrator::HwApi::setOlLraPeriod), + SetUint32Test::MakeParam("duration", &Vibrator::HwApi::setDuration), + SetUint32Test::MakeParam("device/lp_trigger_effect", &Vibrator::HwApi::setLpTriggerEffect), + SetUint32Test::MakeParam("device/lra_wave_shape", &Vibrator::HwApi::setLraWaveShape), + SetUint32Test::MakeParam("device/od_clamp", &Vibrator::HwApi::setOdClamp), }), SetUint32Test::PrintParam); @@ -302,18 +374,25 @@ TEST_P(SetStringTest, success) { TEST_P(SetStringTest, failure) { auto param = GetParam(); + auto name = std::get<0>(param); auto func = std::get<1>(param); std::string value = TemporaryFile().path; + if (isRequired(name)) { + GTEST_SKIP(); + } + EXPECT_FALSE(func(*mNoApi, value)); } -INSTANTIATE_TEST_CASE_P(HwApiTests, SetStringTest, - ValuesIn({ - SetStringTest::MakeParam("EFFECT_QUEUE_PATH", - &Vibrator::HwApi::setEffectQueue), - }), - SetStringTest::PrintParam); +INSTANTIATE_TEST_CASE_P( + HwApiTests, SetStringTest, + ValuesIn({ + SetStringTest::MakeParam("device/autocal", &Vibrator::HwApi::setAutocal), + SetStringTest::MakeParam("device/mode", &Vibrator::HwApi::setMode), + SetStringTest::MakeParam("device/set_sequencer", &Vibrator::HwApi::setSequencer), + }), + SetStringTest::PrintParam); } // namespace implementation } // namespace V1_3 diff --git a/vibrator/drv2624/tests/test-hwcal.cpp b/vibrator/drv2624/tests/test-hwcal.cpp new file mode 100644 index 0000000..b95f943 --- /dev/null +++ b/vibrator/drv2624/tests/test-hwcal.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2019 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 +#include +#include + +#include + +#include "Hardware.h" + +namespace android { +namespace hardware { +namespace vibrator { +namespace V1_3 { +namespace implementation { + +using ::android::base::SetProperty; +using ::android::base::WaitForProperty; + +using ::testing::Test; + +class HwCalTest : public Test { + protected: + static constexpr char PROPERTY_PREFIX[] = "test.vibrator.hal."; + + static constexpr uint32_t DEFAULT_LRA_PERIOD = 262; + + static constexpr uint32_t DEFAULT_FREQUENCY_SHIFT = 10; + static constexpr uint32_t DEFAULT_VOLTAGE_MAX = 107; + + static constexpr uint32_t DEFAULT_CLICK_DURATION_MS = 6; + static constexpr uint32_t DEFAULT_TICK_DURATION_MS = 2; + static constexpr uint32_t DEFAULT_DOUBLE_CLICK_DURATION_MS = 135; + static constexpr uint32_t DEFAULT_HEAVY_CLICK_DURATION_MS = 8; + + public: + void SetUp() override { + setenv("PROPERTY_PREFIX", PROPERTY_PREFIX, true); + setenv("CALIBRATION_FILEPATH", mCalFile.path, true); + } + + private: + template + static void pack(std::ostream &stream, const T &value, std::string lpad, std::string rpad) { + stream << lpad << value << rpad; + } + + protected: + void createHwCal() { mHwCal = std::make_unique(); } + + template + void write(const std::string key, const T &value, std::string lpad = " ", + std::string rpad = "") { + std::ofstream calfile{mCalFile.path, std::ios_base::app}; + calfile << key << ":"; + pack(calfile, value, lpad, rpad); + calfile << std::endl; + } + + void unlink() { ::unlink(mCalFile.path); } + + protected: + std::unique_ptr mHwCal; + TemporaryFile mCalFile; +}; + +TEST_F(HwCalTest, closeloop_present) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = std::rand(); + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "closeloop.threshold", std::to_string(expect))); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getCloseLoopThreshold(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, closeloop_missing) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = UINT32_MAX; + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "closeloop.threshold", std::string())); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getCloseLoopThreshold(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, dynamicconfig_presentFalse) { + std::string prefix{PROPERTY_PREFIX}; + bool expect = false; + bool actual = !expect; + + EXPECT_TRUE(SetProperty(prefix + "config.dynamic", "0")); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getDynamicConfig(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, dynamicconfig_presentTrue) { + std::string prefix{PROPERTY_PREFIX}; + bool expect = true; + bool actual = !expect; + + EXPECT_TRUE(SetProperty(prefix + "config.dynamic", "1")); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getDynamicConfig(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, dynamicconfig_missing) { + std::string prefix{PROPERTY_PREFIX}; + bool expect = false; + bool actual = !expect; + + EXPECT_TRUE(SetProperty(prefix + "config.dynamic", std::string())); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getDynamicConfig(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, freqshift_present) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = std::rand(); + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "long.frequency.shift", std::to_string(expect))); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getLongFrequencyShift(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, freqshift_missing) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = DEFAULT_FREQUENCY_SHIFT; + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "long.frequency.shift", std::string())); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getLongFrequencyShift(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, shortvolt_present) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = std::rand(); + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "short.voltage", std::to_string(expect))); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getShortVoltageMax(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, shortvolt_missing) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = DEFAULT_VOLTAGE_MAX; + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "short.voltage", std::string())); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getShortVoltageMax(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, longvolt_present) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = std::rand(); + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "long.voltage", std::to_string(expect))); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getLongVoltageMax(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, longvolt_missing) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = DEFAULT_VOLTAGE_MAX; + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "long.voltage", std::string())); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getLongVoltageMax(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, click_present) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = std::rand(); + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "click.duration", std::to_string(expect))); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getClickDuration(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, click_missing) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = DEFAULT_CLICK_DURATION_MS; + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "click.duration", std::string())); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getClickDuration(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, tick_present) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = std::rand(); + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "tick.duration", std::to_string(expect))); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getTickDuration(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, tick_missing) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = DEFAULT_TICK_DURATION_MS; + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "tick.duration", std::string())); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getTickDuration(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, doubleclick) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = DEFAULT_DOUBLE_CLICK_DURATION_MS; + uint32_t actual = ~expect; + + createHwCal(); + + EXPECT_TRUE(mHwCal->getDoubleClickDuration(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, heavyclick_present) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = std::rand(); + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "heavyclick.duration", std::to_string(expect))); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getHeavyClickDuration(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, heavyclick_missing) { + std::string prefix{PROPERTY_PREFIX}; + uint32_t expect = DEFAULT_HEAVY_CLICK_DURATION_MS; + uint32_t actual = ~expect; + + EXPECT_TRUE(SetProperty(prefix + "heavyclick.duration", std::string())); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getHeavyClickDuration(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, autocal_present) { + std::string expect = std::to_string(std::rand()) + " " + std::to_string(std::rand()) + " " + + std::to_string(std::rand()); + std::string actual = ""; + + write("autocal", expect); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getAutocal(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, autocal_missing) { + std::string actual; + + createHwCal(); + + EXPECT_FALSE(mHwCal->getAutocal(&actual)); +} + +TEST_F(HwCalTest, lra_period_present) { + uint32_t expect = std::rand(); + uint32_t actual = ~expect; + + write("lra_period", expect); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getLraPeriod(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, lra_period_missing) { + uint32_t expect = DEFAULT_LRA_PERIOD; + uint32_t actual = ~expect; + + createHwCal(); + + EXPECT_TRUE(mHwCal->getLraPeriod(&actual)); + EXPECT_EQ(expect, actual); +} + +TEST_F(HwCalTest, multiple) { + std::string autocalExpect = std::to_string(std::rand()) + " " + std::to_string(std::rand()) + + " " + std::to_string(std::rand()); + std::string autocalActual = ""; + uint32_t lraPeriodExpect = std::rand(); + uint32_t lraPeriodActual = ~lraPeriodExpect; + + write("autocal", autocalExpect); + write("lra_period", lraPeriodExpect); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getAutocal(&autocalActual)); + EXPECT_EQ(autocalExpect, autocalActual); + EXPECT_TRUE(mHwCal->getLraPeriod(&lraPeriodActual)); + EXPECT_EQ(lraPeriodExpect, lraPeriodActual); +} + +TEST_F(HwCalTest, trimming) { + std::string autocalExpect = std::to_string(std::rand()) + " " + std::to_string(std::rand()) + + " " + std::to_string(std::rand()); + std::string autocalActual = ""; + uint32_t lraPeriodExpect = std::rand(); + uint32_t lraPeriodActual = ~lraPeriodExpect; + + write("autocal", autocalExpect, " \t", "\t "); + write("lra_period", lraPeriodExpect, " \t", "\t "); + + createHwCal(); + + EXPECT_TRUE(mHwCal->getAutocal(&autocalActual)); + EXPECT_EQ(autocalExpect, autocalActual); + EXPECT_TRUE(mHwCal->getLraPeriod(&lraPeriodActual)); + EXPECT_EQ(lraPeriodExpect, lraPeriodActual); +} + +} // namespace implementation +} // namespace V1_3 +} // namespace vibrator +} // namespace hardware +} // namespace android diff --git a/vibrator/drv2624/tests/test-vibrator.cpp b/vibrator/drv2624/tests/test-vibrator.cpp new file mode 100644 index 0000000..47bdfad --- /dev/null +++ b/vibrator/drv2624/tests/test-vibrator.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2019 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 +#include +#include + +#include "Vibrator.h" +#include "mocks.h" +#include "types.h" +#include "utils.h" + +namespace android { +namespace hardware { +namespace vibrator { +namespace V1_3 { +namespace implementation { + +using ::android::hardware::vibrator::V1_0::EffectStrength; +using ::android::hardware::vibrator::V1_0::Status; + +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::AnyOf; +using ::testing::Assign; +using ::testing::Combine; +using ::testing::DoAll; +using ::testing::DoDefault; +using ::testing::Exactly; +using ::testing::ExpectationSet; +using ::testing::Mock; +using ::testing::Return; +using ::testing::Sequence; +using ::testing::SetArgPointee; +using ::testing::SetArgReferee; +using ::testing::Test; +using ::testing::TestParamInfo; +using ::testing::ValuesIn; +using ::testing::WithParamInterface; + +// Constants With Prescribed Values + +static const std::map EFFECT_SEQUENCES{ + {{Effect::CLICK, EffectStrength::LIGHT}, {"1 0", 2}}, + {{Effect::CLICK, EffectStrength::MEDIUM}, {"1 0", 0}}, + {{Effect::CLICK, EffectStrength::STRONG}, {"1 0", 0}}, + {{Effect::TICK, EffectStrength::LIGHT}, {"2 0", 2}}, + {{Effect::TICK, EffectStrength::MEDIUM}, {"2 0", 0}}, + {{Effect::TICK, EffectStrength::STRONG}, {"2 0", 0}}, + {{Effect::DOUBLE_CLICK, EffectStrength::LIGHT}, {"3 0", 2}}, + {{Effect::DOUBLE_CLICK, EffectStrength::MEDIUM}, {"3 0", 0}}, + {{Effect::DOUBLE_CLICK, EffectStrength::STRONG}, {"3 0", 0}}, + {{Effect::HEAVY_CLICK, EffectStrength::LIGHT}, {"4 0", 2}}, + {{Effect::HEAVY_CLICK, EffectStrength::MEDIUM}, {"4 0", 0}}, + {{Effect::HEAVY_CLICK, EffectStrength::STRONG}, {"4 0", 0}}, + {{Effect::TEXTURE_TICK, EffectStrength::LIGHT}, {"2 0", 2}}, + {{Effect::TEXTURE_TICK, EffectStrength::MEDIUM}, {"2 0", 0}}, + {{Effect::TEXTURE_TICK, EffectStrength::STRONG}, {"2 0", 0}}, +}; + +static uint32_t freqPeriodFormula(uint32_t in) { + return 1000000000 / (24615 * in); +} + +template +class VibratorTestTemplate : public Test, public WithParamInterface> { + public: + static auto GetDynamicConfig(typename VibratorTestTemplate::ParamType param) { + return std::get<0>(param); + } + template + static auto GetOtherParam(typename VibratorTestTemplate::ParamType param) { + return std::get(param); + } + + static auto PrintParam(const TestParamInfo &info) { + auto dynamic = GetDynamicConfig(info.param); + return std::string() + (dynamic ? "Dynamic" : "Static") + "Config"; + } + + static auto MakeParam(bool dynamicConfig, T... others) { + return std::make_tuple(dynamicConfig, others...); + } + + void SetUp() override { + std::unique_ptr mockapi; + std::unique_ptr mockcal; + + mCloseLoopThreshold = std::rand(); + // ensure close-loop test is possible + if (mCloseLoopThreshold == UINT32_MAX) { + mCloseLoopThreshold--; + } + + mShortLraPeriod = std::rand(); + if (getDynamicConfig()) { + mLongFrequencyShift = std::rand(); + mLongLraPeriod = + freqPeriodFormula(freqPeriodFormula(mShortLraPeriod) - mLongFrequencyShift); + mShortVoltageMax = std::rand(); + mLongVoltageMax = std::rand(); + } + + mEffectDurations[Effect::CLICK] = std::rand(); + mEffectDurations[Effect::TICK] = std::rand(); + mEffectDurations[Effect::DOUBLE_CLICK] = std::rand(); + mEffectDurations[Effect::HEAVY_CLICK] = std::rand(); + mEffectDurations[Effect::TEXTURE_TICK] = mEffectDurations[Effect::TICK]; + + createMock(&mockapi, &mockcal); + createVibrator(std::move(mockapi), std::move(mockcal)); + } + + void TearDown() override { deleteVibrator(); } + + protected: + auto getDynamicConfig() const { return GetDynamicConfig(VibratorTestTemplate::GetParam()); } + + void createMock(std::unique_ptr *mockapi, std::unique_ptr *mockcal) { + *mockapi = std::make_unique(); + *mockcal = std::make_unique(); + + mMockApi = mockapi->get(); + mMockCal = mockcal->get(); + + ON_CALL(*mMockApi, destructor()).WillByDefault(Assign(&mMockApi, nullptr)); + ON_CALL(*mMockApi, setOlLraPeriod(_)).WillByDefault(Return(true)); + ON_CALL(*mMockApi, setActivate(_)).WillByDefault(Return(true)); + ON_CALL(*mMockApi, setDuration(_)).WillByDefault(Return(true)); + ON_CALL(*mMockApi, setMode(_)).WillByDefault(Return(true)); + ON_CALL(*mMockApi, setCtrlLoop(_)).WillByDefault(Return(true)); + ON_CALL(*mMockApi, setLraWaveShape(_)).WillByDefault(Return(true)); + ON_CALL(*mMockApi, setOdClamp(_)).WillByDefault(Return(true)); + + ON_CALL(*mMockCal, destructor()).WillByDefault(Assign(&mMockCal, nullptr)); + ON_CALL(*mMockCal, getLraPeriod(_)) + .WillByDefault(DoAll(SetArgPointee<0>(mShortLraPeriod), Return(true))); + ON_CALL(*mMockCal, getCloseLoopThreshold(_)) + .WillByDefault(DoAll(SetArgPointee<0>(mCloseLoopThreshold), Return(true))); + ON_CALL(*mMockCal, getDynamicConfig(_)) + .WillByDefault(DoAll(SetArgPointee<0>(getDynamicConfig()), Return(true))); + + if (getDynamicConfig()) { + ON_CALL(*mMockCal, getLongFrequencyShift(_)) + .WillByDefault(DoAll(SetArgPointee<0>(mLongFrequencyShift), Return(true))); + ON_CALL(*mMockCal, getShortVoltageMax(_)) + .WillByDefault(DoAll(SetArgPointee<0>(mShortVoltageMax), Return(true))); + ON_CALL(*mMockCal, getLongVoltageMax(_)) + .WillByDefault(DoAll(SetArgPointee<0>(mLongVoltageMax), Return(true))); + } + + ON_CALL(*mMockCal, getClickDuration(_)) + .WillByDefault(DoAll(SetArgPointee<0>(mEffectDurations[Effect::CLICK]), Return(true))); + ON_CALL(*mMockCal, getTickDuration(_)) + .WillByDefault(DoAll(SetArgPointee<0>(mEffectDurations[Effect::TICK]), Return(true))); + ON_CALL(*mMockCal, getDoubleClickDuration(_)) + .WillByDefault( + DoAll(SetArgPointee<0>(mEffectDurations[Effect::DOUBLE_CLICK]), Return(true))); + ON_CALL(*mMockCal, getHeavyClickDuration(_)) + .WillByDefault( + DoAll(SetArgPointee<0>(mEffectDurations[Effect::HEAVY_CLICK]), Return(true))); + + relaxMock(false); + } + + void createVibrator(std::unique_ptr mockapi, std::unique_ptr mockcal, + bool relaxed = true) { + if (relaxed) { + relaxMock(true); + } + mVibrator = new Vibrator(std::move(mockapi), std::move(mockcal)); + if (relaxed) { + relaxMock(false); + } + } + + void deleteVibrator(bool relaxed = true) { + if (relaxed) { + relaxMock(true); + } + mVibrator.clear(); + } + + void relaxMock(bool relax) { + auto times = relax ? AnyNumber() : Exactly(0); + + Mock::VerifyAndClearExpectations(mMockApi); + Mock::VerifyAndClearExpectations(mMockCal); + + EXPECT_CALL(*mMockApi, destructor()).Times(times); + EXPECT_CALL(*mMockApi, setAutocal(_)).Times(times); + EXPECT_CALL(*mMockApi, setOlLraPeriod(_)).Times(times); + EXPECT_CALL(*mMockApi, setActivate(_)).Times(times); + EXPECT_CALL(*mMockApi, setDuration(_)).Times(times); + EXPECT_CALL(*mMockApi, setState(_)).Times(times); + EXPECT_CALL(*mMockApi, hasRtpInput()).Times(times); + EXPECT_CALL(*mMockApi, setRtpInput(_)).Times(times); + EXPECT_CALL(*mMockApi, setMode(_)).Times(times); + EXPECT_CALL(*mMockApi, setSequencer(_)).Times(times); + EXPECT_CALL(*mMockApi, setScale(_)).Times(times); + EXPECT_CALL(*mMockApi, setCtrlLoop(_)).Times(times); + EXPECT_CALL(*mMockApi, setLpTriggerEffect(_)).Times(times); + EXPECT_CALL(*mMockApi, setLraWaveShape(_)).Times(times); + EXPECT_CALL(*mMockApi, setOdClamp(_)).Times(times); + EXPECT_CALL(*mMockApi, debug(_)).Times(times); + + EXPECT_CALL(*mMockCal, destructor()).Times(times); + EXPECT_CALL(*mMockCal, getAutocal(_)).Times(times); + EXPECT_CALL(*mMockCal, getLraPeriod(_)).Times(times); + EXPECT_CALL(*mMockCal, getCloseLoopThreshold(_)).Times(times); + EXPECT_CALL(*mMockCal, getDynamicConfig(_)).Times(times); + EXPECT_CALL(*mMockCal, getLongFrequencyShift(_)).Times(times); + EXPECT_CALL(*mMockCal, getShortVoltageMax(_)).Times(times); + EXPECT_CALL(*mMockCal, getLongVoltageMax(_)).Times(times); + EXPECT_CALL(*mMockCal, getClickDuration(_)).Times(times); + EXPECT_CALL(*mMockCal, getTickDuration(_)).Times(times); + EXPECT_CALL(*mMockCal, getDoubleClickDuration(_)).Times(times); + EXPECT_CALL(*mMockCal, getHeavyClickDuration(_)).Times(times); + EXPECT_CALL(*mMockCal, debug(_)).Times(times); + } + + protected: + MockApi *mMockApi; + MockCal *mMockCal; + sp mVibrator; + + EffectDuration mCloseLoopThreshold; + uint32_t mLongFrequencyShift; + uint32_t mShortLraPeriod; + uint32_t mLongLraPeriod; + uint32_t mShortVoltageMax; + uint32_t mLongVoltageMax; + std::map mEffectDurations; +}; + +using BasicTest = VibratorTestTemplate<>; + +TEST_P(BasicTest, Constructor) { + std::unique_ptr mockapi; + std::unique_ptr mockcal; + std::string autocalVal = std::to_string(std::rand()) + " " + std::to_string(std::rand()) + + " " + std::to_string(std::rand()); + Sequence autocalSeq, lraPeriodSeq; + + EXPECT_CALL(*mMockApi, destructor()).WillOnce(DoDefault()); + EXPECT_CALL(*mMockCal, destructor()).WillOnce(DoDefault()); + + deleteVibrator(false); + + createMock(&mockapi, &mockcal); + + EXPECT_CALL(*mMockApi, setState(true)).WillOnce(Return(true)); + + EXPECT_CALL(*mMockCal, getAutocal(_)) + .InSequence(autocalSeq) + .WillOnce(DoAll(SetArgReferee<0>(autocalVal), Return(true))); + EXPECT_CALL(*mMockApi, setAutocal(autocalVal)).InSequence(autocalSeq).WillOnce(DoDefault()); + + EXPECT_CALL(*mMockCal, getLraPeriod(_)).InSequence(lraPeriodSeq).WillOnce(DoDefault()); + + EXPECT_CALL(*mMockCal, getCloseLoopThreshold(_)).WillOnce(DoDefault()); + EXPECT_CALL(*mMockCal, getDynamicConfig(_)).WillOnce(DoDefault()); + + if (getDynamicConfig()) { + EXPECT_CALL(*mMockCal, getLongFrequencyShift(_)).WillOnce(DoDefault()); + EXPECT_CALL(*mMockCal, getShortVoltageMax(_)).WillOnce(DoDefault()); + EXPECT_CALL(*mMockCal, getLongVoltageMax(_)).WillOnce(DoDefault()); + } else { + EXPECT_CALL(*mMockApi, setOlLraPeriod(mShortLraPeriod)) + .InSequence(lraPeriodSeq) + .WillOnce(DoDefault()); + } + + EXPECT_CALL(*mMockCal, getClickDuration(_)).WillOnce(DoDefault()); + EXPECT_CALL(*mMockCal, getTickDuration(_)).WillOnce(DoDefault()); + EXPECT_CALL(*mMockCal, getDoubleClickDuration(_)).WillOnce(DoDefault()); + EXPECT_CALL(*mMockCal, getHeavyClickDuration(_)).WillOnce(DoDefault()); + + EXPECT_CALL(*mMockApi, setLpTriggerEffect(1)).WillOnce(Return(true)); + + createVibrator(std::move(mockapi), std::move(mockcal), false); +} + +TEST_P(BasicTest, on) { + EffectDuration duration = std::rand(); + ExpectationSet e; + + e += EXPECT_CALL(*mMockApi, setCtrlLoop(_)).WillOnce(DoDefault()); + e += EXPECT_CALL(*mMockApi, setMode("rtp")).WillOnce(DoDefault()); + e += EXPECT_CALL(*mMockApi, setDuration(duration)).WillOnce(DoDefault()); + + if (getDynamicConfig()) { + e += EXPECT_CALL(*mMockApi, setLraWaveShape(0)).WillOnce(DoDefault()); + e += EXPECT_CALL(*mMockApi, setOdClamp(mLongVoltageMax)).WillOnce(DoDefault()); + e += EXPECT_CALL(*mMockApi, setOlLraPeriod(mLongLraPeriod)).WillOnce(DoDefault()); + } + + EXPECT_CALL(*mMockApi, setActivate(true)).After(e).WillOnce(DoDefault()); + + EXPECT_EQ(Status::OK, mVibrator->on(duration)); +} + +TEST_P(BasicTest, on_openLoop) { + EffectDuration duration = mCloseLoopThreshold; + + relaxMock(true); + + EXPECT_CALL(*mMockApi, setCtrlLoop(true)).WillOnce(DoDefault()); + + EXPECT_EQ(Status::OK, mVibrator->on(duration)); +} + +TEST_P(BasicTest, on_closeLoop) { + EffectDuration duration = mCloseLoopThreshold + 1; + + relaxMock(true); + + EXPECT_CALL(*mMockApi, setCtrlLoop(false)).WillOnce(DoDefault()); + + EXPECT_EQ(Status::OK, mVibrator->on(duration)); +} + +TEST_P(BasicTest, off) { + EXPECT_CALL(*mMockApi, setActivate(false)).WillOnce(DoDefault()); + + EXPECT_EQ(Status::OK, mVibrator->off()); +} + +TEST_P(BasicTest, supportsAmplitudeControl_supported) { + EXPECT_CALL(*mMockApi, hasRtpInput()).WillOnce(Return(true)); + + EXPECT_EQ(true, mVibrator->supportsAmplitudeControl()); +} + +TEST_P(BasicTest, supportsAmplitudeControl_unsupported) { + EXPECT_CALL(*mMockApi, hasRtpInput()).WillOnce(Return(false)); + + EXPECT_EQ(false, mVibrator->supportsAmplitudeControl()); +} + +TEST_P(BasicTest, setAmplitude) { + EffectAmplitude amplitude = std::rand(); + + EXPECT_CALL(*mMockApi, setRtpInput(amplitudeToRtpInput(amplitude))).WillOnce(Return(true)); + + EXPECT_EQ(Status::OK, mVibrator->setAmplitude(amplitude)); +} + +TEST_P(BasicTest, supportsExternalControl_unsupported) { + EXPECT_EQ(false, mVibrator->supportsExternalControl()); +} + +TEST_P(BasicTest, setExternalControl_enable) { + EXPECT_EQ(Status::UNSUPPORTED_OPERATION, mVibrator->setExternalControl(true)); +} + +TEST_P(BasicTest, setExternalControl_disable) { + EXPECT_EQ(Status::UNSUPPORTED_OPERATION, mVibrator->setExternalControl(false)); +} + +INSTANTIATE_TEST_CASE_P(VibratorTests, BasicTest, + ValuesIn({BasicTest::MakeParam(false), BasicTest::MakeParam(true)}), + BasicTest::PrintParam); + +class EffectsTest : public VibratorTestTemplate { + public: + static auto GetEffectTuple(ParamType param) { return GetOtherParam<0>(param); } + + static auto PrintParam(const TestParamInfo &info) { + auto prefix = VibratorTestTemplate::PrintParam(info); + auto tuple = GetEffectTuple(info.param); + auto effect = std::get<0>(tuple); + auto strength = std::get<1>(tuple); + return prefix + "_" + toString(effect) + "_" + toString(strength); + } + + protected: + auto getEffectTuple() const { return GetEffectTuple(GetParam()); } +}; + +TEST_P(EffectsTest, perform) { + auto tuple = getEffectTuple(); + auto effect = std::get<0>(tuple); + auto strength = std::get<1>(tuple); + auto seqIter = EFFECT_SEQUENCES.find(tuple); + auto durIter = mEffectDurations.find(effect); + EffectDuration duration; + + if (seqIter != EFFECT_SEQUENCES.end() && durIter != mEffectDurations.end()) { + auto sequence = std::get<0>(seqIter->second); + auto scale = std::get<1>(seqIter->second); + ExpectationSet e; + + duration = durIter->second; + + e += EXPECT_CALL(*mMockApi, setSequencer(sequence)).WillOnce(Return(true)); + e += EXPECT_CALL(*mMockApi, setScale(scale)).WillOnce(Return(true)); + e += EXPECT_CALL(*mMockApi, setCtrlLoop(1)).WillOnce(DoDefault()); + e += EXPECT_CALL(*mMockApi, setMode("waveform")).WillOnce(DoDefault()); + e += EXPECT_CALL(*mMockApi, setDuration(duration)).WillOnce(DoDefault()); + + if (getDynamicConfig()) { + e += EXPECT_CALL(*mMockApi, setLraWaveShape(1)).WillOnce(DoDefault()); + e += EXPECT_CALL(*mMockApi, setOdClamp(mShortVoltageMax)).WillOnce(DoDefault()); + e += EXPECT_CALL(*mMockApi, setOlLraPeriod(mShortLraPeriod)).WillOnce(DoDefault()); + } + + EXPECT_CALL(*mMockApi, setActivate(true)).After(e).WillOnce(DoDefault()); + } else { + duration = 0; + } + + mVibrator->perform_1_3(effect, strength, [&](Status status, uint32_t lengthMs) { + if (duration) { + EXPECT_EQ(Status::OK, status); + EXPECT_LE(duration, lengthMs); + } else { + EXPECT_EQ(Status::UNSUPPORTED_OPERATION, status); + EXPECT_EQ(0, lengthMs); + } + }); +} + +INSTANTIATE_TEST_CASE_P(VibratorTests, EffectsTest, + Combine(ValuesIn({false, true}), + Combine(ValuesIn(hidl_enum_range().begin(), + hidl_enum_range().end()), + ValuesIn(hidl_enum_range().begin(), + hidl_enum_range().end()))), + EffectsTest::PrintParam); + +} // namespace implementation +} // namespace V1_3 +} // namespace vibrator +} // namespace hardware +} // namespace android diff --git a/vibrator/tests/types.h b/vibrator/drv2624/tests/types.h similarity index 81% rename from vibrator/tests/types.h rename to vibrator/drv2624/tests/types.h index 94535c2..60be69c 100644 --- a/vibrator/tests/types.h +++ b/vibrator/drv2624/tests/types.h @@ -18,16 +18,10 @@ #include -using EffectIndex = uint16_t; -using EffectLevel = uint32_t; using EffectAmplitude = uint8_t; -using EffectScale = uint16_t; using EffectDuration = uint32_t; -using EffectQueue = std::tuple; +using EffectSequence = std::tuple; using EffectTuple = std::tuple<::android::hardware::vibrator::V1_3::Effect, ::android::hardware::vibrator::V1_0::EffectStrength>; -using QueueEffect = std::tuple; -using QueueDelay = uint32_t; - #endif // ANDROID_HARDWARE_VIBRATOR_TEST_TYPES_H diff --git a/vibrator/tests/main.cpp b/vibrator/drv2624/tests/utils.h similarity index 67% rename from vibrator/tests/main.cpp rename to vibrator/drv2624/tests/utils.h index 87b18a4..5fe4b62 100644 --- a/vibrator/tests/main.cpp +++ b/vibrator/drv2624/tests/utils.h @@ -13,10 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#ifndef ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H +#define ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H -#include +#include -int main(int argc, char **argv) { - ::testing::InitGoogleMock(&argc, argv); - return RUN_ALL_TESTS(); +#include "types.h" + +static inline int32_t amplitudeToRtpInput(EffectAmplitude amplitude) { + return std::round((amplitude - 1) / 254.0 * 127); } + +#endif // ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H diff --git a/vibrator/tests/mocks.h b/vibrator/tests/mocks.h deleted file mode 100644 index 4e0544d..0000000 --- a/vibrator/tests/mocks.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ -#ifndef ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H -#define ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H - -#include "Vibrator.h" - -class MockApi : public ::android::hardware::vibrator::V1_3::implementation::Vibrator::HwApi { - public: - MOCK_METHOD0(destructor, void()); - MOCK_METHOD1(setF0, bool(uint32_t value)); - MOCK_METHOD1(setRedc, bool(uint32_t value)); - MOCK_METHOD1(setQ, bool(uint32_t value)); - MOCK_METHOD1(setActivate, bool(bool value)); - MOCK_METHOD1(setDuration, bool(uint32_t value)); - MOCK_METHOD1(getEffectDuration, bool(uint32_t *value)); - MOCK_METHOD1(setEffectIndex, bool(uint32_t value)); - MOCK_METHOD1(setEffectQueue, bool(std::string value)); - MOCK_METHOD0(hasEffectScale, bool()); - MOCK_METHOD1(setEffectScale, bool(uint32_t value)); - MOCK_METHOD1(setGlobalScale, bool(uint32_t value)); - MOCK_METHOD1(setState, bool(bool value)); - MOCK_METHOD0(hasAspEnable, bool()); - MOCK_METHOD1(getAspEnable, bool(bool *value)); - MOCK_METHOD1(setAspEnable, bool(bool value)); - MOCK_METHOD1(setGpioFallIndex, bool(uint32_t value)); - MOCK_METHOD1(setGpioFallScale, bool(uint32_t value)); - MOCK_METHOD1(setGpioRiseIndex, bool(uint32_t value)); - MOCK_METHOD1(setGpioRiseScale, bool(uint32_t value)); - MOCK_METHOD1(debug, void(int fd)); - - ~MockApi() override { destructor(); }; -}; - -class MockCal : public ::android::hardware::vibrator::V1_3::implementation::Vibrator::HwCal { - public: - MOCK_METHOD0(destructor, void()); - MOCK_METHOD1(getF0, bool(uint32_t *value)); - MOCK_METHOD1(getRedc, bool(uint32_t *value)); - MOCK_METHOD1(getQ, bool(uint32_t *value)); - MOCK_METHOD1(getVolLevels, bool(std::array *value)); - MOCK_METHOD1(debug, void(int fd)); - - ~MockCal() override { destructor(); }; -}; - -#endif // ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H diff --git a/vibrator/tests/test-hwcal.cpp b/vibrator/tests/test-hwcal.cpp deleted file mode 100644 index 42c7702..0000000 --- a/vibrator/tests/test-hwcal.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ -#define LOG_TAG "android.hardware.vibrator@1.3-tests.redfin" - -#include -#include - -#include - -#include "Hardware.h" - -using namespace ::testing; - -namespace android { -namespace hardware { -namespace vibrator { -namespace V1_3 { -namespace implementation { - -class HwCalTest : public Test { - protected: - static constexpr uint32_t Q_DEFAULT = 15.5f * (1 << 16); - static constexpr std::array V_DEFAULT = {60, 70, 80, 90, 100, 76}; - - public: - void SetUp() override { setenv("CALIBRATION_FILEPATH", mCalFile.path, true); } - - private: - static void pack(std::ostream &stream, const uint32_t &value, std::string lpad, - std::string rpad) { - stream << lpad << value << rpad; - } - - template ::size_type N> - static void pack(std::ostream &stream, const std::array &value, std::string lpad, - std::string rpad) { - for (auto &entry : value) { - pack(stream, entry, lpad, rpad); - } - } - - protected: - void createHwCal() { mHwCal = std::make_unique(); } - - template - void write(const std::string key, const T &value, std::string lpad = " ", - std::string rpad = "") { - std::ofstream calfile{mCalFile.path, std::ios_base::app}; - calfile << key << ":"; - pack(calfile, value, lpad, rpad); - calfile << std::endl; - } - - void unlink() { ::unlink(mCalFile.path); } - - protected: - std::unique_ptr mHwCal; - TemporaryFile mCalFile; -}; - -TEST_F(HwCalTest, f0_measured) { - uint32_t expect = std::rand(); - uint32_t actual = ~expect; - - write("f0_measured", expect); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getF0(&actual)); - EXPECT_EQ(expect, actual); -} - -TEST_F(HwCalTest, f0_missing) { - uint32_t actual; - - createHwCal(); - - EXPECT_FALSE(mHwCal->getF0(&actual)); -} - -TEST_F(HwCalTest, redc_measured) { - uint32_t expect = std::rand(); - uint32_t actual = ~expect; - - write("redc_measured", expect); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getRedc(&actual)); - EXPECT_EQ(expect, actual); -} - -TEST_F(HwCalTest, redc_missing) { - uint32_t actual; - - createHwCal(); - - EXPECT_FALSE(mHwCal->getRedc(&actual)); -} - -TEST_F(HwCalTest, q_measured) { - uint32_t expect = std::rand(); - uint32_t actual = ~expect; - - write("q_measured", expect); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getQ(&actual)); - EXPECT_EQ(expect, actual); -} - -TEST_F(HwCalTest, q_index) { - uint8_t value = std::rand(); - uint32_t expect = value * 1.5f * (1 << 16) + 2.0f * (1 << 16); - uint32_t actual = ~expect; - - write("q_index", value); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getQ(&actual)); - EXPECT_EQ(expect, actual); -} - -TEST_F(HwCalTest, q_missing) { - uint32_t expect = Q_DEFAULT; - uint32_t actual = ~expect; - - createHwCal(); - - EXPECT_TRUE(mHwCal->getQ(&actual)); - EXPECT_EQ(expect, actual); -} - -TEST_F(HwCalTest, q_nofile) { - uint32_t expect = Q_DEFAULT; - uint32_t actual = ~expect; - - write("q_measured", actual); - unlink(); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getQ(&actual)); - EXPECT_EQ(expect, actual); -} - -TEST_F(HwCalTest, v_levels) { - std::array expect; - std::array actual; - - std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { - e = std::rand(); - return ~e; - }); - - write("v_levels", expect); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getVolLevels(&actual)); - EXPECT_EQ(expect, actual); -} - -TEST_F(HwCalTest, v_missing) { - std::array expect = V_DEFAULT; - std::array actual; - - std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; }); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getVolLevels(&actual)); - EXPECT_EQ(expect, actual); -} - -TEST_F(HwCalTest, v_short) { - std::array expect = V_DEFAULT; - std::array actual; - - std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; }); - - write("v_levels", std::array()); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getVolLevels(&actual)); - EXPECT_EQ(expect, actual); -} - -TEST_F(HwCalTest, v_long) { - std::array expect = V_DEFAULT; - std::array actual; - - std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; }); - - write("v_levels", std::array()); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getVolLevels(&actual)); - EXPECT_EQ(expect, actual); -} - -TEST_F(HwCalTest, v_nofile) { - std::array expect = V_DEFAULT; - std::array actual; - - std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; }); - - write("v_levels", actual); - unlink(); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getVolLevels(&actual)); - EXPECT_EQ(expect, actual); -} - -TEST_F(HwCalTest, multiple) { - uint32_t f0Expect = std::rand(); - uint32_t f0Actual = ~f0Expect; - uint32_t redcExpect = std::rand(); - uint32_t redcActual = ~redcExpect; - uint32_t qExpect = std::rand(); - uint32_t qActual = ~qExpect; - std::array volExpect; - std::array volActual; - - std::transform(volExpect.begin(), volExpect.end(), volActual.begin(), [](uint32_t &e) { - e = std::rand(); - return ~e; - }); - - write("f0_measured", f0Expect); - write("redc_measured", redcExpect); - write("q_measured", qExpect); - write("v_levels", volExpect); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getF0(&f0Actual)); - EXPECT_EQ(f0Expect, f0Actual); - EXPECT_TRUE(mHwCal->getRedc(&redcActual)); - EXPECT_EQ(redcExpect, redcActual); - EXPECT_TRUE(mHwCal->getQ(&qActual)); - EXPECT_EQ(qExpect, qActual); - EXPECT_TRUE(mHwCal->getVolLevels(&volActual)); - EXPECT_EQ(volExpect, volActual); -} - -TEST_F(HwCalTest, trimming) { - uint32_t f0Expect = std::rand(); - uint32_t f0Actual = ~f0Expect; - uint32_t redcExpect = std::rand(); - uint32_t redcActual = ~redcExpect; - uint32_t qExpect = std::rand(); - uint32_t qActual = ~qExpect; - std::array volExpect; - std::array volActual; - - std::transform(volExpect.begin(), volExpect.end(), volActual.begin(), [](uint32_t &e) { - e = std::rand(); - return ~e; - }); - - write("f0_measured", f0Expect, " \t", "\t "); - write("redc_measured", redcExpect, " \t", "\t "); - write("q_measured", qExpect, " \t", "\t "); - write("v_levels", volExpect, " \t", "\t "); - - createHwCal(); - - EXPECT_TRUE(mHwCal->getF0(&f0Actual)); - EXPECT_EQ(f0Expect, f0Actual); - EXPECT_TRUE(mHwCal->getRedc(&redcActual)); - EXPECT_EQ(redcExpect, redcActual); - EXPECT_TRUE(mHwCal->getQ(&qActual)); - EXPECT_EQ(qExpect, qActual); - EXPECT_TRUE(mHwCal->getVolLevels(&volActual)); - EXPECT_EQ(volExpect, volActual); -} - -} // namespace implementation -} // namespace V1_3 -} // namespace vibrator -} // namespace hardware -} // namespace android diff --git a/vibrator/tests/test-vibrator.cpp b/vibrator/tests/test-vibrator.cpp deleted file mode 100644 index 85109eb..0000000 --- a/vibrator/tests/test-vibrator.cpp +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ -#define LOG_TAG "PtsVibratorHalRedfinTestSuite" - -#include -#include -#include - -#include "Vibrator.h" -#include "mocks.h" -#include "types.h" -#include "utils.h" - -using namespace ::testing; - -namespace android { -namespace hardware { -namespace vibrator { -namespace V1_3 { -namespace implementation { - -using ::android::hardware::vibrator::V1_0::EffectStrength; -using ::android::hardware::vibrator::V1_0::Status; - -// Forward Declarations - -static EffectQueue Queue(const QueueEffect &effect); -static EffectQueue Queue(const QueueDelay &delay); -template -static EffectQueue Queue(const T &first, const U &second, Args... rest); - -// Constants With Arbitrary Values - -static constexpr std::array V_LEVELS{40, 50, 60, 70, 80, 90}; -static constexpr EffectDuration EFFECT_DURATION{15}; - -// Constants With Prescribed Values - -static constexpr EffectIndex EFFECT_INDEX{2}; -static constexpr EffectIndex QUEUE_INDEX{65534}; - -static constexpr EffectIndex GPIO_FALL_INDEX{EFFECT_INDEX}; -static const EffectScale GPIO_FALL_SCALE{levelToScale(V_LEVELS[2])}; -static constexpr EffectIndex GPIO_RISE_INDEX{EFFECT_INDEX}; -static const EffectScale GPIO_RISE_SCALE{levelToScale(V_LEVELS[3])}; - -static const EffectScale ON_GLOBAL_SCALE{levelToScale(V_LEVELS[5])}; -static const EffectIndex ON_EFFECT_INDEX{0}; - -static const std::map EFFECT_SCALE{ - {{Effect::CLICK, EffectStrength::LIGHT}, levelToScale(V_LEVELS[1])}, - {{Effect::CLICK, EffectStrength::MEDIUM}, levelToScale(V_LEVELS[2])}, - {{Effect::CLICK, EffectStrength::STRONG}, levelToScale(V_LEVELS[3])}, - {{Effect::TICK, EffectStrength::LIGHT}, levelToScale(V_LEVELS[1])}, - {{Effect::TICK, EffectStrength::MEDIUM}, levelToScale(V_LEVELS[1])}, - {{Effect::TICK, EffectStrength::STRONG}, levelToScale(V_LEVELS[1])}, - {{Effect::HEAVY_CLICK, EffectStrength::LIGHT}, levelToScale(V_LEVELS[2])}, - {{Effect::HEAVY_CLICK, EffectStrength::MEDIUM}, levelToScale(V_LEVELS[3])}, - {{Effect::HEAVY_CLICK, EffectStrength::STRONG}, levelToScale(V_LEVELS[4])}, - {{Effect::TEXTURE_TICK, EffectStrength::LIGHT}, levelToScale(V_LEVELS[0])}, - {{Effect::TEXTURE_TICK, EffectStrength::MEDIUM}, levelToScale(V_LEVELS[0])}, - {{Effect::TEXTURE_TICK, EffectStrength::STRONG}, levelToScale(V_LEVELS[0])}, -}; - -static const std::map EFFECT_QUEUE{ - {{Effect::DOUBLE_CLICK, EffectStrength::LIGHT}, - Queue(QueueEffect{EFFECT_INDEX, V_LEVELS[1]}, 100, QueueEffect{EFFECT_INDEX, V_LEVELS[2]})}, - {{Effect::DOUBLE_CLICK, EffectStrength::MEDIUM}, - Queue(QueueEffect{EFFECT_INDEX, V_LEVELS[2]}, 100, QueueEffect{EFFECT_INDEX, V_LEVELS[3]})}, - {{Effect::DOUBLE_CLICK, EffectStrength::STRONG}, - Queue(QueueEffect{EFFECT_INDEX, V_LEVELS[3]}, 100, QueueEffect{EFFECT_INDEX, V_LEVELS[4]})}, -}; - -EffectQueue Queue(const QueueEffect &effect) { - auto index = std::get<0>(effect); - auto level = std::get<1>(effect); - auto string = std::to_string(index) + "." + std::to_string(level); - auto duration = EFFECT_DURATION; - return {string, duration}; -} - -EffectQueue Queue(const QueueDelay &delay) { - auto string = std::to_string(delay); - return {string, delay}; -} - -template -EffectQueue Queue(const T &first, const U &second, Args... rest) { - auto head = Queue(first); - auto tail = Queue(second, rest...); - auto string = std::get<0>(head) + "," + std::get<0>(tail); - auto duration = std::get<1>(head) + std::get<1>(tail); - return {string, duration}; -} - -class VibratorTest : public Test, public WithParamInterface { - public: - void SetUp() override { - std::unique_ptr mockapi; - std::unique_ptr mockcal; - - createMock(&mockapi, &mockcal); - createVibrator(std::move(mockapi), std::move(mockcal)); - } - - void TearDown() override { deleteVibrator(); } - - protected: - void createMock(std::unique_ptr *mockapi, std::unique_ptr *mockcal) { - *mockapi = std::make_unique(); - *mockcal = std::make_unique(); - - mMockApi = mockapi->get(); - mMockCal = mockcal->get(); - - ON_CALL(*mMockApi, destructor()).WillByDefault(Assign(&mMockApi, nullptr)); - - ON_CALL(*mMockApi, getEffectDuration(_)) - .WillByDefault( - DoAll(SetArgPointee<0>(msToCycles(EFFECT_DURATION)), ::testing::Return(true))); - - ON_CALL(*mMockCal, destructor()).WillByDefault(Assign(&mMockCal, nullptr)); - - ON_CALL(*mMockCal, getVolLevels(_)) - .WillByDefault(DoAll(SetArgPointee<0>(V_LEVELS), ::testing::Return(true))); - - relaxMock(false); - } - - void createVibrator(std::unique_ptr mockapi, std::unique_ptr mockcal, - bool relaxed = true) { - std::vector vlevels{std::begin(V_LEVELS), std::end(V_LEVELS)}; - if (relaxed) { - relaxMock(true); - } - mVibrator = new Vibrator(std::move(mockapi), std::move(mockcal)); - if (relaxed) { - relaxMock(false); - } - } - - void deleteVibrator(bool relaxed = true) { - if (relaxed) { - relaxMock(true); - } - mVibrator.clear(); - } - - private: - void relaxMock(bool relax) { - auto times = relax ? AnyNumber() : Exactly(0); - - Mock::VerifyAndClearExpectations(mMockApi); - Mock::VerifyAndClearExpectations(mMockCal); - - EXPECT_CALL(*mMockApi, destructor()).Times(times); - EXPECT_CALL(*mMockApi, setF0(_)).Times(times); - EXPECT_CALL(*mMockApi, setRedc(_)).Times(times); - EXPECT_CALL(*mMockApi, setQ(_)).Times(times); - EXPECT_CALL(*mMockApi, setActivate(_)).Times(times); - EXPECT_CALL(*mMockApi, setDuration(_)).Times(times); - EXPECT_CALL(*mMockApi, getEffectDuration(_)).Times(times); - EXPECT_CALL(*mMockApi, setEffectIndex(_)).Times(times); - EXPECT_CALL(*mMockApi, setEffectQueue(_)).Times(times); - EXPECT_CALL(*mMockApi, hasEffectScale()).Times(times); - EXPECT_CALL(*mMockApi, setEffectScale(_)).Times(times); - EXPECT_CALL(*mMockApi, setGlobalScale(_)).Times(times); - EXPECT_CALL(*mMockApi, setState(_)).Times(times); - EXPECT_CALL(*mMockApi, hasAspEnable()).Times(times); - EXPECT_CALL(*mMockApi, getAspEnable(_)).Times(times); - EXPECT_CALL(*mMockApi, setAspEnable(_)).Times(times); - EXPECT_CALL(*mMockApi, setGpioFallIndex(_)).Times(times); - EXPECT_CALL(*mMockApi, setGpioFallScale(_)).Times(times); - EXPECT_CALL(*mMockApi, setGpioRiseIndex(_)).Times(times); - EXPECT_CALL(*mMockApi, setGpioRiseScale(_)).Times(times); - EXPECT_CALL(*mMockApi, debug(_)).Times(times); - - EXPECT_CALL(*mMockCal, destructor()).Times(times); - EXPECT_CALL(*mMockCal, getF0(_)).Times(times); - EXPECT_CALL(*mMockCal, getRedc(_)).Times(times); - EXPECT_CALL(*mMockCal, getQ(_)).Times(times); - EXPECT_CALL(*mMockCal, getVolLevels(_)).Times(times); - EXPECT_CALL(*mMockApi, debug(_)).Times(times); - } - - protected: - MockApi *mMockApi; - MockCal *mMockCal; - sp mVibrator; -}; - -TEST_F(VibratorTest, HwApi) { - std::unique_ptr mockapi; - std::unique_ptr mockcal; - uint32_t f0Val = std::rand(); - uint32_t redcVal = std::rand(); - uint32_t qVal = std::rand(); - Expectation volGet; - Sequence f0Seq, redcSeq, qSeq, volSeq, durSeq; - - EXPECT_CALL(*mMockApi, destructor()).WillOnce(DoDefault()); - EXPECT_CALL(*mMockCal, destructor()).WillOnce(DoDefault()); - - deleteVibrator(false); - - createMock(&mockapi, &mockcal); - - EXPECT_CALL(*mMockCal, getF0(_)) - .InSequence(f0Seq) - .WillOnce(DoAll(SetArgPointee<0>(f0Val), ::testing::Return(true))); - EXPECT_CALL(*mMockApi, setF0(f0Val)).InSequence(f0Seq).WillOnce(::testing::Return(true)); - - EXPECT_CALL(*mMockCal, getRedc(_)) - .InSequence(redcSeq) - .WillOnce(DoAll(SetArgPointee<0>(redcVal), ::testing::Return(true))); - EXPECT_CALL(*mMockApi, setRedc(redcVal)).InSequence(redcSeq).WillOnce(::testing::Return(true)); - - EXPECT_CALL(*mMockCal, getQ(_)) - .InSequence(qSeq) - .WillOnce(DoAll(SetArgPointee<0>(qVal), ::testing::Return(true))); - EXPECT_CALL(*mMockApi, setQ(qVal)).InSequence(qSeq).WillOnce(::testing::Return(true)); - - volGet = EXPECT_CALL(*mMockCal, getVolLevels(_)).WillOnce(DoDefault()); - - EXPECT_CALL(*mMockApi, setState(true)).WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setEffectIndex(EFFECT_INDEX)) - .InSequence(durSeq) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, getEffectDuration(_)).InSequence(durSeq).WillOnce(DoDefault()); - - EXPECT_CALL(*mMockApi, setGpioFallIndex(GPIO_FALL_INDEX)).WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setGpioFallScale(GPIO_FALL_SCALE)) - .After(volGet) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setGpioRiseIndex(GPIO_RISE_INDEX)).WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setGpioRiseScale(GPIO_RISE_SCALE)) - .After(volGet) - .WillOnce(::testing::Return(true)); - - createVibrator(std::move(mockapi), std::move(mockcal), false); -} - -TEST_F(VibratorTest, on) { - Sequence s1, s2, s3; - uint16_t duration = std::rand() + 1; - - EXPECT_CALL(*mMockApi, setGlobalScale(ON_GLOBAL_SCALE)) - .InSequence(s1) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setEffectIndex(ON_EFFECT_INDEX)) - .InSequence(s2) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setDuration(Ge(duration))) - .InSequence(s3) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setActivate(true)) - .InSequence(s1, s2, s3) - .WillOnce(::testing::Return(true)); - - EXPECT_EQ(Status::OK, mVibrator->on(duration)); -} - -TEST_F(VibratorTest, off) { - EXPECT_CALL(*mMockApi, setActivate(false)).WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setGlobalScale(0)).WillOnce(::testing::Return(true)); - - EXPECT_EQ(Status::OK, mVibrator->off()); -} - -TEST_F(VibratorTest, supportsAmplitudeControl_supported) { - EXPECT_CALL(*mMockApi, hasEffectScale()).WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, getAspEnable(_)) - .WillOnce(DoAll(SetArgPointee<0>(false), ::testing::Return(true))); - - EXPECT_EQ(true, mVibrator->supportsAmplitudeControl()); -} - -TEST_F(VibratorTest, supportsAmplitudeControl_unsupported1) { - MockFunction either; - - EXPECT_CALL(*mMockApi, hasEffectScale()) - .Times(AtMost(1)) - .WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction::Call), - ::testing::Return(false))); - EXPECT_CALL(*mMockApi, getAspEnable(_)) - .Times(AtMost(1)) - .WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction::Call), - SetArgPointee<0>(false), ::testing::Return(true))); - EXPECT_CALL(either, Call()).Times(AtLeast(1)); - - EXPECT_EQ(false, mVibrator->supportsAmplitudeControl()); -} - -TEST_F(VibratorTest, supportsAmplitudeControl_unsupported2) { - MockFunction either; - - EXPECT_CALL(*mMockApi, hasEffectScale()) - .Times(AtMost(1)) - .WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction::Call), - ::testing::Return(false))); - EXPECT_CALL(*mMockApi, getAspEnable(_)) - .Times(AtMost(1)) - .WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction::Call), - SetArgPointee<0>(true), ::testing::Return(true))); - EXPECT_CALL(either, Call()).Times(AtLeast(1)); - - EXPECT_EQ(false, mVibrator->supportsAmplitudeControl()); -} - -TEST_F(VibratorTest, supportsAmplitudeControl_unsupported3) { - MockFunction either; - - EXPECT_CALL(*mMockApi, hasEffectScale()) - .Times(AtMost(1)) - .WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction::Call), - ::testing::Return(true))); - EXPECT_CALL(*mMockApi, getAspEnable(_)) - .Times(AtMost(1)) - .WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction::Call), - SetArgPointee<0>(true), ::testing::Return(true))); - EXPECT_CALL(either, Call()).Times(AtLeast(1)); - - EXPECT_EQ(false, mVibrator->supportsAmplitudeControl()); -} - -TEST_F(VibratorTest, setAmplitude_supported) { - Sequence s; - EffectAmplitude amplitude = std::rand() + 1; - - EXPECT_CALL(*mMockApi, getAspEnable(_)) - .InSequence(s) - .WillOnce(DoAll(SetArgPointee<0>(false), ::testing::Return(true))); - EXPECT_CALL(*mMockApi, setEffectScale(amplitudeToScale(amplitude))) - .InSequence(s) - .WillOnce(::testing::Return(true)); - - EXPECT_EQ(Status::OK, mVibrator->setAmplitude(amplitude)); -} - -TEST_F(VibratorTest, setAmplitude_unsupported) { - EXPECT_CALL(*mMockApi, getAspEnable(_)) - .WillOnce(DoAll(SetArgPointee<0>(true), ::testing::Return(true))); - - EXPECT_EQ(Status::UNSUPPORTED_OPERATION, mVibrator->setAmplitude(1)); -} - -TEST_F(VibratorTest, supportsExternalControl_supported) { - EXPECT_CALL(*mMockApi, hasAspEnable()).WillOnce(::testing::Return(true)); - - EXPECT_EQ(true, mVibrator->supportsExternalControl()); -} - -TEST_F(VibratorTest, supportsExternalControl_unsupported) { - EXPECT_CALL(*mMockApi, hasAspEnable()).WillOnce(::testing::Return(false)); - - EXPECT_EQ(false, mVibrator->supportsExternalControl()); -} - -TEST_F(VibratorTest, setExternalControl_enable) { - Sequence s; - - EXPECT_CALL(*mMockApi, setGlobalScale(ON_GLOBAL_SCALE)) - .InSequence(s) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setAspEnable(true)).InSequence(s).WillOnce(::testing::Return(true)); - - EXPECT_EQ(Status::OK, mVibrator->setExternalControl(true)); -} - -TEST_F(VibratorTest, setExternalControl_disable) { - EXPECT_CALL(*mMockApi, setAspEnable(false)).WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setGlobalScale(0)).WillOnce(::testing::Return(true)); - - EXPECT_EQ(Status::OK, mVibrator->setExternalControl(false)); -} - -TEST_P(VibratorTest, perform) { - auto param = GetParam(); - auto effect = std::get<0>(param); - auto strength = std::get<1>(param); - auto scale = EFFECT_SCALE.find(param); - auto queue = EFFECT_QUEUE.find(param); - EffectDuration duration; - - if (scale != EFFECT_SCALE.end()) { - Sequence s1, s2, s3; - - duration = EFFECT_DURATION; - - EXPECT_CALL(*mMockApi, setEffectIndex(EFFECT_INDEX)) - .InSequence(s1) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setEffectScale(scale->second)) - .InSequence(s2) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setDuration(Ge(duration))) - .InSequence(s3) - .WillOnce(::testing::Return(true)); - - EXPECT_CALL(*mMockApi, setActivate(true)) - .InSequence(s1, s2, s3) - .WillOnce(::testing::Return(true)); - } else if (queue != EFFECT_QUEUE.end()) { - Sequence s1, s2, s3; - - duration = std::get<1>(queue->second); - - EXPECT_CALL(*mMockApi, setEffectIndex(QUEUE_INDEX)) - .InSequence(s1) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setEffectQueue(std::get<0>(queue->second))) - .InSequence(s2) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(*mMockApi, setDuration(Ge(duration))) - .InSequence(s3) - .WillOnce(::testing::Return(true)); - - EXPECT_CALL(*mMockApi, setActivate(true)) - .InSequence(s1, s2, s3) - .WillOnce(::testing::Return(true)); - } else { - duration = 0; - } - - mVibrator->perform_1_3(effect, strength, [&](Status status, uint32_t lengthMs) { - if (duration) { - EXPECT_EQ(Status::OK, status); - EXPECT_LE(duration, lengthMs); - } else { - EXPECT_EQ(Status::UNSUPPORTED_OPERATION, status); - EXPECT_EQ(0, lengthMs); - } - }); -} - -INSTANTIATE_TEST_CASE_P(VibratorEffects, VibratorTest, - Combine(ValuesIn(hidl_enum_range().begin(), - hidl_enum_range().end()), - ValuesIn(hidl_enum_range().begin(), - hidl_enum_range().end())), - [](const testing::TestParamInfo &info) { - auto effect = std::get<0>(info.param); - auto strength = std::get<1>(info.param); - return toString(effect) + "_" + toString(strength); - }); - -} // namespace implementation -} // namespace V1_3 -} // namespace vibrator -} // namespace hardware -} // namespace android diff --git a/vibrator/tests/utils.h b/vibrator/tests/utils.h deleted file mode 100644 index 86cb944..0000000 --- a/vibrator/tests/utils.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ -#ifndef ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H -#define ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H - -#include - -#include "types.h" - -static inline EffectScale toScale(uint8_t target, uint8_t maximum) { - return std::round((-20 * std::log10(target / static_cast(maximum))) / 0.125f); -} - -static inline EffectScale levelToScale(EffectLevel level) { - return toScale(level, 100); -} - -static inline EffectScale amplitudeToScale(EffectAmplitude amplitude) { - return toScale(amplitude, UINT8_MAX); -} - -static inline uint32_t msToCycles(EffectDuration ms) { - return ms * 48; -} - -#endif // ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H diff --git a/vibrator/utils.h b/vibrator/utils.h deleted file mode 100644 index 078310e..0000000 --- a/vibrator/utils.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ -#ifndef ANDROID_HARDWARE_VIBRATOR_UTILS_H -#define ANDROID_HARDWARE_VIBRATOR_UTILS_H - -template -class Is_Iterable { - private: - template - static std::true_type test(typename U::iterator *u); - - template - static std::false_type test(U *u); - - public: - static const bool value = decltype(test(0))::value; -}; - -template -using Enable_If_Iterable = std::enable_if_t::value == B>; - -#endif // ANDROID_HARDWARE_VIBRATOR_UTILS_H