mirror of
https://github.com/Evolution-X-Devices/device_google_redfin
synced 2026-02-01 11:26:33 +00:00
vibrator: Modify some delta and apply calibration fix am: 368d8334b6
Change-Id: I8b1a4ada0ae824c6a944423756badb64377357b9
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) 2019 Google Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<configuration description="Runs PtsVibratorHalRedfinTestSuite.">
|
||||
<option name="test-suite-tag" value="pts" />
|
||||
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
|
||||
<option name="append-bitness" value="true" />
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="PtsVibratorHalRedfinTestSuite->/data/local/tmp/PtsVibratorHalRedfinTestSuite" />
|
||||
</target_preparer>
|
||||
<test class="com.android.tradefed.testtype.GTest" >
|
||||
<option name="module-name" value="PtsVibratorHalRedfinTestSuite" />
|
||||
<option name="native-test-device-path" value="/data/local/tmp" />
|
||||
</test>
|
||||
</configuration>
|
||||
@@ -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 <log/log.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace vibrator {
|
||||
namespace V1_3 {
|
||||
namespace implementation {
|
||||
|
||||
template <typename T>
|
||||
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::ostream, T> ? 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<std::string, std::ifstream> 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 <typename T>
|
||||
static Enable_If_Iterable<T, true> unpack(std::istream &stream, T *value) {
|
||||
for (auto &entry : *value) {
|
||||
stream >> entry;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static Enable_If_Iterable<T, false> 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 <typename T>
|
||||
bool HwApi::has(T &stream) {
|
||||
return !!stream;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
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 <typename T, typename U>
|
||||
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 <typename T>
|
||||
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
|
||||
@@ -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 <typename T>
|
||||
bool has(T &stream);
|
||||
template <typename T, typename U>
|
||||
bool get(T *value, U &stream);
|
||||
template <typename T, typename U>
|
||||
bool set(const T &value, U &stream);
|
||||
|
||||
private:
|
||||
std::map<void *, std::string> 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<uint32_t, 6> 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<uint32_t, 6> *value) override {
|
||||
if (get(VOLTAGES_CONFIG, value)) {
|
||||
return true;
|
||||
}
|
||||
*value = V_LEVELS_DEFAULT;
|
||||
return true;
|
||||
}
|
||||
void debug(int fd) override;
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
bool get(const char *key, T *value);
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string> mCalData;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_3
|
||||
} // namespace vibrator
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_VIBRATOR_HARDWARE_H
|
||||
@@ -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 <log/log.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
#include <hardware/hardware.h>
|
||||
#include <hardware/vibrator.h>
|
||||
|
||||
#include "Vibrator.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#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<float>(maximum))) /
|
||||
(AMP_ATTENUATE_STEP_SIZE));
|
||||
}
|
||||
|
||||
Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> 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<Status> 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<Status> 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<Status> 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<bool> Vibrator::supportsAmplitudeControl() {
|
||||
return !isUnderExternalControl() && mHwApi->hasEffectScale();
|
||||
}
|
||||
|
||||
Return<Status> Vibrator::setAmplitude(uint8_t amplitude) {
|
||||
if (!amplitude) {
|
||||
return Status::BAD_VALUE;
|
||||
}
|
||||
|
||||
if (!isUnderExternalControl()) {
|
||||
return setEffectAmplitude(amplitude, UINT8_MAX);
|
||||
} else {
|
||||
return Status::UNSUPPORTED_OPERATION;
|
||||
}
|
||||
}
|
||||
|
||||
Return<Status> 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<Status> 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<bool> Vibrator::supportsExternalControl() {
|
||||
return (mHwApi->hasAspEnable() ? true : false);
|
||||
}
|
||||
|
||||
Return<Status> 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<void> Vibrator::debug(const hidl_handle &handle,
|
||||
const hidl_vec<hidl_string> & /* 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 <typename T>
|
||||
Return<void> Vibrator::performWrapper(T effect, EffectStrength strength, perform_cb _hidl_cb) {
|
||||
auto validRange = hidl_enum_range<T>();
|
||||
if (effect < *validRange.begin() || effect > *std::prev(validRange.end())) {
|
||||
_hidl_cb(Status::UNSUPPORTED_OPERATION, 0);
|
||||
return Void();
|
||||
}
|
||||
return performEffect(static_cast<Effect>(effect), strength, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> Vibrator::perform(V1_0::Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
|
||||
return performWrapper(effect, strength, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> Vibrator::perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength,
|
||||
perform_cb _hidl_cb) {
|
||||
return performWrapper(effect, strength, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> Vibrator::perform_1_2(V1_2::Effect effect, EffectStrength strength,
|
||||
perform_cb _hidl_cb) {
|
||||
return performWrapper(effect, strength, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> Vibrator::perform_1_3(Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
|
||||
return performWrapper(effect, strength, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<Status> 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<Status> 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<Status> 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<void> 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
|
||||
@@ -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 <android/hardware/vibrator/1.3/IVibrator.h>
|
||||
#include <hidl/Status.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
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<uint32_t, 6> *value) = 0;
|
||||
// Emit diagnostic information to the given file.
|
||||
virtual void debug(int fd) = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal);
|
||||
|
||||
// Methods from ::android::hardware::vibrator::V1_0::IVibrator follow.
|
||||
using Status = ::android::hardware::vibrator::V1_0::Status;
|
||||
Return<Status> on(uint32_t timeoutMs) override;
|
||||
Return<Status> off() override;
|
||||
Return<bool> supportsAmplitudeControl() override;
|
||||
Return<Status> setAmplitude(uint8_t amplitude) override;
|
||||
|
||||
// Methods from ::android::hardware::vibrator::V1_3::IVibrator follow.
|
||||
Return<bool> supportsExternalControl() override;
|
||||
Return<Status> setExternalControl(bool enabled) override;
|
||||
|
||||
using EffectStrength = ::android::hardware::vibrator::V1_0::EffectStrength;
|
||||
Return<void> perform(V1_0::Effect effect, EffectStrength strength,
|
||||
perform_cb _hidl_cb) override;
|
||||
Return<void> perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength,
|
||||
perform_cb _hidl_cb) override;
|
||||
Return<void> perform_1_2(V1_2::Effect effect, EffectStrength strength,
|
||||
perform_cb _hidl_cb) override;
|
||||
Return<void> perform_1_3(Effect effect, EffectStrength strength, perform_cb _hidl_cb) override;
|
||||
|
||||
// Methods from ::android.hidl.base::V1_0::IBase follow.
|
||||
Return<void> debug(const hidl_handle &handle, const hidl_vec<hidl_string> &options) override;
|
||||
|
||||
private:
|
||||
Return<Status> on(uint32_t timeoutMs, uint32_t effectIndex);
|
||||
template <typename T>
|
||||
Return<void> performWrapper(T effect, EffectStrength strength, perform_cb _hidl_cb);
|
||||
// set 'amplitude' based on an arbitrary scale determined by 'maximum'
|
||||
Return<Status> setEffectAmplitude(uint8_t amplitude, uint8_t maximum);
|
||||
Return<Status> setGlobalAmplitude(bool set);
|
||||
// 'simple' effects are those precompiled and loaded into the controller
|
||||
Return<Status> getSimpleDetails(Effect effect, EffectStrength strength, uint32_t *outTimeMs,
|
||||
uint32_t *outVolLevel);
|
||||
// 'compound' effects are those composed by stringing multiple 'simple' effects
|
||||
Return<Status> getCompoundDetails(Effect effect, EffectStrength strength, uint32_t *outTimeMs,
|
||||
uint32_t *outVolLevel, std::string *outEffectQueue);
|
||||
Return<Status> setEffectQueue(const std::string &effectQueue);
|
||||
Return<void> performEffect(Effect effect, EffectStrength strength, perform_cb _hidl_cb);
|
||||
bool isUnderExternalControl();
|
||||
std::unique_ptr<HwApi> mHwApi;
|
||||
std::unique_ptr<HwCal> mHwCal;
|
||||
std::array<uint32_t, 6> mVolLevels;
|
||||
uint32_t mSimpleEffectDuration;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_3
|
||||
} // namespace vibrator
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H
|
||||
@@ -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
|
||||
"
|
||||
33
vibrator/common/Android.bp
Normal file
33
vibrator/common/Android.bp
Normal file
@@ -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,
|
||||
}
|
||||
134
vibrator/common/HardwareBase.cpp
Normal file
134
vibrator/common/HardwareBase.cpp
Normal file
@@ -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 <cutils/properties.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#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<struct context *>(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
|
||||
212
vibrator/common/HardwareBase.h
Normal file
212
vibrator/common/HardwareBase.h
Normal file
@@ -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 <android-base/unique_fd.h>
|
||||
#include <log/log.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <utils/Trace.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace vibrator {
|
||||
namespace common {
|
||||
namespace implementation {
|
||||
|
||||
using base::unique_fd;
|
||||
|
||||
class HwApiBase {
|
||||
private:
|
||||
using NamesMap = std::map<const std::ios *, std::string>;
|
||||
|
||||
class RecordInterface {
|
||||
public:
|
||||
virtual std::string toString(const NamesMap &names) = 0;
|
||||
virtual ~RecordInterface() {}
|
||||
};
|
||||
template <typename T>
|
||||
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<std::unique_ptr<RecordInterface>>;
|
||||
|
||||
static constexpr uint32_t RECORDS_SIZE = 32;
|
||||
|
||||
public:
|
||||
HwApiBase();
|
||||
void debug(int fd);
|
||||
|
||||
protected:
|
||||
template <typename T>
|
||||
void open(const std::string &name, T *stream);
|
||||
bool has(const std::ios &stream);
|
||||
template <typename T>
|
||||
bool get(T *value, std::istream *stream);
|
||||
template <typename T>
|
||||
bool set(const T &value, std::ostream *stream);
|
||||
template <typename T>
|
||||
bool poll(const T &value, std::istream *stream);
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
void HwApiBase::open(const std::string &name, T *stream) {
|
||||
mNames[stream] = name;
|
||||
utils::openNoCreate(mPathPrefix + name, stream);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
void HwApiBase::record(const char *func, const T &value, const std::ios *stream) {
|
||||
std::lock_guard<std::mutex> lock(mRecordsMutex);
|
||||
mRecords.emplace_back(std::make_unique<Record<T>>(func, value, stream));
|
||||
mRecords.pop_front();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string HwApiBase::Record<T>::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 <typename T>
|
||||
bool getProperty(const char *key, T *value, const T defval);
|
||||
template <typename T>
|
||||
bool getPersist(const char *key, T *value);
|
||||
|
||||
private:
|
||||
std::string mPropertyPrefix;
|
||||
std::map<std::string, std::string> mCalData;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bool HwCalBase::getProperty(const char *key, T *outval, const T defval) {
|
||||
ATRACE_NAME("HwCal::getProperty");
|
||||
*outval = utils::getProperty(mPropertyPrefix + key, defval);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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
|
||||
13
vibrator/common/TEST_MAPPING
Normal file
13
vibrator/common/TEST_MAPPING
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"postsubmit": [
|
||||
{
|
||||
"name": "VibratorHalIntegrationBenchmark"
|
||||
},
|
||||
{
|
||||
"name": "VibratorHalIntegrationBenchmark",
|
||||
"keywords": [
|
||||
"primary-device"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
32
vibrator/common/bench/Android.bp
Normal file
32
vibrator/common/bench/Android.bp
Normal file
@@ -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"],
|
||||
}
|
||||
274
vibrator/common/bench/benchmark.cpp
Normal file
274
vibrator/common/bench/benchmark.cpp
Normal file
@@ -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 <android/hardware/vibrator/1.3/IVibrator.h>
|
||||
|
||||
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 <typename I>
|
||||
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<I> mVibrator;
|
||||
};
|
||||
|
||||
enum class EmptyEnum : uint32_t;
|
||||
template <>
|
||||
inline constexpr std::array<EmptyEnum, 0> hidl_enum_values<EmptyEnum> = {};
|
||||
|
||||
template <typename T, typename U>
|
||||
std::set<T> difference(const hidl_enum_range<T> &t, const hidl_enum_range<U> &u) {
|
||||
class Compare {
|
||||
public:
|
||||
bool operator()(const T &a, const U &b) { return a < static_cast<T>(b); }
|
||||
bool operator()(const U &a, const T &b) { return static_cast<T>(a) < b; }
|
||||
};
|
||||
std::set<T> ret;
|
||||
|
||||
std::set_difference(t.begin(), t.end(), u.begin(), u.end(),
|
||||
std::insert_iterator<decltype(ret)>(ret, ret.begin()), Compare());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename I, typename E1, typename E2 = EmptyEnum>
|
||||
class VibratorEffectsBench : public VibratorBench<I> {
|
||||
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<E1>(), hidl_enum_range<E2>())) {
|
||||
for (const auto &strength : hidl_enum_range<EffectStrength>()) {
|
||||
b->Args({static_cast<long>(effect), static_cast<long>(strength)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void performBench(State *state, Return<void> (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<Effect>(this->getOtherArg(state, 0));
|
||||
}
|
||||
|
||||
auto getStrength(const State &state) const {
|
||||
return static_cast<EffectStrength>(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<V1_0::IVibrator>;
|
||||
|
||||
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<V1_0::IVibrator, V1_0::Effect>;
|
||||
|
||||
BENCHMARK_WRAPPER(VibratorEffectsBench_V1_0, perform,
|
||||
{ performBench(&state, &V1_0::IVibrator::perform); });
|
||||
|
||||
using VibratorEffectsBench_V1_1 =
|
||||
VibratorEffectsBench<V1_1::IVibrator, V1_1::Effect_1_1, V1_0::Effect>;
|
||||
|
||||
BENCHMARK_WRAPPER(VibratorEffectsBench_V1_1, perform_1_1,
|
||||
{ performBench(&state, &V1_1::IVibrator::perform_1_1); });
|
||||
|
||||
using VibratorEffectsBench_V1_2 =
|
||||
VibratorEffectsBench<V1_2::IVibrator, V1_2::Effect, V1_1::Effect_1_1>;
|
||||
|
||||
BENCHMARK_WRAPPER(VibratorEffectsBench_V1_2, perform_1_2,
|
||||
{ performBench(&state, &V1_2::IVibrator::perform_1_2); });
|
||||
|
||||
using VibratorBench_V1_3 = VibratorBench<V1_3::IVibrator>;
|
||||
|
||||
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<V1_3::IVibrator, V1_3::Effect, V1_2::Effect>;
|
||||
|
||||
BENCHMARK_WRAPPER(VibratorEffectsBench_V1_3, perform_1_3,
|
||||
{ performBench(&state, &V1_3::IVibrator::perform_1_3); });
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
162
vibrator/common/utils.h
Normal file
162
vibrator/common/utils.h
Normal file
@@ -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 <android-base/macros.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace vibrator {
|
||||
namespace utils {
|
||||
|
||||
template <typename T>
|
||||
class Is_Iterable {
|
||||
private:
|
||||
template <typename U>
|
||||
static std::true_type test(typename U::iterator *u);
|
||||
|
||||
template <typename U>
|
||||
static std::false_type test(U *u);
|
||||
|
||||
public:
|
||||
static const bool value = decltype(test<T>(0))::value;
|
||||
};
|
||||
|
||||
template <typename T, bool B>
|
||||
using Enable_If_Iterable = std::enable_if_t<Is_Iterable<T>::value == B>;
|
||||
|
||||
template <typename T, typename U = void>
|
||||
using Enable_If_Signed = std::enable_if_t<std::is_signed_v<T>, U>;
|
||||
|
||||
template <typename T, typename U = void>
|
||||
using Enable_If_Unsigned = std::enable_if_t<std::is_unsigned_v<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 <typename T>
|
||||
inline auto toUnderlying(const T value) {
|
||||
return static_cast<std::underlying_type_t<T>>(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Enable_If_Iterable<T, true> unpack(std::istream &stream, T *value) {
|
||||
for (auto &entry : *value) {
|
||||
stream >> entry;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Enable_If_Iterable<T, false> unpack(std::istream &stream, T *value) {
|
||||
stream >> *value;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void unpack<std::string>(std::istream &stream, std::string *value) {
|
||||
*value = std::string(std::istreambuf_iterator(stream), {});
|
||||
stream.setstate(std::istream::eofbit);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Enable_If_Signed<T, T> getProperty(const std::string &key, const T def) {
|
||||
return base::GetIntProperty(key, def);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Enable_If_Unsigned<T, T> getProperty(const std::string &key, const T def) {
|
||||
return base::GetUintProperty(key, def);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool getProperty<bool>(const std::string &key, const bool def) {
|
||||
return base::GetBoolProperty(key, def);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void openNoCreate(const std::string &file, T *outStream) {
|
||||
auto mode = std::is_base_of_v<std::ostream, T> ? 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 <typename T>
|
||||
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<std::string, std::ifstream> 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
|
||||
55
vibrator/drv2624/Android.bp
Normal file
55
vibrator/drv2624/Android.bp
Normal file
@@ -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,
|
||||
}
|
||||
175
vibrator/drv2624/Hardware.h
Normal file
175
vibrator/drv2624/Hardware.h
Normal file
@@ -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<HwApi> Create() {
|
||||
auto hwapi = std::unique_ptr<HwApi>(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<float, 4> *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
|
||||
15
vibrator/drv2624/TEST_MAPPING
Normal file
15
vibrator/drv2624/TEST_MAPPING
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "VibratorHalDrv2624TestSuite"
|
||||
}
|
||||
],
|
||||
"postsubmit": [
|
||||
{
|
||||
"name": "VibratorHalDrv2624Benchmark",
|
||||
"keywords": [
|
||||
"primary-device"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
485
vibrator/drv2624/Vibrator.cpp
Normal file
485
vibrator/drv2624/Vibrator.cpp
Normal file
@@ -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 <cutils/properties.h>
|
||||
#include <hardware/hardware.h>
|
||||
#include <hardware/vibrator.h>
|
||||
#include <log/log.h>
|
||||
#include <utils/Trace.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#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<float, 5> EFFECT_TARGET_G = {0.175, 0.325, 0.37, 0.475, 0.6};
|
||||
static constexpr std::array<float, 3> 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<float>(freqPeriodFormula(lraPeriod)) * 8.0 / 10000.0)));
|
||||
|
||||
return round(odClamp);
|
||||
}
|
||||
|
||||
static float targetGToVlevelsUnderLinearEquation(std::array<float, 4> 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<float, 4> 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> hwapi, std::unique_ptr<HwCal> hwcal)
|
||||
: mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)) {
|
||||
std::string autocal;
|
||||
uint32_t lraPeriod = 0;
|
||||
bool dynamicConfig = false;
|
||||
bool hasEffectCoeffs = false;
|
||||
std::array<float, 4> 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<WaveShape>(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<WaveShape>(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<Status> Vibrator::on(uint32_t timeoutMs, const char mode[],
|
||||
const std::unique_ptr<VibrationConfig> &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<Status> Vibrator::on(uint32_t timeoutMs) {
|
||||
ATRACE_NAME("Vibrator::on");
|
||||
return on(timeoutMs, RTP_MODE, mSteadyConfig, 0);
|
||||
}
|
||||
|
||||
Return<Status> 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<bool> Vibrator::supportsAmplitudeControl() {
|
||||
ATRACE_NAME("Vibrator::supportsAmplitudeControl");
|
||||
return (mHwApi->hasRtpInput() ? true : false);
|
||||
}
|
||||
|
||||
Return<Status> 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<bool> Vibrator::supportsExternalControl() {
|
||||
ATRACE_NAME("Vibrator::supportsExternalControl");
|
||||
return false;
|
||||
}
|
||||
|
||||
Return<Status> 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<void> Vibrator::debug(const hidl_handle &handle,
|
||||
const hidl_vec<hidl_string> & /* 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<void> Vibrator::perform(V1_0::Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
|
||||
return performWrapper(effect, strength, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> Vibrator::perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength,
|
||||
perform_cb _hidl_cb) {
|
||||
return performWrapper(effect, strength, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> Vibrator::perform_1_2(V1_2::Effect effect, EffectStrength strength,
|
||||
perform_cb _hidl_cb) {
|
||||
return performWrapper(effect, strength, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> Vibrator::perform_1_3(Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
|
||||
return performWrapper(effect, strength, _hidl_cb);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Return<void> Vibrator::performWrapper(T effect, EffectStrength strength, perform_cb _hidl_cb) {
|
||||
ATRACE_NAME("Vibrator::performWrapper");
|
||||
auto validEffectRange = hidl_enum_range<T>();
|
||||
if (effect < *validEffectRange.begin() || effect > *std::prev(validEffectRange.end())) {
|
||||
_hidl_cb(Status::UNSUPPORTED_OPERATION, 0);
|
||||
return Void();
|
||||
}
|
||||
auto validStrengthRange = hidl_enum_range<EffectStrength>();
|
||||
if (strength < *validStrengthRange.begin() || strength > *std::prev(validStrengthRange.end())) {
|
||||
_hidl_cb(Status::UNSUPPORTED_OPERATION, 0);
|
||||
return Void();
|
||||
}
|
||||
return performEffect(static_cast<Effect>(effect), strength, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> 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
|
||||
201
vibrator/drv2624/Vibrator.h
Normal file
201
vibrator/drv2624/Vibrator.h
Normal file
@@ -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 <android/hardware/vibrator/1.3/IVibrator.h>
|
||||
#include <hidl/Status.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
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.
|
||||
// <COMP> <BEMF> <GAIN>
|
||||
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.
|
||||
// <index-1> <count-1> [<index-2> <cound-2> ...]
|
||||
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<float, 4> *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> hwapi, std::unique_ptr<HwCal> hwcal);
|
||||
|
||||
// Methods from ::android::hardware::vibrator::V1_0::IVibrator follow.
|
||||
using Status = ::android::hardware::vibrator::V1_0::Status;
|
||||
Return<Status> on(uint32_t timeoutMs) override;
|
||||
Return<Status> off() override;
|
||||
Return<bool> supportsAmplitudeControl() override;
|
||||
Return<Status> setAmplitude(uint8_t amplitude) override;
|
||||
|
||||
// Methods from ::android::hardware::vibrator::V1_3::IVibrator follow.
|
||||
Return<bool> supportsExternalControl() override;
|
||||
Return<Status> setExternalControl(bool enabled) override;
|
||||
|
||||
using EffectStrength = ::android::hardware::vibrator::V1_0::EffectStrength;
|
||||
Return<void> perform(V1_0::Effect effect, EffectStrength strength,
|
||||
perform_cb _hidl_cb) override;
|
||||
Return<void> perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength,
|
||||
perform_cb _hidl_cb) override;
|
||||
Return<void> perform_1_2(V1_2::Effect effect, EffectStrength strength,
|
||||
perform_cb _hidl_cb) override;
|
||||
Return<void> perform_1_3(Effect effect, EffectStrength strength, perform_cb _hidl_cb) override;
|
||||
|
||||
// Methods from ::android.hidl.base::V1_0::IBase follow.
|
||||
Return<void> debug(const hidl_handle &handle, const hidl_vec<hidl_string> &options) override;
|
||||
|
||||
private:
|
||||
Return<Status> on(uint32_t timeoutMs, const char mode[],
|
||||
const std::unique_ptr<VibrationConfig> &config, const int8_t volOffset);
|
||||
template <typename T>
|
||||
Return<void> performWrapper(T effect, EffectStrength strength, perform_cb _hidl_cb);
|
||||
Return<void> performEffect(Effect effect, EffectStrength strength, perform_cb _hidl_cb);
|
||||
std::unique_ptr<HwApi> mHwApi;
|
||||
std::unique_ptr<HwCal> mHwCal;
|
||||
uint32_t mCloseLoopThreshold;
|
||||
std::unique_ptr<VibrationConfig> mSteadyConfig;
|
||||
std::unique_ptr<VibrationConfig> mEffectConfig;
|
||||
uint32_t mClickDuration;
|
||||
uint32_t mTickDuration;
|
||||
uint32_t mDoubleClickDuration;
|
||||
uint32_t mHeavyClickDuration;
|
||||
std::array<uint32_t, 5> mEffectTargetOdClamp;
|
||||
uint32_t mSteadyTargetOdClamp;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_3
|
||||
} // namespace vibrator
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H
|
||||
@@ -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
|
||||
"
|
||||
32
vibrator/drv2624/bench/Android.bp
Normal file
32
vibrator/drv2624/bench/Android.bp
Normal file
@@ -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,
|
||||
}
|
||||
203
vibrator/drv2624/bench/benchmark.cpp
Normal file
203
vibrator/drv2624/bench/benchmark.cpp
Normal file
@@ -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 <android-base/file.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <cutils/fs.h>
|
||||
|
||||
#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<HwCal>());
|
||||
}
|
||||
|
||||
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<IVibrator> 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<Effect>()) {
|
||||
for (const auto &strength : hidl_enum_range<EffectStrength>()) {
|
||||
b->Args({dynamic, static_cast<long>(effect), static_cast<long>(strength)});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
auto getEffect(const ::benchmark::State &state) const {
|
||||
return static_cast<Effect>(getOtherArg(state, 0));
|
||||
}
|
||||
|
||||
auto getStrength(const ::benchmark::State &state) const {
|
||||
return static_cast<EffectStrength>(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();
|
||||
BIN
vibrator/drv2624/drv2624.bin
Normal file
BIN
vibrator/drv2624/drv2624.bin
Normal file
Binary file not shown.
@@ -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 <android/hardware/vibrator/1.3/IVibrator.h>
|
||||
#include <hidl/HidlSupport.h>
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#include <utils/Errors.h>
|
||||
@@ -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> vibrator = new Vibrator(std::make_unique<HwApi>(), std::make_unique<HwCal>());
|
||||
auto hwapi = HwApi::Create();
|
||||
|
||||
if (!hwapi) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
sp<Vibrator> vibrator = new Vibrator(std::move(hwapi), std::make_unique<HwCal>());
|
||||
|
||||
return vibrator->registerAsService();
|
||||
}
|
||||
31
vibrator/drv2624/tests/Android.bp
Normal file
31
vibrator/drv2624/tests/Android.bp
Normal file
@@ -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",
|
||||
],
|
||||
}
|
||||
68
vibrator/drv2624/tests/mocks.h
Normal file
68
vibrator/drv2624/tests/mocks.h
Normal file
@@ -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<float, 4> *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
|
||||
@@ -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 <android-base/file.h>
|
||||
#include <cutils/fs.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstdlib>
|
||||
@@ -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<HwApi>();
|
||||
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<HwApi>();
|
||||
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<Vibrator::HwApi> mHwApi;
|
||||
std::unique_ptr<Vibrator::HwApi> mNoApi;
|
||||
@@ -100,14 +139,53 @@ class HwApiTest : public Test {
|
||||
std::map<std::string, std::stringstream> mExpectedContent;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class HwApiTypedTest
|
||||
: public HwApiTest,
|
||||
public testing::WithParamInterface<std::tuple<std::string, std::function<T>>> {
|
||||
class CreateTest : public HwApiTest, public WithParamInterface<const char *> {
|
||||
public:
|
||||
static auto PrintParam(const testing::TestParamInfo<typename HwApiTypedTest::ParamType> &info) {
|
||||
return std::get<0>(info.param);
|
||||
};
|
||||
void SetUp() override{};
|
||||
void TearDown() override{};
|
||||
|
||||
static auto PrintParam(const TestParamInfo<CreateTest::ParamType> &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> 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 <typename T>
|
||||
class HwApiTypedTest : public HwApiTest,
|
||||
public WithParamInterface<std::tuple<std::string, std::function<T>>> {
|
||||
public:
|
||||
static auto PrintParam(const TestParamInfo<typename HwApiTypedTest::ParamType> &info) {
|
||||
return ParamNameFixup(std::get<0>(info.param));
|
||||
}
|
||||
static auto MakeParam(std::string name, std::function<T> 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<bool(Vibrator::HwApi &, bool *)>;
|
||||
|
||||
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<bool(Vibrator::HwApi &, uint32_t *)>;
|
||||
|
||||
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<bool(Vibrator::HwApi &, bool)>;
|
||||
|
||||
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<bool(Vibrator::HwApi &, int8_t)>;
|
||||
|
||||
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<bool(Vibrator::HwApi &, uint8_t)>;
|
||||
|
||||
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<bool(Vibrator::HwApi &, uint32_t)>;
|
||||
|
||||
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
|
||||
396
vibrator/drv2624/tests/test-hwcal.cpp
Normal file
396
vibrator/drv2624/tests/test-hwcal.cpp
Normal file
@@ -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 <android-base/file.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#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 <typename T>
|
||||
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<HwCal>(); }
|
||||
|
||||
template <typename T>
|
||||
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<Vibrator::HwCal> 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
|
||||
449
vibrator/drv2624/tests/test-vibrator.cpp
Normal file
449
vibrator/drv2624/tests/test-vibrator.cpp
Normal file
@@ -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 <android-base/logging.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#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<EffectTuple, EffectSequence> 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 <typename... T>
|
||||
class VibratorTestTemplate : public Test, public WithParamInterface<std::tuple<bool, T...>> {
|
||||
public:
|
||||
static auto GetDynamicConfig(typename VibratorTestTemplate::ParamType param) {
|
||||
return std::get<0>(param);
|
||||
}
|
||||
template <std::size_t I>
|
||||
static auto GetOtherParam(typename VibratorTestTemplate::ParamType param) {
|
||||
return std::get<I + 1>(param);
|
||||
}
|
||||
|
||||
static auto PrintParam(const TestParamInfo<typename VibratorTestTemplate::ParamType> &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> mockapi;
|
||||
std::unique_ptr<MockCal> 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> *mockapi, std::unique_ptr<MockCal> *mockcal) {
|
||||
*mockapi = std::make_unique<MockApi>();
|
||||
*mockcal = std::make_unique<MockCal>();
|
||||
|
||||
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> mockapi, std::unique_ptr<MockCal> 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<IVibrator> mVibrator;
|
||||
|
||||
EffectDuration mCloseLoopThreshold;
|
||||
uint32_t mLongFrequencyShift;
|
||||
uint32_t mShortLraPeriod;
|
||||
uint32_t mLongLraPeriod;
|
||||
uint32_t mShortVoltageMax;
|
||||
uint32_t mLongVoltageMax;
|
||||
std::map<Effect, EffectDuration> mEffectDurations;
|
||||
};
|
||||
|
||||
using BasicTest = VibratorTestTemplate<>;
|
||||
|
||||
TEST_P(BasicTest, Constructor) {
|
||||
std::unique_ptr<MockApi> mockapi;
|
||||
std::unique_ptr<MockCal> 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<EffectTuple> {
|
||||
public:
|
||||
static auto GetEffectTuple(ParamType param) { return GetOtherParam<0>(param); }
|
||||
|
||||
static auto PrintParam(const TestParamInfo<ParamType> &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<Effect>().begin(),
|
||||
hidl_enum_range<Effect>().end()),
|
||||
ValuesIn(hidl_enum_range<EffectStrength>().begin(),
|
||||
hidl_enum_range<EffectStrength>().end()))),
|
||||
EffectsTest::PrintParam);
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_3
|
||||
} // namespace vibrator
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
@@ -18,16 +18,10 @@
|
||||
|
||||
#include <android/hardware/vibrator/1.3/IVibrator.h>
|
||||
|
||||
using EffectIndex = uint16_t;
|
||||
using EffectLevel = uint32_t;
|
||||
using EffectAmplitude = uint8_t;
|
||||
using EffectScale = uint16_t;
|
||||
using EffectDuration = uint32_t;
|
||||
using EffectQueue = std::tuple<std::string, EffectDuration>;
|
||||
using EffectSequence = std::tuple<std::string, uint8_t>;
|
||||
using EffectTuple = std::tuple<::android::hardware::vibrator::V1_3::Effect,
|
||||
::android::hardware::vibrator::V1_0::EffectStrength>;
|
||||
|
||||
using QueueEffect = std::tuple<EffectIndex, EffectLevel>;
|
||||
using QueueDelay = uint32_t;
|
||||
|
||||
#endif // ANDROID_HARDWARE_VIBRATOR_TEST_TYPES_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 <gmock/gmock.h>
|
||||
#include <cmath>
|
||||
|
||||
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
|
||||
@@ -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<uint32_t, 6> *value));
|
||||
MOCK_METHOD1(debug, void(int fd));
|
||||
|
||||
~MockCal() override { destructor(); };
|
||||
};
|
||||
|
||||
#endif // ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H
|
||||
@@ -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 <android-base/file.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#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<uint32_t, 6> 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 <typename T, typename std::array<T, 0>::size_type N>
|
||||
static void pack(std::ostream &stream, const std::array<T, N> &value, std::string lpad,
|
||||
std::string rpad) {
|
||||
for (auto &entry : value) {
|
||||
pack(stream, entry, lpad, rpad);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void createHwCal() { mHwCal = std::make_unique<HwCal>(); }
|
||||
|
||||
template <typename T>
|
||||
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<Vibrator::HwCal> 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<uint32_t, 6> expect;
|
||||
std::array<uint32_t, 6> 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<uint32_t, 6> expect = V_DEFAULT;
|
||||
std::array<uint32_t, 6> 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<uint32_t, 6> expect = V_DEFAULT;
|
||||
std::array<uint32_t, 6> actual;
|
||||
|
||||
std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
|
||||
|
||||
write("v_levels", std::array<uint32_t, expect.size() - 1>());
|
||||
|
||||
createHwCal();
|
||||
|
||||
EXPECT_TRUE(mHwCal->getVolLevels(&actual));
|
||||
EXPECT_EQ(expect, actual);
|
||||
}
|
||||
|
||||
TEST_F(HwCalTest, v_long) {
|
||||
std::array<uint32_t, 6> expect = V_DEFAULT;
|
||||
std::array<uint32_t, 6> actual;
|
||||
|
||||
std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
|
||||
|
||||
write("v_levels", std::array<uint32_t, expect.size() + 1>());
|
||||
|
||||
createHwCal();
|
||||
|
||||
EXPECT_TRUE(mHwCal->getVolLevels(&actual));
|
||||
EXPECT_EQ(expect, actual);
|
||||
}
|
||||
|
||||
TEST_F(HwCalTest, v_nofile) {
|
||||
std::array<uint32_t, 6> expect = V_DEFAULT;
|
||||
std::array<uint32_t, 6> 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<uint32_t, 6> volExpect;
|
||||
std::array<uint32_t, 6> 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<uint32_t, 6> volExpect;
|
||||
std::array<uint32_t, 6> 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
|
||||
@@ -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 <android-base/logging.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#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 <typename T, typename U, typename... Args>
|
||||
static EffectQueue Queue(const T &first, const U &second, Args... rest);
|
||||
|
||||
// Constants With Arbitrary Values
|
||||
|
||||
static constexpr std::array<EffectLevel, 6> 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<EffectTuple, EffectScale> 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<EffectTuple, EffectQueue> 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 <typename T, typename U, typename... Args>
|
||||
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<EffectTuple> {
|
||||
public:
|
||||
void SetUp() override {
|
||||
std::unique_ptr<MockApi> mockapi;
|
||||
std::unique_ptr<MockCal> mockcal;
|
||||
|
||||
createMock(&mockapi, &mockcal);
|
||||
createVibrator(std::move(mockapi), std::move(mockcal));
|
||||
}
|
||||
|
||||
void TearDown() override { deleteVibrator(); }
|
||||
|
||||
protected:
|
||||
void createMock(std::unique_ptr<MockApi> *mockapi, std::unique_ptr<MockCal> *mockcal) {
|
||||
*mockapi = std::make_unique<MockApi>();
|
||||
*mockcal = std::make_unique<MockCal>();
|
||||
|
||||
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> mockapi, std::unique_ptr<MockCal> mockcal,
|
||||
bool relaxed = true) {
|
||||
std::vector<uint32_t> 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<IVibrator> mVibrator;
|
||||
};
|
||||
|
||||
TEST_F(VibratorTest, HwApi) {
|
||||
std::unique_ptr<MockApi> mockapi;
|
||||
std::unique_ptr<MockCal> 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<void()> either;
|
||||
|
||||
EXPECT_CALL(*mMockApi, hasEffectScale())
|
||||
.Times(AtMost(1))
|
||||
.WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction<void()>::Call),
|
||||
::testing::Return(false)));
|
||||
EXPECT_CALL(*mMockApi, getAspEnable(_))
|
||||
.Times(AtMost(1))
|
||||
.WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction<void()>::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<void()> either;
|
||||
|
||||
EXPECT_CALL(*mMockApi, hasEffectScale())
|
||||
.Times(AtMost(1))
|
||||
.WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction<void()>::Call),
|
||||
::testing::Return(false)));
|
||||
EXPECT_CALL(*mMockApi, getAspEnable(_))
|
||||
.Times(AtMost(1))
|
||||
.WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction<void()>::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<void()> either;
|
||||
|
||||
EXPECT_CALL(*mMockApi, hasEffectScale())
|
||||
.Times(AtMost(1))
|
||||
.WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction<void()>::Call),
|
||||
::testing::Return(true)));
|
||||
EXPECT_CALL(*mMockApi, getAspEnable(_))
|
||||
.Times(AtMost(1))
|
||||
.WillOnce(DoAll(InvokeWithoutArgs(&either, &MockFunction<void()>::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<Effect>().begin(),
|
||||
hidl_enum_range<Effect>().end()),
|
||||
ValuesIn(hidl_enum_range<EffectStrength>().begin(),
|
||||
hidl_enum_range<EffectStrength>().end())),
|
||||
[](const testing::TestParamInfo<VibratorTest::ParamType> &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
|
||||
@@ -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 <cmath>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
static inline EffectScale toScale(uint8_t target, uint8_t maximum) {
|
||||
return std::round((-20 * std::log10(target / static_cast<float>(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
|
||||
@@ -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 <typename T>
|
||||
class Is_Iterable {
|
||||
private:
|
||||
template <typename U>
|
||||
static std::true_type test(typename U::iterator *u);
|
||||
|
||||
template <typename U>
|
||||
static std::false_type test(U *u);
|
||||
|
||||
public:
|
||||
static const bool value = decltype(test<T>(0))::value;
|
||||
};
|
||||
|
||||
template <typename T, bool B>
|
||||
using Enable_If_Iterable = std::enable_if_t<Is_Iterable<T>::value == B>;
|
||||
|
||||
#endif // ANDROID_HARDWARE_VIBRATOR_UTILS_H
|
||||
Reference in New Issue
Block a user