| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "vibrator-impl/Vibrator.h" |
| |
| #include <android-base/logging.h> |
| #include <thread> |
| #include <fstream> |
| |
| namespace aidl { |
| namespace android { |
| namespace hardware { |
| namespace vibrator { |
| |
| Vibrator::Vibrator() { |
| if (nodeExists(kVibratorStrength)) { |
| mVibratorStrengthSupported = true; |
| mVibratorStrengthMax = getNode(kVibratorStrengthMax, 9); |
| } |
| } |
| |
| ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) { |
| LOG(INFO) << "Vibrator reporting capabilities"; |
| *_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK; |
| |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| ndk::ScopedAStatus Vibrator::off() { |
| LOG(INFO) << "Vibrator off"; |
| return activate(0); |
| } |
| |
| ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs, |
| const std::shared_ptr<IVibratorCallback>& callback) { |
| LOG(INFO) << "Vibrator on for timeoutMs: " << timeoutMs; |
| ndk::ScopedAStatus status; |
| status = activate(timeoutMs); |
| |
| if (!status.isOk()) |
| return status; |
| |
| if (callback != nullptr) { |
| std::thread([=] { |
| LOG(INFO) << "Starting on on another thread"; |
| usleep(timeoutMs * 1000); |
| LOG(INFO) << "Notifying on complete"; |
| if (!callback->onComplete().isOk()) { |
| LOG(ERROR) << "Failed to call onComplete"; |
| } |
| }).detach(); |
| } |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength, |
| const std::shared_ptr<IVibratorCallback>& callback, |
| int32_t* _aidl_return) { |
| ndk::ScopedAStatus status; |
| int32_t timeoutMs; |
| |
| LOG(INFO) << "Vibrator perform"; |
| |
| if (strength != EffectStrength::LIGHT && strength != EffectStrength::MEDIUM && |
| strength != EffectStrength::STRONG) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| if (vibEffects.find(effect) == vibEffects.end()) |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| |
| if (mVibratorStrengthSupported) { |
| // Vibration strength is relative to the max strength reported by the kernel. |
| int vib_strength = static_cast<int>(mVibratorStrengthMax * vibStrengths[strength] / 10.0 + 0.5); |
| status = setNode(kVibratorStrength, vib_strength); |
| if (!status.isOk()) |
| return status; |
| } |
| |
| timeoutMs = vibEffects[effect]; |
| |
| status = activate(timeoutMs); |
| if (!status.isOk()) |
| return status; |
| |
| if (callback != nullptr) { |
| std::thread([=] { |
| LOG(INFO) << "Starting perform on another thread"; |
| usleep(timeoutMs * 1000); |
| LOG(INFO) << "Notifying perform complete"; |
| callback->onComplete(); |
| }).detach(); |
| } |
| |
| *_aidl_return = timeoutMs; |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) { |
| for (auto const& pair : vibEffects) |
| _aidl_return->push_back(pair.first); |
| |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude __unused) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled __unused) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| 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)); |
| } |
| |
| ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz __unused) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor __unused) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz __unused) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz __unused) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return __unused) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs __unused) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize __unused) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported __unused) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite __unused, |
| const std::shared_ptr<IVibratorCallback> &callback __unused) { |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); |
| } |
| |
| /* |
| * Write value to path and close file. |
| */ |
| ndk::ScopedAStatus Vibrator::setNode(const std::string path, const std::string value) { |
| std::ofstream file(path); |
| |
| if (!file.is_open()) { |
| LOG(ERROR) << "Failed to write " << value.c_str() << " to " << path.c_str(); |
| return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC)); |
| } |
| |
| file << value << std::endl; |
| |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| ndk::ScopedAStatus Vibrator::setNode(std::string path, const int32_t value) { |
| return setNode(path, std::to_string(value)); |
| } |
| |
| /* |
| * Read integer from path |
| */ |
| int Vibrator::getNode(const std::string path, const int fallback) { |
| std::ifstream file(path); |
| int value; |
| |
| if (!file.is_open()) { |
| LOG(ERROR) << "failed to read from " << path.c_str(); |
| return fallback; |
| } |
| |
| file >> value; |
| return value; |
| } |
| |
| bool Vibrator::nodeExists(const std::string path) { |
| std::ofstream file(path); |
| return file.is_open(); |
| } |
| |
| ndk::ScopedAStatus Vibrator::activate(const int32_t timeoutMs) { |
| ndk::ScopedAStatus status; |
| std::string active = "0"; |
| |
| if (timeoutMs > 0) |
| active = "1"; |
| else |
| goto set_activate; |
| |
| status = setNode(kVibratorState, "1"); |
| if (!status.isOk()) |
| return status; |
| |
| status = setNode(kVibratorDuration, timeoutMs); |
| if (!status.isOk()) |
| return status; |
| |
| set_activate: |
| status = setNode(kVibratorActivate, active); |
| if (!status.isOk()) |
| return status; |
| |
| return ndk::ScopedAStatus::ok(); |
| } |
| } // namespace vibrator |
| } // namespace hardware |
| } // namespace android |
| } // namespace aidl |