blob: 165a3bf4718ac928ce08c2fa353554ee19a04473 [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
Lais Andradef1120b12024-08-23 15:12:12 +010048void Vibrator::dispatchVibrate(int32_t timeoutMs,
49 const std::shared_ptr<IVibratorCallback>& callback) {
50 std::lock_guard lock(mMutex);
51 if (mIsVibrating) {
52 // Already vibrating, ignore new request.
53 return;
54 }
55 mVibrationCallback = callback;
56 mIsVibrating = true;
57 // Note that thread lambdas aren't using implicit capture [=], to avoid capturing "this",
58 // which may be asynchronously destructed.
59 std::thread([timeoutMs, callback, sharedThis = this->ref<Vibrator>()] {
60 LOG(VERBOSE) << "Starting delayed callback on another thread";
61 usleep(timeoutMs * 1000);
62
63 if (sharedThis) {
64 std::lock_guard lock(sharedThis->mMutex);
65 sharedThis->mIsVibrating = false;
66 if (sharedThis->mVibrationCallback && (callback == sharedThis->mVibrationCallback)) {
67 LOG(VERBOSE) << "Notifying callback onComplete";
68 if (!sharedThis->mVibrationCallback->onComplete().isOk()) {
69 LOG(ERROR) << "Failed to call onComplete";
70 }
71 sharedThis->mVibrationCallback = nullptr;
72 }
73 if (sharedThis->mGlobalVibrationCallback) {
74 LOG(VERBOSE) << "Notifying global callback onComplete";
75 if (!sharedThis->mGlobalVibrationCallback->onComplete().isOk()) {
76 LOG(ERROR) << "Failed to call onComplete";
77 }
78 sharedThis->mGlobalVibrationCallback = nullptr;
79 }
80 }
81 }).detach();
82}
83
84void Vibrator::setGlobalVibrationCallback(const std::shared_ptr<IVibratorCallback>& callback) {
85 std::lock_guard lock(mMutex);
86 if (mIsVibrating) {
87 mGlobalVibrationCallback = callback;
88 } else if (callback) {
89 std::thread([callback] {
90 LOG(VERBOSE) << "Notifying global callback onComplete";
91 if (!callback->onComplete().isOk()) {
92 LOG(ERROR) << "Failed to call onComplete";
93 }
94 }).detach();
95 }
96}
97
Steven Morelandd44007e2019-10-24 18:12:46 -070098ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
chasewu8af5e842022-03-22 01:42:24 +080099 LOG(VERBOSE) << "Vibrator reporting capabilities";
Ahmad Khalila788bed2024-08-05 14:43:38 +0000100 std::lock_guard lock(mMutex);
101 if (mCapabilities == 0) {
Lais Andradef1120b12024-08-23 15:12:12 +0100102 int32_t version;
103 if (!getInterfaceVersion(&version).isOk()) {
Ahmad Khalila788bed2024-08-05 14:43:38 +0000104 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_STATE));
105 }
106 mCapabilities = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
107 IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
108 IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS |
109 IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
110 IVibrator::CAP_GET_Q_FACTOR | IVibrator::CAP_FREQUENCY_CONTROL |
111 IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
112
Lais Andradef1120b12024-08-23 15:12:12 +0100113 if (version >= 3) {
114 mCapabilities |=
115 IVibrator::CAP_PERFORM_VENDOR_EFFECTS | IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2;
Ahmad Khalila788bed2024-08-05 14:43:38 +0000116 }
117 }
118
119 *_aidl_return = mCapabilities;
Steven Morelandd44007e2019-10-24 18:12:46 -0700120 return ndk::ScopedAStatus::ok();
121}
122
123ndk::ScopedAStatus Vibrator::off() {
chasewu8af5e842022-03-22 01:42:24 +0800124 LOG(VERBOSE) << "Vibrator off";
Lais Andradef1120b12024-08-23 15:12:12 +0100125 std::lock_guard lock(mMutex);
126 std::shared_ptr<IVibratorCallback> callback = mVibrationCallback;
127 std::shared_ptr<IVibratorCallback> globalCallback = mGlobalVibrationCallback;
128 mIsVibrating = false;
129 mVibrationCallback = nullptr;
130 mGlobalVibrationCallback = nullptr;
131 if (callback || globalCallback) {
132 std::thread([callback, globalCallback] {
133 if (callback) {
134 LOG(VERBOSE) << "Notifying callback onComplete";
135 if (!callback->onComplete().isOk()) {
136 LOG(ERROR) << "Failed to call onComplete";
137 }
138 }
139 if (globalCallback) {
140 LOG(VERBOSE) << "Notifying global callback onComplete";
141 if (!globalCallback->onComplete().isOk()) {
142 LOG(ERROR) << "Failed to call onComplete";
143 }
144 }
145 }).detach();
146 }
Steven Morelandd44007e2019-10-24 18:12:46 -0700147 return ndk::ScopedAStatus::ok();
148}
149
150ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
151 const std::shared_ptr<IVibratorCallback>& callback) {
chasewu8af5e842022-03-22 01:42:24 +0800152 LOG(VERBOSE) << "Vibrator on for timeoutMs: " << timeoutMs;
Lais Andradef1120b12024-08-23 15:12:12 +0100153 dispatchVibrate(timeoutMs, callback);
Steven Morelandd44007e2019-10-24 18:12:46 -0700154 return ndk::ScopedAStatus::ok();
155}
156
157ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
158 const std::shared_ptr<IVibratorCallback>& callback,
159 int32_t* _aidl_return) {
chasewu8af5e842022-03-22 01:42:24 +0800160 LOG(VERBOSE) << "Vibrator perform";
Steven Morelandd44007e2019-10-24 18:12:46 -0700161
162 if (effect != Effect::CLICK && effect != Effect::TICK) {
163 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
164 }
165 if (strength != EffectStrength::LIGHT && strength != EffectStrength::MEDIUM &&
166 strength != EffectStrength::STRONG) {
167 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
168 }
169
170 constexpr size_t kEffectMillis = 100;
Lais Andradef1120b12024-08-23 15:12:12 +0100171 dispatchVibrate(kEffectMillis, callback);
Steven Morelandd44007e2019-10-24 18:12:46 -0700172 *_aidl_return = kEffectMillis;
173 return ndk::ScopedAStatus::ok();
174}
175
Lais Andradea3c332f2024-06-20 10:46:06 +0100176ndk::ScopedAStatus Vibrator::performVendorEffect(
177 const VendorEffect& effect, const std::shared_ptr<IVibratorCallback>& callback) {
178 LOG(VERBOSE) << "Vibrator perform vendor effect";
Ahmad Khalila788bed2024-08-05 14:43:38 +0000179 int32_t capabilities = 0;
180 if (!getCapabilities(&capabilities).isOk()) {
181 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
182 }
183 if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) {
184 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
185 }
Lais Andradea3c332f2024-06-20 10:46:06 +0100186 EffectStrength strength = effect.strength;
187 if (strength != EffectStrength::LIGHT && strength != EffectStrength::MEDIUM &&
188 strength != EffectStrength::STRONG) {
189 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
190 }
191 float scale = effect.scale;
192 if (scale <= 0) {
193 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
194 }
Lais Andrade4526faf2024-08-09 11:57:20 +0100195 float vendorScale = effect.vendorScale;
196 if (vendorScale <= 0) {
197 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
198 }
Lais Andradea3c332f2024-06-20 10:46:06 +0100199
200 int32_t durationMs = 0;
201 if (!effect.vendorData.getInt("DURATION_MS", &durationMs) || durationMs <= 0) {
202 return ndk::ScopedAStatus::fromServiceSpecificError(ERROR_CODE_INVALID_DURATION);
203 }
204
Lais Andradef1120b12024-08-23 15:12:12 +0100205 dispatchVibrate(durationMs, callback);
Lais Andradea3c332f2024-06-20 10:46:06 +0100206 return ndk::ScopedAStatus::ok();
207}
208
Steven Moreland2932b222019-11-05 14:30:17 -0800209ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
210 *_aidl_return = {Effect::CLICK, Effect::TICK};
211 return ndk::ScopedAStatus::ok();
212}
213
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900214ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
chasewu8af5e842022-03-22 01:42:24 +0800215 LOG(VERBOSE) << "Vibrator set amplitude: " << amplitude;
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900216 if (amplitude <= 0.0f || amplitude > 1.0f) {
Steven Morelandd44007e2019-10-24 18:12:46 -0700217 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
218 }
219 return ndk::ScopedAStatus::ok();
220}
221
222ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
chasewu8af5e842022-03-22 01:42:24 +0800223 LOG(VERBOSE) << "Vibrator set external control: " << enabled;
Steven Morelandd44007e2019-10-24 18:12:46 -0700224 return ndk::ScopedAStatus::ok();
225}
226
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900227ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) {
chasewu8af5e842022-03-22 01:42:24 +0800228 *maxDelayMs = COMPOSE_DELAY_MAX_MS;
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900229 return ndk::ScopedAStatus::ok();
230}
231
232ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) {
chasewu8af5e842022-03-22 01:42:24 +0800233 *maxSize = COMPOSE_SIZE_MAX;
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900234 return ndk::ScopedAStatus::ok();
235}
236
Harpreet \"Eli\" Sangha523e2962020-01-21 16:15:42 +0900237ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
238 *supported = {
239 CompositePrimitive::NOOP, CompositePrimitive::CLICK,
240 CompositePrimitive::THUD, CompositePrimitive::SPIN,
241 CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
Harpreet \"Eli\" Sanghafe5d3982020-01-17 14:46:37 +0900242 CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
Vince Leungdeb46ec2020-12-22 00:03:16 +0000243 CompositePrimitive::LOW_TICK,
Harpreet \"Eli\" Sangha523e2962020-01-21 16:15:42 +0900244 };
245 return ndk::ScopedAStatus::ok();
246}
247
248ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
249 int32_t* durationMs) {
Lais Andrade3c7f0d92021-06-16 10:20:09 +0000250 std::vector<CompositePrimitive> supported;
251 getSupportedPrimitives(&supported);
252 if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
253 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
254 }
Harpreet \"Eli\" Sangha523e2962020-01-21 16:15:42 +0900255 if (primitive != CompositePrimitive::NOOP) {
256 *durationMs = 100;
257 } else {
258 *durationMs = 0;
259 }
260 return ndk::ScopedAStatus::ok();
261}
262
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900263ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
264 const std::shared_ptr<IVibratorCallback>& callback) {
chasewu8af5e842022-03-22 01:42:24 +0800265 if (composite.size() > COMPOSE_SIZE_MAX) {
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900266 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
267 }
268
Harpreet \"Eli\" Sangha13ef28c2020-01-23 13:22:07 +0900269 std::vector<CompositePrimitive> supported;
270 getSupportedPrimitives(&supported);
271
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900272 for (auto& e : composite) {
chasewu8af5e842022-03-22 01:42:24 +0800273 if (e.delayMs > COMPOSE_DELAY_MAX_MS) {
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900274 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
275 }
Harpreet \"Eli\" Sangha7aec5022020-03-11 06:00:55 +0900276 if (e.scale < 0.0f || e.scale > 1.0f) {
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900277 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
278 }
Harpreet \"Eli\" Sangha13ef28c2020-01-23 13:22:07 +0900279 if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900280 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
281 }
282 }
283
Lais Andradef1120b12024-08-23 15:12:12 +0100284 int32_t totalDuration = 0;
285 for (auto& e : composite) {
286 int32_t durationMs;
287 getPrimitiveDuration(e.primitive, &durationMs);
288 totalDuration += e.delayMs + durationMs;
289 }
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900290
Lais Andradef1120b12024-08-23 15:12:12 +0100291 dispatchVibrate(totalDuration, callback);
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900292 return ndk::ScopedAStatus::ok();
293}
294
Harpreet \"Eli\" Sangha63624092019-09-09 11:04:54 +0900295ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) {
296 return getSupportedEffects(_aidl_return);
297}
298
299ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
300 std::vector<Effect> effects;
301 getSupportedAlwaysOnEffects(&effects);
302
303 if (std::find(effects.begin(), effects.end(), effect) == effects.end()) {
304 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
305 } else {
chasewu8af5e842022-03-22 01:42:24 +0800306 LOG(VERBOSE) << "Enabling always-on ID " << id << " with " << toString(effect) << "/"
307 << toString(strength);
Harpreet \"Eli\" Sangha63624092019-09-09 11:04:54 +0900308 return ndk::ScopedAStatus::ok();
309 }
310}
311
312ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id) {
chasewu8af5e842022-03-22 01:42:24 +0800313 LOG(VERBOSE) << "Disabling always-on ID " << id;
Harpreet \"Eli\" Sangha63624092019-09-09 11:04:54 +0900314 return ndk::ScopedAStatus::ok();
315}
316
Vince Leung4bae4f92021-02-03 06:21:58 +0000317ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
chasewu8af5e842022-03-22 01:42:24 +0800318 *resonantFreqHz = RESONANT_FREQUENCY_HZ;
Vince Leung4bae4f92021-02-03 06:21:58 +0000319 return ndk::ScopedAStatus::ok();
320}
321
322ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
chasewu8af5e842022-03-22 01:42:24 +0800323 *qFactor = Q_FACTOR;
Vince Leung4bae4f92021-02-03 06:21:58 +0000324 return ndk::ScopedAStatus::ok();
325}
326
Vince Leung823cf5f2021-02-11 02:21:57 +0000327ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
328 *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ;
329 return ndk::ScopedAStatus::ok();
330}
331
332ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
333 *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ;
334 return ndk::ScopedAStatus::ok();
335}
336
337ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) {
chasewu8af5e842022-03-22 01:42:24 +0800338 // The output BandwidthAmplitudeMap will be as below and the maximum
339 // amplitude 1.0 will be set on RESONANT_FREQUENCY_HZ
340 // {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,
341 // 0.96, 0.95, 0.94, 0.93, 0.92, 0.91, 0.9}
342 int32_t capabilities = 0;
343 int halfMapSize = PWLE_BW_MAP_SIZE / 2;
344 Vibrator::getCapabilities(&capabilities);
345 if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
346 std::vector<float> bandwidthAmplitudeMap(PWLE_BW_MAP_SIZE, PWLE_LEVEL_MAX);
347 for (int i = 0; i < halfMapSize; ++i) {
348 bandwidthAmplitudeMap[halfMapSize + i + 1] =
349 bandwidthAmplitudeMap[halfMapSize + i] - 0.01;
350 bandwidthAmplitudeMap[halfMapSize - i - 1] =
351 bandwidthAmplitudeMap[halfMapSize - i] - 0.01;
352 }
353 *_aidl_return = bandwidthAmplitudeMap;
354 return ndk::ScopedAStatus::ok();
355 } else {
356 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
357 }
Vince Leung823cf5f2021-02-11 02:21:57 +0000358}
359
360ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
361 *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS;
362 return ndk::ScopedAStatus::ok();
363}
364
365ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
chasewu8af5e842022-03-22 01:42:24 +0800366 *maxSize = COMPOSE_PWLE_SIZE_MAX;
Vince Leung823cf5f2021-02-11 02:21:57 +0000367 return ndk::ScopedAStatus::ok();
368}
369
370ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) {
371 *supported = {
372 Braking::NONE,
373 Braking::CLAB,
374 };
375 return ndk::ScopedAStatus::ok();
376}
377
378void resetPreviousEndAmplitudeEndFrequency(float &prevEndAmplitude, float &prevEndFrequency) {
379 const float reset = -1.0;
380 prevEndAmplitude = reset;
381 prevEndFrequency = reset;
382}
383
384void incrementIndex(int &index) {
385 index += 1;
386}
387
388void constructActiveDefaults(std::ostringstream &pwleBuilder, const int &segmentIdx) {
389 pwleBuilder << ",C" << segmentIdx << ":1";
390 pwleBuilder << ",B" << segmentIdx << ":0";
391 pwleBuilder << ",AR" << segmentIdx << ":0";
392 pwleBuilder << ",V" << segmentIdx << ":0";
393}
394
395void constructActiveSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
396 float amplitude, float frequency) {
397 pwleBuilder << ",T" << segmentIdx << ":" << duration;
398 pwleBuilder << ",L" << segmentIdx << ":" << amplitude;
399 pwleBuilder << ",F" << segmentIdx << ":" << frequency;
400 constructActiveDefaults(pwleBuilder, segmentIdx);
401}
402
403void constructBrakingSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
404 Braking brakingType) {
405 pwleBuilder << ",T" << segmentIdx << ":" << duration;
406 pwleBuilder << ",L" << segmentIdx << ":" << 0;
407 pwleBuilder << ",F" << segmentIdx << ":" << 0;
408 pwleBuilder << ",C" << segmentIdx << ":0";
409 pwleBuilder << ",B" << segmentIdx << ":"
410 << static_cast<std::underlying_type<Braking>::type>(brakingType);
411 pwleBuilder << ",AR" << segmentIdx << ":0";
412 pwleBuilder << ",V" << segmentIdx << ":0";
413}
414
415ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
416 const std::shared_ptr<IVibratorCallback> &callback) {
417 std::ostringstream pwleBuilder;
418 std::string pwleQueue;
419
420 int compositionSizeMax;
421 getPwleCompositionSizeMax(&compositionSizeMax);
422 if (composite.size() <= 0 || composite.size() > compositionSizeMax) {
423 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
424 }
425
426 float prevEndAmplitude;
427 float prevEndFrequency;
428 resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
429
430 int segmentIdx = 0;
431 uint32_t totalDuration = 0;
432
433 pwleBuilder << "S:0,WF:4,RP:0,WT:0";
434
435 for (auto &e : composite) {
436 switch (e.getTag()) {
437 case PrimitivePwle::active: {
438 auto active = e.get<PrimitivePwle::active>();
439 if (active.duration < 0 ||
440 active.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
441 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
442 }
443 if (active.startAmplitude < PWLE_LEVEL_MIN ||
444 active.startAmplitude > PWLE_LEVEL_MAX ||
445 active.endAmplitude < PWLE_LEVEL_MIN || active.endAmplitude > PWLE_LEVEL_MAX) {
446 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
447 }
448 if (active.startFrequency < PWLE_FREQUENCY_MIN_HZ ||
449 active.startFrequency > PWLE_FREQUENCY_MAX_HZ ||
450 active.endFrequency < PWLE_FREQUENCY_MIN_HZ ||
451 active.endFrequency > PWLE_FREQUENCY_MAX_HZ) {
452 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
453 }
454
455 if (!((active.startAmplitude == prevEndAmplitude) &&
456 (active.startFrequency == prevEndFrequency))) {
457 constructActiveSegment(pwleBuilder, segmentIdx, 0, active.startAmplitude,
458 active.startFrequency);
459 incrementIndex(segmentIdx);
460 }
461
462 constructActiveSegment(pwleBuilder, segmentIdx, active.duration,
463 active.endAmplitude, active.endFrequency);
464 incrementIndex(segmentIdx);
465
466 prevEndAmplitude = active.endAmplitude;
467 prevEndFrequency = active.endFrequency;
468 totalDuration += active.duration;
469 break;
470 }
471 case PrimitivePwle::braking: {
472 auto braking = e.get<PrimitivePwle::braking>();
473 if (braking.braking > Braking::CLAB) {
474 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
475 }
476 if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
477 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
478 }
479
480 constructBrakingSegment(pwleBuilder, segmentIdx, 0, braking.braking);
481 incrementIndex(segmentIdx);
482
483 constructBrakingSegment(pwleBuilder, segmentIdx, braking.duration, braking.braking);
484 incrementIndex(segmentIdx);
485
486 resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
487 totalDuration += braking.duration;
488 break;
489 }
490 }
491 }
492
Lais Andradef1120b12024-08-23 15:12:12 +0100493 dispatchVibrate(totalDuration, callback);
Vince Leung823cf5f2021-02-11 02:21:57 +0000494 return ndk::ScopedAStatus::ok();
495}
496
Ahmad Khalilbb669e12024-09-28 20:08:14 +0000497ndk::ScopedAStatus Vibrator::getFrequencyToOutputAccelerationMap(
498 std::vector<FrequencyAccelerationMapEntry>* _aidl_return) {
499 int32_t capabilities = 0;
500 if (!getCapabilities(&capabilities).isOk()) {
501 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
502 }
503 if (!(capabilities & IVibrator::CAP_FREQUENCY_CONTROL)) {
504 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
505 }
506
507 std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
Ahmad Khalila788bed2024-08-05 14:43:38 +0000508
509 std::vector<std::pair<float, float>> frequencyToOutputAccelerationData = {
510 {30.0f, 0.01f}, {46.0f, 0.09f}, {50.0f, 0.1f}, {55.0f, 0.12f}, {62.0f, 0.66f},
511 {83.0f, 0.82f}, {85.0f, 0.85f}, {92.0f, 1.05f}, {107.0f, 1.63f}, {115.0f, 1.72f},
512 {123.0f, 1.81f}, {135.0f, 2.23f}, {144.0f, 2.47f}, {145.0f, 2.5f}, {150.0f, 3.0f},
513 {175.0f, 2.51f}, {181.0f, 2.41f}, {190.0f, 2.28f}, {200.0f, 2.08f}, {204.0f, 1.96f},
514 {205.0f, 1.9f}, {224.0f, 1.7f}, {235.0f, 1.5f}, {242.0f, 1.46f}, {253.0f, 1.41f},
515 {263.0f, 1.39f}, {65.0f, 1.38f}, {278.0f, 1.37f}, {294.0f, 1.35f}, {300.0f, 1.34f}};
516 for (const auto& entry : frequencyToOutputAccelerationData) {
517 frequencyToOutputAccelerationMap.push_back(
Ahmad Khalilbb669e12024-09-28 20:08:14 +0000518 FrequencyAccelerationMapEntry(/*frequency=*/entry.first,
519 /*maxOutputAcceleration=*/entry.second));
Ahmad Khalila788bed2024-08-05 14:43:38 +0000520 }
521
522 *_aidl_return = frequencyToOutputAccelerationMap;
523
524 return ndk::ScopedAStatus::ok();
525}
526
527ndk::ScopedAStatus Vibrator::getPwleV2PrimitiveDurationMaxMillis(int32_t* maxDurationMs) {
528 *maxDurationMs = COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MAX_MS;
529 return ndk::ScopedAStatus::ok();
530}
531
532ndk::ScopedAStatus Vibrator::getPwleV2CompositionSizeMax(int32_t* maxSize) {
533 *maxSize = COMPOSE_PWLE_V2_SIZE_MAX;
534 return ndk::ScopedAStatus::ok();
535}
536
537ndk::ScopedAStatus Vibrator::getPwleV2PrimitiveDurationMinMillis(int32_t* minDurationMs) {
538 *minDurationMs = COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MIN_MS;
539 return ndk::ScopedAStatus::ok();
540}
541
Ahmad Khalilbb669e12024-09-28 20:08:14 +0000542float getPwleV2FrequencyMinHz(
543 std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap) {
Ahmad Khalila788bed2024-08-05 14:43:38 +0000544 if (frequencyToOutputAccelerationMap.empty()) {
545 return 0.0f;
546 }
547
548 float minFrequency = frequencyToOutputAccelerationMap[0].frequencyHz;
549
550 for (const auto& entry : frequencyToOutputAccelerationMap) {
551 if (entry.frequencyHz < minFrequency) {
552 minFrequency = entry.frequencyHz;
553 }
554 }
555
556 return minFrequency;
557}
558
Ahmad Khalilbb669e12024-09-28 20:08:14 +0000559float getPwleV2FrequencyMaxHz(
560 std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap) {
Ahmad Khalila788bed2024-08-05 14:43:38 +0000561 if (frequencyToOutputAccelerationMap.empty()) {
562 return 0.0f;
563 }
564
565 float maxFrequency = frequencyToOutputAccelerationMap[0].frequencyHz;
566
567 for (const auto& entry : frequencyToOutputAccelerationMap) {
568 if (entry.frequencyHz > maxFrequency) {
569 maxFrequency = entry.frequencyHz;
570 }
571 }
572
573 return maxFrequency;
574}
575
Ahmad Khalilbb669e12024-09-28 20:08:14 +0000576ndk::ScopedAStatus Vibrator::composePwleV2(const CompositePwleV2& composite,
Ahmad Khalila788bed2024-08-05 14:43:38 +0000577 const std::shared_ptr<IVibratorCallback>& callback) {
Lais Andradef1120b12024-08-23 15:12:12 +0100578 LOG(VERBOSE) << "Vibrator compose PWLE V2";
Ahmad Khalil7eacaad2024-08-22 12:25:36 +0000579 int32_t capabilities = 0;
580 if (!getCapabilities(&capabilities).isOk()) {
581 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
582 }
Ahmad Khalilbb669e12024-09-28 20:08:14 +0000583 if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2) ||
584 !(capabilities & IVibrator::CAP_FREQUENCY_CONTROL)) {
Ahmad Khalil7eacaad2024-08-22 12:25:36 +0000585 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
586 }
587
Ahmad Khalila788bed2024-08-05 14:43:38 +0000588 int compositionSizeMax;
589 getPwleV2CompositionSizeMax(&compositionSizeMax);
Ahmad Khalilbb669e12024-09-28 20:08:14 +0000590 if (composite.pwlePrimitives.empty() || composite.pwlePrimitives.size() > compositionSizeMax) {
Ahmad Khalila788bed2024-08-05 14:43:38 +0000591 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
592 }
593
594 int32_t totalEffectDuration = 0;
Ahmad Khalilbb669e12024-09-28 20:08:14 +0000595 std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
596 getFrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
Ahmad Khalila788bed2024-08-05 14:43:38 +0000597 float minFrequency = getPwleV2FrequencyMinHz(frequencyToOutputAccelerationMap);
598 float maxFrequency = getPwleV2FrequencyMaxHz(frequencyToOutputAccelerationMap);
599
Ahmad Khalilbb669e12024-09-28 20:08:14 +0000600 for (auto& e : composite.pwlePrimitives) {
Ahmad Khalila788bed2024-08-05 14:43:38 +0000601 if (e.timeMillis < 0.0f || e.timeMillis > COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MAX_MS) {
602 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
603 }
604 if (e.amplitude < 0.0f || e.amplitude > 1.0f) {
605 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
606 }
607 if (e.frequencyHz < minFrequency || e.frequencyHz > maxFrequency) {
608 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
609 }
610 totalEffectDuration += e.timeMillis;
611 }
612
Lais Andradef1120b12024-08-23 15:12:12 +0100613 dispatchVibrate(totalEffectDuration, callback);
Ahmad Khalila788bed2024-08-05 14:43:38 +0000614 return ndk::ScopedAStatus::ok();
615}
616
Steven Morelandd44007e2019-10-24 18:12:46 -0700617} // namespace vibrator
618} // namespace hardware
619} // namespace android
620} // namespace aidl