cs40l26: Add vibrator manager support

Bug: 181615889
Test: Manual type and trigger a long/short vibration
Test: cmd vibrator_manager synced xxxx
Test: cmd vibrator_manager sequential -v 0 xxxx
Test: atest VtsHalVibratorManagerTargetTest \
VtsHalVibratorTargetTest android.os.cts.VibratorTest \
android.os.cts.VibratorManagerTest android.os.cts.VibrationEffectTest \
android.os.cts.VibrationAttributesTest \
android.os.cts.CombinedVibrationTest \
Signed-off-by: Chase Wu <chasewu@google.com>
Change-Id: Ib93e8eb4a0de9269116e07f76b66a77b58915211
This commit is contained in:
Chase Wu 2022-10-25 22:14:22 +08:00
parent fd52691553
commit 3584a67955
18 changed files with 876 additions and 33 deletions

View file

@ -21,7 +21,6 @@ cc_defaults {
name: "android.hardware.vibrator-defaults.cs40l26-private",
cflags: [
"-DATRACE_TAG=(ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)",
"-DLOG_TAG=\"android.hardware.vibrator-cs40l26\"",
],
shared_libs: [
"libbinder",
@ -34,13 +33,14 @@ cc_defaults {
"PixelVibratorBinaryDefaultsPrivate",
"android.hardware.vibrator-defaults.cs40l26-private",
],
include_dirs: [
"external/tinyalsa/include",
],
shared_libs: [
"android.hardware.vibrator.cs40l26-private-cpp",
"libcutils",
"libtinyalsa",
],
include_dirs: [
"external/tinyalsa/include",
],
}
cc_defaults {
@ -53,12 +53,19 @@ cc_defaults {
"android.hardware.vibrator-impl.cs40l26-private",
"libtinyalsa",
],
shared_libs: [
"android.hardware.vibrator.cs40l26-private-cpp",
],
}
cc_library {
name: "android.hardware.vibrator-impl.cs40l26-private",
defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"],
srcs: ["Vibrator.cpp"],
srcs: [
"Vibrator.cpp",
"VibratorSync.cpp",
"VibratorManager.cpp",
],
export_include_dirs: ["."],
vendor_available: true,
visibility: [":__subpackages__"],
@ -70,7 +77,12 @@ cc_binary {
init_rc: ["android.hardware.vibrator-service.cs40l26-private.rc"],
vintf_fragments: ["android.hardware.vibrator-service.cs40l26-private.xml"],
srcs: ["service.cpp"],
shared_libs: ["android.hardware.vibrator-impl.cs40l26-private"],
shared_libs: [
"android.hardware.vibrator-impl.cs40l26-private",
],
cflags: [
"-DLOG_TAG=\"android.hardware.vibrator-cs40l26-private\"",
],
proprietary: true,
}
@ -80,7 +92,27 @@ cc_binary {
init_rc: ["android.hardware.vibrator-service.cs40l26-dual-private.rc"],
vintf_fragments: ["android.hardware.vibrator-service.cs40l26-dual-private.xml"],
srcs: ["service.cpp"],
shared_libs: ["android.hardware.vibrator-impl.cs40l26-private"],
cflags: ["-DVIBRATOR_NAME=\"dual\""],
shared_libs: [
"android.hardware.vibrator-impl.cs40l26-private",
],
cflags: [
"-DVIBRATOR_NAME=\"dual\"",
"-DLOG_TAG=\"android.hardware.vibrator-cs40l26-dual-private\"",
],
proprietary: true,
}
cc_binary {
name: "android.hardware.vibrator-service.cs40l26-stereo-private",
defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"],
init_rc: ["android.hardware.vibrator-service.cs40l26-stereo-private.rc"],
vintf_fragments: ["android.hardware.vibrator-service.cs40l26-stereo-private.xml"],
srcs: ["service-stereo.cpp"],
shared_libs: [
"android.hardware.vibrator-impl.cs40l26-private",
],
cflags: [
"-DLOG_TAG=\"android.hardware.vibrator-cs40l26-stereo-private\"",
],
proprietary: true,
}

View file

@ -250,6 +250,17 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
}
return true;
}
void clearTrigBtn(int fd, struct ff_effect *effect, int8_t id) override {
if ((*effect).trigger.button != 0x00) {
(*effect).trigger.button = 0x00;
if (id < WAVEFORM_MAX_PHYSICAL_INDEX) {
/* Clear the trigger pin setting */
if ((ioctl(fd, EVIOCSFF, effect) < 0)) {
ALOGE("OFF: Failed to edit effect %d (%d): %s", id, errno, strerror(errno));
}
}
}
}
void debug(int fd) override { HwApiBase::debug(fd); }

