[DO NOT MERGE] device/lynx: Change to use common vibrator HAL
Use common vibrator hal in hardware/google/pixel instead of device/google/lynx. This will use the common vibrator hal from now on to support the adaptive haptics feature. Bug: 198239103 Bug: 269762222 Test: Verified on device Change-Id: Ia90fe77222fbce0c54ecf3fc5439fea5cda5c892
This commit is contained in:
@@ -116,10 +116,3 @@ on override-sf-uclamp
|
|||||||
# it should be written by the system init.
|
# it should be written by the system init.
|
||||||
on property:ro.boot.hardware.sku=G82U8
|
on property:ro.boot.hardware.sku=G82U8
|
||||||
setprop audio.camerasound.force true
|
setprop audio.camerasound.force true
|
||||||
|
|
||||||
# Route vibrator.adaptive_haptics.enabled to persist
|
|
||||||
on property:vibrator.adaptive_haptics.enabled=0
|
|
||||||
setprop persist.vendor.vibrator.hal.context.enable false
|
|
||||||
|
|
||||||
on property:vibrator.adaptive_haptics.enabled=1
|
|
||||||
setprop persist.vendor.vibrator.hal.context.enable true
|
|
||||||
|
@@ -28,7 +28,7 @@ DEVICE_PACKAGE_OVERLAYS += device/google/lynx/lynx/overlay
|
|||||||
|
|
||||||
include device/google/lynx/audio/lynx/audio-tables.mk
|
include device/google/lynx/audio/lynx/audio-tables.mk
|
||||||
include device/google/gs201/device-shipping-common.mk
|
include device/google/gs201/device-shipping-common.mk
|
||||||
include device/google/lynx/vibrator/cs40l26/device.mk
|
include hardware/google/pixel/vibrator/cs40l26/device.mk
|
||||||
|
|
||||||
# go/lyric-soong-variables
|
# go/lyric-soong-variables
|
||||||
$(call soong_config_set,lyric,camera_hardware,lynx)
|
$(call soong_config_set,lyric,camera_hardware,lynx)
|
||||||
@@ -154,12 +154,7 @@ endif
|
|||||||
PRODUCT_VENDOR_PROPERTIES += \
|
PRODUCT_VENDOR_PROPERTIES += \
|
||||||
ro.vendor.vibrator.hal.supported_primitives=243 \
|
ro.vendor.vibrator.hal.supported_primitives=243 \
|
||||||
ro.vendor.vibrator.hal.f0.comp.enabled=1 \
|
ro.vendor.vibrator.hal.f0.comp.enabled=1 \
|
||||||
ro.vendor.vibrator.hal.redc.comp.enabled=0 \
|
ro.vendor.vibrator.hal.redc.comp.enabled=0
|
||||||
persist.vendor.vibrator.hal.context.enable=false \
|
|
||||||
persist.vendor.vibrator.hal.context.scale=40 \
|
|
||||||
persist.vendor.vibrator.hal.context.fade=true \
|
|
||||||
persist.vendor.vibrator.hal.context.cooldowntime=1600 \
|
|
||||||
persist.vendor.vibrator.hal.context.settlingtime=5000
|
|
||||||
|
|
||||||
# Trusty liboemcrypto.so
|
# Trusty liboemcrypto.so
|
||||||
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/lynx/prebuilts
|
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/lynx/prebuilts
|
||||||
|
@@ -1,52 +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.
|
|
||||||
|
|
||||||
package {
|
|
||||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_defaults {
|
|
||||||
name: "PixelVibratorDefaultsPrivateLynx",
|
|
||||||
relative_install_path: "hw",
|
|
||||||
static_libs: [
|
|
||||||
"PixelVibratorCommonPrivateLynx",
|
|
||||||
],
|
|
||||||
shared_libs: [
|
|
||||||
"libbase",
|
|
||||||
"libbinder_ndk",
|
|
||||||
"libcutils",
|
|
||||||
"libhardware",
|
|
||||||
"liblog",
|
|
||||||
"libutils",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_defaults {
|
|
||||||
name: "PixelVibratorBinaryDefaultsPrivateLynx",
|
|
||||||
defaults: ["PixelVibratorDefaultsPrivateLynx"],
|
|
||||||
shared_libs: [
|
|
||||||
"android.hardware.vibrator-V2-ndk",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_defaults {
|
|
||||||
name: "PixelVibratorTestDefaultsPrivateLynx",
|
|
||||||
defaults: ["PixelVibratorDefaultsPrivateLynx"],
|
|
||||||
static_libs: [
|
|
||||||
"android.hardware.vibrator-V2-ndk",
|
|
||||||
],
|
|
||||||
test_suites: ["device-tests"],
|
|
||||||
require_root: true,
|
|
||||||
}
|
|
@@ -1,4 +0,0 @@
|
|||||||
chasewu@google.com
|
|
||||||
michaelwr@google.com
|
|
||||||
taikuo@google.com
|
|
||||||
chrispaulo@google.com
|
|
@@ -1,37 +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.
|
|
||||||
|
|
||||||
package {
|
|
||||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_library {
|
|
||||||
name: "PixelVibratorCommonPrivateLynx",
|
|
||||||
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,
|
|
||||||
}
|
|
@@ -1,136 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "HardwareBase.h"
|
|
||||||
|
|
||||||
#include <cutils/properties.h>
|
|
||||||
#include <log/log.h>
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace android {
|
|
||||||
namespace hardware {
|
|
||||||
namespace vibrator {
|
|
||||||
|
|
||||||
HwApiBase::HwApiBase() {
|
|
||||||
mPathPrefix = std::getenv("HWAPI_PATH_PREFIX") ?: "";
|
|
||||||
if (mPathPrefix.empty()) {
|
|
||||||
ALOGE("Failed get HWAPI path prefix!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HwApiBase::saveName(const std::string &name, const std::ios *stream) {
|
|
||||||
mNames[stream] = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 vibrator
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace android
|
|
||||||
} // namespace aidl
|
|
@@ -1,221 +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.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <map>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <android-base/unique_fd.h>
|
|
||||||
#include <log/log.h>
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace android {
|
|
||||||
namespace hardware {
|
|
||||||
namespace vibrator {
|
|
||||||
|
|
||||||
using ::android::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:
|
|
||||||
void saveName(const std::string &name, const std::ios *stream);
|
|
||||||
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, const int32_t timeout = -1);
|
|
||||||
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;
|
|
||||||
std::mutex mIoMutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define HWAPI_RECORD(args...) HwApiBase::record(__FUNCTION__, ##args)
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void HwApiBase::open(const std::string &name, T *stream) {
|
|
||||||
saveName(name, stream);
|
|
||||||
utils::openNoCreate(mPathPrefix + name, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool HwApiBase::get(T *value, std::istream *stream) {
|
|
||||||
ATRACE_NAME("HwApi::get");
|
|
||||||
std::scoped_lock ioLock{mIoMutex};
|
|
||||||
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<<;
|
|
||||||
std::scoped_lock ioLock{mIoMutex};
|
|
||||||
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, const int32_t timeoutMs) {
|
|
||||||
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;
|
|
||||||
int epollRet;
|
|
||||||
|
|
||||||
if (timeoutMs < -1) {
|
|
||||||
ALOGE("Invalid polling timeout!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
|
||||||
epollRet = epoll_wait(epollFd, &event, 1, timeoutMs);
|
|
||||||
if (epollRet <= 0) {
|
|
||||||
ALOGE("Polling error or timeout! (%d)", epollRet);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 vibrator
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace android
|
|
||||||
} // namespace aidl
|
|
@@ -1,173 +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.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <android-base/macros.h>
|
|
||||||
#include <android-base/parsedouble.h>
|
|
||||||
#include <android-base/properties.h>
|
|
||||||
#include <log/log.h>
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <map>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
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) {
|
|
||||||
if (std::is_floating_point_v<T>) {
|
|
||||||
float result;
|
|
||||||
std::string value = ::android::base::GetProperty(key, "");
|
|
||||||
if (!value.empty() && ::android::base::ParseFloat(value, &result)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return def;
|
|
||||||
} else {
|
|
||||||
return ::android::base::GetIntProperty(key, def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline Enable_If_Unsigned<T, T> getProperty(const std::string &key, const T def) {
|
|
||||||
return ::android::base::GetUintProperty(key, def);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline bool getProperty<bool>(const std::string &key, const bool def) {
|
|
||||||
return ::android::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
|
|
||||||
} // namespace aidl
|
|
@@ -1,107 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (C) 2022 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.
|
|
||||||
|
|
||||||
package {
|
|
||||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_defaults {
|
|
||||||
name: "android.hardware.vibrator-defaults.cs40l26-private-lynx",
|
|
||||||
cflags: [
|
|
||||||
"-DATRACE_TAG=(ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)",
|
|
||||||
"-DLOG_TAG=\"android.hardware.vibrator-cs40l26\"",
|
|
||||||
],
|
|
||||||
shared_libs: [
|
|
||||||
"libbinder",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_defaults {
|
|
||||||
name: "VibratorHalCs40l26BinaryDefaultsPrivateLynx",
|
|
||||||
defaults: [
|
|
||||||
"PixelVibratorBinaryDefaultsPrivateLynx",
|
|
||||||
"android.hardware.vibrator-defaults.cs40l26-private-lynx",
|
|
||||||
],
|
|
||||||
include_dirs: [
|
|
||||||
"external/tinyalsa/include",
|
|
||||||
],
|
|
||||||
shared_libs: [
|
|
||||||
"libcutils",
|
|
||||||
"libtinyalsa",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_defaults {
|
|
||||||
name: "VibratorHalCs40l26TestDefaultsPrivateLynx",
|
|
||||||
defaults: [
|
|
||||||
"PixelVibratorTestDefaultsPrivateLynx",
|
|
||||||
"android.hardware.vibrator-defaults.cs40l26-private-lynx",
|
|
||||||
],
|
|
||||||
static_libs: [
|
|
||||||
"libtinyalsa",
|
|
||||||
],
|
|
||||||
shared_libs: ["android.hardware.vibrator-impl.cs40l26-private-lynx"],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_library_shared {
|
|
||||||
name: "libvibecapo_proto_lynx",
|
|
||||||
vendor_available: true,
|
|
||||||
owner: "google",
|
|
||||||
srcs: [
|
|
||||||
"proto/capo.proto",
|
|
||||||
],
|
|
||||||
shared_libs: [
|
|
||||||
"libprotobuf-cpp-full",
|
|
||||||
],
|
|
||||||
export_include_dirs: [
|
|
||||||
"inc",
|
|
||||||
],
|
|
||||||
proto: {
|
|
||||||
type: "lite",
|
|
||||||
export_proto_headers: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_library_shared {
|
|
||||||
name: "android.hardware.vibrator-impl.cs40l26-private-lynx",
|
|
||||||
defaults: ["VibratorHalCs40l26BinaryDefaultsPrivateLynx"],
|
|
||||||
srcs: [
|
|
||||||
"Vibrator.cpp",
|
|
||||||
"CapoDetector.cpp",
|
|
||||||
],
|
|
||||||
static_libs: [
|
|
||||||
"chre_client",
|
|
||||||
],
|
|
||||||
shared_libs: [
|
|
||||||
"libvibecapo_proto_lynx",
|
|
||||||
"libprotobuf-cpp-full",
|
|
||||||
],
|
|
||||||
export_include_dirs: [
|
|
||||||
".",
|
|
||||||
"inc",
|
|
||||||
],
|
|
||||||
vendor_available: true,
|
|
||||||
visibility: [":__subpackages__"],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_binary {
|
|
||||||
name: "android.hardware.vibrator-service.cs40l26-private-lynx",
|
|
||||||
defaults: ["VibratorHalCs40l26BinaryDefaultsPrivateLynx"],
|
|
||||||
init_rc: ["android.hardware.vibrator-service.cs40l26-private-lynx.rc"],
|
|
||||||
vintf_fragments: ["android.hardware.vibrator-service.cs40l26-private-lynx.xml"],
|
|
||||||
srcs: ["service.cpp"],
|
|
||||||
shared_libs: ["android.hardware.vibrator-impl.cs40l26-private-lynx"],
|
|
||||||
proprietary: true,
|
|
||||||
}
|
|
@@ -1,215 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 Google LLC. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* 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 "CapoDetector.h"
|
|
||||||
#include <google/protobuf/message.h>
|
|
||||||
#include <google/protobuf/io/coded_stream.h>
|
|
||||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
|
||||||
|
|
||||||
#include <log/log.h>
|
|
||||||
|
|
||||||
#ifdef LOG_TAG
|
|
||||||
#undef LOG_TAG
|
|
||||||
#define LOG_TAG "CapoDetector"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace chre {
|
|
||||||
|
|
||||||
namespace { // anonymous namespace for file-local definitions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when onConnected() to send NanoappList request.
|
|
||||||
*/
|
|
||||||
void requestNanoappList(SocketClient &client) {
|
|
||||||
flatbuffers::FlatBufferBuilder builder;
|
|
||||||
HostProtocolHost::encodeNanoappListRequest(builder);
|
|
||||||
if (!client.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
|
|
||||||
ALOGE("Failed to send NanoappList request");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when initializing connection with CHRE socket.
|
|
||||||
*/
|
|
||||||
sp<CapoDetector> CapoDetector::start() {
|
|
||||||
sp<CapoDetector> listener = new CapoDetector();
|
|
||||||
if (!listener->connectInBackground(kChreSocketName, listener)) {
|
|
||||||
ALOGE("Couldn't connect to CHRE socket");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
ALOGI("%s connect to CHRE socket.", __func__);
|
|
||||||
|
|
||||||
return listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the socket is successfully (re-)connected.
|
|
||||||
* Reset the position and try to send NanoappList request.
|
|
||||||
*/
|
|
||||||
void CapoDetector::onConnected() {
|
|
||||||
flatbuffers::FlatBufferBuilder builder;
|
|
||||||
|
|
||||||
// Reset the last position type.
|
|
||||||
last_position_type_ = capo::PositionType::UNKNOWN;
|
|
||||||
requestNanoappList(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when we have failed to (re-)connect the socket after many attempts
|
|
||||||
* and are giving up.
|
|
||||||
*/
|
|
||||||
void CapoDetector::onConnectionAborted() {
|
|
||||||
ALOGE("%s, Capo Aborting Connection!", __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked when the socket is disconnected, and this connection loss was not
|
|
||||||
* the result of an explicit call to disconnect().
|
|
||||||
* Reset the position while disconnecting.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void CapoDetector::onDisconnected() {
|
|
||||||
last_position_type_ = capo::PositionType::UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode unix socket msgs to CHRE messages, and call the appropriate
|
|
||||||
* callback depending on the CHRE message.
|
|
||||||
*/
|
|
||||||
void CapoDetector::onMessageReceived(const void *data, size_t length) {
|
|
||||||
if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
|
|
||||||
ALOGE("Failed to decode message");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen for messages from capo nanoapp and handle the message.
|
|
||||||
*/
|
|
||||||
void CapoDetector::handleNanoappMessage(const fbs::NanoappMessageT &message) {
|
|
||||||
ALOGI("%s, Id %" PRIu64 ", type %d, size %d", __func__, message.app_id, message.message_type,
|
|
||||||
static_cast<int>(message.message.size()));
|
|
||||||
// Exclude the message with unmatched nanoapp id.
|
|
||||||
if (message.app_id != kCapoNanoappId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Handle the message with message_type.
|
|
||||||
switch (message.message_type) {
|
|
||||||
case capo::MessageType::ACK_NOTIFICATION: {
|
|
||||||
capo::AckNotification gd;
|
|
||||||
gd.set_notification_type(static_cast<capo::NotificationType>(message.message[1]));
|
|
||||||
ALOGD("%s, get notification event from capo nanoapp, type %d", __func__,
|
|
||||||
gd.notification_type());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case capo::MessageType::POSITION_DETECTED: {
|
|
||||||
capo::PositionDetected gd;
|
|
||||||
gd.set_position_type(static_cast<capo::PositionType>(message.message[1]));
|
|
||||||
ALOGD("%s, get position event from capo nanoapp, type %d", __func__,
|
|
||||||
gd.position_type());
|
|
||||||
|
|
||||||
// Callback to function while getting carried position event.
|
|
||||||
if (callback_func_ != nullptr) {
|
|
||||||
last_position_type_ = gd.position_type();
|
|
||||||
ALOGD("%s, sent position type %d to callback function", __func__,
|
|
||||||
last_position_type_);
|
|
||||||
callback_func_(last_position_type_);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ALOGE("%s, get invalid message, type: %" PRIu32 ", from capo nanoapp.", __func__,
|
|
||||||
message.message_type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the response of a NanoappList request.
|
|
||||||
* Ensure that capo nanoapp is running.
|
|
||||||
*/
|
|
||||||
void CapoDetector::handleNanoappListResponse(const fbs::NanoappListResponseT &response) {
|
|
||||||
for (const std::unique_ptr<fbs::NanoappListEntryT> &nanoapp : response.nanoapps) {
|
|
||||||
if (nanoapp->app_id == kCapoNanoappId) {
|
|
||||||
if (nanoapp->enabled)
|
|
||||||
enable();
|
|
||||||
else
|
|
||||||
ALOGE("Capo nanoapp not enabled");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ALOGE("Capo nanoapp not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send enabling message to the nanoapp.
|
|
||||||
*/
|
|
||||||
void CapoDetector::enable() {
|
|
||||||
// Create CHRE message with serialized message
|
|
||||||
flatbuffers::FlatBufferBuilder builder, config_builder, force_builder;
|
|
||||||
|
|
||||||
auto config_data = std::make_unique<capo::ConfigureDetector_ConfigData>();
|
|
||||||
auto msg = std::make_unique<capo::ConfigureDetector>();
|
|
||||||
|
|
||||||
config_data->set_still_time_threshold_nanosecond(mCapoDetectorMDParameters.still_time_threshold_ns);
|
|
||||||
config_data->set_window_width_nanosecond(mCapoDetectorMDParameters.window_width_ns);
|
|
||||||
config_data->set_motion_confidence_threshold(mCapoDetectorMDParameters.motion_confidence_threshold);
|
|
||||||
config_data->set_still_confidence_threshold(mCapoDetectorMDParameters.still_confidence_threshold);
|
|
||||||
config_data->set_var_threshold(mCapoDetectorMDParameters.var_threshold);
|
|
||||||
config_data->set_var_threshold_delta(mCapoDetectorMDParameters.var_threshold_delta);
|
|
||||||
|
|
||||||
msg->set_allocated_config_data(config_data.release());
|
|
||||||
|
|
||||||
auto pb_size = msg->ByteSizeLong();
|
|
||||||
auto pb_data = std::make_unique<uint8_t[]>(pb_size);
|
|
||||||
|
|
||||||
if (!msg->SerializeToArray(pb_data.get(), pb_size)) {
|
|
||||||
ALOGE("Failed to serialize message.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ALOGI("Configuring CapoDetector");
|
|
||||||
// Configure the detector from host-side
|
|
||||||
android::chre::HostProtocolHost::encodeNanoappMessage(
|
|
||||||
config_builder, getNanoppAppId(), capo::MessageType::CONFIGURE_DETECTOR, getHostEndPoint(),
|
|
||||||
pb_data.get(), pb_size);
|
|
||||||
ALOGI("Sending capo config message to Nanoapp, %" PRIu32 " bytes", config_builder.GetSize());
|
|
||||||
if (!sendMessage(config_builder.GetBufferPointer(), config_builder.GetSize())) {
|
|
||||||
ALOGE("Failed to send config event for capo nanoapp");
|
|
||||||
}
|
|
||||||
|
|
||||||
ALOGI("Enabling CapoDetector");
|
|
||||||
android::chre::HostProtocolHost::encodeNanoappMessage(
|
|
||||||
builder, getNanoppAppId(), capo::MessageType::ENABLE_DETECTOR, getHostEndPoint(),
|
|
||||||
/*messageData*/ nullptr, /*messageDataLenbuffer*/ 0);
|
|
||||||
ALOGI("Sending enable message to Nanoapp, %" PRIu32 " bytes", builder.GetSize());
|
|
||||||
if (!sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
|
|
||||||
ALOGE("Failed to send enable event for capo nanoapp");
|
|
||||||
}
|
|
||||||
|
|
||||||
ALOGI("Forcing CapoDetector to update state");
|
|
||||||
// Force an updated state upon connection
|
|
||||||
android::chre::HostProtocolHost::encodeNanoappMessage(
|
|
||||||
force_builder, getNanoppAppId(), capo::MessageType::FORCE_UPDATE, getHostEndPoint(),
|
|
||||||
/*messageData*/ nullptr, /*messageDataLenbuffer*/ 0);
|
|
||||||
ALOGI("Sending force-update message to Nanoapp, %" PRIu32 " bytes", force_builder.GetSize());
|
|
||||||
if (!sendMessage(force_builder.GetBufferPointer(), force_builder.GetSize())) {
|
|
||||||
ALOGE("Failed to send force-update event for capo nanoapp");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace chre
|
|
||||||
} // namespace android
|
|
@@ -1,362 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2021 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "HardwareBase.h"
|
|
||||||
#include "Vibrator.h"
|
|
||||||
|
|
||||||
#define PROC_SND_PCM "/proc/asound/pcm"
|
|
||||||
#define HAPTIC_PCM_DEVICE_SYMBOL "haptic nohost playback"
|
|
||||||
|
|
||||||
static struct pcm_config haptic_nohost_config = {
|
|
||||||
.channels = 1,
|
|
||||||
.rate = 48000,
|
|
||||||
.period_size = 80,
|
|
||||||
.period_count = 2,
|
|
||||||
.format = PCM_FORMAT_S16_LE,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum WaveformIndex : uint16_t {
|
|
||||||
/* Physical waveform */
|
|
||||||
WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
|
|
||||||
WAVEFORM_RESERVED_INDEX_1 = 1,
|
|
||||||
WAVEFORM_CLICK_INDEX = 2,
|
|
||||||
WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
|
|
||||||
WAVEFORM_THUD_INDEX = 4,
|
|
||||||
WAVEFORM_SPIN_INDEX = 5,
|
|
||||||
WAVEFORM_QUICK_RISE_INDEX = 6,
|
|
||||||
WAVEFORM_SLOW_RISE_INDEX = 7,
|
|
||||||
WAVEFORM_QUICK_FALL_INDEX = 8,
|
|
||||||
WAVEFORM_LIGHT_TICK_INDEX = 9,
|
|
||||||
WAVEFORM_LOW_TICK_INDEX = 10,
|
|
||||||
WAVEFORM_RESERVED_MFG_1,
|
|
||||||
WAVEFORM_RESERVED_MFG_2,
|
|
||||||
WAVEFORM_RESERVED_MFG_3,
|
|
||||||
WAVEFORM_MAX_PHYSICAL_INDEX,
|
|
||||||
/* OWT waveform */
|
|
||||||
WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
|
|
||||||
WAVEFORM_PWLE,
|
|
||||||
/*
|
|
||||||
* Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
|
|
||||||
* #define FF_GAIN 0x60 // 96 in decimal
|
|
||||||
* #define FF_MAX_EFFECTS FF_GAIN
|
|
||||||
*/
|
|
||||||
WAVEFORM_MAX_INDEX,
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace android {
|
|
||||||
namespace hardware {
|
|
||||||
namespace vibrator {
|
|
||||||
|
|
||||||
class HwApi : public Vibrator::HwApi, private HwApiBase {
|
|
||||||
public:
|
|
||||||
HwApi() {
|
|
||||||
open("calibration/f0_stored", &mF0);
|
|
||||||
open("default/f0_offset", &mF0Offset);
|
|
||||||
open("calibration/redc_stored", &mRedc);
|
|
||||||
open("calibration/q_stored", &mQ);
|
|
||||||
open("default/vibe_state", &mVibeState);
|
|
||||||
open("default/num_waves", &mEffectCount);
|
|
||||||
open("default/owt_free_space", &mOwtFreeSpace);
|
|
||||||
open("default/f0_comp_enable", &mF0CompEnable);
|
|
||||||
open("default/redc_comp_enable", &mRedcCompEnable);
|
|
||||||
open("default/delay_before_stop_playback_us", &mMinOnOffInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setF0(std::string value) override { return set(value, &mF0); }
|
|
||||||
bool setF0Offset(uint32_t value) override { return set(value, &mF0Offset); }
|
|
||||||
bool setRedc(std::string value) override { return set(value, &mRedc); }
|
|
||||||
bool setQ(std::string value) override { return set(value, &mQ); }
|
|
||||||
bool getEffectCount(uint32_t *value) override { return get(value, &mEffectCount); }
|
|
||||||
bool pollVibeState(uint32_t value, int32_t timeoutMs) override {
|
|
||||||
return poll(value, &mVibeState, timeoutMs);
|
|
||||||
}
|
|
||||||
bool hasOwtFreeSpace() override { return has(mOwtFreeSpace); }
|
|
||||||
bool getOwtFreeSpace(uint32_t *value) override { return get(value, &mOwtFreeSpace); }
|
|
||||||
bool setF0CompEnable(bool value) override { return set(value, &mF0CompEnable); }
|
|
||||||
bool setRedcCompEnable(bool value) override { return set(value, &mRedcCompEnable); }
|
|
||||||
bool setMinOnOffInterval(uint32_t value) override { return set(value, &mMinOnOffInterval); }
|
|
||||||
uint32_t getContextScale() override {
|
|
||||||
return utils::getProperty("persist.vendor.vibrator.hal.context.scale", 100);
|
|
||||||
}
|
|
||||||
bool getContextEnable() override {
|
|
||||||
return utils::getProperty("persist.vendor.vibrator.hal.context.enable", false);
|
|
||||||
}
|
|
||||||
uint32_t getContextSettlingTime() override {
|
|
||||||
return utils::getProperty("persist.vendor.vibrator.hal.context.settlingtime", 3000);
|
|
||||||
}
|
|
||||||
uint32_t getContextCooldownTime() override {
|
|
||||||
return utils::getProperty("persist.vendor.vibrator.hal.context.cooldowntime", 1000);
|
|
||||||
}
|
|
||||||
bool getContextFadeEnable() override {
|
|
||||||
return utils::getProperty("persist.vendor.vibrator.hal.context.fade", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(b/234338136): Need to add the force feedback HW API test cases
|
|
||||||
bool setFFGain(int fd, uint16_t value) override {
|
|
||||||
struct input_event gain = {
|
|
||||||
.type = EV_FF,
|
|
||||||
.code = FF_GAIN,
|
|
||||||
.value = value,
|
|
||||||
};
|
|
||||||
if (write(fd, (const void *)&gain, sizeof(gain)) != sizeof(gain)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool setFFEffect(int fd, struct ff_effect *effect, uint16_t timeoutMs) override {
|
|
||||||
if (((*effect).replay.length != timeoutMs) || (ioctl(fd, EVIOCSFF, effect) < 0)) {
|
|
||||||
ALOGE("setFFEffect fail");
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool setFFPlay(int fd, int8_t index, bool value) override {
|
|
||||||
struct input_event play = {
|
|
||||||
.type = EV_FF,
|
|
||||||
.code = static_cast<uint16_t>(index),
|
|
||||||
.value = value,
|
|
||||||
};
|
|
||||||
if (write(fd, (const void *)&play, sizeof(play)) != sizeof(play)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool getHapticAlsaDevice(int *card, int *device) override {
|
|
||||||
std::string line;
|
|
||||||
std::ifstream myfile(PROC_SND_PCM);
|
|
||||||
if (myfile.is_open()) {
|
|
||||||
while (getline(myfile, line)) {
|
|
||||||
if (line.find(HAPTIC_PCM_DEVICE_SYMBOL) != std::string::npos) {
|
|
||||||
std::stringstream ss(line);
|
|
||||||
std::string currentToken;
|
|
||||||
std::getline(ss, currentToken, ':');
|
|
||||||
sscanf(currentToken.c_str(), "%d-%d", card, device);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
myfile.close();
|
|
||||||
} else {
|
|
||||||
ALOGE("Failed to read file: %s", PROC_SND_PCM);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device) override {
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (enable) {
|
|
||||||
*haptic_pcm = pcm_open(card, device, PCM_OUT, &haptic_nohost_config);
|
|
||||||
if (!pcm_is_ready(*haptic_pcm)) {
|
|
||||||
ALOGE("cannot open pcm_out driver: %s", pcm_get_error(*haptic_pcm));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = pcm_prepare(*haptic_pcm);
|
|
||||||
if (ret < 0) {
|
|
||||||
ALOGE("cannot prepare haptic_pcm: %s", pcm_get_error(*haptic_pcm));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = pcm_start(*haptic_pcm);
|
|
||||||
if (ret < 0) {
|
|
||||||
ALOGE("cannot start haptic_pcm: %s", pcm_get_error(*haptic_pcm));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (*haptic_pcm) {
|
|
||||||
pcm_close(*haptic_pcm);
|
|
||||||
*haptic_pcm = NULL;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fail:
|
|
||||||
pcm_close(*haptic_pcm);
|
|
||||||
*haptic_pcm = NULL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool uploadOwtEffect(int fd, uint8_t *owtData, uint32_t numBytes, struct ff_effect *effect,
|
|
||||||
uint32_t *outEffectIndex, int *status) override {
|
|
||||||
(*effect).u.periodic.custom_len = numBytes / sizeof(uint16_t);
|
|
||||||
delete[] ((*effect).u.periodic.custom_data);
|
|
||||||
(*effect).u.periodic.custom_data = new int16_t[(*effect).u.periodic.custom_len]{0x0000};
|
|
||||||
if ((*effect).u.periodic.custom_data == nullptr) {
|
|
||||||
ALOGE("Failed to allocate memory for custom data\n");
|
|
||||||
*status = EX_NULL_POINTER;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
memcpy((*effect).u.periodic.custom_data, owtData, numBytes);
|
|
||||||
|
|
||||||
if ((*effect).id != -1) {
|
|
||||||
ALOGE("(*effect).id != -1");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create a new OWT waveform to update the PWLE or composite effect. */
|
|
||||||
(*effect).id = -1;
|
|
||||||
if (ioctl(fd, EVIOCSFF, effect) < 0) {
|
|
||||||
ALOGE("Failed to upload effect %d (%d): %s", *outEffectIndex, errno, strerror(errno));
|
|
||||||
delete[] ((*effect).u.periodic.custom_data);
|
|
||||||
*status = EX_ILLEGAL_STATE;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((*effect).id >= FF_MAX_EFFECTS || (*effect).id < 0) {
|
|
||||||
ALOGE("Invalid waveform index after upload OWT effect: %d", (*effect).id);
|
|
||||||
*status = EX_ILLEGAL_ARGUMENT;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*outEffectIndex = (*effect).id;
|
|
||||||
*status = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) override {
|
|
||||||
uint32_t effectCountBefore, effectCountAfter, i, successFlush = 0;
|
|
||||||
|
|
||||||
if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
|
|
||||||
ALOGE("Invalid waveform index for OWT erase: %d", effectIndex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (effectIndex < WAVEFORM_MAX_INDEX) {
|
|
||||||
/* Normal situation. Only erase the effect which we just played. */
|
|
||||||
if (ioctl(fd, EVIOCRMFF, effectIndex) < 0) {
|
|
||||||
ALOGE("Failed to erase effect %d (%d): %s", effectIndex, errno, strerror(errno));
|
|
||||||
}
|
|
||||||
for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < WAVEFORM_MAX_INDEX; i++) {
|
|
||||||
if ((*effect)[i].id == effectIndex) {
|
|
||||||
(*effect)[i].id = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Flush all non-prestored effects of ff-core and driver. */
|
|
||||||
getEffectCount(&effectCountBefore);
|
|
||||||
for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < FF_MAX_EFFECTS; i++) {
|
|
||||||
if (ioctl(fd, EVIOCRMFF, i) >= 0) {
|
|
||||||
successFlush++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getEffectCount(&effectCountAfter);
|
|
||||||
ALOGW("Flushed effects: ff: %d; driver: %d -> %d; success: %d", effectIndex,
|
|
||||||
effectCountBefore, effectCountAfter, successFlush);
|
|
||||||
/* Reset all OWT effect index of HAL. */
|
|
||||||
for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < WAVEFORM_MAX_INDEX; i++) {
|
|
||||||
(*effect)[i].id = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void debug(int fd) override { HwApiBase::debug(fd); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::ofstream mF0;
|
|
||||||
std::ofstream mF0Offset;
|
|
||||||
std::ofstream mRedc;
|
|
||||||
std::ofstream mQ;
|
|
||||||
std::ifstream mEffectCount;
|
|
||||||
std::ifstream mVibeState;
|
|
||||||
std::ifstream mOwtFreeSpace;
|
|
||||||
std::ofstream mF0CompEnable;
|
|
||||||
std::ofstream mRedcCompEnable;
|
|
||||||
std::ofstream mMinOnOffInterval;
|
|
||||||
};
|
|
||||||
|
|
||||||
class HwCal : public Vibrator::HwCal, private HwCalBase {
|
|
||||||
private:
|
|
||||||
static constexpr char VERSION[] = "version";
|
|
||||||
static constexpr char F0_CONFIG[] = "f0_measured";
|
|
||||||
static constexpr char REDC_CONFIG[] = "redc_measured";
|
|
||||||
static constexpr char Q_CONFIG[] = "q_measured";
|
|
||||||
static constexpr char TICK_VOLTAGES_CONFIG[] = "v_tick";
|
|
||||||
static constexpr char CLICK_VOLTAGES_CONFIG[] = "v_click";
|
|
||||||
static constexpr char LONG_VOLTAGES_CONFIG[] = "v_long";
|
|
||||||
|
|
||||||
static constexpr uint32_t VERSION_DEFAULT = 2;
|
|
||||||
static constexpr int32_t DEFAULT_FREQUENCY_SHIFT = 0;
|
|
||||||
static constexpr std::array<uint32_t, 2> V_TICK_DEFAULT = {1, 100};
|
|
||||||
static constexpr std::array<uint32_t, 2> V_CLICK_DEFAULT = {1, 100};
|
|
||||||
static constexpr std::array<uint32_t, 2> V_LONG_DEFAULT = {1, 100};
|
|
||||||
|
|
||||||
public:
|
|
||||||
HwCal() {}
|
|
||||||
|
|
||||||
bool getVersion(uint32_t *value) override {
|
|
||||||
if (getPersist(VERSION, value)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
*value = VERSION_DEFAULT;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool getLongFrequencyShift(int32_t *value) override {
|
|
||||||
return getProperty("long.frequency.shift", value, DEFAULT_FREQUENCY_SHIFT);
|
|
||||||
}
|
|
||||||
bool getF0(std::string *value) override { return getPersist(F0_CONFIG, value); }
|
|
||||||
bool getRedc(std::string *value) override { return getPersist(REDC_CONFIG, value); }
|
|
||||||
bool getQ(std::string *value) override { return getPersist(Q_CONFIG, value); }
|
|
||||||
bool getTickVolLevels(std::array<uint32_t, 2> *value) override {
|
|
||||||
if (getPersist(TICK_VOLTAGES_CONFIG, value)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
*value = V_TICK_DEFAULT;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool getClickVolLevels(std::array<uint32_t, 2> *value) override {
|
|
||||||
if (getPersist(CLICK_VOLTAGES_CONFIG, value)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
*value = V_CLICK_DEFAULT;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool getLongVolLevels(std::array<uint32_t, 2> *value) override {
|
|
||||||
if (getPersist(LONG_VOLTAGES_CONFIG, value)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
*value = V_LONG_DEFAULT;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool isChirpEnabled() override {
|
|
||||||
bool value;
|
|
||||||
getProperty("chirp.enabled", &value, false);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
bool getSupportedPrimitives(uint32_t *value) override {
|
|
||||||
return getProperty("supported_primitives", value, (uint32_t)0);
|
|
||||||
}
|
|
||||||
bool isF0CompEnabled() override {
|
|
||||||
bool value;
|
|
||||||
getProperty("f0.comp.enabled", &value, true);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
bool isRedcCompEnabled() override {
|
|
||||||
bool value;
|
|
||||||
getProperty("redc.comp.enabled", &value, true);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
void debug(int fd) override { HwCalBase::debug(fd); }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace vibrator
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace android
|
|
||||||
} // namespace aidl
|
|
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"presubmit": [
|
|
||||||
{
|
|
||||||
"name": "VibratorHalCs40l26TestSuite",
|
|
||||||
"keywords": [
|
|
||||||
"nextgen"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@@ -1,235 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2021 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <aidl/android/hardware/vibrator/BnVibrator.h>
|
|
||||||
#include <android-base/unique_fd.h>
|
|
||||||
#include <linux/input.h>
|
|
||||||
#include <tinyalsa/asoundlib.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <fstream>
|
|
||||||
#include <future>
|
|
||||||
#include <ctime>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace android {
|
|
||||||
namespace hardware {
|
|
||||||
namespace vibrator {
|
|
||||||
|
|
||||||
class Vibrator : public BnVibrator {
|
|
||||||
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(std::string value) = 0;
|
|
||||||
// Stores the frequency offset for long vibrations.
|
|
||||||
virtual bool setF0Offset(uint32_t value) = 0;
|
|
||||||
// Stores the LRA series resistance to be used for click
|
|
||||||
// compensation.
|
|
||||||
virtual bool setRedc(std::string value) = 0;
|
|
||||||
// Stores the LRA Q factor to be used for Q-dependent waveform
|
|
||||||
// selection.
|
|
||||||
virtual bool setQ(std::string value) = 0;
|
|
||||||
// Reports the number of effect waveforms loaded in firmware.
|
|
||||||
virtual bool getEffectCount(uint32_t *value) = 0;
|
|
||||||
// Blocks until timeout or vibrator reaches desired state
|
|
||||||
// (2 = ASP enabled, 1 = haptic enabled, 0 = disabled).
|
|
||||||
virtual bool pollVibeState(uint32_t value, int32_t timeoutMs = -1) = 0;
|
|
||||||
// Reports whether getOwtFreeSpace() is supported.
|
|
||||||
virtual bool hasOwtFreeSpace() = 0;
|
|
||||||
// Reports the available OWT bytes.
|
|
||||||
virtual bool getOwtFreeSpace(uint32_t *value) = 0;
|
|
||||||
// Enables/Disables F0 compensation enable status
|
|
||||||
virtual bool setF0CompEnable(bool value) = 0;
|
|
||||||
// Enables/Disables Redc compensation enable status
|
|
||||||
virtual bool setRedcCompEnable(bool value) = 0;
|
|
||||||
// Stores the minumun delay time between playback and stop effects.
|
|
||||||
virtual bool setMinOnOffInterval(uint32_t value) = 0;
|
|
||||||
// Gets the scaling factor for contextual haptic events.
|
|
||||||
virtual uint32_t getContextScale() = 0;
|
|
||||||
// Gets the enable status for contextual haptic events.
|
|
||||||
virtual bool getContextEnable() = 0;
|
|
||||||
// Gets the settling time for contextual haptic events.
|
|
||||||
// This will allow the device to stay face up for the duration given,
|
|
||||||
// even if InMotion events were detected.
|
|
||||||
virtual uint32_t getContextSettlingTime() = 0;
|
|
||||||
// Gets the cooldown time for contextual haptic events.
|
|
||||||
// This is used to avoid changing the scale of close playback events.
|
|
||||||
virtual uint32_t getContextCooldownTime() = 0;
|
|
||||||
// Checks the enable status for contextual haptics fade feature. When enabled
|
|
||||||
// this feature will cause the scaling factor to fade back up to max over
|
|
||||||
// the setting time set, instead of instantaneously changing it back to max.
|
|
||||||
virtual bool getContextFadeEnable() = 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 setFFGain(int fd, uint16_t value) = 0;
|
|
||||||
// Create/modify custom effects for all physical waveforms.
|
|
||||||
virtual bool setFFEffect(int fd, struct ff_effect *effect, uint16_t timeoutMs) = 0;
|
|
||||||
// Activates/deactivates the effect index after setFFGain() and setFFEffect().
|
|
||||||
virtual bool setFFPlay(int fd, int8_t index, bool value) = 0;
|
|
||||||
// Get the Alsa device for the audio coupled haptics effect
|
|
||||||
virtual bool getHapticAlsaDevice(int *card, int *device) = 0;
|
|
||||||
// Set haptics PCM amplifier before triggering audio haptics feature
|
|
||||||
virtual bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card,
|
|
||||||
int device) = 0;
|
|
||||||
// Set OWT waveform for compose or compose PWLE request
|
|
||||||
virtual bool uploadOwtEffect(int fd, uint8_t *owtData, uint32_t numBytes,
|
|
||||||
struct ff_effect *effect, uint32_t *outEffectIndex,
|
|
||||||
int *status) = 0;
|
|
||||||
// Erase OWT waveform
|
|
||||||
virtual bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) = 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;
|
|
||||||
// Obtain the calibration version
|
|
||||||
virtual bool getVersion(uint32_t *value) = 0;
|
|
||||||
// Obtains the LRA resonant frequency to be used for PWLE playback
|
|
||||||
// and click compensation.
|
|
||||||
virtual bool getF0(std::string *value) = 0;
|
|
||||||
// Obtains the LRA series resistance to be used for click
|
|
||||||
// compensation.
|
|
||||||
virtual bool getRedc(std::string *value) = 0;
|
|
||||||
// Obtains the LRA Q factor to be used for Q-dependent waveform
|
|
||||||
// selection.
|
|
||||||
virtual bool getQ(std::string *value) = 0;
|
|
||||||
// Obtains frequency shift for long vibrations.
|
|
||||||
virtual bool getLongFrequencyShift(int32_t *value) = 0;
|
|
||||||
// Obtains the v0/v1(min/max) voltage levels to be applied for
|
|
||||||
// tick/click/long in units of 1%.
|
|
||||||
virtual bool getTickVolLevels(std::array<uint32_t, 2> *value) = 0;
|
|
||||||
virtual bool getClickVolLevels(std::array<uint32_t, 2> *value) = 0;
|
|
||||||
virtual bool getLongVolLevels(std::array<uint32_t, 2> *value) = 0;
|
|
||||||
// Checks if the chirp feature is enabled.
|
|
||||||
virtual bool isChirpEnabled() = 0;
|
|
||||||
// Obtains the supported primitive effects.
|
|
||||||
virtual bool getSupportedPrimitives(uint32_t *value) = 0;
|
|
||||||
// Checks if the f0 compensation feature needs to be enabled.
|
|
||||||
virtual bool isF0CompEnabled() = 0;
|
|
||||||
// Checks if the redc compensation feature needs to be enabled.
|
|
||||||
virtual bool isRedcCompEnabled() = 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);
|
|
||||||
|
|
||||||
ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return) override;
|
|
||||||
ndk::ScopedAStatus off() override;
|
|
||||||
ndk::ScopedAStatus on(int32_t timeoutMs,
|
|
||||||
const std::shared_ptr<IVibratorCallback> &callback) override;
|
|
||||||
ndk::ScopedAStatus perform(Effect effect, EffectStrength strength,
|
|
||||||
const std::shared_ptr<IVibratorCallback> &callback,
|
|
||||||
int32_t *_aidl_return) override;
|
|
||||||
ndk::ScopedAStatus getSupportedEffects(std::vector<Effect> *_aidl_return) override;
|
|
||||||
ndk::ScopedAStatus setAmplitude(float amplitude) override;
|
|
||||||
ndk::ScopedAStatus setExternalControl(bool enabled) override;
|
|
||||||
ndk::ScopedAStatus getCompositionDelayMax(int32_t *maxDelayMs);
|
|
||||||
ndk::ScopedAStatus getCompositionSizeMax(int32_t *maxSize);
|
|
||||||
ndk::ScopedAStatus getSupportedPrimitives(std::vector<CompositePrimitive> *supported) override;
|
|
||||||
ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive,
|
|
||||||
int32_t *durationMs) override;
|
|
||||||
ndk::ScopedAStatus compose(const std::vector<CompositeEffect> &composite,
|
|
||||||
const std::shared_ptr<IVibratorCallback> &callback) override;
|
|
||||||
ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector<Effect> *_aidl_return) override;
|
|
||||||
ndk::ScopedAStatus alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) override;
|
|
||||||
ndk::ScopedAStatus alwaysOnDisable(int32_t id) override;
|
|
||||||
ndk::ScopedAStatus getResonantFrequency(float *resonantFreqHz) override;
|
|
||||||
ndk::ScopedAStatus getQFactor(float *qFactor) override;
|
|
||||||
ndk::ScopedAStatus getFrequencyResolution(float *freqResolutionHz) override;
|
|
||||||
ndk::ScopedAStatus getFrequencyMinimum(float *freqMinimumHz) override;
|
|
||||||
ndk::ScopedAStatus getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) override;
|
|
||||||
ndk::ScopedAStatus getPwlePrimitiveDurationMax(int32_t *durationMs) override;
|
|
||||||
ndk::ScopedAStatus getPwleCompositionSizeMax(int32_t *maxSize) override;
|
|
||||||
ndk::ScopedAStatus getSupportedBraking(std::vector<Braking> *supported) override;
|
|
||||||
ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
|
|
||||||
const std::shared_ptr<IVibratorCallback> &callback) override;
|
|
||||||
|
|
||||||
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, struct dspmem_chunk *ch,
|
|
||||||
const std::shared_ptr<IVibratorCallback> &callback);
|
|
||||||
// set 'amplitude' based on an arbitrary scale determined by 'maximum'
|
|
||||||
ndk::ScopedAStatus setEffectAmplitude(float amplitude, float maximum, bool scalable);
|
|
||||||
ndk::ScopedAStatus setGlobalAmplitude(bool set);
|
|
||||||
// 'simple' effects are those precompiled and loaded into the controller
|
|
||||||
ndk::ScopedAStatus getSimpleDetails(Effect effect, EffectStrength strength,
|
|
||||||
uint32_t *outEffectIndex, uint32_t *outTimeMs,
|
|
||||||
uint32_t *outVolLevel);
|
|
||||||
// 'compound' effects are those composed by stringing multiple 'simple' effects
|
|
||||||
ndk::ScopedAStatus getCompoundDetails(Effect effect, EffectStrength strength,
|
|
||||||
uint32_t *outTimeMs, struct dspmem_chunk *outCh);
|
|
||||||
ndk::ScopedAStatus getPrimitiveDetails(CompositePrimitive primitive, uint32_t *outEffectIndex);
|
|
||||||
ndk::ScopedAStatus performEffect(Effect effect, EffectStrength strength,
|
|
||||||
const std::shared_ptr<IVibratorCallback> &callback,
|
|
||||||
int32_t *outTimeMs);
|
|
||||||
ndk::ScopedAStatus performEffect(uint32_t effectIndex, uint32_t volLevel,
|
|
||||||
struct dspmem_chunk *ch,
|
|
||||||
const std::shared_ptr<IVibratorCallback> &callback);
|
|
||||||
ndk::ScopedAStatus setPwle(const std::string &pwleQueue);
|
|
||||||
bool isUnderExternalControl();
|
|
||||||
void waitForComplete(std::shared_ptr<IVibratorCallback> &&callback);
|
|
||||||
uint32_t intensityToVolLevel(float intensity, uint32_t effectIndex);
|
|
||||||
bool findHapticAlsaDevice(int *card, int *device);
|
|
||||||
bool hasHapticAlsaDevice();
|
|
||||||
bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device);
|
|
||||||
uint16_t amplitudeToScale(float amplitude, float maximum, bool scalable);
|
|
||||||
void updateContext();
|
|
||||||
|
|
||||||
std::unique_ptr<HwApi> mHwApi;
|
|
||||||
std::unique_ptr<HwCal> mHwCal;
|
|
||||||
uint32_t mF0Offset;
|
|
||||||
std::array<uint32_t, 2> mTickEffectVol;
|
|
||||||
std::array<uint32_t, 2> mClickEffectVol;
|
|
||||||
std::array<uint32_t, 2> mLongEffectVol;
|
|
||||||
std::vector<ff_effect> mFfEffects;
|
|
||||||
std::vector<uint32_t> mEffectDurations;
|
|
||||||
std::future<void> mAsyncHandle;
|
|
||||||
::android::base::unique_fd mInputFd;
|
|
||||||
int8_t mActiveId{-1};
|
|
||||||
struct pcm *mHapticPcm;
|
|
||||||
int mCard;
|
|
||||||
int mDevice;
|
|
||||||
bool mHasHapticAlsaDevice{false};
|
|
||||||
bool mIsUnderExternalControl;
|
|
||||||
float mLongEffectScale = 1.0;
|
|
||||||
bool mIsChirpEnabled;
|
|
||||||
uint32_t mScaleTime;
|
|
||||||
bool mFadeEnable;
|
|
||||||
uint32_t mScalingFactor;
|
|
||||||
uint32_t mScaleCooldown;
|
|
||||||
bool mContextEnable;
|
|
||||||
uint32_t mSupportedPrimitivesBits = 0x0;
|
|
||||||
std::vector<CompositePrimitive> mSupportedPrimitives;
|
|
||||||
bool mConfigHapticAlsaDeviceDone{false};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace vibrator
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace android
|
|
||||||
} // namespace aidl
|
|
@@ -1,47 +0,0 @@
|
|||||||
on property:vendor.all.modules.ready=1
|
|
||||||
wait /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_cal_time_ms
|
|
||||||
|
|
||||||
mkdir /mnt/vendor/persist/haptics 0770 system system
|
|
||||||
chmod 770 /mnt/vendor/persist/haptics
|
|
||||||
chmod 440 /mnt/vendor/persist/haptics/cs40l26.cal
|
|
||||||
chown system system /mnt/vendor/persist/haptics
|
|
||||||
chown system system /mnt/vendor/persist/haptics/cs40l26.cal
|
|
||||||
|
|
||||||
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/f0_stored
|
|
||||||
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/q_stored
|
|
||||||
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_stored
|
|
||||||
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/vibe_state
|
|
||||||
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/num_waves
|
|
||||||
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_offset
|
|
||||||
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/owt_free_space
|
|
||||||
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_comp_enable
|
|
||||||
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/redc_comp_enable
|
|
||||||
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/delay_before_stop_playback_us
|
|
||||||
|
|
||||||
enable vendor.vibrator.cs40l26
|
|
||||||
|
|
||||||
service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-private-lynx
|
|
||||||
class hal
|
|
||||||
user system
|
|
||||||
group system input context_hub
|
|
||||||
|
|
||||||
setenv INPUT_EVENT_NAME cs40l26_input
|
|
||||||
setenv INPUT_EVENT_PATH /dev/input/event*
|
|
||||||
setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
|
|
||||||
setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal
|
|
||||||
|
|
||||||
setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a/
|
|
||||||
setenv HWAPI_DEBUG_PATHS "
|
|
||||||
calibration/f0_stored
|
|
||||||
calibration/redc_stored
|
|
||||||
calibration/q_stored
|
|
||||||
default/vibe_state
|
|
||||||
default/num_waves
|
|
||||||
default/f0_offset
|
|
||||||
default/owt_free_space
|
|
||||||
default/f0_comp_enable
|
|
||||||
default/redc_comp_enable
|
|
||||||
default/delay_before_stop_playback_us
|
|
||||||
"
|
|
||||||
|
|
||||||
disabled
|
|
@@ -1,7 +0,0 @@
|
|||||||
<manifest version="1.0" type="device">
|
|
||||||
<hal format="aidl">
|
|
||||||
<name>android.hardware.vibrator</name>
|
|
||||||
<version>2</version>
|
|
||||||
<fqname>IVibrator/default</fqname>
|
|
||||||
</hal>
|
|
||||||
</manifest>
|
|
@@ -1,6 +0,0 @@
|
|||||||
PRODUCT_PACKAGES += \
|
|
||||||
android.hardware.vibrator-service.cs40l26-private-lynx
|
|
||||||
|
|
||||||
BOARD_SEPOLICY_DIRS += \
|
|
||||||
hardware/google/pixel-sepolicy/vibrator/common \
|
|
||||||
hardware/google/pixel-sepolicy/vibrator/cs40l26
|
|
@@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 Google LLC. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* 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 <chre_host/host_protocol_host.h>
|
|
||||||
#include <chre_host/socket_client.h>
|
|
||||||
|
|
||||||
#include "proto/capo.pb.h"
|
|
||||||
|
|
||||||
using android::sp;
|
|
||||||
using android::chre::HostProtocolHost;
|
|
||||||
using android::chre::IChreMessageHandlers;
|
|
||||||
using android::chre::SocketClient;
|
|
||||||
|
|
||||||
// following convention of CHRE code.
|
|
||||||
namespace fbs = ::chre::fbs;
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace chre {
|
|
||||||
|
|
||||||
#define NS_FROM_MS(x) ((x)*1000000)
|
|
||||||
|
|
||||||
struct CapoMDParams {
|
|
||||||
uint64_t still_time_threshold_ns;
|
|
||||||
uint32_t window_width_ns;
|
|
||||||
float motion_confidence_threshold;
|
|
||||||
float still_confidence_threshold;
|
|
||||||
float var_threshold;
|
|
||||||
float var_threshold_delta;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CapoDetector : public android::chre::SocketClient::ICallbacks,
|
|
||||||
public android::chre::IChreMessageHandlers,
|
|
||||||
public android::chre::SocketClient {
|
|
||||||
public:
|
|
||||||
// Typedef declaration for callback function.
|
|
||||||
typedef std::function<void(uint8_t)> cb_fn_t;
|
|
||||||
|
|
||||||
// Called when initializing connection with CHRE socket.
|
|
||||||
static android::sp<CapoDetector> start();
|
|
||||||
// Called when the socket is successfully (re-)connected.
|
|
||||||
// Reset the position and try to send NanoappList request.
|
|
||||||
void onConnected() override;
|
|
||||||
// Called when we have failed to (re-)connect the socket after many attempts
|
|
||||||
// and are giving up.
|
|
||||||
void onConnectionAborted() override;
|
|
||||||
// Invoked when the socket is disconnected, and this connection loss
|
|
||||||
// was not the result of an explicit call to disconnect().
|
|
||||||
// Reset the position while disconnecting.
|
|
||||||
void onDisconnected() override;
|
|
||||||
// Decode unix socket msgs to CHRE messages, and call the appropriate
|
|
||||||
// callback depending on the CHRE message.
|
|
||||||
void onMessageReceived(const void *data, size_t length) override;
|
|
||||||
// Listen for messages from capo nanoapp and handle the message.
|
|
||||||
void handleNanoappMessage(const ::chre::fbs::NanoappMessageT &message) override;
|
|
||||||
// Handle the response of a NanoappList request.
|
|
||||||
// Ensure that capo nanoapp is running.
|
|
||||||
void handleNanoappListResponse(const ::chre::fbs::NanoappListResponseT &response) override;
|
|
||||||
// Send enabling message to the nanoapp.
|
|
||||||
void enable();
|
|
||||||
|
|
||||||
// Get last carried position type.
|
|
||||||
uint8_t getCarriedPosition() { return last_position_type_; }
|
|
||||||
// Get the host endpoint.
|
|
||||||
uint16_t getHostEndPoint() { return kHostEndpoint; }
|
|
||||||
// Get the capo nanoapp ID.
|
|
||||||
uint64_t getNanoppAppId() { return kCapoNanoappId; }
|
|
||||||
// Set up callback_func_ if needed.
|
|
||||||
void setCallback(cb_fn_t cb) { callback_func_ = cb; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Nanoapp ID of capo, ref: go/nanoapp-id-tracker.
|
|
||||||
static constexpr uint64_t kCapoNanoappId = 0x476f6f676c001020ULL;
|
|
||||||
// String of socket name for connecting chre.
|
|
||||||
static constexpr char kChreSocketName[] = "chre";
|
|
||||||
// The host endpoint we use when sending message.
|
|
||||||
// Set with 0x9020 based on 0x8000 AND capo_app_id(1020).
|
|
||||||
// Ref: go/host-endpoint-id-tracker.
|
|
||||||
static constexpr uint16_t kHostEndpoint = 0x9020;
|
|
||||||
// Using for hal layer callback function.
|
|
||||||
cb_fn_t callback_func_ = nullptr;
|
|
||||||
// Last carried position received from the nano app
|
|
||||||
capo::PositionType last_position_type_ = capo::PositionType::UNKNOWN;
|
|
||||||
// Motion detector parameters for host-driven capo config
|
|
||||||
const struct CapoMDParams mCapoDetectorMDParameters {
|
|
||||||
.still_time_threshold_ns = NS_FROM_MS(500),
|
|
||||||
.window_width_ns = NS_FROM_MS(100),
|
|
||||||
.motion_confidence_threshold = 0.98f,
|
|
||||||
.still_confidence_threshold = 0.99f,
|
|
||||||
.var_threshold = 0.0125f,
|
|
||||||
.var_threshold_delta = 0.0125f,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace chre
|
|
||||||
} // namespace android
|
|
@@ -1,148 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package capo;
|
|
||||||
|
|
||||||
// The message types used in capo nanoapp. Some of them are H2C
|
|
||||||
// (Host-To-CHRE) and others are C2H (CHRE-To-Host). One message type must be
|
|
||||||
// either H2C or C2H. Each message type can choose to have payload or not.
|
|
||||||
enum MessageType {
|
|
||||||
// Explicitly prevents 0 from being used as a valid message type.
|
|
||||||
// Doing so protects from obscure bugs caused by default-initialized values.
|
|
||||||
INVALID = 0;
|
|
||||||
|
|
||||||
// Detector configuration related message start from 100.
|
|
||||||
// Signal for host to acknowledge the notification.
|
|
||||||
// It contains AckNotification payload.
|
|
||||||
ACK_NOTIFICATION = 100;
|
|
||||||
|
|
||||||
// Signal to enable the carried position detector for device. No payload.
|
|
||||||
ENABLE_DETECTOR = 101;
|
|
||||||
|
|
||||||
// Signal to disable the carried position detector for device. No payload.
|
|
||||||
DISABLE_DETECTOR = 102;
|
|
||||||
|
|
||||||
// Signal to request most recent carried position detector state. No payload.
|
|
||||||
REQUEST_UPDATE = 103;
|
|
||||||
|
|
||||||
// Signal to force carried position detector to refresh state. No payload.
|
|
||||||
FORCE_UPDATE = 104;
|
|
||||||
|
|
||||||
// Configure the detector with desired parameters. ConfigureDetector payload.
|
|
||||||
CONFIGURE_DETECTOR = 105;
|
|
||||||
|
|
||||||
// Position Detection related message start from 200.
|
|
||||||
// Signal while carried position of device detected.
|
|
||||||
// It contains PositionDetected payload.
|
|
||||||
POSITION_DETECTED = 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notification Type.
|
|
||||||
enum NotificationType {
|
|
||||||
// Explicitly prevents 0 from being used as a valid notification type.
|
|
||||||
// Doing so protects from obscure bugs caused by default-initialized values.
|
|
||||||
INVALID_NOTIFICATION = 0;
|
|
||||||
|
|
||||||
// Notification of enabling the carried position detector for device.
|
|
||||||
ENABLE_NOTIFICATION = 1;
|
|
||||||
|
|
||||||
// Notification of disabling the carried position detector for device.
|
|
||||||
DISABLE_NOTIFICATION = 2;
|
|
||||||
|
|
||||||
// Notification of request update from the carried position detector.
|
|
||||||
REQUEST_UPDATE_NOTIFICATION = 3;
|
|
||||||
|
|
||||||
// Notification of force update from the carried position detector.
|
|
||||||
FORCE_UPDATE_NOTIFICATION = 4;
|
|
||||||
|
|
||||||
// Notification of configure message.
|
|
||||||
CONFIGURE_NOTIFICATION = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This message type used for host to acknowledge the notification.
|
|
||||||
message AckNotification {
|
|
||||||
// Sent a notification type for host to acknowledge.
|
|
||||||
NotificationType notification_type = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position type.
|
|
||||||
enum PositionType {
|
|
||||||
// Explicitly prevents 0 from being used as a valid carried position type.
|
|
||||||
// Doing so protects from obscure bugs caused by default-initialized values.
|
|
||||||
UNKNOWN = 0;
|
|
||||||
|
|
||||||
// Carried position while device is in motion.
|
|
||||||
IN_MOTION = 1;
|
|
||||||
|
|
||||||
// Carried position while device is on table and faces up.
|
|
||||||
ON_TABLE_FACE_UP = 2;
|
|
||||||
|
|
||||||
// Carried position while device is on table and faces down.
|
|
||||||
ON_TABLE_FACE_DOWN = 3;
|
|
||||||
|
|
||||||
// Carried position while device is stationary in unknown orientation.
|
|
||||||
STATIONARY_UNKNOWN = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This message type used to notify host a position was a detected.
|
|
||||||
message PositionDetected {
|
|
||||||
// Sent a position type that is defined in PositionTypes.
|
|
||||||
PositionType position_type = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Predefined configurations for detector.
|
|
||||||
enum ConfigPresetType {
|
|
||||||
// Explicitly prevents 0 from being used as a valid type.
|
|
||||||
// Doing so protects from obscure bugs caused by default-initialized values.
|
|
||||||
CONFIG_PRESET_UNSPECIFIED = 0;
|
|
||||||
|
|
||||||
// Default preset.
|
|
||||||
CONFIG_PRESET_DEFAULT = 1;
|
|
||||||
|
|
||||||
// Preset for sticky-stationary behavior.
|
|
||||||
CONFIG_PRESET_STICKY_STATIONARY = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ConfigureDetector {
|
|
||||||
// Ref: cs/location/lbs/contexthub/nanoapps/motiondetector/motion_detector.h
|
|
||||||
message ConfigData {
|
|
||||||
// These algo parameters are exposed to enable tuning via server flags.
|
|
||||||
// The amount of time that the algorithm's computed stillness confidence
|
|
||||||
// must exceed still_confidence_threshold before entering the stationary
|
|
||||||
// state. Increasing this value will make the algorithm take longer to
|
|
||||||
// transition from the in motion state to the stationary state.
|
|
||||||
uint64 still_time_threshold_nanosecond = 1;
|
|
||||||
|
|
||||||
// The amount of time in which the variance should be averaged. Increasing
|
|
||||||
// this value will effectively smooth the input data, making the algorithm
|
|
||||||
// less likely to transition between states.
|
|
||||||
uint32 window_width_nanosecond = 2;
|
|
||||||
|
|
||||||
// The required confidence that the device is in motion before entering the
|
|
||||||
// motion state. Valid range is [0.0, 1.0], where 1.0 indicates that the
|
|
||||||
// algorithm must be 100% certain that the device is moving before entering
|
|
||||||
// the motion state. If the Instant Motion sensor is triggered, this value
|
|
||||||
// is ignored and the algorithm is immediately transitioned into the in
|
|
||||||
// motion state.
|
|
||||||
float motion_confidence_threshold = 3;
|
|
||||||
|
|
||||||
// The required confidence that the device is stationary before entering the
|
|
||||||
// stationary state. Valid range is [0.0, 1.0], where 1.0 indicates that the
|
|
||||||
// algorithm must be 100% certain that the device is stationary before
|
|
||||||
// entering the stationary state.
|
|
||||||
float still_confidence_threshold = 4;
|
|
||||||
|
|
||||||
// The variance threshold for the StillnessDetector algorithm. Increasing
|
|
||||||
// this value causes the algorithm to be less likely to detect motion.
|
|
||||||
float var_threshold = 5;
|
|
||||||
|
|
||||||
// The variance threshold delta for the StillnessDetector algorithm about
|
|
||||||
// which the stationary confidence is calculated. Valid range is
|
|
||||||
// [0.0, var_threshold].
|
|
||||||
float var_threshold_delta = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
oneof type {
|
|
||||||
ConfigPresetType preset_type = 1;
|
|
||||||
ConfigData config_data = 2;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2021 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/binder_manager.h>
|
|
||||||
#include <android/binder_process.h>
|
|
||||||
#include <binder/IServiceManager.h>
|
|
||||||
#include <binder/ProcessState.h>
|
|
||||||
#include <log/log.h>
|
|
||||||
|
|
||||||
#include "Hardware.h"
|
|
||||||
#include "Vibrator.h"
|
|
||||||
|
|
||||||
using ::aidl::android::hardware::vibrator::HwApi;
|
|
||||||
using ::aidl::android::hardware::vibrator::HwCal;
|
|
||||||
using ::aidl::android::hardware::vibrator::Vibrator;
|
|
||||||
using ::android::defaultServiceManager;
|
|
||||||
using ::android::ProcessState;
|
|
||||||
using ::android::sp;
|
|
||||||
using ::android::String16;
|
|
||||||
|
|
||||||
#if !defined(VIBRATOR_NAME)
|
|
||||||
#define VIBRATOR_NAME "default"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
auto svc = ndk::SharedRefBase::make<Vibrator>(std::make_unique<HwApi>(),
|
|
||||||
std::make_unique<HwCal>());
|
|
||||||
const auto svcName = std::string() + svc->descriptor + "/" + VIBRATOR_NAME;
|
|
||||||
|
|
||||||
ProcessState::initWithDriver("/dev/vndbinder");
|
|
||||||
|
|
||||||
auto svcBinder = svc->asBinder();
|
|
||||||
binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str());
|
|
||||||
LOG_ALWAYS_FATAL_IF(status != STATUS_OK);
|
|
||||||
|
|
||||||
ProcessState::self()->setThreadPoolMaxThreadCount(1);
|
|
||||||
ProcessState::self()->startThreadPool();
|
|
||||||
|
|
||||||
ABinderProcess_setThreadPoolMaxThreadCount(0);
|
|
||||||
ABinderProcess_joinThreadPool();
|
|
||||||
|
|
||||||
return EXIT_FAILURE; // should not reach
|
|
||||||
}
|
|
@@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (C) 2022 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.
|
|
||||||
|
|
||||||
package {
|
|
||||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_test {
|
|
||||||
name: "VibratorHalCs40l26TestSuitePrivateLynx",
|
|
||||||
defaults: ["VibratorHalCs40l26TestDefaultsPrivateLynx"],
|
|
||||||
srcs: [
|
|
||||||
"test-hwcal.cpp",
|
|
||||||
"test-hwapi.cpp",
|
|
||||||
"test-vibrator.cpp",
|
|
||||||
],
|
|
||||||
static_libs: [
|
|
||||||
"libc++fs",
|
|
||||||
"libgmock",
|
|
||||||
],
|
|
||||||
shared_libs: [
|
|
||||||
"libbase",
|
|
||||||
],
|
|
||||||
}
|
|
@@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 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 <aidl/android/hardware/vibrator/BnVibratorCallback.h>
|
|
||||||
|
|
||||||
#include "Vibrator.h"
|
|
||||||
|
|
||||||
class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
|
|
||||||
public:
|
|
||||||
MOCK_METHOD0(destructor, void());
|
|
||||||
MOCK_METHOD1(setF0, bool(std::string value));
|
|
||||||
MOCK_METHOD1(setF0Offset, bool(uint32_t value));
|
|
||||||
MOCK_METHOD1(setRedc, bool(std::string value));
|
|
||||||
MOCK_METHOD1(setQ, bool(std::string value));
|
|
||||||
MOCK_METHOD1(getEffectCount, bool(uint32_t *value));
|
|
||||||
MOCK_METHOD2(pollVibeState, bool(uint32_t value, int32_t timeoutMs));
|
|
||||||
MOCK_METHOD0(hasOwtFreeSpace, bool());
|
|
||||||
MOCK_METHOD1(getOwtFreeSpace, bool(uint32_t *value));
|
|
||||||
MOCK_METHOD1(setF0CompEnable, bool(bool value));
|
|
||||||
MOCK_METHOD1(setRedcCompEnable, bool(bool value));
|
|
||||||
MOCK_METHOD1(setMinOnOffInterval, bool(uint32_t value));
|
|
||||||
MOCK_METHOD0(getContextScale, uint32_t());
|
|
||||||
MOCK_METHOD0(getContextEnable, bool());
|
|
||||||
MOCK_METHOD0(getContextSettlingTime, uint32_t());
|
|
||||||
MOCK_METHOD0(getContextCooldownTime, uint32_t());
|
|
||||||
MOCK_METHOD0(getContextFadeEnable, bool());
|
|
||||||
MOCK_METHOD2(setFFGain, bool(int fd, uint16_t value));
|
|
||||||
MOCK_METHOD3(setFFEffect, bool(int fd, struct ff_effect *effect, uint16_t timeoutMs));
|
|
||||||
MOCK_METHOD3(setFFPlay, bool(int fd, int8_t index, bool value));
|
|
||||||
MOCK_METHOD2(getHapticAlsaDevice, bool(int *card, int *device));
|
|
||||||
MOCK_METHOD4(setHapticPcmAmp, bool(struct pcm **haptic_pcm, bool enable, int card, int device));
|
|
||||||
MOCK_METHOD6(uploadOwtEffect,
|
|
||||||
bool(int fd, uint8_t *owtData, uint32_t numBytes, struct ff_effect *effect,
|
|
||||||
uint32_t *outEffectIndex, int *status));
|
|
||||||
MOCK_METHOD3(eraseOwtEffect, bool(int fd, int8_t effectIndex, std::vector<ff_effect> *effect));
|
|
||||||
MOCK_METHOD1(debug, void(int fd));
|
|
||||||
|
|
||||||
~MockApi() override { destructor(); };
|
|
||||||
};
|
|
||||||
|
|
||||||
class MockCal : public ::aidl::android::hardware::vibrator::Vibrator::HwCal {
|
|
||||||
public:
|
|
||||||
MOCK_METHOD0(destructor, void());
|
|
||||||
MOCK_METHOD1(getVersion, bool(uint32_t *value));
|
|
||||||
MOCK_METHOD1(getF0, bool(std::string &value));
|
|
||||||
MOCK_METHOD1(getRedc, bool(std::string &value));
|
|
||||||
MOCK_METHOD1(getQ, bool(std::string &value));
|
|
||||||
MOCK_METHOD1(getLongFrequencyShift, bool(int32_t *value));
|
|
||||||
MOCK_METHOD1(getTickVolLevels, bool(std::array<uint32_t, 2> *value));
|
|
||||||
MOCK_METHOD1(getClickVolLevels, bool(std::array<uint32_t, 2> *value));
|
|
||||||
MOCK_METHOD1(getLongVolLevels, bool(std::array<uint32_t, 2> *value));
|
|
||||||
MOCK_METHOD0(isChirpEnabled, bool());
|
|
||||||
MOCK_METHOD1(getSupportedPrimitives, bool(uint32_t *value));
|
|
||||||
MOCK_METHOD0(isF0CompEnabled, bool());
|
|
||||||
MOCK_METHOD0(isRedcCompEnabled, bool());
|
|
||||||
MOCK_METHOD1(debug, void(int fd));
|
|
||||||
|
|
||||||
~MockCal() override { destructor(); };
|
|
||||||
// b/132668253: Workaround gMock Compilation Issue
|
|
||||||
bool getF0(std::string *value) { return getF0(*value); }
|
|
||||||
bool getRedc(std::string *value) { return getRedc(*value); }
|
|
||||||
bool getQ(std::string *value) { return getQ(*value); }
|
|
||||||
};
|
|
||||||
|
|
||||||
class MockVibratorCallback : public aidl::android::hardware::vibrator::BnVibratorCallback {
|
|
||||||
public:
|
|
||||||
MOCK_METHOD(ndk::ScopedAStatus, onComplete, ());
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // ANDROID_HARDWARE_VIBRATOR_TEST_MOCKS_H
|
|
@@ -1,288 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 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 <cutils/fs.h>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include "Hardware.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace android {
|
|
||||||
namespace hardware {
|
|
||||||
namespace vibrator {
|
|
||||||
|
|
||||||
using ::testing::Test;
|
|
||||||
using ::testing::TestParamInfo;
|
|
||||||
using ::testing::ValuesIn;
|
|
||||||
using ::testing::WithParamInterface;
|
|
||||||
|
|
||||||
class HwApiTest : public Test {
|
|
||||||
private:
|
|
||||||
static constexpr const char *FILE_NAMES[]{
|
|
||||||
"calibration/f0_stored",
|
|
||||||
"default/f0_offset",
|
|
||||||
"calibration/redc_stored",
|
|
||||||
"calibration/q_stored",
|
|
||||||
"default/f0_comp_enable",
|
|
||||||
"default/redc_comp_enable",
|
|
||||||
"default/owt_free_space",
|
|
||||||
"default/num_waves",
|
|
||||||
"default/delay_before_stop_playback_us",
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
void SetUp() override {
|
|
||||||
std::string prefix;
|
|
||||||
for (auto n : FILE_NAMES) {
|
|
||||||
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};
|
|
||||||
mFileMap[name] = path;
|
|
||||||
}
|
|
||||||
prefix = std::filesystem::path(mFilesDir.path) / "";
|
|
||||||
setenv("HWAPI_PATH_PREFIX", prefix.c_str(), true);
|
|
||||||
mHwApi = std::make_unique<HwApi>();
|
|
||||||
|
|
||||||
for (auto n : FILE_NAMES) {
|
|
||||||
auto name = std::filesystem::path(n);
|
|
||||||
auto path = std::filesystem::path(mEmptyDir.path) / name;
|
|
||||||
}
|
|
||||||
prefix = std::filesystem::path(mEmptyDir.path) / "";
|
|
||||||
setenv("HWAPI_PATH_PREFIX", prefix.c_str(), true);
|
|
||||||
mNoApi = std::make_unique<HwApi>();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TearDown() override { verifyContents(); }
|
|
||||||
|
|
||||||
static auto ParamNameFixup(std::string str) {
|
|
||||||
std::replace(str.begin(), str.end(), '/', '_');
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// Set expected file content for a test.
|
|
||||||
template <typename T>
|
|
||||||
void expectContent(const std::string &name, const T &value) {
|
|
||||||
mExpectedContent[name] << value << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set actual file content for an input test.
|
|
||||||
template <typename T>
|
|
||||||
void updateContent(const std::string &name, const T &value) {
|
|
||||||
std::ofstream(mFileMap[name]) << value << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void expectAndUpdateContent(const std::string &name, const T &value) {
|
|
||||||
expectContent(name, value);
|
|
||||||
updateContent(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare all file contents against expected contents.
|
|
||||||
void verifyContents() {
|
|
||||||
for (auto &a : mFileMap) {
|
|
||||||
std::ifstream file{a.second};
|
|
||||||
std::string expect = mExpectedContent[a.first].str();
|
|
||||||
std::string actual = std::string(std::istreambuf_iterator<char>(file),
|
|
||||||
std::istreambuf_iterator<char>());
|
|
||||||
EXPECT_EQ(expect, actual) << a.first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::unique_ptr<Vibrator::HwApi> mHwApi;
|
|
||||||
std::unique_ptr<Vibrator::HwApi> mNoApi;
|
|
||||||
std::map<std::string, std::string> mFileMap;
|
|
||||||
TemporaryDir mFilesDir;
|
|
||||||
TemporaryDir mEmptyDir;
|
|
||||||
std::map<std::string, std::stringstream> mExpectedContent;
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using HasTest = HwApiTypedTest<bool(Vibrator::HwApi &)>;
|
|
||||||
|
|
||||||
TEST_P(HasTest, success_returnsTrue) {
|
|
||||||
auto param = GetParam();
|
|
||||||
auto func = std::get<1>(param);
|
|
||||||
|
|
||||||
EXPECT_TRUE(func(*mHwApi));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(HasTest, success_returnsFalse) {
|
|
||||||
auto param = GetParam();
|
|
||||||
auto func = std::get<1>(param);
|
|
||||||
|
|
||||||
EXPECT_FALSE(func(*mNoApi));
|
|
||||||
}
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(HwApiTests, HasTest,
|
|
||||||
ValuesIn({
|
|
||||||
HasTest::MakeParam("default/owt_free_space",
|
|
||||||
&Vibrator::HwApi::hasOwtFreeSpace),
|
|
||||||
}),
|
|
||||||
HasTest::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("default/num_waves",
|
|
||||||
&Vibrator::HwApi::getEffectCount),
|
|
||||||
GetUint32Test::MakeParam("default/owt_free_space",
|
|
||||||
&Vibrator::HwApi::getOwtFreeSpace),
|
|
||||||
}),
|
|
||||||
GetUint32Test::PrintParam);
|
|
||||||
|
|
||||||
using SetBoolTest = HwApiTypedTest<bool(Vibrator::HwApi &, bool)>;
|
|
||||||
|
|
||||||
TEST_P(SetBoolTest, success_returnsTrue) {
|
|
||||||
auto param = GetParam();
|
|
||||||
auto name = std::get<0>(param);
|
|
||||||
auto func = std::get<1>(param);
|
|
||||||
|
|
||||||
expectContent(name, "1");
|
|
||||||
|
|
||||||
EXPECT_TRUE(func(*mHwApi, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(SetBoolTest, success_returnsFalse) {
|
|
||||||
auto param = GetParam();
|
|
||||||
auto name = std::get<0>(param);
|
|
||||||
auto func = std::get<1>(param);
|
|
||||||
|
|
||||||
expectContent(name, "0");
|
|
||||||
|
|
||||||
EXPECT_TRUE(func(*mHwApi, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(SetBoolTest, failure) {
|
|
||||||
auto param = GetParam();
|
|
||||||
auto func = std::get<1>(param);
|
|
||||||
|
|
||||||
EXPECT_FALSE(func(*mNoApi, true));
|
|
||||||
EXPECT_FALSE(func(*mNoApi, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(HwApiTests, SetBoolTest,
|
|
||||||
ValuesIn({
|
|
||||||
SetBoolTest::MakeParam("default/f0_comp_enable",
|
|
||||||
&Vibrator::HwApi::setF0CompEnable),
|
|
||||||
SetBoolTest::MakeParam("default/redc_comp_enable",
|
|
||||||
&Vibrator::HwApi::setRedcCompEnable),
|
|
||||||
}),
|
|
||||||
SetBoolTest::PrintParam);
|
|
||||||
|
|
||||||
using SetUint32Test = HwApiTypedTest<bool(Vibrator::HwApi &, uint32_t)>;
|
|
||||||
|
|
||||||
TEST_P(SetUint32Test, success) {
|
|
||||||
auto param = GetParam();
|
|
||||||
auto name = std::get<0>(param);
|
|
||||||
auto func = std::get<1>(param);
|
|
||||||
uint32_t value = std::rand();
|
|
||||||
|
|
||||||
expectContent(name, value);
|
|
||||||
|
|
||||||
EXPECT_TRUE(func(*mHwApi, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(SetUint32Test, failure) {
|
|
||||||
auto param = GetParam();
|
|
||||||
auto func = std::get<1>(param);
|
|
||||||
uint32_t value = std::rand();
|
|
||||||
|
|
||||||
EXPECT_FALSE(func(*mNoApi, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(HwApiTests, SetUint32Test,
|
|
||||||
ValuesIn({
|
|
||||||
SetUint32Test::MakeParam("default/f0_offset",
|
|
||||||
&Vibrator::HwApi::setF0Offset),
|
|
||||||
SetUint32Test::MakeParam("default/delay_before_stop_playback_us",
|
|
||||||
&Vibrator::HwApi::setMinOnOffInterval),
|
|
||||||
}),
|
|
||||||
SetUint32Test::PrintParam);
|
|
||||||
|
|
||||||
using SetStringTest = HwApiTypedTest<bool(Vibrator::HwApi &, std::string)>;
|
|
||||||
|
|
||||||
TEST_P(SetStringTest, success) {
|
|
||||||
auto param = GetParam();
|
|
||||||
auto name = std::get<0>(param);
|
|
||||||
auto func = std::get<1>(param);
|
|
||||||
std::string value = TemporaryFile().path;
|
|
||||||
|
|
||||||
expectContent(name, value);
|
|
||||||
|
|
||||||
EXPECT_TRUE(func(*mHwApi, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(SetStringTest, failure) {
|
|
||||||
auto param = GetParam();
|
|
||||||
auto func = std::get<1>(param);
|
|
||||||
std::string value = TemporaryFile().path;
|
|
||||||
|
|
||||||
EXPECT_FALSE(func(*mNoApi, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(
|
|
||||||
HwApiTests, SetStringTest,
|
|
||||||
ValuesIn({
|
|
||||||
SetStringTest::MakeParam("calibration/f0_stored", &Vibrator::HwApi::setF0),
|
|
||||||
SetStringTest::MakeParam("calibration/redc_stored", &Vibrator::HwApi::setRedc),
|
|
||||||
SetStringTest::MakeParam("calibration/q_stored", &Vibrator::HwApi::setQ),
|
|
||||||
}),
|
|
||||||
SetStringTest::PrintParam);
|
|
||||||
|
|
||||||
} // namespace vibrator
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace android
|
|
||||||
} // namespace aidl
|
|
@@ -1,386 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 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 <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include "Hardware.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace android {
|
|
||||||
namespace hardware {
|
|
||||||
namespace vibrator {
|
|
||||||
|
|
||||||
using ::testing::Test;
|
|
||||||
|
|
||||||
class HwCalTest : public Test {
|
|
||||||
protected:
|
|
||||||
static constexpr std::array<uint32_t, 2> V_TICK_DEFAULT = {1, 100};
|
|
||||||
static constexpr std::array<uint32_t, 2> V_CLICK_DEFAULT = {1, 100};
|
|
||||||
static constexpr std::array<uint32_t, 2> V_LONG_DEFAULT = {1, 100};
|
|
||||||
|
|
||||||
public:
|
|
||||||
void SetUp() override { 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 randInput = std::rand();
|
|
||||||
std::string expect = std::to_string(randInput);
|
|
||||||
std::string actual = std::to_string(~randInput);
|
|
||||||
|
|
||||||
write("f0_measured", expect);
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getF0(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, f0_missing) {
|
|
||||||
std::string actual;
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_FALSE(mHwCal->getF0(&actual));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, redc_measured) {
|
|
||||||
uint32_t randInput = std::rand();
|
|
||||||
std::string expect = std::to_string(randInput);
|
|
||||||
std::string actual = std::to_string(~randInput);
|
|
||||||
|
|
||||||
write("redc_measured", expect);
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getRedc(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, redc_missing) {
|
|
||||||
std::string actual;
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_FALSE(mHwCal->getRedc(&actual));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, q_measured) {
|
|
||||||
uint32_t randInput = std::rand();
|
|
||||||
std::string expect = std::to_string(randInput);
|
|
||||||
std::string actual = std::to_string(~randInput);
|
|
||||||
|
|
||||||
write("q_measured", expect);
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getQ(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, q_missing) {
|
|
||||||
std::string actual;
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_FALSE(mHwCal->getQ(&actual));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, v_levels) {
|
|
||||||
std::array<uint32_t, 2> expect;
|
|
||||||
std::array<uint32_t, 2> actual;
|
|
||||||
|
|
||||||
// voltage for tick effects
|
|
||||||
std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) {
|
|
||||||
e = std::rand();
|
|
||||||
return ~e;
|
|
||||||
});
|
|
||||||
|
|
||||||
write("v_tick", expect);
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
|
|
||||||
// voltage for click effects
|
|
||||||
std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) {
|
|
||||||
e = std::rand();
|
|
||||||
return ~e;
|
|
||||||
});
|
|
||||||
|
|
||||||
write("v_click", expect);
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
|
|
||||||
// voltage for long effects
|
|
||||||
std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) {
|
|
||||||
e = std::rand();
|
|
||||||
return ~e;
|
|
||||||
});
|
|
||||||
|
|
||||||
write("v_long", expect);
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, v_missing) {
|
|
||||||
std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
|
|
||||||
std::array<uint32_t, 2> actual;
|
|
||||||
|
|
||||||
std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
|
|
||||||
expect = V_CLICK_DEFAULT;
|
|
||||||
|
|
||||||
std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
|
|
||||||
expect = V_LONG_DEFAULT;
|
|
||||||
|
|
||||||
std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, v_short) {
|
|
||||||
std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
|
|
||||||
std::array<uint32_t, 2> actual;
|
|
||||||
|
|
||||||
std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
|
|
||||||
|
|
||||||
write("v_tick", std::array<uint32_t, expect.size() - 1>());
|
|
||||||
write("v_click", std::array<uint32_t, expect.size() - 1>());
|
|
||||||
write("v_long", std::array<uint32_t, expect.size() - 1>());
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
|
|
||||||
expect = V_CLICK_DEFAULT;
|
|
||||||
EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
|
|
||||||
expect = V_LONG_DEFAULT;
|
|
||||||
EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, v_long) {
|
|
||||||
std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
|
|
||||||
std::array<uint32_t, 2> actual;
|
|
||||||
|
|
||||||
std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
|
|
||||||
|
|
||||||
write("v_tick", std::array<uint32_t, expect.size() + 1>());
|
|
||||||
write("v_click", std::array<uint32_t, expect.size() + 1>());
|
|
||||||
write("v_long", std::array<uint32_t, expect.size() + 1>());
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
|
|
||||||
expect = V_CLICK_DEFAULT;
|
|
||||||
EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
|
|
||||||
expect = V_LONG_DEFAULT;
|
|
||||||
EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, v_nofile) {
|
|
||||||
std::array<uint32_t, 2> expect = V_TICK_DEFAULT;
|
|
||||||
std::array<uint32_t, 2> actual;
|
|
||||||
|
|
||||||
std::transform(expect.begin(), expect.end(), actual.begin(), [](uint32_t &e) { return ~e; });
|
|
||||||
|
|
||||||
write("v_tick", actual);
|
|
||||||
write("v_click", actual);
|
|
||||||
write("v_long", actual);
|
|
||||||
unlink();
|
|
||||||
|
|
||||||
createHwCal();
|
|
||||||
|
|
||||||
EXPECT_TRUE(mHwCal->getTickVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
|
|
||||||
expect = V_CLICK_DEFAULT;
|
|
||||||
EXPECT_TRUE(mHwCal->getClickVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
|
|
||||||
expect = V_LONG_DEFAULT;
|
|
||||||
EXPECT_TRUE(mHwCal->getLongVolLevels(&actual));
|
|
||||||
EXPECT_EQ(expect, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, multiple) {
|
|
||||||
uint32_t randInput = std::rand();
|
|
||||||
std::string f0Expect = std::to_string(randInput);
|
|
||||||
std::string f0Actual = std::to_string(~randInput);
|
|
||||||
randInput = std::rand();
|
|
||||||
std::string redcExpect = std::to_string(randInput);
|
|
||||||
std::string redcActual = std::to_string(~randInput);
|
|
||||||
randInput = std::rand();
|
|
||||||
std::string qExpect = std::to_string(randInput);
|
|
||||||
std::string qActual = std::to_string(~randInput);
|
|
||||||
std::array<uint32_t, 2> volTickExpect, volClickExpect, volLongExpect;
|
|
||||||
std::array<uint32_t, 2> volActual;
|
|
||||||
|
|
||||||
std::transform(volTickExpect.begin(), volTickExpect.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_tick", volTickExpect);
|
|
||||||
std::transform(volClickExpect.begin(), volClickExpect.end(), volActual.begin(),
|
|
||||||
[](uint32_t &e) {
|
|
||||||
e = std::rand();
|
|
||||||
return ~e;
|
|
||||||
});
|
|
||||||
write("v_click", volClickExpect);
|
|
||||||
std::transform(volLongExpect.begin(), volLongExpect.end(), volActual.begin(), [](uint32_t &e) {
|
|
||||||
e = std::rand();
|
|
||||||
return ~e;
|
|
||||||
});
|
|
||||||
write("v_long", volLongExpect);
|
|
||||||
|
|
||||||
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->getTickVolLevels(&volActual));
|
|
||||||
EXPECT_EQ(volTickExpect, volActual);
|
|
||||||
EXPECT_TRUE(mHwCal->getClickVolLevels(&volActual));
|
|
||||||
EXPECT_EQ(volClickExpect, volActual);
|
|
||||||
EXPECT_TRUE(mHwCal->getLongVolLevels(&volActual));
|
|
||||||
EXPECT_EQ(volLongExpect, volActual);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(HwCalTest, trimming) {
|
|
||||||
uint32_t randInput = std::rand();
|
|
||||||
std::string f0Expect = std::to_string(randInput);
|
|
||||||
std::string f0Actual = std::to_string(~randInput);
|
|
||||||
randInput = std::rand();
|
|
||||||
std::string redcExpect = std::to_string(randInput);
|
|
||||||
std::string redcActual = std::to_string(randInput);
|
|
||||||
randInput = std::rand();
|
|
||||||
std::string qExpect = std::to_string(randInput);
|
|
||||||
std::string qActual = std::to_string(randInput);
|
|
||||||
std::array<uint32_t, 2> volTickExpect, volClickExpect, volLongExpect;
|
|
||||||
std::array<uint32_t, 2> volActual;
|
|
||||||
|
|
||||||
std::transform(volTickExpect.begin(), volTickExpect.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_tick", volTickExpect, " \t", "\t ");
|
|
||||||
std::transform(volClickExpect.begin(), volClickExpect.end(), volActual.begin(),
|
|
||||||
[](uint32_t &e) {
|
|
||||||
e = std::rand();
|
|
||||||
return ~e;
|
|
||||||
});
|
|
||||||
write("v_click", volClickExpect, " \t", "\t ");
|
|
||||||
std::transform(volLongExpect.begin(), volLongExpect.end(), volActual.begin(), [](uint32_t &e) {
|
|
||||||
e = std::rand();
|
|
||||||
return ~e;
|
|
||||||
});
|
|
||||||
write("v_long", volLongExpect, " \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->getTickVolLevels(&volActual));
|
|
||||||
EXPECT_EQ(volTickExpect, volActual);
|
|
||||||
EXPECT_TRUE(mHwCal->getClickVolLevels(&volActual));
|
|
||||||
EXPECT_EQ(volClickExpect, volActual);
|
|
||||||
EXPECT_TRUE(mHwCal->getLongVolLevels(&volActual));
|
|
||||||
EXPECT_EQ(volLongExpect, volActual);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace vibrator
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace android
|
|
||||||
} // namespace aidl
|
|
@@ -1,692 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 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 <aidl/android/hardware/vibrator/BnVibratorCallback.h>
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <gmock/gmock.h>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <linux/input.h>
|
|
||||||
#include <linux/uinput.h>
|
|
||||||
|
|
||||||
#include <future>
|
|
||||||
|
|
||||||
#include "Vibrator.h"
|
|
||||||
#include "mocks.h"
|
|
||||||
#include "types.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace android {
|
|
||||||
namespace hardware {
|
|
||||||
namespace vibrator {
|
|
||||||
|
|
||||||
using ::testing::_;
|
|
||||||
using ::testing::AnyNumber;
|
|
||||||
using ::testing::Assign;
|
|
||||||
using ::testing::AtLeast;
|
|
||||||
using ::testing::AtMost;
|
|
||||||
using ::testing::Combine;
|
|
||||||
using ::testing::DoAll;
|
|
||||||
using ::testing::DoDefault;
|
|
||||||
using ::testing::Exactly;
|
|
||||||
using ::testing::Expectation;
|
|
||||||
using ::testing::ExpectationSet;
|
|
||||||
using ::testing::Ge;
|
|
||||||
using ::testing::Mock;
|
|
||||||
using ::testing::MockFunction;
|
|
||||||
using ::testing::Range;
|
|
||||||
using ::testing::Return;
|
|
||||||
using ::testing::Sequence;
|
|
||||||
using ::testing::SetArgPointee;
|
|
||||||
using ::testing::SetArgReferee;
|
|
||||||
using ::testing::Test;
|
|
||||||
using ::testing::TestParamInfo;
|
|
||||||
using ::testing::ValuesIn;
|
|
||||||
using ::testing::WithParamInterface;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
static EffectLevel Level(float intensity, float levelLow, float levelHigh);
|
|
||||||
static EffectScale Scale(float intensity, float levelLow, float levelHigh);
|
|
||||||
|
|
||||||
// Constants With Arbitrary Values
|
|
||||||
|
|
||||||
static constexpr uint32_t CAL_VERSION = 2;
|
|
||||||
static constexpr std::array<EffectLevel, 2> V_TICK_DEFAULT = {1, 100};
|
|
||||||
static constexpr std::array<EffectLevel, 2> V_CLICK_DEFAULT{1, 100};
|
|
||||||
static constexpr std::array<EffectLevel, 2> V_LONG_DEFAULT{1, 100};
|
|
||||||
static constexpr std::array<EffectDuration, 14> EFFECT_DURATIONS{
|
|
||||||
0, 100, 30, 1000, 300, 130, 150, 500, 100, 15, 20, 1000, 1000, 1000};
|
|
||||||
|
|
||||||
// Constants With Prescribed Values
|
|
||||||
|
|
||||||
static const std::map<Effect, EffectIndex> EFFECT_INDEX{
|
|
||||||
{Effect::CLICK, 2},
|
|
||||||
{Effect::TICK, 2},
|
|
||||||
{Effect::HEAVY_CLICK, 2},
|
|
||||||
{Effect::TEXTURE_TICK, 9},
|
|
||||||
};
|
|
||||||
static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500;
|
|
||||||
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 auto POLLING_TIMEOUT = 20;
|
|
||||||
enum WaveformIndex : uint16_t {
|
|
||||||
/* Physical waveform */
|
|
||||||
WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
|
|
||||||
WAVEFORM_RESERVED_INDEX_1 = 1,
|
|
||||||
WAVEFORM_CLICK_INDEX = 2,
|
|
||||||
WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
|
|
||||||
WAVEFORM_THUD_INDEX = 4,
|
|
||||||
WAVEFORM_SPIN_INDEX = 5,
|
|
||||||
WAVEFORM_QUICK_RISE_INDEX = 6,
|
|
||||||
WAVEFORM_SLOW_RISE_INDEX = 7,
|
|
||||||
WAVEFORM_QUICK_FALL_INDEX = 8,
|
|
||||||
WAVEFORM_LIGHT_TICK_INDEX = 9,
|
|
||||||
WAVEFORM_LOW_TICK_INDEX = 10,
|
|
||||||
WAVEFORM_RESERVED_MFG_1,
|
|
||||||
WAVEFORM_RESERVED_MFG_2,
|
|
||||||
WAVEFORM_RESERVED_MFG_3,
|
|
||||||
WAVEFORM_MAX_PHYSICAL_INDEX,
|
|
||||||
/* OWT waveform */
|
|
||||||
WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
|
|
||||||
WAVEFORM_PWLE,
|
|
||||||
/*
|
|
||||||
* Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
|
|
||||||
* #define FF_GAIN 0x60 // 96 in decimal
|
|
||||||
* #define FF_MAX_EFFECTS FF_GAIN
|
|
||||||
*/
|
|
||||||
WAVEFORM_MAX_INDEX,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const EffectScale ON_GLOBAL_SCALE{levelToScale(V_LONG_DEFAULT[1])};
|
|
||||||
static const EffectIndex ON_EFFECT_INDEX{0};
|
|
||||||
|
|
||||||
static const std::map<EffectTuple, EffectScale> EFFECT_SCALE{
|
|
||||||
{{Effect::TICK, EffectStrength::LIGHT},
|
|
||||||
Scale(0.5f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
{{Effect::TICK, EffectStrength::MEDIUM},
|
|
||||||
Scale(0.5f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
{{Effect::TICK, EffectStrength::STRONG},
|
|
||||||
Scale(0.5f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
{{Effect::CLICK, EffectStrength::LIGHT},
|
|
||||||
Scale(0.7f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
{{Effect::CLICK, EffectStrength::MEDIUM},
|
|
||||||
Scale(0.7f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
{{Effect::CLICK, EffectStrength::STRONG},
|
|
||||||
Scale(0.7f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
{{Effect::HEAVY_CLICK, EffectStrength::LIGHT},
|
|
||||||
Scale(1.0f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
{{Effect::HEAVY_CLICK, EffectStrength::MEDIUM},
|
|
||||||
Scale(1.0f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
{{Effect::HEAVY_CLICK, EffectStrength::STRONG},
|
|
||||||
Scale(1.0f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
{{Effect::TEXTURE_TICK, EffectStrength::LIGHT},
|
|
||||||
Scale(0.5f * 0.5f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
|
|
||||||
{{Effect::TEXTURE_TICK, EffectStrength::MEDIUM},
|
|
||||||
Scale(0.5f * 0.7f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
|
|
||||||
{{Effect::TEXTURE_TICK, EffectStrength::STRONG},
|
|
||||||
Scale(0.5f * 1.0f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::map<EffectTuple, EffectQueue> EFFECT_QUEUE{
|
|
||||||
{{Effect::DOUBLE_CLICK, EffectStrength::LIGHT},
|
|
||||||
Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
|
|
||||||
Level(0.7f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
100,
|
|
||||||
QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
|
|
||||||
Level(1.0f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
|
|
||||||
{{Effect::DOUBLE_CLICK, EffectStrength::MEDIUM},
|
|
||||||
Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
|
|
||||||
Level(0.7f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
100,
|
|
||||||
QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
|
|
||||||
Level(1.0f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
|
|
||||||
{{Effect::DOUBLE_CLICK, EffectStrength::STRONG},
|
|
||||||
Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
|
|
||||||
Level(0.7f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
|
|
||||||
100,
|
|
||||||
QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
|
|
||||||
Level(1.0f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
|
|
||||||
};
|
|
||||||
|
|
||||||
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_DURATIONS[index];
|
|
||||||
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};
|
|
||||||
}
|
|
||||||
|
|
||||||
static EffectLevel Level(float intensity, float levelLow, float levelHigh) {
|
|
||||||
return std::lround(intensity * (levelHigh - levelLow)) + levelLow;
|
|
||||||
}
|
|
||||||
|
|
||||||
static EffectScale Scale(float intensity, float levelLow, float levelHigh) {
|
|
||||||
return levelToScale(Level(intensity, levelLow, levelHigh));
|
|
||||||
}
|
|
||||||
|
|
||||||
class VibratorTest : public Test {
|
|
||||||
public:
|
|
||||||
void SetUp() override {
|
|
||||||
setenv("INPUT_EVENT_NAME", "CS40L26TestSuite", true);
|
|
||||||
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, setFFGain(_, _)).WillByDefault(Return(true));
|
|
||||||
ON_CALL(*mMockApi, setFFEffect(_, _, _)).WillByDefault(Return(true));
|
|
||||||
ON_CALL(*mMockApi, setFFPlay(_, _, _)).WillByDefault(Return(true));
|
|
||||||
ON_CALL(*mMockApi, pollVibeState(_, _)).WillByDefault(Return(true));
|
|
||||||
ON_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _)).WillByDefault(Return(true));
|
|
||||||
ON_CALL(*mMockApi, eraseOwtEffect(_, _, _)).WillByDefault(Return(true));
|
|
||||||
|
|
||||||
ON_CALL(*mMockApi, getOwtFreeSpace(_))
|
|
||||||
.WillByDefault(DoAll(SetArgPointee<0>(11504), Return(true)));
|
|
||||||
|
|
||||||
ON_CALL(*mMockCal, destructor()).WillByDefault(Assign(&mMockCal, nullptr));
|
|
||||||
|
|
||||||
ON_CALL(*mMockCal, getVersion(_))
|
|
||||||
.WillByDefault(DoAll(SetArgPointee<0>(CAL_VERSION), Return(true)));
|
|
||||||
|
|
||||||
ON_CALL(*mMockCal, getTickVolLevels(_))
|
|
||||||
.WillByDefault(DoAll(SetArgPointee<0>(V_TICK_DEFAULT), Return(true)));
|
|
||||||
ON_CALL(*mMockCal, getClickVolLevels(_))
|
|
||||||
.WillByDefault(DoAll(SetArgPointee<0>(V_CLICK_DEFAULT), Return(true)));
|
|
||||||
ON_CALL(*mMockCal, getLongVolLevels(_))
|
|
||||||
.WillByDefault(DoAll(SetArgPointee<0>(V_LONG_DEFAULT), Return(true)));
|
|
||||||
|
|
||||||
relaxMock(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void createVibrator(std::unique_ptr<MockApi> mockapi, std::unique_ptr<MockCal> mockcal,
|
|
||||||
bool relaxed = true) {
|
|
||||||
if (relaxed) {
|
|
||||||
relaxMock(true);
|
|
||||||
}
|
|
||||||
mVibrator = ndk::SharedRefBase::make<Vibrator>(std::move(mockapi), std::move(mockcal));
|
|
||||||
if (relaxed) {
|
|
||||||
relaxMock(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void deleteVibrator(bool relaxed = true) {
|
|
||||||
if (relaxed) {
|
|
||||||
relaxMock(true);
|
|
||||||
}
|
|
||||||
mVibrator.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
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, setF0Offset(_)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, setRedc(_)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, setQ(_)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, setF0CompEnable(_)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, setRedcCompEnable(_)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, pollVibeState(_, _)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, setFFGain(_, _)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, setFFEffect(_, _, _)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, setFFPlay(_, _, _)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, setMinOnOffInterval(_)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, getContextScale()).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, getContextEnable()).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, getContextSettlingTime()).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, getContextCooldownTime()).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, getContextFadeEnable()).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, _, _, _)).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, getTickVolLevels(_)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockCal, getClickVolLevels(_)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockCal, getLongVolLevels(_)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockCal, isChirpEnabled()).Times(times);
|
|
||||||
EXPECT_CALL(*mMockCal, getLongFrequencyShift(_)).Times(times);
|
|
||||||
EXPECT_CALL(*mMockCal, isF0CompEnabled()).Times(times);
|
|
||||||
EXPECT_CALL(*mMockCal, isRedcCompEnabled()).Times(times);
|
|
||||||
EXPECT_CALL(*mMockCal, debug(_)).Times(times);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
MockApi *mMockApi;
|
|
||||||
MockCal *mMockCal;
|
|
||||||
std::shared_ptr<IVibrator> mVibrator;
|
|
||||||
uint32_t mEffectIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(VibratorTest, Constructor) {
|
|
||||||
std::unique_ptr<MockApi> mockapi;
|
|
||||||
std::unique_ptr<MockCal> mockcal;
|
|
||||||
std::string f0Val = std::to_string(std::rand());
|
|
||||||
std::string redcVal = std::to_string(std::rand());
|
|
||||||
std::string qVal = std::to_string(std::rand());
|
|
||||||
uint32_t calVer;
|
|
||||||
uint32_t supportedPrimitivesBits = 0x0;
|
|
||||||
Expectation volGet;
|
|
||||||
Sequence f0Seq, redcSeq, qSeq, supportedPrimitivesSeq;
|
|
||||||
|
|
||||||
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(SetArgReferee<0>(f0Val), Return(true)));
|
|
||||||
EXPECT_CALL(*mMockApi, setF0(f0Val)).InSequence(f0Seq).WillOnce(Return(true));
|
|
||||||
|
|
||||||
EXPECT_CALL(*mMockCal, getRedc(_))
|
|
||||||
.InSequence(redcSeq)
|
|
||||||
.WillOnce(DoAll(SetArgReferee<0>(redcVal), Return(true)));
|
|
||||||
EXPECT_CALL(*mMockApi, setRedc(redcVal)).InSequence(redcSeq).WillOnce(Return(true));
|
|
||||||
|
|
||||||
EXPECT_CALL(*mMockCal, getQ(_))
|
|
||||||
.InSequence(qSeq)
|
|
||||||
.WillOnce(DoAll(SetArgReferee<0>(qVal), Return(true)));
|
|
||||||
EXPECT_CALL(*mMockApi, setQ(qVal)).InSequence(qSeq).WillOnce(Return(true));
|
|
||||||
|
|
||||||
EXPECT_CALL(*mMockCal, getLongFrequencyShift(_)).WillOnce(Return(true));
|
|
||||||
|
|
||||||
mMockCal->getVersion(&calVer);
|
|
||||||
if (calVer == 2) {
|
|
||||||
volGet = EXPECT_CALL(*mMockCal, getTickVolLevels(_)).WillOnce(DoDefault());
|
|
||||||
volGet = EXPECT_CALL(*mMockCal, getClickVolLevels(_)).WillOnce(DoDefault());
|
|
||||||
volGet = EXPECT_CALL(*mMockCal, getLongVolLevels(_)).WillOnce(DoDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_CALL(*mMockCal, isF0CompEnabled()).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*mMockApi, setF0CompEnable(true)).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*mMockCal, isRedcCompEnabled()).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*mMockApi, setRedcCompEnable(true)).WillOnce(Return(true));
|
|
||||||
|
|
||||||
EXPECT_CALL(*mMockCal, isChirpEnabled()).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*mMockCal, getSupportedPrimitives(_))
|
|
||||||
.InSequence(supportedPrimitivesSeq)
|
|
||||||
.WillOnce(DoAll(SetArgPointee<0>(supportedPrimitivesBits), Return(true)));
|
|
||||||
|
|
||||||
EXPECT_CALL(*mMockApi, setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US)).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*mMockApi, getContextScale()).WillOnce(Return(0));
|
|
||||||
EXPECT_CALL(*mMockApi, getContextEnable()).WillOnce(Return(false));
|
|
||||||
EXPECT_CALL(*mMockApi, getContextSettlingTime()).WillOnce(Return(0));
|
|
||||||
EXPECT_CALL(*mMockApi, getContextCooldownTime()).WillOnce(Return(0));
|
|
||||||
EXPECT_CALL(*mMockApi, getContextFadeEnable()).WillOnce(Return(false));
|
|
||||||
createVibrator(std::move(mockapi), std::move(mockcal), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(VibratorTest, on) {
|
|
||||||
Sequence s1, s2;
|
|
||||||
uint16_t duration = std::rand() + 1;
|
|
||||||
|
|
||||||
EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
|
|
||||||
EXPECT_CALL(*mMockApi, setFFEffect(_, _, duration + MAX_COLD_START_LATENCY_MS))
|
|
||||||
.InSequence(s2)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
EXPECT_CALL(*mMockApi, setFFPlay(_, ON_EFFECT_INDEX, true))
|
|
||||||
.InSequence(s1, s2)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
EXPECT_TRUE(mVibrator->on(duration, nullptr).isOk());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(VibratorTest, off) {
|
|
||||||
Sequence s1;
|
|
||||||
EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
|
|
||||||
EXPECT_TRUE(mVibrator->off().isOk());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(VibratorTest, supportsAmplitudeControl_supported) {
|
|
||||||
int32_t capabilities;
|
|
||||||
EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
|
|
||||||
|
|
||||||
EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
|
|
||||||
EXPECT_GT(capabilities & IVibrator::CAP_AMPLITUDE_CONTROL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(VibratorTest, supportsExternalAmplitudeControl_unsupported) {
|
|
||||||
int32_t capabilities;
|
|
||||||
EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
|
|
||||||
|
|
||||||
EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
|
|
||||||
EXPECT_EQ(capabilities & IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(VibratorTest, setAmplitude_supported) {
|
|
||||||
EffectAmplitude amplitude = static_cast<float>(std::rand()) / RAND_MAX ?: 1.0f;
|
|
||||||
|
|
||||||
EXPECT_CALL(*mMockApi, setFFGain(_, amplitudeToScale(amplitude))).WillOnce(Return(true));
|
|
||||||
|
|
||||||
EXPECT_TRUE(mVibrator->setAmplitude(amplitude).isOk());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(VibratorTest, supportsExternalControl_supported) {
|
|
||||||
int32_t capabilities;
|
|
||||||
EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
|
|
||||||
|
|
||||||
EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
|
|
||||||
EXPECT_GT(capabilities & IVibrator::CAP_EXTERNAL_CONTROL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(VibratorTest, supportsExternalControl_unsupported) {
|
|
||||||
int32_t capabilities;
|
|
||||||
EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(false));
|
|
||||||
|
|
||||||
EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
|
|
||||||
EXPECT_EQ(capabilities & IVibrator::CAP_EXTERNAL_CONTROL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(VibratorTest, setExternalControl_enable) {
|
|
||||||
Sequence s1, s2;
|
|
||||||
EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
|
|
||||||
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).InSequence(s2).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, true, _, _))
|
|
||||||
.InSequence(s1, s2)
|
|
||||||
.WillOnce(Return(true));
|
|
||||||
|
|
||||||
EXPECT_TRUE(mVibrator->setExternalControl(true).isOk());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(VibratorTest, setExternalControl_disable) {
|
|
||||||
Sequence s1, s2, s3, s4;
|
|
||||||
|
|
||||||
// The default mIsUnderExternalControl is false, so it needs to turn on the External Control
|
|
||||||
// to make mIsUnderExternalControl become true.
|
|
||||||
EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
|
|
||||||
.InSequence(s1)
|
|
||||||
.InSequence(s1)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).InSequence(s2).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, true, _, _)).InSequence(s3).WillOnce(Return(true));
|
|
||||||
|
|
||||||
EXPECT_TRUE(mVibrator->setExternalControl(true).isOk());
|
|
||||||
|
|
||||||
EXPECT_CALL(*mMockApi, setFFGain(_, levelToScale(VOLTAGE_SCALE_MAX)))
|
|
||||||
.InSequence(s4)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, false, _, _))
|
|
||||||
.InSequence(s1, s2, s3, s4)
|
|
||||||
.WillOnce(Return(true));
|
|
||||||
|
|
||||||
EXPECT_TRUE(mVibrator->setExternalControl(false).isOk());
|
|
||||||
}
|
|
||||||
|
|
||||||
class EffectsTest : public VibratorTest, public WithParamInterface<EffectTuple> {
|
|
||||||
public:
|
|
||||||
static auto PrintParam(const TestParamInfo<ParamType> &info) {
|
|
||||||
auto param = info.param;
|
|
||||||
auto effect = std::get<0>(param);
|
|
||||||
auto strength = std::get<1>(param);
|
|
||||||
return toString(effect) + "_" + toString(strength);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_P(EffectsTest, 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;
|
|
||||||
auto callback = ndk::SharedRefBase::make<MockVibratorCallback>();
|
|
||||||
std::promise<void> promise;
|
|
||||||
std::future<void> future{promise.get_future()};
|
|
||||||
auto complete = [&promise] {
|
|
||||||
promise.set_value();
|
|
||||||
return ndk::ScopedAStatus::ok();
|
|
||||||
};
|
|
||||||
bool composeEffect;
|
|
||||||
|
|
||||||
ExpectationSet eSetup;
|
|
||||||
Expectation eActivate, ePollHaptics, ePollStop, eEraseDone;
|
|
||||||
|
|
||||||
if (scale != EFFECT_SCALE.end()) {
|
|
||||||
EffectIndex index = EFFECT_INDEX.at(effect);
|
|
||||||
duration = EFFECT_DURATIONS[index];
|
|
||||||
|
|
||||||
eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, levelToScale(scale->second)))
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, index, true))
|
|
||||||
.After(eSetup)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
} else if (queue != EFFECT_QUEUE.end()) {
|
|
||||||
duration = std::get<1>(queue->second);
|
|
||||||
eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
|
|
||||||
.After(eSetup)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
eSetup += EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).WillOnce(DoDefault());
|
|
||||||
eSetup += EXPECT_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _))
|
|
||||||
.After(eSetup)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, WAVEFORM_COMPOSE, true))
|
|
||||||
.After(eSetup)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
composeEffect = true;
|
|
||||||
} else {
|
|
||||||
duration = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (duration) {
|
|
||||||
ePollHaptics = EXPECT_CALL(*mMockApi, pollVibeState(1, POLLING_TIMEOUT))
|
|
||||||
.After(eActivate)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
ePollStop = EXPECT_CALL(*mMockApi, pollVibeState(0, -1))
|
|
||||||
.After(ePollHaptics)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
if (composeEffect) {
|
|
||||||
eEraseDone = EXPECT_CALL(*mMockApi, eraseOwtEffect(_, _, _))
|
|
||||||
.After(ePollStop)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
EXPECT_CALL(*callback, onComplete()).After(eEraseDone).WillOnce(complete);
|
|
||||||
} else {
|
|
||||||
EXPECT_CALL(*callback, onComplete()).After(ePollStop).WillOnce(complete);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t lengthMs;
|
|
||||||
ndk::ScopedAStatus status = mVibrator->perform(effect, strength, callback, &lengthMs);
|
|
||||||
if (status.isOk()) {
|
|
||||||
EXPECT_LE(duration, lengthMs);
|
|
||||||
} else {
|
|
||||||
EXPECT_EQ(EX_UNSUPPORTED_OPERATION, status.getExceptionCode());
|
|
||||||
EXPECT_EQ(0, lengthMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (duration) {
|
|
||||||
EXPECT_EQ(future.wait_for(std::chrono::milliseconds(100)), std::future_status::ready);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<Effect> kEffects{ndk::enum_range<Effect>().begin(),
|
|
||||||
ndk::enum_range<Effect>().end()};
|
|
||||||
const std::vector<EffectStrength> kEffectStrengths{ndk::enum_range<EffectStrength>().begin(),
|
|
||||||
ndk::enum_range<EffectStrength>().end()};
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(VibratorTests, EffectsTest,
|
|
||||||
Combine(ValuesIn(kEffects.begin(), kEffects.end()),
|
|
||||||
ValuesIn(kEffectStrengths.begin(), kEffectStrengths.end())),
|
|
||||||
EffectsTest::PrintParam);
|
|
||||||
|
|
||||||
struct PrimitiveParam {
|
|
||||||
CompositePrimitive primitive;
|
|
||||||
EffectIndex index;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PrimitiveTest : public VibratorTest, public WithParamInterface<PrimitiveParam> {
|
|
||||||
public:
|
|
||||||
static auto PrintParam(const TestParamInfo<ParamType> &info) {
|
|
||||||
return toString(info.param.primitive);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::vector<PrimitiveParam> kPrimitiveParams = {
|
|
||||||
{CompositePrimitive::CLICK, 2}, {CompositePrimitive::THUD, 4},
|
|
||||||
{CompositePrimitive::SPIN, 5}, {CompositePrimitive::QUICK_RISE, 6},
|
|
||||||
{CompositePrimitive::SLOW_RISE, 7}, {CompositePrimitive::QUICK_FALL, 8},
|
|
||||||
{CompositePrimitive::LIGHT_TICK, 9}, {CompositePrimitive::LOW_TICK, 10},
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_P(PrimitiveTest, getPrimitiveDuration) {
|
|
||||||
auto param = GetParam();
|
|
||||||
auto primitive = param.primitive;
|
|
||||||
auto index = param.index;
|
|
||||||
int32_t duration;
|
|
||||||
|
|
||||||
EXPECT_EQ(EX_NONE, mVibrator->getPrimitiveDuration(primitive, &duration).getExceptionCode());
|
|
||||||
EXPECT_EQ(EFFECT_DURATIONS[index], duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(VibratorTests, PrimitiveTest,
|
|
||||||
ValuesIn(kPrimitiveParams.begin(), kPrimitiveParams.end()),
|
|
||||||
PrimitiveTest::PrintParam);
|
|
||||||
|
|
||||||
struct ComposeParam {
|
|
||||||
std::string name;
|
|
||||||
std::vector<CompositeEffect> composite;
|
|
||||||
EffectQueue queue;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ComposeTest : public VibratorTest, public WithParamInterface<ComposeParam> {
|
|
||||||
public:
|
|
||||||
static auto PrintParam(const TestParamInfo<ParamType> &info) { return info.param.name; }
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_P(ComposeTest, compose) {
|
|
||||||
auto param = GetParam();
|
|
||||||
auto composite = param.composite;
|
|
||||||
auto queue = std::get<0>(param.queue);
|
|
||||||
ExpectationSet eSetup;
|
|
||||||
Expectation eActivate, ePollHaptics, ePollStop, eEraseDone;
|
|
||||||
auto callback = ndk::SharedRefBase::make<MockVibratorCallback>();
|
|
||||||
std::promise<void> promise;
|
|
||||||
std::future<void> future{promise.get_future()};
|
|
||||||
auto complete = [&promise] {
|
|
||||||
promise.set_value();
|
|
||||||
return ndk::ScopedAStatus::ok();
|
|
||||||
};
|
|
||||||
|
|
||||||
eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
|
|
||||||
.After(eSetup)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
eSetup += EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).WillOnce(DoDefault());
|
|
||||||
eSetup += EXPECT_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _))
|
|
||||||
.After(eSetup)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, WAVEFORM_COMPOSE, true))
|
|
||||||
.After(eSetup)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
|
|
||||||
ePollHaptics = EXPECT_CALL(*mMockApi, pollVibeState(1, POLLING_TIMEOUT))
|
|
||||||
.After(eActivate)
|
|
||||||
.WillOnce(DoDefault());
|
|
||||||
ePollStop =
|
|
||||||
EXPECT_CALL(*mMockApi, pollVibeState(0, -1)).After(ePollHaptics).WillOnce(DoDefault());
|
|
||||||
eEraseDone =
|
|
||||||
EXPECT_CALL(*mMockApi, eraseOwtEffect(_, _, _)).After(ePollStop).WillOnce(DoDefault());
|
|
||||||
EXPECT_CALL(*callback, onComplete()).After(eEraseDone).WillOnce(complete);
|
|
||||||
|
|
||||||
EXPECT_EQ(EX_NONE, mVibrator->compose(composite, callback).getExceptionCode());
|
|
||||||
|
|
||||||
EXPECT_EQ(future.wait_for(std::chrono::milliseconds(100)), std::future_status::ready);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<ComposeParam> kComposeParams = {
|
|
||||||
{"click",
|
|
||||||
{{0, CompositePrimitive::CLICK, 1.0f}},
|
|
||||||
Queue(QueueEffect(2, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
|
|
||||||
{"thud",
|
|
||||||
{{1, CompositePrimitive::THUD, 0.8f}},
|
|
||||||
Queue(1, QueueEffect(4, Level(0.8f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
|
|
||||||
{"spin",
|
|
||||||
{{2, CompositePrimitive::SPIN, 0.6f}},
|
|
||||||
Queue(2, QueueEffect(5, Level(0.6f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
|
|
||||||
{"quick_rise",
|
|
||||||
{{3, CompositePrimitive::QUICK_RISE, 0.4f}},
|
|
||||||
Queue(3, QueueEffect(6, Level(0.4f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
|
|
||||||
{"slow_rise",
|
|
||||||
{{4, CompositePrimitive::SLOW_RISE, 0.0f}},
|
|
||||||
Queue(4, QueueEffect(7, Level(0.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
|
|
||||||
{"quick_fall",
|
|
||||||
{{5, CompositePrimitive::QUICK_FALL, 1.0f}},
|
|
||||||
Queue(5, QueueEffect(8, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
|
|
||||||
{"pop",
|
|
||||||
{{6, CompositePrimitive::SLOW_RISE, 1.0f}, {50, CompositePrimitive::THUD, 1.0f}},
|
|
||||||
Queue(6, QueueEffect(7, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 50,
|
|
||||||
QueueEffect(4, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
|
|
||||||
{"snap",
|
|
||||||
{{7, CompositePrimitive::QUICK_RISE, 1.0f}, {0, CompositePrimitive::QUICK_FALL, 1.0f}},
|
|
||||||
Queue(7, QueueEffect(6, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])),
|
|
||||||
QueueEffect(8, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
|
|
||||||
};
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(VibratorTests, ComposeTest,
|
|
||||||
ValuesIn(kComposeParams.begin(), kComposeParams.end()),
|
|
||||||
ComposeTest::PrintParam);
|
|
||||||
} // namespace vibrator
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace android
|
|
||||||
} // namespace aidl
|
|
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 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_TYPES_H
|
|
||||||
#define ANDROID_HARDWARE_VIBRATOR_TEST_TYPES_H
|
|
||||||
|
|
||||||
#include <aidl/android/hardware/vibrator/IVibrator.h>
|
|
||||||
|
|
||||||
using EffectIndex = uint16_t;
|
|
||||||
using EffectLevel = uint32_t;
|
|
||||||
using EffectAmplitude = float;
|
|
||||||
using EffectScale = uint16_t;
|
|
||||||
using EffectDuration = uint32_t;
|
|
||||||
using EffectQueue = std::tuple<std::string, EffectDuration>;
|
|
||||||
using EffectTuple = std::tuple<::aidl::android::hardware::vibrator::Effect,
|
|
||||||
::aidl::android::hardware::vibrator::EffectStrength>;
|
|
||||||
|
|
||||||
using QueueEffect = std::tuple<EffectIndex, EffectLevel>;
|
|
||||||
using QueueDelay = uint32_t;
|
|
||||||
|
|
||||||
#endif // ANDROID_HARDWARE_VIBRATOR_TEST_TYPES_H
|
|
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 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(float amplitude, float maximum) {
|
|
||||||
float ratio = 100; /* Unit: % */
|
|
||||||
if (maximum != 0)
|
|
||||||
ratio = amplitude / maximum * 100;
|
|
||||||
|
|
||||||
if (maximum == 0 || ratio > 100)
|
|
||||||
ratio = 100;
|
|
||||||
|
|
||||||
return std::round(ratio);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline EffectScale levelToScale(EffectLevel level) {
|
|
||||||
return toScale(level, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline EffectScale amplitudeToScale(EffectAmplitude amplitude) {
|
|
||||||
return toScale(amplitude, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t msToCycles(EffectDuration ms) {
|
|
||||||
return ms * 48;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ANDROID_HARDWARE_VIBRATOR_TEST_UTILS_H
|
|
Reference in New Issue
Block a user