This repository has been archived on 2025-09-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
hardware_oplus-CherishOS/aidl/vibrator/Vibrator.cpp
jabashque 12fcd01d96 vibrator: Redo LedVibratorDevice effects and vibrator timeout handling
For aw8697_haptic, OOS 12 never really properly implemented any effect
other than CLICK and HEAVY_CLICK. Let's implement our own TEXTURE_TICK,
TICK, CLICK, and HEAVY_CLICK so that the user can actually feel a
noticeable difference between each haptic effect.

As a quick summary:
- CLICK: same as oplus CLICK, but with vmax set to 2500.
- DOUBLE_CLICK: replaced with our CLICK played twice in a row, w/ a
100ms delay
- TICK: same as oplus CLICK, but with vmax set to 1400.
- HEAVY_CLICK: same as oplus CLICK, but with vmax set to 2500 and
  waveform_index set to 4.
- TEXTURE_TICK: same as oplus CLICK, but with vmax set to 60 and
  waveform_index set to 2.

In addition, modify LedVibratorDevice's on() to better reflect the
timeout duration actually being passed in to the function. The original
tried to change waveforms instead for various durations at 102 ms or
lower, which didn't really work out in practice for our vibrator.

Change-Id: Ifb769bc2292774afa4977026787a7882430c09d0
2022-11-12 20:20:44 +00:00

650 lines
22 KiB
C++