View file

@ -0,0 +1,123 @@
/*
* 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 <fcntl.h>
#include <linux/gpio.h>
#include <log/log.h>
#include <map>
#include "VibratorManager.h"
#include "utils.h"
namespace aidl {
namespace android {
namespace hardware {
namespace vibrator {
class VibMgrHwApi : public VibratorManager::HwApi {
private:
const uint32_t DEBUG_GPI_PIN = UINT16_MAX;
const uint32_t DEBUG_GPI_PIN_SHIFT = UINT16_MAX;
std::string mPropertyPrefix;
uint32_t mGPIOPin;
uint32_t mGPIOShift;
struct gpiohandle_request mRq;
public:
static std::unique_ptr<VibMgrHwApi> Create() {
auto hwapi = std::unique_ptr<VibMgrHwApi>(new VibMgrHwApi());
return hwapi;
}
bool getGPIO() override {
auto propertyPrefix = std::getenv("PROPERTY_PREFIX");
if (propertyPrefix != NULL) {
mPropertyPrefix = std::string(propertyPrefix);
} else {
ALOGE("GetGPIO: Failed get property prefix!");
return false;
}
mGPIOPin = utils::getProperty(mPropertyPrefix + "gpio.num", DEBUG_GPI_PIN);
if (mGPIOPin == DEBUG_GPI_PIN) {
ALOGE("GetGPIO: Fail to get the GPIO num: %s", strerror(errno));
return false;
}
mGPIOShift = utils::getProperty(mPropertyPrefix + "gpio.shift", DEBUG_GPI_PIN_SHIFT);
if (mGPIOShift == DEBUG_GPI_PIN_SHIFT) {
ALOGE("GetGPIO: Fail to get the GPIO shift num: %s", strerror(errno));
return false;
}
return true;
}
bool initGPIO() override {
const auto gpio_dev = std::string() + "/dev/gpiochip" + std::to_string(mGPIOPin);
int fd = open(gpio_dev.c_str(), O_RDONLY);
if (fd < 0) {
ALOGE("InitGPIO: Unabled to open gpio dev: %s", strerror(errno));
return false;
}
mRq.lineoffsets[0] = mGPIOShift;
mRq.lines = 1;
mRq.flags = GPIOHANDLE_REQUEST_OUTPUT;
int ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &mRq);
close(fd);
if (ret == -1) {
ALOGE("InitGPIO: Unable to line handle from ioctl : %s", strerror(errno));
close(fd);
return false;
}
// Reset gpio status to LOW
struct gpiohandle_data data;
data.values[0] = 0;
ret = ioctl(mRq.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (ret == -1) {
ALOGE("InitGPIO: Unable to set line value using ioctl : %s", strerror(errno));
close(mRq.fd);
return false;
}
return true;
}
bool setTrigger(bool value) override {
struct gpiohandle_data data;
data.values[0] = value;
int ret = ioctl(mRq.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (ret == -1) {
ALOGE("SetTrigger: Unable to set line value using ioctl : %s", strerror(errno));
close(mRq.fd);
return false;
}
close(mRq.fd);
return true;
}
void debug(int fd) override { ALOGD("Debug: %d", fd); }
private:
VibMgrHwApi() { ALOGD("Constructor"); }
};
} // namespace vibrator
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -54,6 +54,7 @@ static constexpr uint32_t MAX_TIME_MS = UINT16_MAX;
static constexpr auto ASYNC_COMPLETION_TIMEOUT = std::chrono::milliseconds(100);
static constexpr auto POLLING_TIMEOUT = 20;
static constexpr auto POLLING_TIMEOUT_IN_SYNC = 100;
static constexpr int32_t COMPOSE_DELAY_MAX_MS = 10000;
/* nsections is 8 bits. Need to preserve 1 section for the first delay before the first effect. */
@ -89,6 +90,16 @@ static constexpr float PWLE_FREQUENCY_MAX_HZ = 1000.00;
static constexpr float PWLE_BW_MAP_SIZE =
1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
/*
* [15] Edge, 0:Falling, 1:Rising
* [14:12] GPI_NUM, 1:GPI1 (with CS40L26A, 1 is the only supported GPI)
* [8] BANK, 0:ROM, 1:RAM
* [7] USE_BUZZGEN, 0:Not buzzgen, 1:buzzgen
* [6:0] WAVEFORM_INDEX
* 0x9100 = 1001 0001 0000 0000: Rising + GPI1 + ROM + Not buzzgen
*/
static constexpr uint32_t GPIO_TRIGGER_CONFIG = 0x9100;
static uint16_t amplitudeToScale(float amplitude, float maximum) {
float ratio = 100; /* Unit: % */
if (maximum != 0)
@ -341,7 +352,10 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
mHwCal->getClickVolLevels(&mClickEffectVol);
mHwCal->getLongVolLevels(&mLongEffectVol);
} else {
ALOGD("Unsupported calibration version: %u!", calVer);
ALOGW("Unsupported calibration version! Using the default calibration value");
mHwCal->getTickVolLevels(&mTickEffectVol);
mHwCal->getClickVolLevels(&mClickEffectVol);
mHwCal->getLongVolLevels(&mLongEffectVol);
}
mHwApi->setF0CompEnable(mHwCal->isF0CompEnabled());
@ -364,7 +378,6 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
}
mSupportedPrimitives = defaultSupportedPrimitives;
}
mHwApi->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
}
@ -406,6 +419,8 @@ ndk::ScopedAStatus Vibrator::off() {
ALOGE("Failed to clean up the composed effect %d", mActiveId);
ret = false;
}
mHwApi->clearTrigBtn(mInputFd, &mFfEffects[mActiveId], mActiveId);
} else {
ALOGV("Vibrator is already off");
}
@ -425,7 +440,14 @@ ndk::ScopedAStatus Vibrator::off() {
ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
const std::shared_ptr<IVibratorCallback> &callback) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::on");
if (isBusy()) {
ALOGD("Vibrator::on, isBusy");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (timeoutMs > MAX_TIME_MS) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
@ -445,6 +467,7 @@ ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
const std::shared_ptr<IVibratorCallback> &callback,
int32_t *_aidl_return) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::perform");
return performEffect(effect, strength, callback, _aidl_return);
}
@ -456,7 +479,9 @@ ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect> *_aidl_retu
}
ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::setAmplitude");
if (amplitude <= 0.0f || amplitude > 1.0f) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
@ -470,7 +495,13 @@ ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
}
ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::setExternalControl");
if (isSynced()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
setGlobalAmplitude(enabled);
if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) {
@ -523,6 +554,7 @@ ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composite,
const std::shared_ptr<IVibratorCallback> &callback) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::compose");
uint16_t size;
uint16_t nextEffectDelay;
@ -635,6 +667,9 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
int errorStatus;
if (isSynced()) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
}
if (!mHwApi->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch),
&mFfEffects[effectIndex], &effectIndex, &errorStatus)) {
delete ch;
@ -647,22 +682,36 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) {
/* Update duration for long/short vibration. */
mFfEffects[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
if (isSynced()) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
}
if (!mHwApi->setFFEffect(mInputFd, &mFfEffects[effectIndex],
static_cast<uint16_t>(timeoutMs))) {
ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
mActiveId = effectIndex;
/* Play the event now. */
if (!mHwApi->setFFPlay(mInputFd, effectIndex, true)) {
ALOGE("Failed to play effect %d (%d): %s", effectIndex, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
{
const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
mActiveId = effectIndex;
if (isSynced() &&
(effectIndex == WAVEFORM_CLICK_INDEX || effectIndex == WAVEFORM_LIGHT_TICK_INDEX)) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
if (!mHwApi->setFFEffect(mInputFd, &mFfEffects[effectIndex], mFfEffects[effectIndex].replay.length)) {
ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
} else if (!isSynced()) {
// /* Play the event now. */
if (!mHwApi->setFFPlay(mInputFd, effectIndex, true)) {
ALOGE("Failed to play effect %d (%d): %s", effectIndex, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
}
mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback);
return ndk::ScopedAStatus::ok();
}
@ -999,6 +1048,39 @@ bool Vibrator::isUnderExternalControl() {
return mIsUnderExternalControl;
}
// BnVibratorSync APIs
Status Vibrator::prepareSynced(const sp<IVibratorSyncCallback> &callback) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::prepareSynced");
if (isBusy() || isSynced() || isUnderExternalControl()) {
ALOGE("Vibrator::prepareSynced, isBusy or isSynced");
return Status::fromExceptionCode(EX_ILLEGAL_STATE);
}
mSyncedCallback = callback;
return Status::ok();
}
Status Vibrator::cancelSynced() {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::cancelSynced");
if (!isSynced()) {
ALOGE("Vibrator::cancelSynced, isSynced");
return Status::fromExceptionCode(EX_ILLEGAL_STATE);
}
off();
mSyncedCallback = nullptr;
return Status::ok();
}
// BnCInterface APIs
binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
if (fd < 0) {
ALOGE("Called debug() with invalid fd.");
@ -1022,14 +1104,15 @@ binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
dprintf(fd, " FF effect:\n");
dprintf(fd, " Physical waveform:\n");
dprintf(fd, "\tId\tIndex\tt ->\tt'\n");
dprintf(fd, "\tId\tIndex\tt ->\tt'\ttrigger button\n");
for (uint8_t effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
dprintf(fd, "\t%d\t%d\t%d\t%d\n", mFfEffects[effectId].id,
dprintf(fd, "\t%d\t%d\t%d\t%d\t%X\n", mFfEffects[effectId].id,
mFfEffects[effectId].u.periodic.custom_data[1], mEffectDurations[effectId],
mFfEffects[effectId].replay.length);
mFfEffects[effectId].replay.length, mFfEffects[effectId].trigger.button);
}
dprintf(fd, " OWT waveform:\n");
dprintf(fd, "\tId\tBytes\tData\n");
dprintf(fd, "\tId\tBytes\tData\ttrigger button\n");
for (uint8_t effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX;
effectId++) {
uint32_t numBytes = mFfEffects[effectId].u.periodic.custom_len * 2;
@ -1042,7 +1125,8 @@ binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
i))
<< " ";
}
dprintf(fd, "\t%d\t%d\t{%s}\n", mFfEffects[effectId].id, numBytes, ss.str().c_str());
dprintf(fd, "\t%d\t%d\t{%s}\t%X\n", mFfEffects[effectId].id, numBytes, ss.str().c_str(),
mFfEffects[effectId].trigger.button);
}
dprintf(fd, "\n");
@ -1271,17 +1355,26 @@ ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLev
}
void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
if (!mHwApi->pollVibeState(VIBE_STATE_HAPTIC, POLLING_TIMEOUT)) {
ALOGW("Failed to get state \"Haptic\"");
}
mHwApi->pollVibeState(VIBE_STATE_STOPPED);
ALOGD("Callback status in waitForComplete(): mSync: %d, callBack: %d", isSynced(),
(callback != nullptr));
const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
ALOGE("Failed to clean up the composed effect %d", mActiveId);
if (!mHwApi->pollVibeState(VIBE_STATE_HAPTIC,
(mSyncedCallback) ? POLLING_TIMEOUT_IN_SYNC : POLLING_TIMEOUT)) {
ALOGV("Fail to get state \"Haptic\"");
}
mHwApi->pollVibeState(VIBE_STATE_STOPPED);
{
const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
ALOGE("Failed to clean up the composed effect %d", mActiveId);
}
mHwApi->clearTrigBtn(mInputFd, &mFfEffects[mActiveId], mActiveId);
mActiveId = -1;
}
mActiveId = -1;
if (callback) {
auto ret = callback->onComplete();
@ -1289,6 +1382,13 @@ void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
ALOGE("Failed completion callback: %d", ret.getExceptionCode());
}
}
if (mSyncedCallback) {
auto ret = mSyncedCallback->onComplete();
if (!ret.isOk()) {
ALOGE("Failed completion mSyncedCallback: %d", ret.exceptionCode());
}
mSyncedCallback = nullptr;
}
}
uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
@ -1321,6 +1421,16 @@ uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
return volLevel;
}
bool Vibrator::isBusy() {
auto timeout = std::chrono::seconds::zero();
auto status = mAsyncHandle.wait_for(timeout);
return status != std::future_status::ready;
}
bool Vibrator::isSynced() {
return (mSyncedCallback != nullptr);
}
} // namespace vibrator
} // namespace hardware
} // namespace android

