blob: 4f8c2b84f0a983e847c729265d38a6a5c83a0bdc [file] [log] [blame]
Steven Morelandd44007e2019-10-24 18:12:46 -07001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Steven Moreland8ba8c032019-11-18 16:23:39 -080017#include "vibrator-impl/Vibrator.h"
Steven Morelandd44007e2019-10-24 18:12:46 -070018
19#include <android-base/logging.h>
20#include <thread>
21
22namespace aidl {
23namespace android {
24namespace hardware {
25namespace vibrator {
26
chasewu8af5e842022-03-22 01:42:24 +080027static constexpr int32_t COMPOSE_DELAY_MAX_MS = 1000;
28static constexpr int32_t COMPOSE_SIZE_MAX = 256;
29static constexpr int32_t COMPOSE_PWLE_SIZE_MAX = 127;
Ahmad Khalila788bed2024-08-05 14:43:38 +000030static constexpr int32_t COMPOSE_PWLE_V2_SIZE_MAX = 16;
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +090031
chasewu8af5e842022-03-22 01:42:24 +080032static constexpr float Q_FACTOR = 11.0;
Vince Leung823cf5f2021-02-11 02:21:57 +000033static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;
Ahmad Khalila788bed2024-08-05 14:43:38 +000034static constexpr int32_t COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MAX_MS = 1000;
35static constexpr int32_t COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MIN_MS = 20;
Vince Leung823cf5f2021-02-11 02:21:57 +000036static constexpr float PWLE_LEVEL_MIN = 0.0;
chasewu8af5e842022-03-22 01:42:24 +080037static constexpr float PWLE_LEVEL_MAX = 1.0;
Vince Leung823cf5f2021-02-11 02:21:57 +000038static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.0;
39static constexpr float PWLE_FREQUENCY_MIN_HZ = 140.0;
chasewu8af5e842022-03-22 01:42:24 +080040static constexpr float RESONANT_FREQUENCY_HZ = 150.0;
Vince Leung823cf5f2021-02-11 02:21:57 +000041static constexpr float PWLE_FREQUENCY_MAX_HZ = 160.0;
chasewu8af5e842022-03-22 01:42:24 +080042static constexpr float PWLE_BW_MAP_SIZE =
43 1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
Vince Leung4bae4f92021-02-03 06:21:58 +000044
Lais Andradea3c332f2024-06-20 10:46:06 +010045// Service specific error code used for vendor vibration effects.
46static constexpr int32_t ERROR_CODE_INVALID_DURATION = 1;
47
Steven Morelandd44007e2019-10-24 18:12:46 -070048ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
chasewu8af5e842022-03-22 01:42:24 +080049 LOG(VERBOSE) << "Vibrator reporting capabilities";
Ahmad Khalila788bed2024-08-05 14:43:38 +000050 std::lock_guard lock(mMutex);
51 if (mCapabilities == 0) {
52 if (!getInterfaceVersion(&mVersion).isOk()) {
53 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_STATE));
54 }
55 mCapabilities = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
56 IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
57 IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS |
58 IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
59 IVibrator::CAP_GET_Q_FACTOR | IVibrator::CAP_FREQUENCY_CONTROL |
60 IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
61
62 if (mVersion >= 3) {
63 mCapabilities |= (IVibrator::CAP_PERFORM_VENDOR_EFFECTS |
64 IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2);
65 }
66 }
67
68 *_aidl_return = mCapabilities;
Steven Morelandd44007e2019-10-24 18:12:46 -070069 return ndk::ScopedAStatus::ok();
70}
71
72ndk::ScopedAStatus Vibrator::off() {
chasewu8af5e842022-03-22 01:42:24 +080073 LOG(VERBOSE) << "Vibrator off";
Steven Morelandd44007e2019-10-24 18:12:46 -070074 return ndk::ScopedAStatus::ok();
75}
76
77ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
78 const std::shared_ptr<IVibratorCallback>& callback) {
chasewu8af5e842022-03-22 01:42:24 +080079 LOG(VERBOSE) << "Vibrator on for timeoutMs: " << timeoutMs;
Steven Morelandd44007e2019-10-24 18:12:46 -070080 if (callback != nullptr) {
Simon Bowden608655b2022-06-01 12:09:41 +000081 // Note that thread lambdas aren't using implicit capture [=], to avoid capturing "this",
82 // which may be asynchronously destructed.
83 // If "this" is needed, use [sharedThis = this->ref<Vibrator>()].
84 std::thread([timeoutMs, callback] {
chasewu8af5e842022-03-22 01:42:24 +080085 LOG(VERBOSE) << "Starting on on another thread";
Steven Morelandd44007e2019-10-24 18:12:46 -070086 usleep(timeoutMs * 1000);
chasewu8af5e842022-03-22 01:42:24 +080087 LOG(VERBOSE) << "Notifying on complete";
Steven Morelandf57ad9b2019-11-27 16:04:52 -080088 if (!callback->onComplete().isOk()) {
89 LOG(ERROR) << "Failed to call onComplete";
90 }
Steven Morelandd44007e2019-10-24 18:12:46 -070091 }).detach();
92 }
93 return ndk::ScopedAStatus::ok();
94}
95
96ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
97 const std::shared_ptr<IVibratorCallback>& callback,
98 int32_t* _aidl_return) {
chasewu8af5e842022-03-22 01:42:24 +080099 LOG(VERBOSE) << "Vibrator perform";
Steven Morelandd44007e2019-10-24 18:12:46 -0700100
101 if (effect != Effect::CLICK && effect != Effect::TICK) {
102 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
103 }
104 if (strength != EffectStrength::LIGHT && strength != EffectStrength::MEDIUM &&
105 strength != EffectStrength::STRONG) {
106 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
107 }
108
109 constexpr size_t kEffectMillis = 100;
110
111 if (callback != nullptr) {
Simon Bowden608655b2022-06-01 12:09:41 +0000112 std::thread([callback] {
chasewu8af5e842022-03-22 01:42:24 +0800113 LOG(VERBOSE) << "Starting perform on another thread";
Steven Morelandd44007e2019-10-24 18:12:46 -0700114 usleep(kEffectMillis * 1000);
chasewu8af5e842022-03-22 01:42:24 +0800115 LOG(VERBOSE) << "Notifying perform complete";
Steven Morelandd44007e2019-10-24 18:12:46 -0700116 callback->onComplete();
117 }).detach();
118 }
119
120 *_aidl_return = kEffectMillis;
121 return ndk::ScopedAStatus::ok();
122}
123
Lais Andradea3c332f2024-06-20 10:46:06 +0100124ndk::ScopedAStatus Vibrator::performVendorEffect(
125 const VendorEffect& effect, const std::shared_ptr<IVibratorCallback>& callback) {
126 LOG(VERBOSE) << "Vibrator perform vendor effect";
Ahmad Khalila788bed2024-08-05 14:43:38 +0000127 int32_t capabilities = 0;
128 if (!getCapabilities(&capabilities).isOk()) {
129 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
130 }
131 if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) {
132 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
133 }
Lais Andradea3c332f2024-06-20 10:46:06 +0100134 EffectStrength strength = effect.strength;
135 if (strength != EffectStrength::LIGHT && strength != EffectStrength::MEDIUM &&
136 strength != EffectStrength::STRONG) {
137 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
138 }
139 float scale = effect.scale;
140 if (scale <= 0) {
141 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
142 }
Lais Andrade4526faf2024-08-09 11:57:20 +0100143 float vendorScale = effect.vendorScale;
144 if (vendorScale <= 0) {
145 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
146 }
Lais Andradea3c332f2024-06-20 10:46:06 +0100147
148 int32_t durationMs = 0;
149 if (!effect.vendorData.getInt("DURATION_MS", &durationMs) || durationMs <= 0) {
150 return ndk::ScopedAStatus::fromServiceSpecificError(ERROR_CODE_INVALID_DURATION);
151 }
152
153 if (callback != nullptr) {
154 std::thread([callback, durationMs] {
155 LOG(VERBOSE) << "Starting perform on another thread for durationMs:" << durationMs;
156 usleep(durationMs * 1000);
157 LOG(VERBOSE) << "Notifying perform vendor effect complete";
158 callback->onComplete();
159 }).detach();
160 }
161
162 return ndk::ScopedAStatus::ok();
163}
164
Steven Moreland2932b222019-11-05 14:30:17 -0800165ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
166 *_aidl_return = {Effect::CLICK, Effect::TICK};
167 return ndk::ScopedAStatus::ok();
168}
169
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900170ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
chasewu8af5e842022-03-22 01:42:24 +0800171 LOG(VERBOSE) << "Vibrator set amplitude: " << amplitude;
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900172 if (amplitude <= 0.0f || amplitude > 1.0f) {
Steven Morelandd44007e2019-10-24 18:12:46 -0700173 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
174 }
175 return ndk::ScopedAStatus::ok();
176}
177
178ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
chasewu8af5e842022-03-22 01:42:24 +0800179 LOG(VERBOSE) << "Vibrator set external control: " << enabled;
Steven Morelandd44007e2019-10-24 18:12:46 -0700180 return ndk::ScopedAStatus::ok();
181}
182
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900183ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) {
chasewu8af5e842022-03-22 01:42:24 +0800184 *maxDelayMs = COMPOSE_DELAY_MAX_MS;
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900185 return ndk::ScopedAStatus::ok();
186}
187
188ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) {
chasewu8af5e842022-03-22 01:42:24 +0800189 *maxSize = COMPOSE_SIZE_MAX;
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900190 return ndk::ScopedAStatus::ok();
191}
192
Harpreet \"Eli\" Sangha523e2962020-01-21 16:15:42 +0900193ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
194 *supported = {
195 CompositePrimitive::NOOP, CompositePrimitive::CLICK,
196 CompositePrimitive::THUD, CompositePrimitive::SPIN,
197 CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
Harpreet \"Eli\" Sanghafe5d3982020-01-17 14:46:37 +0900198 CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
Vince Leungdeb46ec2020-12-22 00:03:16 +0000199 CompositePrimitive::LOW_TICK,
Harpreet \"Eli\" Sangha523e2962020-01-21 16:15:42 +0900200 };
201 return ndk::ScopedAStatus::ok();
202}
203
204ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
205 int32_t* durationMs) {
Lais Andrade3c7f0d92021-06-16 10:20:09 +0000206 std::vector<CompositePrimitive> supported;
207 getSupportedPrimitives(&supported);
208 if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
209 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
210 }
Harpreet \"Eli\" Sangha523e2962020-01-21 16:15:42 +0900211 if (primitive != CompositePrimitive::NOOP) {
212 *durationMs = 100;
213 } else {
214 *durationMs = 0;
215 }
216 return ndk::ScopedAStatus::ok();
217}
218
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900219ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
220 const std::shared_ptr<IVibratorCallback>& callback) {
chasewu8af5e842022-03-22 01:42:24 +0800221 if (composite.size() > COMPOSE_SIZE_MAX) {
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900222 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
223 }
224
Harpreet \"Eli\" Sangha13ef28c2020-01-23 13:22:07 +0900225 std::vector<CompositePrimitive> supported;
226 getSupportedPrimitives(&supported);
227
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900228 for (auto& e : composite) {
chasewu8af5e842022-03-22 01:42:24 +0800229 if (e.delayMs > COMPOSE_DELAY_MAX_MS) {
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900230 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
231 }
Harpreet \"Eli\" Sangha7aec5022020-03-11 06:00:55 +0900232 if (e.scale < 0.0f || e.scale > 1.0f) {
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900233 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
234 }
Harpreet \"Eli\" Sangha13ef28c2020-01-23 13:22:07 +0900235 if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900236 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
237 }
238 }
239
Simon Bowden608655b2022-06-01 12:09:41 +0000240 // The thread may theoretically outlive the vibrator, so take a proper reference to it.
241 std::thread([sharedThis = this->ref<Vibrator>(), composite, callback] {
chasewu8af5e842022-03-22 01:42:24 +0800242 LOG(VERBOSE) << "Starting compose on another thread";
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900243
244 for (auto& e : composite) {
245 if (e.delayMs) {
246 usleep(e.delayMs * 1000);
247 }
chasewu8af5e842022-03-22 01:42:24 +0800248 LOG(VERBOSE) << "triggering primitive " << static_cast<int>(e.primitive) << " @ scale "
249 << e.scale;
Harpreet \"Eli\" Sanghab075a6a2020-04-10 15:11:58 +0900250
251 int32_t durationMs;
Simon Bowden608655b2022-06-01 12:09:41 +0000252 sharedThis->getPrimitiveDuration(e.primitive, &durationMs);
Harpreet \"Eli\" Sanghab075a6a2020-04-10 15:11:58 +0900253 usleep(durationMs * 1000);
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900254 }
255
256 if (callback != nullptr) {
chasewu8af5e842022-03-22 01:42:24 +0800257 LOG(VERBOSE) << "Notifying perform complete";
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900258 callback->onComplete();
259 }
260 }).detach();
261
262 return ndk::ScopedAStatus::ok();
263}
264
Harpreet \"Eli\" Sangha63624092019-09-09 11:04:54 +0900265ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) {
266 return getSupportedEffects(_aidl_return);
267}
268
269ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
270 std::vector<Effect> effects;
271 getSupportedAlwaysOnEffects(&effects);
272
273 if (std::find(effects.begin(), effects.end(), effect) == effects.end()) {
274 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
275 } else {
chasewu8af5e842022-03-22 01:42:24 +0800276 LOG(VERBOSE) << "Enabling always-on ID " << id << " with " << toString(effect) << "/"
277 << toString(strength);
Harpreet \"Eli\" Sangha63624092019-09-09 11:04:54 +0900278 return ndk::ScopedAStatus::ok();
279 }
280}
281
282ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id) {
chasewu8af5e842022-03-22 01:42:24 +0800283 LOG(VERBOSE) << "Disabling always-on ID " << id;
Harpreet \"Eli\" Sangha63624092019-09-09 11:04:54 +0900284 return ndk::ScopedAStatus::ok();
285}
286
Vince Leung4bae4f92021-02-03 06:21:58 +0000287ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
chasewu8af5e842022-03-22 01:42:24 +0800288 *resonantFreqHz = RESONANT_FREQUENCY_HZ;
Vince Leung4bae4f92021-02-03 06:21:58 +0000289 return ndk::ScopedAStatus::ok();
290}
291
292ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
chasewu8af5e842022-03-22 01:42:24 +0800293 *qFactor = Q_FACTOR;
Vince Leung4bae4f92021-02-03 06:21:58 +0000294 return ndk::ScopedAStatus::ok();
295}
296
Vince Leung823cf5f2021-02-11 02:21:57 +0000297ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
298 *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ;
299 return ndk::ScopedAStatus::ok();
300}
301
302ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
303 *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ;
304 return ndk::ScopedAStatus::ok();
305}
306
307ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) {
chasewu8af5e842022-03-22 01:42:24 +0800308 // The output BandwidthAmplitudeMap will be as below and the maximum
309 // amplitude 1.0 will be set on RESONANT_FREQUENCY_HZ
310 // {0.9, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1, 0.99, 0.98, 0.97,
311 // 0.96, 0.95, 0.94, 0.93, 0.92, 0.91, 0.9}
312 int32_t capabilities = 0;
313 int halfMapSize = PWLE_BW_MAP_SIZE / 2;
314 Vibrator::getCapabilities(&capabilities);
315 if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
316 std::vector<float> bandwidthAmplitudeMap(PWLE_BW_MAP_SIZE, PWLE_LEVEL_MAX);
317 for (int i = 0; i < halfMapSize; ++i) {
318 bandwidthAmplitudeMap[halfMapSize + i + 1] =
319 bandwidthAmplitudeMap[halfMapSize + i] - 0.01;
320 bandwidthAmplitudeMap[halfMapSize - i - 1] =
321 bandwidthAmplitudeMap[halfMapSize - i] - 0.01;
322 }
323 *_aidl_return = bandwidthAmplitudeMap;
324 return ndk::ScopedAStatus::ok();
325 } else {
326 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
327 }
Vince Leung823cf5f2021-02-11 02:21:57 +0000328}
329
330ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
331 *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS;
332 return ndk::ScopedAStatus::ok();
333}
334
335ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
chasewu8af5e842022-03-22 01:42:24 +0800336 *maxSize = COMPOSE_PWLE_SIZE_MAX;
Vince Leung823cf5f2021-02-11 02:21:57 +0000337 return ndk::ScopedAStatus::ok();
338}
339
340ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) {
341 *supported = {
342 Braking::NONE,
343 Braking::CLAB,
344 };
345 return ndk::ScopedAStatus::ok();
346}
347
348void resetPreviousEndAmplitudeEndFrequency(float &prevEndAmplitude, float &prevEndFrequency) {
349 const float reset = -1.0;
350 prevEndAmplitude = reset;
351 prevEndFrequency = reset;
352}
353
354void incrementIndex(int &index) {
355 index += 1;
356}
357
358void constructActiveDefaults(std::ostringstream &pwleBuilder, const int &segmentIdx) {
359 pwleBuilder << ",C" << segmentIdx << ":1";
360 pwleBuilder << ",B" << segmentIdx << ":0";
361 pwleBuilder << ",AR" << segmentIdx << ":0";
362 pwleBuilder << ",V" << segmentIdx << ":0";
363}
364
365void constructActiveSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
366 float amplitude, float frequency) {
367 pwleBuilder << ",T" << segmentIdx << ":" << duration;
368 pwleBuilder << ",L" << segmentIdx << ":" << amplitude;
369 pwleBuilder << ",F" << segmentIdx << ":" << frequency;
370 constructActiveDefaults(pwleBuilder, segmentIdx);
371}
372
373void constructBrakingSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
374 Braking brakingType) {
375 pwleBuilder << ",T" << segmentIdx << ":" << duration;
376 pwleBuilder << ",L" << segmentIdx << ":" << 0;
377 pwleBuilder << ",F" << segmentIdx << ":" << 0;
378 pwleBuilder << ",C" << segmentIdx << ":0";
379 pwleBuilder << ",B" << segmentIdx << ":"
380 << static_cast<std::underlying_type<Braking>::type>(brakingType);
381 pwleBuilder << ",AR" << segmentIdx << ":0";
382 pwleBuilder << ",V" << segmentIdx << ":0";
383}
384
385ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
386 const std::shared_ptr<IVibratorCallback> &callback) {
387 std::ostringstream pwleBuilder;
388 std::string pwleQueue;
389
390 int compositionSizeMax;
391 getPwleCompositionSizeMax(&compositionSizeMax);
392 if (composite.size() <= 0 || composite.size() > compositionSizeMax) {
393 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
394 }
395
396 float prevEndAmplitude;
397 float prevEndFrequency;
398 resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
399
400 int segmentIdx = 0;
401 uint32_t totalDuration = 0;
402
403 pwleBuilder << "S:0,WF:4,RP:0,WT:0";
404
405 for (auto &e : composite) {
406 switch (e.getTag()) {
407 case PrimitivePwle::active: {
408 auto active = e.get<PrimitivePwle::active>();
409 if (active.duration < 0 ||
410 active.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
411 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
412 }
413 if (active.startAmplitude < PWLE_LEVEL_MIN ||
414 active.startAmplitude > PWLE_LEVEL_MAX ||
415 active.endAmplitude < PWLE_LEVEL_MIN || active.endAmplitude > PWLE_LEVEL_MAX) {
416 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
417 }
418 if (active.startFrequency < PWLE_FREQUENCY_MIN_HZ ||
419 active.startFrequency > PWLE_FREQUENCY_MAX_HZ ||
420 active.endFrequency < PWLE_FREQUENCY_MIN_HZ ||
421 active.endFrequency > PWLE_FREQUENCY_MAX_HZ) {
422 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
423 }
424
425 if (!((active.startAmplitude == prevEndAmplitude) &&
426 (active.startFrequency == prevEndFrequency))) {
427 constructActiveSegment(pwleBuilder, segmentIdx, 0, active.startAmplitude,
428 active.startFrequency);
429 incrementIndex(segmentIdx);
430 }
431
432 constructActiveSegment(pwleBuilder, segmentIdx, active.duration,
433 active.endAmplitude, active.endFrequency);
434 incrementIndex(segmentIdx);
435
436 prevEndAmplitude = active.endAmplitude;
437 prevEndFrequency = active.endFrequency;
438 totalDuration += active.duration;
439 break;
440 }
441 case PrimitivePwle::braking: {
442 auto braking = e.get<PrimitivePwle::braking>();
443 if (braking.braking > Braking::CLAB) {
444 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
445 }
446 if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
447 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
448 }
449
450 constructBrakingSegment(pwleBuilder, segmentIdx, 0, braking.braking);
451 incrementIndex(segmentIdx);
452
453 constructBrakingSegment(pwleBuilder, segmentIdx, braking.duration, braking.braking);
454 incrementIndex(segmentIdx);
455
456 resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
457 totalDuration += braking.duration;
458 break;
459 }
460 }
461 }
462
Simon Bowden608655b2022-06-01 12:09:41 +0000463 std::thread([totalDuration, callback] {
chasewu8af5e842022-03-22 01:42:24 +0800464 LOG(VERBOSE) << "Starting composePwle on another thread";
Vince Leung823cf5f2021-02-11 02:21:57 +0000465 usleep(totalDuration * 1000);
466 if (callback != nullptr) {
chasewu8af5e842022-03-22 01:42:24 +0800467 LOG(VERBOSE) << "Notifying compose PWLE complete";
Vince Leung823cf5f2021-02-11 02:21:57 +0000468 callback->onComplete();
469 }
470 }).detach();
471
472 return ndk::ScopedAStatus::ok();
473}
474
Ahmad Khalila788bed2024-08-05 14:43:38 +0000475ndk::ScopedAStatus Vibrator::getPwleV2FrequencyToOutputAccelerationMap(
476 std::vector<PwleV2OutputMapEntry>* _aidl_return) {
477 std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap;
478
479 std::vector<std::pair<float, float>> frequencyToOutputAccelerationData = {
480 {30.0f, 0.01f}, {46.0f, 0.09f}, {50.0f, 0.1f}, {55.0f, 0.12f}, {62.0f, 0.66f},
481 {83.0f, 0.82f}, {85.0f, 0.85f}, {92.0f, 1.05f}, {107.0f, 1.63f}, {115.0f, 1.72f},
482 {123.0f, 1.81f}, {135.0f, 2.23f}, {144.0f, 2.47f}, {145.0f, 2.5f}, {150.0f, 3.0f},
483 {175.0f, 2.51f}, {181.0f, 2.41f}, {190.0f, 2.28f}, {200.0f, 2.08f}, {204.0f, 1.96f},
484 {205.0f, 1.9f}, {224.0f, 1.7f}, {235.0f, 1.5f}, {242.0f, 1.46f}, {253.0f, 1.41f},
485 {263.0f, 1.39f}, {65.0f, 1.38f}, {278.0f, 1.37f}, {294.0f, 1.35f}, {300.0f, 1.34f}};
486 for (const auto& entry : frequencyToOutputAccelerationData) {
487 frequencyToOutputAccelerationMap.push_back(
488 PwleV2OutputMapEntry(/*frequency=*/entry.first,
489 /*maxOutputAcceleration=*/entry.second));
490 }
491
492 *_aidl_return = frequencyToOutputAccelerationMap;
493
494 return ndk::ScopedAStatus::ok();
495}
496
497ndk::ScopedAStatus Vibrator::getPwleV2PrimitiveDurationMaxMillis(int32_t* maxDurationMs) {
498 *maxDurationMs = COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MAX_MS;
499 return ndk::ScopedAStatus::ok();
500}
501
502ndk::ScopedAStatus Vibrator::getPwleV2CompositionSizeMax(int32_t* maxSize) {
503 *maxSize = COMPOSE_PWLE_V2_SIZE_MAX;
504 return ndk::ScopedAStatus::ok();
505}
506
507ndk::ScopedAStatus Vibrator::getPwleV2PrimitiveDurationMinMillis(int32_t* minDurationMs) {
508 *minDurationMs = COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MIN_MS;
509 return ndk::ScopedAStatus::ok();
510}
511
512float getPwleV2FrequencyMinHz(std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap) {
513 if (frequencyToOutputAccelerationMap.empty()) {
514 return 0.0f;
515 }
516
517 float minFrequency = frequencyToOutputAccelerationMap[0].frequencyHz;
518
519 for (const auto& entry : frequencyToOutputAccelerationMap) {
520 if (entry.frequencyHz < minFrequency) {
521 minFrequency = entry.frequencyHz;
522 }
523 }
524
525 return minFrequency;
526}
527
528float getPwleV2FrequencyMaxHz(std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap) {
529 if (frequencyToOutputAccelerationMap.empty()) {
530 return 0.0f;
531 }
532
533 float maxFrequency = frequencyToOutputAccelerationMap[0].frequencyHz;
534
535 for (const auto& entry : frequencyToOutputAccelerationMap) {
536 if (entry.frequencyHz > maxFrequency) {
537 maxFrequency = entry.frequencyHz;
538 }
539 }
540
541 return maxFrequency;
542}
543
544ndk::ScopedAStatus Vibrator::composePwleV2(const std::vector<PwleV2Primitive>& composite,
545 const std::shared_ptr<IVibratorCallback>& callback) {
Ahmad Khalil7eacaad2024-08-22 12:25:36 +0000546 int32_t capabilities = 0;
547 if (!getCapabilities(&capabilities).isOk()) {
548 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
549 }
550 if ((capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2) == 0) {
551 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
552 }
553
Ahmad Khalila788bed2024-08-05 14:43:38 +0000554 int compositionSizeMax;
555 getPwleV2CompositionSizeMax(&compositionSizeMax);
556 if (composite.size() <= 0 || composite.size() > compositionSizeMax) {
557 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
558 }
559
560 int32_t totalEffectDuration = 0;
561 std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap;
562 getPwleV2FrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
563 float minFrequency = getPwleV2FrequencyMinHz(frequencyToOutputAccelerationMap);
564 float maxFrequency = getPwleV2FrequencyMaxHz(frequencyToOutputAccelerationMap);
565
566 for (auto& e : composite) {
567 if (e.timeMillis < 0.0f || e.timeMillis > COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MAX_MS) {
568 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
569 }
570 if (e.amplitude < 0.0f || e.amplitude > 1.0f) {
571 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
572 }
573 if (e.frequencyHz < minFrequency || e.frequencyHz > maxFrequency) {
574 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
575 }
576 totalEffectDuration += e.timeMillis;
577 }
578
579 std::thread([totalEffectDuration, callback] {
580 LOG(VERBOSE) << "Starting composePwleV2 on another thread";
581 usleep(totalEffectDuration * 1000);
582 if (callback != nullptr) {
583 LOG(VERBOSE) << "Notifying compose PWLE V2 complete";
584 callback->onComplete();
585 }
586 }).detach();
587
588 return ndk::ScopedAStatus::ok();
589}
590
Steven Morelandd44007e2019-10-24 18:12:46 -0700591} // namespace vibrator
592} // namespace hardware
593} // namespace android
594} // namespace aidl