blob: c6682b318f360f0e21fc608c9402019a402390b4 [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
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +090027static constexpr int32_t kComposeDelayMaxMs = 1000;
28static constexpr int32_t kComposeSizeMax = 256;
Vince Leung823cf5f2021-02-11 02:21:57 +000029static constexpr int32_t kComposePwleSizeMax = 127;
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +090030
Vince Leung4bae4f92021-02-03 06:21:58 +000031static constexpr float kResonantFrequency = 150.0;
32static constexpr float kQFactor = 11.0;
Vince Leung823cf5f2021-02-11 02:21:57 +000033static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;
34static constexpr float PWLE_LEVEL_MIN = 0.0;
35static constexpr float PWLE_LEVEL_MAX = 0.98256;
36static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.0;
37static constexpr float PWLE_FREQUENCY_MIN_HZ = 140.0;
38static constexpr float PWLE_FREQUENCY_MAX_HZ = 160.0;
Vince Leung4bae4f92021-02-03 06:21:58 +000039
Steven Morelandd44007e2019-10-24 18:12:46 -070040ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
41 LOG(INFO) << "Vibrator reporting capabilities";
42 *_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
Steven Morelandc0b92d52019-11-05 13:31:41 -080043 IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
Harpreet \"Eli\" Sangha63624092019-09-09 11:04:54 +090044 IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS |
Vince Leung4bae4f92021-02-03 06:21:58 +000045 IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
Vince Leung823cf5f2021-02-11 02:21:57 +000046 IVibrator::CAP_GET_Q_FACTOR | IVibrator::CAP_FREQUENCY_CONTROL |
47 IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
Steven Morelandd44007e2019-10-24 18:12:46 -070048 return ndk::ScopedAStatus::ok();
49}
50
51ndk::ScopedAStatus Vibrator::off() {
52 LOG(INFO) << "Vibrator off";
53 return ndk::ScopedAStatus::ok();
54}
55
56ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
57 const std::shared_ptr<IVibratorCallback>& callback) {
58 LOG(INFO) << "Vibrator on for timeoutMs: " << timeoutMs;
59 if (callback != nullptr) {
60 std::thread([=] {
61 LOG(INFO) << "Starting on on another thread";
62 usleep(timeoutMs * 1000);
63 LOG(INFO) << "Notifying on complete";
Steven Morelandf57ad9b2019-11-27 16:04:52 -080064 if (!callback->onComplete().isOk()) {
65 LOG(ERROR) << "Failed to call onComplete";
66 }
Steven Morelandd44007e2019-10-24 18:12:46 -070067 }).detach();
68 }
69 return ndk::ScopedAStatus::ok();
70}
71
72ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
73 const std::shared_ptr<IVibratorCallback>& callback,
74 int32_t* _aidl_return) {
75 LOG(INFO) << "Vibrator perform";
76
77 if (effect != Effect::CLICK && effect != Effect::TICK) {
78 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
79 }
80 if (strength != EffectStrength::LIGHT && strength != EffectStrength::MEDIUM &&
81 strength != EffectStrength::STRONG) {
82 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
83 }
84
85 constexpr size_t kEffectMillis = 100;
86
87 if (callback != nullptr) {
88 std::thread([=] {
89 LOG(INFO) << "Starting perform on another thread";
90 usleep(kEffectMillis * 1000);
91 LOG(INFO) << "Notifying perform complete";
92 callback->onComplete();
93 }).detach();
94 }
95
96 *_aidl_return = kEffectMillis;
97 return ndk::ScopedAStatus::ok();
98}
99
Steven Moreland2932b222019-11-05 14:30:17 -0800100ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
101 *_aidl_return = {Effect::CLICK, Effect::TICK};
102 return ndk::ScopedAStatus::ok();
103}
104
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900105ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
Steven Morelandd44007e2019-10-24 18:12:46 -0700106 LOG(INFO) << "Vibrator set amplitude: " << amplitude;
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900107 if (amplitude <= 0.0f || amplitude > 1.0f) {
Steven Morelandd44007e2019-10-24 18:12:46 -0700108 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
109 }
110 return ndk::ScopedAStatus::ok();
111}
112
113ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
114 LOG(INFO) << "Vibrator set external control: " << enabled;
115 return ndk::ScopedAStatus::ok();
116}
117
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900118ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) {
119 *maxDelayMs = kComposeDelayMaxMs;
120 return ndk::ScopedAStatus::ok();
121}
122
123ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) {
124 *maxSize = kComposeSizeMax;
125 return ndk::ScopedAStatus::ok();
126}
127
Harpreet \"Eli\" Sangha523e2962020-01-21 16:15:42 +0900128ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
129 *supported = {
130 CompositePrimitive::NOOP, CompositePrimitive::CLICK,
131 CompositePrimitive::THUD, CompositePrimitive::SPIN,
132 CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
Harpreet \"Eli\" Sanghafe5d3982020-01-17 14:46:37 +0900133 CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
Vince Leungdeb46ec2020-12-22 00:03:16 +0000134 CompositePrimitive::LOW_TICK,
Harpreet \"Eli\" Sangha523e2962020-01-21 16:15:42 +0900135 };
136 return ndk::ScopedAStatus::ok();
137}
138
139ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
140 int32_t* durationMs) {
141 if (primitive != CompositePrimitive::NOOP) {
142 *durationMs = 100;
143 } else {
144 *durationMs = 0;
145 }
146 return ndk::ScopedAStatus::ok();
147}
148
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900149ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
150 const std::shared_ptr<IVibratorCallback>& callback) {
151 if (composite.size() > kComposeSizeMax) {
152 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
153 }
154
Harpreet \"Eli\" Sangha13ef28c2020-01-23 13:22:07 +0900155 std::vector<CompositePrimitive> supported;
156 getSupportedPrimitives(&supported);
157
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900158 for (auto& e : composite) {
159 if (e.delayMs > kComposeDelayMaxMs) {
160 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
161 }
Harpreet \"Eli\" Sangha7aec5022020-03-11 06:00:55 +0900162 if (e.scale < 0.0f || e.scale > 1.0f) {
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900163 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
164 }
Harpreet \"Eli\" Sangha13ef28c2020-01-23 13:22:07 +0900165 if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900166 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
167 }
168 }
169
170 std::thread([=] {
171 LOG(INFO) << "Starting compose on another thread";
172
173 for (auto& e : composite) {
174 if (e.delayMs) {
175 usleep(e.delayMs * 1000);
176 }
177 LOG(INFO) << "triggering primitive " << static_cast<int>(e.primitive) << " @ scale "
178 << e.scale;
Harpreet \"Eli\" Sanghab075a6a2020-04-10 15:11:58 +0900179
180 int32_t durationMs;
181 getPrimitiveDuration(e.primitive, &durationMs);
182 usleep(durationMs * 1000);
Harpreet \"Eli\" Sanghaf4de5b02019-10-23 09:25:52 +0900183 }
184
185 if (callback != nullptr) {
186 LOG(INFO) << "Notifying perform complete";
187 callback->onComplete();
188 }
189 }).detach();
190
191 return ndk::ScopedAStatus::ok();
192}
193
Harpreet \"Eli\" Sangha63624092019-09-09 11:04:54 +0900194ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) {
195 return getSupportedEffects(_aidl_return);
196}
197
198ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
199 std::vector<Effect> effects;
200 getSupportedAlwaysOnEffects(&effects);
201
202 if (std::find(effects.begin(), effects.end(), effect) == effects.end()) {
203 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
204 } else {
205 LOG(INFO) << "Enabling always-on ID " << id << " with " << toString(effect) << "/"
206 << toString(strength);
207 return ndk::ScopedAStatus::ok();
208 }
209}
210
211ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id) {
212 LOG(INFO) << "Disabling always-on ID " << id;
213 return ndk::ScopedAStatus::ok();
214}
215
Vince Leung4bae4f92021-02-03 06:21:58 +0000216ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
217 *resonantFreqHz = kResonantFrequency;
218 return ndk::ScopedAStatus::ok();
219}
220
221ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
222 *qFactor = kQFactor;
223 return ndk::ScopedAStatus::ok();
224}
225
Vince Leung823cf5f2021-02-11 02:21:57 +0000226ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
227 *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ;
228 return ndk::ScopedAStatus::ok();
229}
230
231ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
232 *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ;
233 return ndk::ScopedAStatus::ok();
234}
235
236ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) {
237 // A valid array should be of size:
238 // (PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ
239 *_aidl_return = {0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10,
240 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20};
241 return ndk::ScopedAStatus::ok();
242}
243
244ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
245 *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS;
246 return ndk::ScopedAStatus::ok();
247}
248
249ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
250 *maxSize = kComposePwleSizeMax;
251 return ndk::ScopedAStatus::ok();
252}
253
254ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) {
255 *supported = {
256 Braking::NONE,
257 Braking::CLAB,
258 };
259 return ndk::ScopedAStatus::ok();
260}
261
262void resetPreviousEndAmplitudeEndFrequency(float &prevEndAmplitude, float &prevEndFrequency) {
263 const float reset = -1.0;
264 prevEndAmplitude = reset;
265 prevEndFrequency = reset;
266}
267
268void incrementIndex(int &index) {
269 index += 1;
270}
271
272void constructActiveDefaults(std::ostringstream &pwleBuilder, const int &segmentIdx) {
273 pwleBuilder << ",C" << segmentIdx << ":1";
274 pwleBuilder << ",B" << segmentIdx << ":0";
275 pwleBuilder << ",AR" << segmentIdx << ":0";
276 pwleBuilder << ",V" << segmentIdx << ":0";
277}
278
279void constructActiveSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
280 float amplitude, float frequency) {
281 pwleBuilder << ",T" << segmentIdx << ":" << duration;
282 pwleBuilder << ",L" << segmentIdx << ":" << amplitude;
283 pwleBuilder << ",F" << segmentIdx << ":" << frequency;
284 constructActiveDefaults(pwleBuilder, segmentIdx);
285}
286
287void constructBrakingSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
288 Braking brakingType) {
289 pwleBuilder << ",T" << segmentIdx << ":" << duration;
290 pwleBuilder << ",L" << segmentIdx << ":" << 0;
291 pwleBuilder << ",F" << segmentIdx << ":" << 0;
292 pwleBuilder << ",C" << segmentIdx << ":0";
293 pwleBuilder << ",B" << segmentIdx << ":"
294 << static_cast<std::underlying_type<Braking>::type>(brakingType);
295 pwleBuilder << ",AR" << segmentIdx << ":0";
296 pwleBuilder << ",V" << segmentIdx << ":0";
297}
298
299ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
300 const std::shared_ptr<IVibratorCallback> &callback) {
301 std::ostringstream pwleBuilder;
302 std::string pwleQueue;
303
304 int compositionSizeMax;
305 getPwleCompositionSizeMax(&compositionSizeMax);
306 if (composite.size() <= 0 || composite.size() > compositionSizeMax) {
307 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
308 }
309
310 float prevEndAmplitude;
311 float prevEndFrequency;
312 resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
313
314 int segmentIdx = 0;
315 uint32_t totalDuration = 0;
316
317 pwleBuilder << "S:0,WF:4,RP:0,WT:0";
318
319 for (auto &e : composite) {
320 switch (e.getTag()) {
321 case PrimitivePwle::active: {
322 auto active = e.get<PrimitivePwle::active>();
323 if (active.duration < 0 ||
324 active.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
325 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
326 }
327 if (active.startAmplitude < PWLE_LEVEL_MIN ||
328 active.startAmplitude > PWLE_LEVEL_MAX ||
329 active.endAmplitude < PWLE_LEVEL_MIN || active.endAmplitude > PWLE_LEVEL_MAX) {
330 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
331 }
332 if (active.startFrequency < PWLE_FREQUENCY_MIN_HZ ||
333 active.startFrequency > PWLE_FREQUENCY_MAX_HZ ||
334 active.endFrequency < PWLE_FREQUENCY_MIN_HZ ||
335 active.endFrequency > PWLE_FREQUENCY_MAX_HZ) {
336 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
337 }
338
339 if (!((active.startAmplitude == prevEndAmplitude) &&
340 (active.startFrequency == prevEndFrequency))) {
341 constructActiveSegment(pwleBuilder, segmentIdx, 0, active.startAmplitude,
342 active.startFrequency);
343 incrementIndex(segmentIdx);
344 }
345
346 constructActiveSegment(pwleBuilder, segmentIdx, active.duration,
347 active.endAmplitude, active.endFrequency);
348 incrementIndex(segmentIdx);
349
350 prevEndAmplitude = active.endAmplitude;
351 prevEndFrequency = active.endFrequency;
352 totalDuration += active.duration;
353 break;
354 }
355 case PrimitivePwle::braking: {
356 auto braking = e.get<PrimitivePwle::braking>();
357 if (braking.braking > Braking::CLAB) {
358 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
359 }
360 if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
361 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
362 }
363
364 constructBrakingSegment(pwleBuilder, segmentIdx, 0, braking.braking);
365 incrementIndex(segmentIdx);
366
367 constructBrakingSegment(pwleBuilder, segmentIdx, braking.duration, braking.braking);
368 incrementIndex(segmentIdx);
369
370 resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
371 totalDuration += braking.duration;
372 break;
373 }
374 }
375 }
376
377 std::thread([=] {
378 LOG(INFO) << "Starting composePwle on another thread";
379 usleep(totalDuration * 1000);
380 if (callback != nullptr) {
381 LOG(INFO) << "Notifying compose PWLE complete";
382 callback->onComplete();
383 }
384 }).detach();
385
386 return ndk::ScopedAStatus::ok();
387}
388
Steven Morelandd44007e2019-10-24 18:12:46 -0700389} // namespace vibrator
390} // namespace hardware
391} // namespace android
392} // namespace aidl