View file

@ -17,6 +17,7 @@
#include <aidl/android/hardware/vibrator/BnVibrator.h>
#include <android-base/unique_fd.h>
#include <android/hardware/vibrator/BnVibratorSyncCallback.h>
#include <linux/input.h>
#include <tinyalsa/asoundlib.h>
@ -29,6 +30,10 @@ namespace android {
namespace hardware {
namespace vibrator {
using ::android::sp;
using ::android::binder::Status;
using ::android::hardware::vibrator::IVibratorSyncCallback;
class Vibrator : public BnVibrator {
public:
// APIs for interfacing with the kernel driver.
@ -80,6 +85,8 @@ class Vibrator : public BnVibrator {
int *status) = 0;
// Erase OWT waveform
virtual bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) = 0;
// Erase trigger button information
virtual void clearTrigBtn(int fd, struct ff_effect *effect, int8_t id) = 0;
// Emit diagnostic information to the given file.
virtual void debug(int fd) = 0;
};
@ -121,6 +128,7 @@ class Vibrator : public BnVibrator {
public:
Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal);
// BnVibrator APIs
ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return) override;
ndk::ScopedAStatus off() override;
ndk::ScopedAStatus on(int32_t timeoutMs,
@ -152,6 +160,11 @@ class Vibrator : public BnVibrator {
ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
const std::shared_ptr<IVibratorCallback> &callback) override;
// BnVibratorSync APIs
Status prepareSynced(const sp<IVibratorSyncCallback> &callback);
Status cancelSynced();
// BnCInterface APIs
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
private:
@ -182,6 +195,9 @@ class Vibrator : public BnVibrator {
bool hasHapticAlsaDevice();
bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device);
bool isBusy();
bool isSynced();
std::unique_ptr<HwApi> mHwApi;
std::unique_ptr<HwCal> mHwCal;
uint32_t mF0Offset;
@ -203,6 +219,9 @@ class Vibrator : public BnVibrator {
uint32_t mSupportedPrimitivesBits = 0x0;
std::vector<CompositePrimitive> mSupportedPrimitives;
bool mConfigHapticAlsaDeviceDone{false};
// prevent concurrent execution of IVibrator and IVibratorSync APIs
sp<IVibratorSyncCallback> mSyncedCallback;
std::recursive_mutex mApiMutex;
};
} // namespace vibrator

View file

@ -0,0 +1,210 @@
/*
* 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 "VibratorManager.h"
#include <android/hardware/vibrator/BnVibratorSyncCallback.h>
#include <log/log.h>
#include <utils/Trace.h>
#include <numeric>
namespace aidl {
namespace android {
namespace hardware {
namespace vibrator {
using ::android::sp;
using ::android::binder::Status;
using ::android::hardware::vibrator::BnVibratorSyncCallback;
class VibratorSyncCallback : public BnVibratorSyncCallback {
public:
Status onComplete() override {
mPromise.set_value();
return Status::ok();
}
auto getFuture() { return mPromise.get_future(); }
private:
std::promise<void> mPromise;
};
VibratorManager::VibratorManager(std::unique_ptr<HwApi> hwapi,
const std::vector<VibratorTuple> &&vibrators)
: mHwApi(std::move(hwapi)), mVibrators(std::move(vibrators)), mAsyncHandle(std::async([] {})) {
mGPIOStatus = mHwApi->getGPIO();
}
// BnVibratorManager APIs
ndk::ScopedAStatus VibratorManager::getCapabilities(int32_t *_aidl_return) {
ATRACE_NAME("VibratorManager::getCapabilities");
int32_t ret =
IVibratorManager::CAP_SYNC | IVibratorManager::CAP_PREPARE_ON |
IVibratorManager::CAP_PREPARE_PERFORM | IVibratorManager::CAP_PREPARE_COMPOSE |
IVibratorManager::CAP_MIXED_TRIGGER_ON | IVibratorManager::CAP_MIXED_TRIGGER_PERFORM |
IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE | IVibratorManager::CAP_TRIGGER_CALLBACK;
*_aidl_return = ret;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::getVibratorIds(std::vector<int> *_aidl_return) {
ATRACE_NAME("VibratorManager::getVibratorIds");
_aidl_return->resize(mVibrators.size());
std::iota(_aidl_return->begin(), _aidl_return->end(), 0);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::getVibrator(int vibratorId,
std::shared_ptr<IVibrator> *_aidl_return) {
ATRACE_NAME("VibratorManager::getVibrator");
if (!mGPIOStatus) {
ALOGE("GetVibrator: GPIO status error");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (vibratorId >= mVibrators.size()) {
ALOGE("GetVibrator: wrong requested vibrator ID");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
std::tie(*_aidl_return, std::ignore) = mVibrators.at(vibratorId);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::prepareSynced(const std::vector<int32_t> &ids) {
ATRACE_NAME("VibratorManager::prepareSynced");
if (!mGPIOStatus) {
ALOGE("GetVibrator: GPIO status error");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (ids.empty()) {
ALOGE("PrepareSynced: No vibrator could be synced");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (!mSyncContext.empty()) {
ALOGE("PrepareSynced: mSyncContext is not EMPTY!!!");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (isBusy()) {
ALOGE("PrepareSynced: IS BUSY!!!");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
for (auto &id : ids) {
auto &[vib, ext] = mVibrators.at(id);
auto callback = sp<VibratorSyncCallback>::make();
ext->prepareSynced(callback);
mSyncContext.emplace_back(id, callback->getFuture());
}
if (mHwApi->initGPIO()) {
return ndk::ScopedAStatus::ok();
} else {
ALOGE("PrepareSynced: GPIO status init fail");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
ndk::ScopedAStatus VibratorManager::triggerSynced(
const std::shared_ptr<IVibratorCallback> &callback) {
ATRACE_NAME("VibratorManager::triggerSynced");
if (isBusy()) {
ALOGE("TriggerSynced isBusy");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
mHwApi->setTrigger(true);
doAsync([=]() {
{
std::shared_lock lock(mContextMutex);
for (auto &[id, future] : mSyncContext) {
future.wait();
}
}
{
std::unique_lock lock(mContextMutex);
mSyncContext.clear();
}
if (callback) {
auto ret = callback->onComplete();
if (!ret.isOk()) {
ALOGE("Failed completion callback: %d", ret.getExceptionCode());
}
ALOGD("Callback in MANAGER onComplete()");
}
});
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::cancelSynced() {
ATRACE_NAME("VibratorManager::cancelSynced");
mHwApi->setTrigger(false);
{
std::shared_lock lock(mContextMutex);
for (auto &[id, future] : mSyncContext) {
auto &[vib, ext] = mVibrators.at(id);
ext->cancelSynced();
}
}
{
std::unique_lock lock(mContextMutex);
mSyncContext.clear();
}
mAsyncHandle.wait();
return ndk::ScopedAStatus::ok();
}
// BnCInterface APIs
binder_status_t VibratorManager::dump(int fd, const char ** /*args*/, uint32_t /*numArgs*/) {
if (fd < 0) {
ALOGE("Called debug() with invalid fd.");
return STATUS_OK;
}
mHwApi->debug(fd);
fsync(fd);
return STATUS_OK;
}
// Private Methods
template <typename Func, typename... Args>
void VibratorManager::doAsync(Func &&func, Args &&...args) {
mAsyncHandle = std::async(func, std::forward<Args>(args)...);
}
bool VibratorManager::isBusy() {
auto timeout = std::chrono::seconds::zero();
auto status = mAsyncHandle.wait_for(timeout);
return status != std::future_status::ready;
}
} // namespace vibrator
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,83 @@
/*
* 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 <aidl/android/hardware/vibrator/BnVibratorManager.h>
#include <android/hardware/vibrator/IVibratorSync.h>
#include <future>
#include <shared_mutex>
namespace aidl {
namespace android {
namespace hardware {
namespace vibrator {
using ::android::hardware::vibrator::IVibratorSync;
class VibratorManager : public BnVibratorManager {
public:
// APIs for interfacing with the kernel driver.
class HwApi {
public:
virtual ~HwApi() = default;
// Get the GPIO pin num and address shift information
virtual bool getGPIO() = 0;
// Init the GPIO function
virtual bool initGPIO() = 0;
// Trigger activation of the synchronized vibrators.
virtual bool setTrigger(bool value) = 0;
// Emit diagnostic information to the given file.
virtual void debug(int fd) = 0;
};
using VibratorTuple = std::tuple<std::shared_ptr<IVibrator>, ::android::sp<IVibratorSync>>;
public:
VibratorManager(std::unique_ptr<HwApi> hwapi, const std::vector<VibratorTuple> &&vibrators);
// BnVibratorManager APIs
ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return);
ndk::ScopedAStatus getVibratorIds(std::vector<int> *_aidl_return);
ndk::ScopedAStatus getVibrator(int vibratorId, std::shared_ptr<IVibrator> *_aidl_return);
ndk::ScopedAStatus prepareSynced(const std::vector<int32_t> &ids) override;
ndk::ScopedAStatus triggerSynced(const std::shared_ptr<IVibratorCallback> &callback) override;
ndk::ScopedAStatus cancelSynced() override;
// BnCInterface APIs
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
private:
template <typename Func, typename... Args>
void doAsync(Func &&func, Args &&...args);
bool isBusy();
private:
using SyncContext = std::tuple<int32_t, std::future<void>>;
const std::unique_ptr<HwApi> mHwApi;
const std::vector<VibratorTuple> mVibrators;
std::vector<SyncContext> mSyncContext;
std::shared_mutex mContextMutex;
std::future<void> mAsyncHandle;
bool mGPIOStatus;
};
} // namespace vibrator
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,39 @@
/*
* 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 "VibratorSync.h"
namespace android {
namespace hardware {
namespace vibrator {
VibratorSync::VibratorSync(std::shared_ptr<Vibrator> vibrator) : mVibrator(vibrator) {
ALOGE("VibratorSync constructor");
}
// BnVibratorSync APIs
binder::Status VibratorSync::prepareSynced(const sp<IVibratorSyncCallback> &callback) {
return mVibrator->prepareSynced(callback);
}
binder::Status VibratorSync::cancelSynced() {
return mVibrator->cancelSynced();
}
} // namespace vibrator
} // namespace hardware
} // namespace android

View file

@ -0,0 +1,46 @@
/*
* 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 <android/hardware/vibrator/BnVibratorSync.h>
#include <array>
#include <fstream>
#include <future>
#include "Vibrator.h"
namespace android {
namespace hardware {
namespace vibrator {
using ::aidl::android::hardware::vibrator::Vibrator;
class VibratorSync : public BnVibratorSync {
public:
VibratorSync(std::shared_ptr<Vibrator> vibrator);
// BnVibratorSync APIs
binder::Status prepareSynced(const android::sp<IVibratorSyncCallback> &callback) override;
binder::Status cancelSynced() override;
private:
std::shared_ptr<Vibrator> mVibrator;
};
} // namespace vibrator
} // namespace hardware
} // namespace android

View file

@ -0,0 +1,13 @@
aidl_interface {
name: "android.hardware.vibrator.cs40l26-private",
srcs: [
"android/hardware/vibrator/*.aidl"
],
backend: {
java: {
enabled: false,
},
},
unstable: true,
vendor_available: true,
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (C) 2020 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 android.hardware.vibrator;
import android.hardware.vibrator.IVibratorSyncCallback;
interface IVibratorSync {
void prepareSynced(in IVibratorSyncCallback callback);
void cancelSynced();
}

View file

@ -0,0 +1,21 @@
/*
* 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.
*/
package android.hardware.vibrator;
interface IVibratorSyncCallback {
oneway void onComplete();
}

