vibrator: Modify some delta and apply calibration fix am: 368d8334b6

Change-Id: I8b1a4ada0ae824c6a944423756badb64377357b9
This commit is contained in:
Automerger Merge Worker
2020-02-06 09:16:04 +00:00
committed by Cyan_Hsieh
39 changed files with 3230 additions and 2085 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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",
},
},
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
"

View 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,
}

View 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

View 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

View File

@@ -0,0 +1,13 @@
{
"postsubmit": [
{
"name": "VibratorHalIntegrationBenchmark"
},
{
"name": "VibratorHalIntegrationBenchmark",
"keywords": [
"primary-device"
]
}
]
}

View 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"],
}

View 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
View 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

View 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
View 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

View File

@@ -0,0 +1,15 @@
{
"presubmit": [
{
"name": "VibratorHalDrv2624TestSuite"
}
],
"postsubmit": [
{
"name": "VibratorHalDrv2624Benchmark",
"keywords": [
"primary-device"
]
}
]
}

View 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
View 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

View File

@@ -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
"

View 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,
}

View 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();

Binary file not shown.

View File

@@ -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();
}

View 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",
],
}

View 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

View File

@@ -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

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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