Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 1 | /* |
SamarV-121 | 31d105a | 2023-01-25 16:00:10 +0530 | [diff] [blame] | 2 | * Copyright (C) 2021-2023 The LineageOS Project |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 3 | * |
| 4 | * SPDX-License-Identifier: Apache-2.0 |
| 5 | */ |
| 6 | |
| 7 | #include "Vibrator.h" |
| 8 | |
| 9 | #include <android-base/logging.h> |
| 10 | |
| 11 | #include <cmath> |
| 12 | #include <fstream> |
| 13 | #include <iostream> |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 14 | #include <map> |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 15 | #include <thread> |
| 16 | |
| 17 | namespace aidl { |
| 18 | namespace android { |
| 19 | namespace hardware { |
| 20 | namespace vibrator { |
| 21 | |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 22 | static std::map<Effect, int> CP_TRIGGER_EFFECTS { |
| 23 | { Effect::CLICK, 10 }, |
| 24 | { Effect::DOUBLE_CLICK, 14 }, |
| 25 | { Effect::HEAVY_CLICK, 23 }, |
| 26 | { Effect::TEXTURE_TICK, 50 }, |
| 27 | { Effect::TICK, 50 } |
| 28 | }; |
| 29 | |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 30 | /* |
| 31 | * Write value to path and close file. |
| 32 | */ |
| 33 | template <typename T> |
| 34 | static ndk::ScopedAStatus writeNode(const std::string& path, const T& value) { |
| 35 | std::ofstream node(path); |
| 36 | if (!node) { |
| 37 | LOG(ERROR) << "Failed to open: " << path; |
| 38 | return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_ERROR); |
| 39 | } |
| 40 | |
| 41 | LOG(DEBUG) << "writeNode node: " << path << " value: " << value; |
| 42 | |
| 43 | node << value << std::endl; |
| 44 | if (!node) { |
| 45 | LOG(ERROR) << "Failed to write: " << value; |
| 46 | return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_ERROR); |
| 47 | } |
| 48 | |
| 49 | return ndk::ScopedAStatus::ok(); |
| 50 | } |
| 51 | |
| 52 | static bool nodeExists(const std::string& path) { |
| 53 | std::ofstream f(path.c_str()); |
| 54 | return f.good(); |
| 55 | } |
| 56 | |
| 57 | Vibrator::Vibrator() { |
| 58 | mIsTimedOutVibrator = nodeExists(VIBRATOR_TIMEOUT_PATH); |
| 59 | mHasTimedOutIntensity = nodeExists(VIBRATOR_INTENSITY_PATH); |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 60 | mHasTimedOutEffect = nodeExists(VIBRATOR_CP_TRIGGER_PATH); |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) { |
| 64 | *_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK | |
| 65 | IVibrator::CAP_EXTERNAL_CONTROL /*| IVibrator::CAP_COMPOSE_EFFECTS | |
| 66 | IVibrator::CAP_ALWAYS_ON_CONTROL*/; |
| 67 | |
| 68 | if (mHasTimedOutIntensity) { |
| 69 | *_aidl_return = *_aidl_return | IVibrator::CAP_AMPLITUDE_CONTROL | |
| 70 | IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL; |
| 71 | } |
| 72 | |
| 73 | return ndk::ScopedAStatus::ok(); |
| 74 | } |
| 75 | |
| 76 | ndk::ScopedAStatus Vibrator::off() { |
| 77 | return activate(0); |
| 78 | } |
| 79 | |
| 80 | ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs, const std::shared_ptr<IVibratorCallback>& callback) { |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 81 | ndk::ScopedAStatus status; |
| 82 | |
| 83 | if (mHasTimedOutEffect) |
| 84 | writeNode(VIBRATOR_CP_TRIGGER_PATH, 0); // Clear all effects |
| 85 | |
| 86 | status = activate(timeoutMs); |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 87 | |
| 88 | if (callback != nullptr) { |
| 89 | std::thread([=] { |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 90 | LOG(DEBUG) << "Starting on on another thread"; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 91 | usleep(timeoutMs * 1000); |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 92 | LOG(DEBUG) << "Notifying on complete"; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 93 | if (!callback->onComplete().isOk()) { |
| 94 | LOG(ERROR) << "Failed to call onComplete"; |
| 95 | } |
| 96 | }).detach(); |
| 97 | } |
| 98 | |
| 99 | return status; |
| 100 | } |
| 101 | |
| 102 | ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength, const std::shared_ptr<IVibratorCallback>& callback, int32_t* _aidl_return) { |
| 103 | ndk::ScopedAStatus status; |
SamarV-121 | 31d105a | 2023-01-25 16:00:10 +0530 | [diff] [blame] | 104 | float amplitude = strengthToAmplitude(strength, &status); |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 105 | uint32_t ms = 1000; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 106 | |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 107 | if (!status.isOk()) |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 108 | return status; |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 109 | |
| 110 | activate(0); |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 111 | setAmplitude(amplitude); |
| 112 | |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 113 | if (mHasTimedOutEffect && CP_TRIGGER_EFFECTS.find(effect) != CP_TRIGGER_EFFECTS.end()) { |
| 114 | writeNode(VIBRATOR_CP_TRIGGER_PATH, CP_TRIGGER_EFFECTS[effect]); |
| 115 | } else { |
| 116 | if (mHasTimedOutEffect) |
| 117 | writeNode(VIBRATOR_CP_TRIGGER_PATH, 0); // Clear previous effect |
| 118 | |
| 119 | ms = effectToMs(effect, &status); |
| 120 | |
| 121 | if (!status.isOk()) |
| 122 | return status; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 123 | } |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 124 | |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 125 | status = activate(ms); |
| 126 | |
| 127 | if (callback != nullptr) { |
| 128 | std::thread([=] { |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 129 | LOG(DEBUG) << "Starting perform on another thread"; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 130 | usleep(ms * 1000); |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 131 | LOG(DEBUG) << "Notifying perform complete"; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 132 | callback->onComplete(); |
| 133 | }).detach(); |
| 134 | } |
| 135 | |
| 136 | *_aidl_return = ms; |
| 137 | return status; |
| 138 | } |
| 139 | |
| 140 | ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) { |
Tim Zimmermann | c48c38d | 2023-03-12 12:53:51 +0100 | [diff] [blame^] | 141 | *_aidl_return = { Effect::CLICK, Effect::TICK }; |
| 142 | |
| 143 | if (mHasTimedOutEffect) { |
| 144 | for (const auto& effect : CP_TRIGGER_EFFECTS) { |
| 145 | _aidl_return->push_back(effect.first); |
| 146 | } |
| 147 | } |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 148 | return ndk::ScopedAStatus::ok(); |
| 149 | } |
| 150 | |
| 151 | ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) { |
| 152 | uint32_t intensity; |
| 153 | |
SamarV-121 | 31d105a | 2023-01-25 16:00:10 +0530 | [diff] [blame] | 154 | if (amplitude <= 0.0f || amplitude > 1.0f) { |
| 155 | return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT)); |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 156 | } |
| 157 | |
SamarV-121 | 31d105a | 2023-01-25 16:00:10 +0530 | [diff] [blame] | 158 | LOG(DEBUG) << "Setting amplitude: " << amplitude; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 159 | |
SamarV-121 | 31d105a | 2023-01-25 16:00:10 +0530 | [diff] [blame] | 160 | intensity = amplitude * INTENSITY_MAX; |
Tim Zimmermann | 36977e2 | 2022-04-15 10:04:25 +0200 | [diff] [blame] | 161 | |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 162 | LOG(DEBUG) << "Setting intensity: " << intensity; |
| 163 | |
| 164 | if (mHasTimedOutIntensity) { |
| 165 | return writeNode(VIBRATOR_INTENSITY_PATH, intensity); |
| 166 | } |
| 167 | |
| 168 | return ndk::ScopedAStatus::ok(); |
| 169 | } |
| 170 | |
| 171 | ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) { |
| 172 | if (mEnabled) { |
| 173 | LOG(WARNING) << "Setting external control while the vibrator is enabled is " |
| 174 | "unsupported!"; |
| 175 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 176 | } |
| 177 | |
| 178 | LOG(INFO) << "ExternalControl: " << mExternalControl << " -> " << enabled; |
| 179 | mExternalControl = enabled; |
| 180 | return ndk::ScopedAStatus::ok(); |
| 181 | } |
| 182 | |
| 183 | ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* /*_aidl_return*/) { |
| 184 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 185 | } |
| 186 | |
| 187 | ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* /*_aidl_return*/) { |
| 188 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 189 | } |
| 190 | |
| 191 | ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* /*_aidl_return*/) { |
| 192 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 193 | } |
| 194 | |
| 195 | ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive /*primitive*/, int32_t* /*_aidl_return*/) { |
| 196 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 197 | } |
| 198 | |
| 199 | ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& /*composite*/, const std::shared_ptr<IVibratorCallback>& /*callback*/) { |
| 200 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 201 | } |
| 202 | |
| 203 | ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* /*_aidl_return*/) { |
| 204 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 205 | } |
| 206 | |
| 207 | ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t /*id*/, Effect /*effect*/, EffectStrength /*strength*/) { |
| 208 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 209 | } |
| 210 | |
| 211 | ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t /*id*/) { |
| 212 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 213 | } |
| 214 | |
| 215 | ndk::ScopedAStatus Vibrator::getResonantFrequency(float* /*_aidl_return*/) { |
| 216 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 217 | } |
| 218 | |
| 219 | ndk::ScopedAStatus Vibrator::getQFactor(float* /*_aidl_return*/) { |
| 220 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 221 | } |
| 222 | |
| 223 | ndk::ScopedAStatus Vibrator::getFrequencyResolution(float* /*_aidl_return*/) { |
| 224 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 225 | } |
| 226 | |
| 227 | ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float* /*_aidl_return*/) { |
| 228 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 229 | } |
| 230 | |
| 231 | ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float>* /*_aidl_return*/) { |
| 232 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 233 | } |
| 234 | |
| 235 | ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t* /*_aidl_return*/) { |
| 236 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 237 | } |
| 238 | |
| 239 | ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t* /*_aidl_return*/) { |
| 240 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 241 | } |
| 242 | |
| 243 | ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking>* /*_aidl_return*/) { |
| 244 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 245 | } |
| 246 | |
| 247 | ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle>& /*composite*/, const std::shared_ptr<IVibratorCallback>& /*callback*/) { |
| 248 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 249 | } |
| 250 | |
| 251 | ndk::ScopedAStatus Vibrator::activate(uint32_t timeoutMs) { |
| 252 | std::lock_guard<std::mutex> lock{mMutex}; |
| 253 | if (!mIsTimedOutVibrator) { |
| 254 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 255 | } |
| 256 | |
| 257 | return writeNode(VIBRATOR_TIMEOUT_PATH, timeoutMs); |
| 258 | } |
| 259 | |
SamarV-121 | 31d105a | 2023-01-25 16:00:10 +0530 | [diff] [blame] | 260 | float Vibrator::strengthToAmplitude(EffectStrength strength, ndk::ScopedAStatus* status) { |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 261 | *status = ndk::ScopedAStatus::ok(); |
| 262 | |
| 263 | switch (strength) { |
| 264 | case EffectStrength::LIGHT: |
SamarV-121 | 31d105a | 2023-01-25 16:00:10 +0530 | [diff] [blame] | 265 | return AMPLITUDE_LIGHT; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 266 | case EffectStrength::MEDIUM: |
SamarV-121 | 31d105a | 2023-01-25 16:00:10 +0530 | [diff] [blame] | 267 | return AMPLITUDE_MEDIUM; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 268 | case EffectStrength::STRONG: |
SamarV-121 | 31d105a | 2023-01-25 16:00:10 +0530 | [diff] [blame] | 269 | return AMPLITUDE_STRONG; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 270 | } |
| 271 | |
| 272 | *status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 273 | return 0; |
| 274 | } |
| 275 | |
| 276 | uint32_t Vibrator::effectToMs(Effect effect, ndk::ScopedAStatus* status) { |
| 277 | *status = ndk::ScopedAStatus::ok(); |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 278 | switch (effect) { |
| 279 | case Effect::CLICK: |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 280 | return 10; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 281 | case Effect::TICK: |
Tim Zimmermann | 428bd3e | 2022-01-31 12:01:42 +0100 | [diff] [blame] | 282 | return 5; |
Tim Zimmermann | c48c38d | 2023-03-12 12:53:51 +0100 | [diff] [blame^] | 283 | default: |
| 284 | break; |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 285 | } |
Tim Zimmermann | 1bc99f5 | 2021-11-23 20:45:19 +0100 | [diff] [blame] | 286 | *status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 287 | return 0; |
| 288 | } |
| 289 | |
| 290 | } // namespace vibrator |
| 291 | } // namespace hardware |
| 292 | } // namespace android |
| 293 | } // namespace aidl |