View file

@ -0,0 +1,14 @@
on property:vendor.all.modules.ready=1
wait_for_prop init.svc.vendor.vibrator.cs40l26 running
wait_for_prop init.svc.vendor.vibrator.cs40l26-dual running
enable vendor.vibrator.cs40l26-stereo
service vendor.vibrator.cs40l26-stereo /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-stereo-private
class hal
user root
group root
setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
disabled

View file

@ -0,0 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>2</version>
<fqname>IVibratorManager/default</fqname>
</hal>
</manifest>

View file

@ -1,6 +1,7 @@
PRODUCT_PACKAGES += \
android.hardware.vibrator-service.cs40l26 \
android.hardware.vibrator-service.cs40l26-dual \
android.hardware.vibrator-service.cs40l26-private \
android.hardware.vibrator-service.cs40l26-dual-private \
android.hardware.vibrator-service.cs40l26-stereo-private \
BOARD_SEPOLICY_DIRS += \
device/google/felix-sepolicy/vibrator/common \

View file

@ -0,0 +1,82 @@
/*
* 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 "VibMgrHwApi.h"
#include "VibratorManager.h"
using ::aidl::android::hardware::vibrator::IVibrator;
using ::aidl::android::hardware::vibrator::VibMgrHwApi;
using ::aidl::android::hardware::vibrator::VibratorManager;
using ::android::ProcessState;
using ::android::String16;
using ::android::waitForService;
using ::android::hardware::vibrator::IVibratorSync;
#if !defined(VIBRATOR_NAME)
#define VIBRATOR_NAME "default"
#endif
using ndk::SharedRefBase;
using ndk::SpAIBinder;
int main() {
auto hwapi = VibMgrHwApi::Create();
if (!hwapi) {
return EXIT_FAILURE;
}
const std::string vibratorInstances[] = {
"default",
"dual",
};
std::vector<VibratorManager::VibratorTuple> vibrators;
ProcessState::initWithDriver("/dev/vndbinder");
for (auto &instance : vibratorInstances) {
const auto svcName = std::string() + IVibrator::descriptor + "/" + instance;
const auto extName = std::stringstream() << IVibratorSync::descriptor << "/" << instance;
SpAIBinder svcBinder;
svcBinder = SpAIBinder(AServiceManager_getService(svcName.c_str()));
auto svc = IVibrator::fromBinder(svcBinder);
auto ext = waitForService<IVibratorSync>(String16(extName.str().c_str()));
vibrators.emplace_back(svc, ext);
}
auto mgr = ndk::SharedRefBase::make<VibratorManager>(std::move(hwapi), std::move(vibrators));
binder_status_t status;
const std::string mgrInst = std::string() + VibratorManager::descriptor + "/" VIBRATOR_NAME;
status = AServiceManager_addService(mgr->asBinder().get(), mgrInst.c_str());
LOG_ALWAYS_FATAL_IF(status != STATUS_OK);
// Only used for callbacks
ProcessState::self()->setThreadPoolMaxThreadCount(1);
ProcessState::self()->startThreadPool();
ABinderProcess_setThreadPoolMaxThreadCount(0);
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
}

View file

@ -21,6 +21,7 @@
#include "Hardware.h"
#include "Vibrator.h"
#include "VibratorSync.h"
using ::aidl::android::hardware::vibrator::HwApi;
using ::aidl::android::hardware::vibrator::HwCal;
@ -29,6 +30,7 @@ using ::android::defaultServiceManager;
using ::android::ProcessState;
using ::android::sp;
using ::android::String16;
using ::android::hardware::vibrator::VibratorSync;
#if !defined(VIBRATOR_NAME)
#define VIBRATOR_NAME "default"
@ -39,8 +41,13 @@ int main() {
std::make_unique<HwCal>());
const auto svcName = std::string() + svc->descriptor + "/" + VIBRATOR_NAME;
auto ext = sp<VibratorSync>::make(svc);
const auto extName = std::stringstream() << ext->descriptor << "/" << VIBRATOR_NAME;
ProcessState::initWithDriver("/dev/vndbinder");
defaultServiceManager()->addService(String16(extName.str().c_str()), ext);
auto svcBinder = svc->asBinder();
binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str());
LOG_ALWAYS_FATAL_IF(status != STATUS_OK);

View file

@ -43,6 +43,7 @@ class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
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_METHOD3(clearTrigBtn, void(int fd, struct ff_effect *effect, int8_t index));
MOCK_METHOD1(debug, void(int fd));
~MockApi() override { destructor(); };