diff --git a/device.mk b/device.mk index 27d08ce..af959ff 100644 --- a/device.mk +++ b/device.mk @@ -137,5 +137,9 @@ PRODUCT_SHIPPING_API_LEVEL := 30 PRODUCT_SOONG_NAMESPACES += \ $(LOCAL_PATH) +# Vibrator +PRODUCT_PACKAGES += \ + android.hardware.vibrator-service.rosemary + # Inherit the proprietary files $(call inherit-product, vendor/xiaomi/rosemary/rosemary-vendor.mk) diff --git a/vibrator/Android.bp b/vibrator/Android.bp new file mode 100644 index 0000000..599d4eb --- /dev/null +++ b/vibrator/Android.bp @@ -0,0 +1,37 @@ +// Copyright (C) 2022 StatiX +// SPDX-License-Identifer: Apache-2.0 + +cc_library_static { + name: "android.hardware.vibrator-impl.rosemary", + vendor: true, + shared_libs: [ + "libbase", + "libbinder_ndk", + "android.hardware.vibrator-V2-ndk_platform", + ], + export_include_dirs: ["include"], + srcs: [ + "Vibrator.cpp", + ], + visibility: [ + ":__subpackages__", + "//hardware/interfaces/tests/extension/vibrator:__subpackages__", + ], +} + +cc_binary { + name: "android.hardware.vibrator-service.rosemary", + relative_install_path: "hw", + init_rc: ["vibrator-rosemary.rc"], + vintf_fragments: ["vibrator-rosemary.xml"], + vendor: true, + shared_libs: [ + "libbase", + "libbinder_ndk", + "android.hardware.vibrator-V2-ndk_platform", + ], + static_libs: [ + "android.hardware.vibrator-impl.rosemary", + ], + srcs: ["main.cpp"], +} diff --git a/vibrator/Vibrator.cpp b/vibrator/Vibrator.cpp new file mode 100644 index 0000000..249db6f --- /dev/null +++ b/vibrator/Vibrator.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2021 StatiX + * SPDX-License-Identifer: Apache-2.0 + */ + +#define LOG_TAG "vibrator.rosemary" + +#include "vibrator-impl/Vibrator.h" + +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace vibrator { + +template +static void write_haptic_node(const std::string& path, const T& value) { + std::ofstream file(path); + file << value; + } + +ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) { + LOG(INFO) << "Vibrator reporting capabilities"; + *_aidl_return = 0; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::off() { + LOG(INFO) << "Vibrator off"; + /* Reset index before triggering another set of haptics */ + write_haptic_node(index_node, 0); + write_haptic_node(activate_node, 0); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs, + const std::shared_ptr& callback) { + LOG(INFO) << "Vibrator on for timeoutMs: " << timeoutMs; + write_haptic_node(duration_node, timeoutMs); + write_haptic_node(activate_node, 1); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength, + const std::shared_ptr& callback, + int32_t* _aidl_return) { + ndk::ScopedAStatus status; + uint32_t index = 0; + uint32_t timeMs = 0; + + LOG(INFO) << "Vibrator perform"; + + switch (effect) { + case Effect::TICK: + LOG(INFO) << "Vibrator effect set to TICK"; + index = WAVEFORM_TICK_EFFECT_INDEX; + timeMs = WAVEFORM_TICK_EFFECT_MS; + break; + case Effect::TEXTURE_TICK: + LOG(INFO) << "Vibrator effect set to TEXTURE_TICK"; + index = WAVEFORM_TEXTURE_TICK_EFFECT_INDEX; + timeMs = WAVEFORM_TEXTURE_TICK_EFFECT_MS; + break; + case Effect::CLICK: + LOG(INFO) << "Vibrator effect set to CLICK"; + index = WAVEFORM_CLICK_EFFECT_INDEX; + timeMs = WAVEFORM_CLICK_EFFECT_MS; + break; + case Effect::HEAVY_CLICK: + LOG(INFO) << "Vibrator effect set to HEAVY_CLICK"; + index = WAVEFORM_HEAVY_CLICK_EFFECT_INDEX; + timeMs = WAVEFORM_HEAVY_CLICK_EFFECT_MS; + break; + case Effect::DOUBLE_CLICK: + LOG(INFO) << "Vibrator effect set to DOUBLE_CLICK"; + index = WAVEFORM_DOUBLE_CLICK_EFFECT_INDEX; + timeMs = WAVEFORM_DOUBLE_CLICK_EFFECT_MS; + break; + case Effect::THUD: + LOG(INFO) << "Vibrator effect set to THUD"; + index = WAVEFORM_THUD_EFFECT_INDEX; + timeMs = WAVEFORM_THUD_EFFECT_MS; + break; + case Effect::POP: + LOG(INFO) << "Vibrator effect set to POP"; + index = WAVEFORM_TICK_EFFECT_INDEX; + timeMs = WAVEFORM_POP_EFFECT_MS; + break; + default: + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + + /* Setup effect index */ + write_haptic_node(index_node, index); + + status = on(timeMs, nullptr); + if (!status.isOk()) { + return status; + } else { + *_aidl_return = timeMs; + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector *_aidl_return) { + + *_aidl_return = { + Effect::TICK, + Effect::TEXTURE_TICK, + Effect::CLICK, + Effect::HEAVY_CLICK, + Effect::DOUBLE_CLICK, + Effect::THUD, + Effect::POP + }; + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector* supported) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive, + int32_t* durationMs) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::compose(const std::vector& composite, + const std::shared_ptr& callback) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector* _aidl_return) { + return getSupportedEffects(_aidl_return); +} + +ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector *_aidl_return) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector* supported) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Vibrator::composePwle(const std::vector &composite, + const std::shared_ptr &callback) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +} // namespace vibrator +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/vibrator/include/vibrator-impl/Vibrator.h b/vibrator/include/vibrator-impl/Vibrator.h new file mode 100644 index 0000000..6a32c83 --- /dev/null +++ b/vibrator/include/vibrator-impl/Vibrator.h @@ -0,0 +1,84 @@ +/* + * 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 + +namespace aidl { +namespace android { +namespace hardware { +namespace vibrator { + +// Driver Nodes +static constexpr char activate_node[] = "/sys/devices/platform/haptic_pwm/activate"; +static constexpr char duration_node[] = "/sys/devices/platform/haptic_pwm/duration"; +static constexpr char index_node[] = "/sys/devices/platform/haptic_pwm/index"; + +// Define durations for waveforms +static constexpr uint32_t WAVEFORM_TICK_EFFECT_MS = 10; +static constexpr uint32_t WAVEFORM_TEXTURE_TICK_EFFECT_MS = 20; +static constexpr uint32_t WAVEFORM_CLICK_EFFECT_MS = 15; +static constexpr uint32_t WAVEFORM_HEAVY_CLICK_EFFECT_MS = 30; +static constexpr uint32_t WAVEFORM_DOUBLE_CLICK_EFFECT_MS = 60; +static constexpr uint32_t WAVEFORM_THUD_EFFECT_MS = 35; +static constexpr uint32_t WAVEFORM_POP_EFFECT_MS = 15; + +// Select waveform index from firmware through index list +static constexpr uint32_t WAVEFORM_TICK_EFFECT_INDEX = 1; +static constexpr uint32_t WAVEFORM_TEXTURE_TICK_EFFECT_INDEX = 4; +static constexpr uint32_t WAVEFORM_CLICK_EFFECT_INDEX = 2; +static constexpr uint32_t WAVEFORM_HEAVY_CLICK_EFFECT_INDEX = 5; +static constexpr uint32_t WAVEFORM_DOUBLE_CLICK_EFFECT_INDEX = 6; +static constexpr uint32_t WAVEFORM_THUD_EFFECT_INDEX = 7; + +class Vibrator : public BnVibrator { + 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; + 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 *_aidl_return) override; + ndk::ScopedAStatus getPwlePrimitiveDurationMax(int32_t *durationMs) override; + ndk::ScopedAStatus getPwleCompositionSizeMax(int32_t *maxSize) override; + ndk::ScopedAStatus getSupportedBraking(std::vector* supported) override; + ndk::ScopedAStatus composePwle(const std::vector &composite, + const std::shared_ptr &callback) override; +}; + +} // namespace vibrator +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/vibrator/main.cpp b/vibrator/main.cpp new file mode 100644 index 0000000..6186808 --- /dev/null +++ b/vibrator/main.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2022 StatiX + * SPDX-License-Identifer: Apache-2.0 + */ + +#include "vibrator-impl/Vibrator.h" + +#include +#include +#include + +using aidl::android::hardware::vibrator::Vibrator; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + + // make a default vibrator service + auto vib = ndk::SharedRefBase::make(); + const std::string vibName = std::string() + Vibrator::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(vib->asBinder().get(), vibName.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/vibrator/vibrator-rosemary.rc b/vibrator/vibrator-rosemary.rc new file mode 100644 index 0000000..a45e945 --- /dev/null +++ b/vibrator/vibrator-rosemary.rc @@ -0,0 +1,9 @@ +service vendor.vibrator-rosemary /vendor/bin/hw/android.hardware.vibrator-service.rosemary + class hal + user system + group system input + +on post-fs + chown system system /sys/devices/platform/haptic_pwm/activate + chown system system /sys/devices/platform/haptic_pwm/duration + chown system system /sys/devices/platform/haptic_pwm/index diff --git a/vibrator/vibrator-rosemary.xml b/vibrator/vibrator-rosemary.xml new file mode 100644 index 0000000..4db8f8c --- /dev/null +++ b/vibrator/vibrator-rosemary.xml @@ -0,0 +1,7 @@ + + + android.hardware.vibrator + 2 + IVibrator/default + +