/*
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LOG_TAG "vendor.qti.vibrator"
#include <cutils/properties.h>
#include <dirent.h>
#include <inttypes.h>
#include <linux/input.h>
#include <log/log.h>
#include <string.h>
#include <sys/ioctl.h>
#include <thread>
#include "include/Vibrator.h"
#ifdef USE_EFFECT_STREAM
#include "effect.h"
#endif
namespace aidl {
namespace android {
namespace hardware {
namespace vibrator {
#define STRONG_MAGNITUDE 0x7fff
#define MEDIUM_MAGNITUDE 0x5fff
#define LIGHT_MAGNITUDE 0x3fff
#define INVALID_VALUE -1
#define CUSTOM_DATA_LEN 3
#define NAME_BUF_SIZE 32
#define MSM_CPU_LAHAINA 415
#define APQ_CPU_LAHAINA 439
#define MSM_CPU_SHIMA 450
#define MSM_CPU_SM8325 501
#define APQ_CPU_SM8325P 502
#define MSM_CPU_YUPIK 475
#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8)))
#define LED_DEVICE "/sys/class/leds/vibrator"
InputFFDevice::InputFFDevice()
{
DIR *dp;
FILE *fp = NULL;
struct dirent *dir;
uint8_t ffBitmask[FF_CNT / 8];
char devicename[PATH_MAX];
const char *INPUT_DIR = "/dev/input/";
char name[NAME_BUF_SIZE];
int fd, ret;
int soc = property_get_int32("ro.vendor.qti.soc_id", -1);
mVibraFd = INVALID_VALUE;
mSupportGain = false;
mSupportEffects = false;
mSupportExternalControl = false;
mCurrAppId = INVALID_VALUE;
mCurrMagnitude = 0x7fff;
mInExternalControl = false;
dp = opendir(INPUT_DIR);
if (!dp) {
ALOGE("open %s failed, errno = %d", INPUT_DIR, errno);
return;
}
memset(ffBitmask, 0, sizeof(ffBitmask));
while ((dir = readdir(dp)) != NULL){
if (dir->d_name[0] == '.' &&
(dir->d_name[1] == '\0' ||
(dir->d_name[1] == '.' && dir->d_name[2] == '\0')))
continue;
snprintf(devicename, PATH_MAX, "%s%s", INPUT_DIR, dir->d_name);
fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR));
if (fd < 0) {
ALOGE("open %s failed, errno = %d", devicename, errno);
continue;
}
ret = TEMP_FAILURE_RETRY(ioctl(fd, EVIOCGNAME(sizeof(name)), name));
if (ret == -1) {
ALOGE("get input device name %s failed, errno = %d\n", devicename, errno);
close(fd);
continue;
}
if (strcmp(name, "qcom-hv-haptics") && strcmp(name, "qti-haptics")) {
ALOGD("not a qcom/qti haptics device\n");
close(fd);
continue;
}
ALOGI("%s is detected at %s\n", name, devicename);
ret = TEMP_FAILURE_RETRY(ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffBitmask)), ffBitmask));
if (ret == -1) {
ALOGE("ioctl failed, errno = %d", errno);
close(fd);
continue;
}
if (test_bit(FF_CONSTANT, ffBitmask) ||
test_bit(FF_PERIODIC, ffBitmask)) {
mVibraFd = fd;
if (test_bit(FF_CUSTOM, ffBitmask))
mSupportEffects = true;
if (test_bit(FF_GAIN, ffBitmask))
mSupportGain = true;
if (soc <= 0 && (fp = fopen("/sys/devices/soc0/soc_id", "r")) != NULL) {
fscanf(fp, "%u", &soc);
fclose(fp);
}
switch (soc) {
case MSM_CPU_LAHAINA:
case APQ_CPU_LAHAINA:
case MSM_CPU_SHIMA:
case MSM_CPU_SM8325:
case APQ_CPU_SM8325P:
case MSM_CPU_YUPIK:
mSupportExternalControl = true;
break;
default:
mSupportExternalControl = false;
break;
}
break;
}
close(fd);
}
closedir(dp);
}
/** Play vibration
*
* @param effectId: ID of the predefined effect will be played. If effectId is valid
* (non-negative value), the timeoutMs value will be ignored, and the
* real playing length will be set in param@playLengtMs and returned
* to VibratorService. If effectId is invalid, value in param@timeoutMs
* will be used as the play length for playing a constant effect.
* @param timeoutMs: playing length, non-zero means playing, zero means stop playing.
* @param playLengthMs: the playing length in ms unit which will be returned to
* VibratorService if the request is playing a predefined effect.
* The custom_data in periodic is reused for returning the playLengthMs
* from kernel space to userspace if the pattern is defined in kernel
* driver. It's been defined with following format:
* <effect-ID, play-time-in-seconds, play-time-in-milliseconds>.
* The effect-ID is used for passing down the predefined effect to
* kernel driver, and the rest two parameters are used for returning
* back the real playing length from kernel driver.
*/
int InputFFDevice::play(int effectId, uint32_t timeoutMs, long *playLengthMs) {
struct ff_effect effect;
struct input_event play;
int16_t data[CUSTOM_DATA_LEN] = {0, 0, 0};
int ret;
#ifdef USE_EFFECT_STREAM
const struct effect_stream *stream;
#endif
/* For QMAA compliance, return OK even if vibrator device doesn't exist */
if (mVibraFd == INVALID_VALUE) {
if (playLengthMs != NULL)
*playLengthMs = 0;
return 0;
}
if (timeoutMs != 0) {
if (mCurrAppId != INVALID_VALUE) {
ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
if (ret == -1) {
ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
goto errout;
}
mCurrAppId = INVALID_VALUE;
}
memset(&effect, 0, sizeof(effect));
if (effectId != INVALID_VALUE) {
data[0] = effectId;
effect.type = FF_PERIODIC;
effect.u.periodic.waveform = FF_CUSTOM;
effect.u.periodic.magnitude = mCurrMagnitude;
effect.u.periodic.custom_data = data;
effect.u.periodic.custom_len = sizeof(int16_t) * CUSTOM_DATA_LEN;
#ifdef USE_EFFECT_STREAM
stream = get_effect_stream(effectId);
if (stream != NULL) {
effect.u.periodic.custom_data = (int16_t *)stream;
effect.u.periodic.custom_len = sizeof(*stream);
}
#endif
} else {
effect.type = FF_CONSTANT;
effect.u.constant.level = mCurrMagnitude;
effect.replay.length = timeoutMs;
}
effect.id = mCurrAppId;
effect.replay.delay = 0;
ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCSFF, &effect));
if (ret == -1) {
ALOGE("ioctl EVIOCSFF failed, errno = %d", -errno);
goto errout;
}
mCurrAppId = effect.id;
if (effectId != INVALID_VALUE && playLengthMs != NULL) {
*playLengthMs = data[1] * 1000 + data[2];
#ifdef USE_EFFECT_STREAM
if (stream != NULL && stream->play_rate_hz != 0)
*playLengthMs = ((stream->length * 1000) / stream->play_rate_hz) + 1;
#endif
}
play.value = 1;
play.type = EV_FF;
play.code = mCurrAppId;
play.time.tv_sec = 0;
play.time.tv_usec = 0;
ret = TEMP_FAILURE_RETRY(write(mVibraFd, (const void*)&play, sizeof(play)));
if (ret == -1) {
ALOGE("write failed, errno = %d\n", -errno);
ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
if (ret == -1)
ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
goto errout;
}
} else if (mCurrAppId != INVALID_VALUE) {
ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
if (ret == -1) {
ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
goto errout;
}
mCurrAppId = INVALID_VALUE;
}
return 0;
errout:
mCurrAppId = INVALID_VALUE;
return ret;
}
int InputFFDevice::on(int32_t timeoutMs) {
return play(INVALID_VALUE, timeoutMs, NULL);
}
int InputFFDevice::off() {
return play(INVALID_VALUE, 0, NULL);
}
int InputFFDevice::setAmplitude(uint8_t amplitude) {
int tmp, ret;
struct input_event ie;
/* For QMAA compliance, return OK even if vibrator device doesn't exist */
if (mVibraFd == INVALID_VALUE)
return 0;
tmp = amplitude * (STRONG_MAGNITUDE - LIGHT_MAGNITUDE) / 255;
tmp += LIGHT_MAGNITUDE;
ie.type = EV_FF;
ie.code = FF_GAIN;
ie.value = tmp;
ret = TEMP_FAILURE_RETRY(write(mVibraFd, &ie, sizeof(ie)));
if (ret == -1) {
ALOGE("write FF_GAIN failed, errno = %d", -errno);
return ret;
}
mCurrMagnitude = tmp;
return 0;
}
int InputFFDevice::playEffect(int effectId, EffectStrength es, long *playLengthMs) {
switch (es) {
case EffectStrength::LIGHT:
mCurrMagnitude = LIGHT_MAGNITUDE;
break;
case EffectStrength::MEDIUM:
mCurrMagnitude = MEDIUM_MAGNITUDE;
break;
case EffectStrength::STRONG:
mCurrMagnitude = STRONG_MAGNITUDE;
break;
default:
return -1;
}
return play(effectId, INVALID_VALUE, playLengthMs);
}
LedVibratorDevice::LedVibratorDevice() {
char devicename[PATH_MAX];
int fd;
mDetected = false;
snprintf(devicename, sizeof(devicename), "%s/%s", LED_DEVICE, "activate");
fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR));
if (fd < 0) {
ALOGE("open %s failed, errno = %d", devicename, errno);
return;
}
mDetected = true;
}
int LedVibratorDevice::write_value(const char *file, const char *value) {
int fd;
int ret;
fd = TEMP_FAILURE_RETRY(open(file, O_WRONLY));
if (fd < 0) {
ALOGE("open %s failed, errno = %d", file, errno);
return -errno;
}
ret = TEMP_FAILURE_RETRY(write(fd, value, strlen(value) + 1));
if (ret == -1) {
ret = -errno;
} else if (ret != strlen(value) + 1) {
/* even though EAGAIN is an errno value that could be set
by write() in some cases, none of them apply here. So, this return
value can be clearly identified when debugging and suggests the
caller that it may try to call vibrator_on() again */
ret = -EAGAIN;
} else {
ret = 0;
}
errno = 0;
close(fd);
return ret;
}
int LedVibratorDevice::write_value(const char *file, int value) {
return write_value(file, std::to_string(value).c_str());
}
int LedVibratorDevice::on(int32_t timeoutMs) {
int ret = 0;
if (timeoutMs <= 0) {
return ret;
} else if (timeoutMs <= 20) {
ret |= write_value(LED_DEVICE "/vmax", timeoutMs * 10);
} else {
ret |= write_value(LED_DEVICE "/vmax", 1600);
}
ret |= write_value(LED_DEVICE "/waveform_index", 7);
ret |= write_value(LED_DEVICE "/duration", timeoutMs);
ret |= write_value(LED_DEVICE "/state", "1");
ret |= write_value(LED_DEVICE "/activate", "1");
ret |= write_value(LED_DEVICE "/activate", "0");
return ret;
}
int LedVibratorDevice::onWaveform(int waveformIndex) {
int ret = 0;
ret |= write_value(LED_DEVICE "/rtp", "0");
ret |= write_value(LED_DEVICE "/vmax", "1600");
ret |= write_value(LED_DEVICE "/waveform_index", waveformIndex);
ret |= write_value(LED_DEVICE "/brightness", "1");
ret |= write_value(LED_DEVICE "/rtp", "0");
return ret;
}
int LedVibratorDevice::off()
{
char file[PATH_MAX];
int ret;
snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate");
ret = write_value(file, "0");
return ret;
}
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
*_aidl_return = IVibrator::CAP_ON_CALLBACK;
if (ledVib.mDetected) {
*_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK;
ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
return ndk::ScopedAStatus::ok();
}
if (ff.mSupportGain)
*_aidl_return |= IVibrator::CAP_AMPLITUDE_CONTROL;
if (ff.mSupportEffects)
*_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK;
if (ff.mSupportExternalControl)
*_aidl_return |= IVibrator::CAP_EXTERNAL_CONTROL;
ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::off() {
int ret;
ALOGD("QTI Vibrator off");
if (ledVib.mDetected)
ret = ledVib.off();
else
ret = ff.off();
if (ret != 0)
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
const std::shared_ptr<IVibratorCallback>& callback) {
int ret;
ALOGD("Vibrator on for timeoutMs: %d", timeoutMs);
if (ledVib.mDetected)
ret = ledVib.on(timeoutMs);
else
ret = ff.on(timeoutMs);
if (ret != 0)
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
if (callback != nullptr) {
std::thread([=] {
ALOGD("Starting on on another thread");
usleep(timeoutMs * 1000);
ALOGD("Notifying on complete");
if (!callback->onComplete().isOk()) {
ALOGE("Failed to call onComplete");
}
}).detach();
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength es, const std::shared_ptr<IVibratorCallback>& callback, int32_t* _aidl_return) {
long playLengthMs;
int ret;
ALOGD("Vibrator perform effect %d", effect);
if (ledVib.mDetected) {
switch (effect) {
case Effect::CLICK:
ledVib.write_value(LED_DEVICE "/rtp", "0");
ledVib.write_value(LED_DEVICE "/vmax", "2500");
ledVib.write_value(LED_DEVICE "/waveform_index", "1");
ledVib.write_value(LED_DEVICE "/brightness", "1");
ledVib.write_value(LED_DEVICE "/rtp", "0");
break;
case Effect::DOUBLE_CLICK:
ledVib.write_value(LED_DEVICE "/rtp", "0");
ledVib.write_value(LED_DEVICE "/vmax", "2500");
ledVib.write_value(LED_DEVICE "/waveform_index", "1");
ledVib.write_value(LED_DEVICE "/brightness", "1");
ledVib.write_value(LED_DEVICE "/rtp", "0");
usleep(100 * 1000);
ledVib.write_value(LED_DEVICE "/rtp", "0");
ledVib.write_value(LED_DEVICE "/vmax", "2500");
ledVib.write_value(LED_DEVICE "/waveform_index", "1");
ledVib.write_value(LED_DEVICE "/brightness", "1");
ledVib.write_value(LED_DEVICE "/rtp", "0");
break;
case Effect::TICK:
ledVib.write_value(LED_DEVICE "/rtp", "0");
ledVib.write_value(LED_DEVICE "/vmax", "1400");
ledVib.write_value(LED_DEVICE "/waveform_index", "1");
ledVib.write_value(LED_DEVICE "/brightness", "1");
ledVib.write_value(LED_DEVICE "/rtp", "0");
break;
case Effect::HEAVY_CLICK:
ledVib.write_value(LED_DEVICE "/rtp", "0");
ledVib.write_value(LED_DEVICE "/vmax", "2500");
ledVib.write_value(LED_DEVICE "/waveform_index", "4");
ledVib.write_value(LED_DEVICE "/brightness", "1");
ledVib.write_value(LED_DEVICE "/rtp", "0");
break;
case Effect::TEXTURE_TICK:
ledVib.write_value(LED_DEVICE "/rtp", "0");
ledVib.write_value(LED_DEVICE "/vmax", "60");
ledVib.write_value(LED_DEVICE "/waveform_index", "2");
ledVib.write_value(LED_DEVICE "/brightness", "1");
ledVib.write_value(LED_DEVICE "/rtp", "0");
break;
default:
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
// Return magic value for play length so that we won't end up calling on() / off()
playLengthMs = 150;
} else {
#ifdef TARGET_SUPPORTS_OFFLOAD
if (effect < Effect::CLICK || effect > Effect::RINGTONE_15)
#else
if (effect < Effect::CLICK || effect > Effect::HEAVY_CLICK)
#endif
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
if (es != EffectStrength::LIGHT && es != EffectStrength::MEDIUM &&
es != EffectStrength::STRONG)
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
ret = ff.playEffect((static_cast<int>(effect)), es, &playLengthMs);
if (ret != 0)
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
}
if (callback != nullptr) {
std::thread([=] {
ALOGD("Starting perform on another thread");
usleep(playLengthMs * 1000);
ALOGD("Notifying perform complete");
callback->onComplete();
}).detach();
}
*_aidl_return = playLengthMs;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
if (ledVib.mDetected) {
*_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::HEAVY_CLICK,
Effect::TEXTURE_TICK};
} else {
#ifdef TARGET_SUPPORTS_OFFLOAD
*_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
Effect::POP, Effect::HEAVY_CLICK, Effect::RINGTONE_12,
Effect::RINGTONE_13, Effect::RINGTONE_14, Effect::RINGTONE_15};
#else
*_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
Effect::POP, Effect::HEAVY_CLICK};
#endif
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
uint8_t tmp;
int ret;
if (ledVib.mDetected)
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
ALOGD("Vibrator set amplitude: %f", amplitude);
if (amplitude <= 0.0f || amplitude > 1.0f)
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
if (ff.mInExternalControl)
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
tmp = (uint8_t)(amplitude * 0xff);
ret = ff.setAmplitude(tmp);
if (ret != 0)
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
if (ledVib.mDetected)
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
ALOGD("Vibrator set external control: %d", enabled);
if (!ff.mSupportExternalControl)
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
ff.mInExternalControl = enabled;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs __unused) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize __unused) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported __unused) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive __unused,
int32_t* durationMs __unused) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite __unused,
const std::shared_ptr<IVibratorCallback>& callback __unused) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return __unused) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id __unused, Effect effect __unused,
EffectStrength strength __unused) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id __unused) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
} // namespace vibrator
} // namespace hardware
} // namespace android
} // namespace aidl