diff --git a/vibrator/Android.bp b/vibrator/Android.bp new file mode 100644 index 0000000..ff62eb6 --- /dev/null +++ b/vibrator/Android.bp @@ -0,0 +1,42 @@ +Common_CFlags = ["-Wall"] +Common_CFlags += ["-Werror"] + +cc_library_shared { + name: "vendor.qti.hardware.vibrator.impl", + vendor: true, + cflags: Common_CFlags, + srcs: [ + "Vibrator.cpp", + ], + shared_libs: [ + "libcutils", + "libutils", + "liblog", + "libqtivibratoreffect", + "libbinder_ndk", + "android.hardware.vibrator-ndk_platform", + ], + export_include_dirs: ["include"] +} + +cc_binary { + name: "vendor.qti.hardware.vibrator.service", + vendor: true, + relative_install_path: "hw", + init_rc: ["vendor.qti.hardware.vibrator.service.rc"], + vintf_fragments: [ + "vendor.qti.hardware.vibrator.service.xml", + ], + cflags: Common_CFlags, + srcs: [ + "service.cpp", + ], + shared_libs: [ + "libcutils", + "libutils", + "libbase", + "libbinder_ndk", + "android.hardware.vibrator-ndk_platform", + "vendor.qti.hardware.vibrator.impl", + ], +} diff --git a/vibrator/Vibrator.cpp b/vibrator/Vibrator.cpp new file mode 100644 index 0000000..c7df267 --- /dev/null +++ b/vibrator/Vibrator.cpp @@ -0,0 +1,586 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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))) + +static const char 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: + * . + * 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::on(int32_t timeoutMs) { + char file[PATH_MAX]; + char value[32]; + int ret; + + snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "state"); + ret = write_value(file, "1"); + if (ret < 0) + goto error; + + snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "duration"); + snprintf(value, sizeof(value), "%u\n", timeoutMs); + ret = write_value(file, value); + if (ret < 0) + goto error; + + snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate"); + ret = write_value(file, "1"); + if (ret < 0) + goto error; + + return 0; + +error: + ALOGE("Failed to turn on vibrator ret: %d\n", ret); + 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& 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& callback, int32_t* _aidl_return) { + long playLengthMs; + int ret; + + if (ledVib.mDetected) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); + + ALOGD("Vibrator perform effect %d", effect); + + if (effect < Effect::CLICK || + effect > Effect::HEAVY_CLICK) + 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(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* _aidl_return) { + if (ledVib.mDetected) + return ndk::ScopedAStatus::ok(); + + *_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD, + Effect::POP, Effect::HEAVY_CLICK}; + + 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* 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& composite __unused, + const std::shared_ptr& callback __unused) { + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); +} + +ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector* _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 + diff --git a/vibrator/excluded-input-devices.xml b/vibrator/excluded-input-devices.xml new file mode 100755 index 0000000..cc22d2a --- /dev/null +++ b/vibrator/excluded-input-devices.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vibrator/include/Vibrator.h b/vibrator/include/Vibrator.h new file mode 100644 index 0000000..e0694ca --- /dev/null +++ b/vibrator/include/Vibrator.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018,2020, 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. + */ + +#pragma once + +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace vibrator { + +class InputFFDevice { +public: + InputFFDevice(); + int playEffect(int effectId, EffectStrength es, long *playLengthMs); + int on(int32_t timeoutMs); + int off(); + int setAmplitude(uint8_t amplitude); + bool mSupportGain; + bool mSupportEffects; + bool mSupportExternalControl; + bool mInExternalControl; +private: + int play(int effectId, uint32_t timeoutMs, long *playLengthMs); + int mVibraFd; + int16_t mCurrAppId; + int16_t mCurrMagnitude; +}; + +class LedVibratorDevice { +public: + LedVibratorDevice(); + int on(int32_t timeoutMs); + int off(); + bool mDetected; +private: + int write_value(const char *file, const char *value); +}; + +class Vibrator : public BnVibrator { +public: + class InputFFDevice ff; + class LedVibratorDevice ledVib; + ndk::ScopedAStatus getCapabilities(int32_t* _aidl_return) override; + ndk::ScopedAStatus off() override; + ndk::ScopedAStatus on(int32_t timeoutMs, + const std::shared_ptr& callback) override; + ndk::ScopedAStatus perform(Effect effect, EffectStrength strength, + const std::shared_ptr& callback, + int32_t* _aidl_return) override; + ndk::ScopedAStatus getSupportedEffects(std::vector* _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* supported) override; + ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive, + int32_t* durationMs) override; + ndk::ScopedAStatus compose(const std::vector& composite, + const std::shared_ptr& callback) override; + ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector* _aidl_return) override; + ndk::ScopedAStatus alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) override; + ndk::ScopedAStatus alwaysOnDisable(int32_t id) override; +}; + +} // namespace vibrator +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/vibrator/service.cpp b/vibrator/service.cpp new file mode 100644 index 0000000..4fe3118 --- /dev/null +++ b/vibrator/service.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020, 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.hardware.vibrator.service" + +#include +#include +#include + +#include "Vibrator.h" + +using aidl::android::hardware::vibrator::Vibrator; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr vib = ndk::SharedRefBase::make(); + + const std::string instance = std::string() + Vibrator::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(vib->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/vibrator/vendor.qti.hardware.vibrator.service.rc b/vibrator/vendor.qti.hardware.vibrator.service.rc new file mode 100644 index 0000000..ffa8ce9 --- /dev/null +++ b/vibrator/vendor.qti.hardware.vibrator.service.rc @@ -0,0 +1,4 @@ +service vendor.qti.vibrator /vendor/bin/hw/vendor.qti.hardware.vibrator.service + class hal + user system + group system input diff --git a/vibrator/vendor.qti.hardware.vibrator.service.xml b/vibrator/vendor.qti.hardware.vibrator.service.xml new file mode 100644 index 0000000..df29ada --- /dev/null +++ b/vibrator/vendor.qti.hardware.vibrator.service.xml @@ -0,0 +1,33 @@ + + + + android.hardware.vibrator + IVibrator/default + +