1 /*
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
17 #include "vibrator-impl/Vibrator.h"
18
19 #include <android-base/logging.h>
20 #include <thread>
21
22 namespace aidl {
23 namespace android {
24 namespace hardware {
25 namespace vibrator {
26
27 static constexpr int32_t kComposeDelayMaxMs = 1000;
28 static constexpr int32_t kComposeSizeMax = 256;
29 static constexpr int32_t kComposePwleSizeMax = 127;
30
31 static constexpr float kResonantFrequency = 150.0;
32 static constexpr float kQFactor = 11.0;
33 static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;
34 static constexpr float PWLE_LEVEL_MIN = 0.0;
35 static constexpr float PWLE_LEVEL_MAX = 0.98256;
36 static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.0;
37 static constexpr float PWLE_FREQUENCY_MIN_HZ = 140.0;
38 static constexpr float PWLE_FREQUENCY_MAX_HZ = 160.0;
39
getCapabilities(int32_t * _aidl_return)40 ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
41 LOG(INFO) << "Vibrator reporting capabilities";
42 *_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
43 IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
44 IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS |
45 IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
46 IVibrator::CAP_GET_Q_FACTOR | IVibrator::CAP_FREQUENCY_CONTROL |
47 IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
48 return ndk::ScopedAStatus::ok();
49 }
50
off()51 ndk::ScopedAStatus Vibrator::off() {
52 LOG(INFO) << "Vibrator off";
53 return ndk::ScopedAStatus::ok();
54 }
55
on(int32_t timeoutMs,const std::shared_ptr<IVibratorCallback> & callback)56 ndk::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";
64 if (!callback->onComplete().isOk()) {
65 LOG(ERROR) << "Failed to call onComplete";
66 }
67 }).detach();
68 }
69 return ndk::ScopedAStatus::ok();
70 }
71
perform(Effect effect,EffectStrength strength,const std::shared_ptr<IVibratorCallback> & callback,int32_t * _aidl_return)72 ndk::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
getSupportedEffects(std::vector<Effect> * _aidl_return)100 ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
101 *_aidl_return = {Effect::CLICK, Effect::TICK};
102 return ndk::ScopedAStatus::ok();
103 }
104
setAmplitude(float amplitude)105 ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
106 LOG(INFO) << "Vibrator set amplitude: " << amplitude;
107 if (amplitude <= 0.0f || amplitude > 1.0f) {
108 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
109 }
110 return ndk::ScopedAStatus::ok();
111 }
112
setExternalControl(bool enabled)113 ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
114 LOG(INFO) << "Vibrator set external control: " << enabled;
115 return ndk::ScopedAStatus::ok();
116 }
117
getCompositionDelayMax(int32_t * maxDelayMs)118 ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) {
119 *maxDelayMs = kComposeDelayMaxMs;
120 return ndk::ScopedAStatus::ok();
121 }
122
getCompositionSizeMax(int32_t * maxSize)123 ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) {
124 *maxSize = kComposeSizeMax;
125 return ndk::ScopedAStatus::ok();
126 }
127
getSupportedPrimitives(std::vector<CompositePrimitive> * supported)128 ndk::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,
133 CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
134 CompositePrimitive::LOW_TICK,
135 };
136 return ndk::ScopedAStatus::ok();
137 }
138
getPrimitiveDuration(CompositePrimitive primitive,int32_t * durationMs)139 ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
140 int32_t* durationMs) {
141 std::vector<CompositePrimitive> supported;
142 getSupportedPrimitives(&supported);
143 if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
144 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
145 }
146 if (primitive != CompositePrimitive::NOOP) {
147 *durationMs = 100;
148 } else {
149 *durationMs = 0;
150 }
151 return ndk::ScopedAStatus::ok();
152 }
153
compose(const std::vector<CompositeEffect> & composite,const std::shared_ptr<IVibratorCallback> & callback)154 ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
155 const std::shared_ptr<IVibratorCallback>& callback) {
156 if (composite.size() > kComposeSizeMax) {
157 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
158 }
159
160 std::vector<CompositePrimitive> supported;
161 getSupportedPrimitives(&supported);
162
163 for (auto& e : composite) {
164 if (e.delayMs > kComposeDelayMaxMs) {
165 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
166 }
167 if (e.scale < 0.0f || e.scale > 1.0f) {
168 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
169 }
170 if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
171 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
172 }
173 }
174
175 std::thread([=] {
176 LOG(INFO) << "Starting compose on another thread";
177
178 for (auto& e : composite) {
179 if (e.delayMs) {
180 usleep(e.delayMs * 1000);
181 }
182 LOG(INFO) << "triggering primitive " << static_cast<int>(e.primitive) << " @ scale "
183 << e.scale;
184
185 int32_t durationMs;
186 getPrimitiveDuration(e.primitive, &durationMs);
187 usleep(durationMs * 1000);
188 }
189
190 if (callback != nullptr) {
191 LOG(INFO) << "Notifying perform complete";
192 callback->onComplete();
193 }
194 }).detach();
195
196 return ndk::ScopedAStatus::ok();
197 }
198
getSupportedAlwaysOnEffects(std::vector<Effect> * _aidl_return)199 ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) {
200 return getSupportedEffects(_aidl_return);
201 }
202
alwaysOnEnable(int32_t id,Effect effect,EffectStrength strength)203 ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
204 std::vector<Effect> effects;
205 getSupportedAlwaysOnEffects(&effects);
206
207 if (std::find(effects.begin(), effects.end(), effect) == effects.end()) {
208 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
209 } else {
210 LOG(INFO) << "Enabling always-on ID " << id << " with " << toString(effect) << "/"
211 << toString(strength);
212 return ndk::ScopedAStatus::ok();
213 }
214 }
215
alwaysOnDisable(int32_t id)216 ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id) {
217 LOG(INFO) << "Disabling always-on ID " << id;
218 return ndk::ScopedAStatus::ok();
219 }
220
getResonantFrequency(float * resonantFreqHz)221 ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
222 *resonantFreqHz = kResonantFrequency;
223 return ndk::ScopedAStatus::ok();
224 }
225
getQFactor(float * qFactor)226 ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
227 *qFactor = kQFactor;
228 return ndk::ScopedAStatus::ok();
229 }
230
getFrequencyResolution(float * freqResolutionHz)231 ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
232 *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ;
233 return ndk::ScopedAStatus::ok();
234 }
235
getFrequencyMinimum(float * freqMinimumHz)236 ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
237 *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ;
238 return ndk::ScopedAStatus::ok();
239 }
240
getBandwidthAmplitudeMap(std::vector<float> * _aidl_return)241 ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) {
242 // A valid array should be of size:
243 // (PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ
244 *_aidl_return = {0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10,
245 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20};
246 return ndk::ScopedAStatus::ok();
247 }
248
getPwlePrimitiveDurationMax(int32_t * durationMs)249 ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
250 *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS;
251 return ndk::ScopedAStatus::ok();
252 }
253
getPwleCompositionSizeMax(int32_t * maxSize)254 ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
255 *maxSize = kComposePwleSizeMax;
256 return ndk::ScopedAStatus::ok();
257 }
258
getSupportedBraking(std::vector<Braking> * supported)259 ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) {
260 *supported = {
261 Braking::NONE,
262 Braking::CLAB,
263 };
264 return ndk::ScopedAStatus::ok();
265 }
266
resetPreviousEndAmplitudeEndFrequency(float & prevEndAmplitude,float & prevEndFrequency)267 void resetPreviousEndAmplitudeEndFrequency(float &prevEndAmplitude, float &prevEndFrequency) {
268 const float reset = -1.0;
269 prevEndAmplitude = reset;
270 prevEndFrequency = reset;
271 }
272
incrementIndex(int & index)273 void incrementIndex(int &index) {
274 index += 1;
275 }
276
constructActiveDefaults(std::ostringstream & pwleBuilder,const int & segmentIdx)277 void constructActiveDefaults(std::ostringstream &pwleBuilder, const int &segmentIdx) {
278 pwleBuilder << ",C" << segmentIdx << ":1";
279 pwleBuilder << ",B" << segmentIdx << ":0";
280 pwleBuilder << ",AR" << segmentIdx << ":0";
281 pwleBuilder << ",V" << segmentIdx << ":0";
282 }
283
constructActiveSegment(std::ostringstream & pwleBuilder,const int & segmentIdx,int duration,float amplitude,float frequency)284 void constructActiveSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
285 float amplitude, float frequency) {
286 pwleBuilder << ",T" << segmentIdx << ":" << duration;
287 pwleBuilder << ",L" << segmentIdx << ":" << amplitude;
288 pwleBuilder << ",F" << segmentIdx << ":" << frequency;
289 constructActiveDefaults(pwleBuilder, segmentIdx);
290 }
291
constructBrakingSegment(std::ostringstream & pwleBuilder,const int & segmentIdx,int duration,Braking brakingType)292 void constructBrakingSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
293 Braking brakingType) {
294 pwleBuilder << ",T" << segmentIdx << ":" << duration;
295 pwleBuilder << ",L" << segmentIdx << ":" << 0;
296 pwleBuilder << ",F" << segmentIdx << ":" << 0;
297 pwleBuilder << ",C" << segmentIdx << ":0";
298 pwleBuilder << ",B" << segmentIdx << ":"
299 << static_cast<std::underlying_type<Braking>::type>(brakingType);
300 pwleBuilder << ",AR" << segmentIdx << ":0";
301 pwleBuilder << ",V" << segmentIdx << ":0";
302 }
303
composePwle(const std::vector<PrimitivePwle> & composite,const std::shared_ptr<IVibratorCallback> & callback)304 ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
305 const std::shared_ptr<IVibratorCallback> &callback) {
306 std::ostringstream pwleBuilder;
307 std::string pwleQueue;
308
309 int compositionSizeMax;
310 getPwleCompositionSizeMax(&compositionSizeMax);
311 if (composite.size() <= 0 || composite.size() > compositionSizeMax) {
312 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
313 }
314
315 float prevEndAmplitude;
316 float prevEndFrequency;
317 resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
318
319 int segmentIdx = 0;
320 uint32_t totalDuration = 0;
321
322 pwleBuilder << "S:0,WF:4,RP:0,WT:0";
323
324 for (auto &e : composite) {
325 switch (e.getTag()) {
326 case PrimitivePwle::active: {
327 auto active = e.get<PrimitivePwle::active>();
328 if (active.duration < 0 ||
329 active.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
330 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
331 }
332 if (active.startAmplitude < PWLE_LEVEL_MIN ||
333 active.startAmplitude > PWLE_LEVEL_MAX ||
334 active.endAmplitude < PWLE_LEVEL_MIN || active.endAmplitude > PWLE_LEVEL_MAX) {
335 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
336 }
337 if (active.startFrequency < PWLE_FREQUENCY_MIN_HZ ||
338 active.startFrequency > PWLE_FREQUENCY_MAX_HZ ||
339 active.endFrequency < PWLE_FREQUENCY_MIN_HZ ||
340 active.endFrequency > PWLE_FREQUENCY_MAX_HZ) {
341 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
342 }
343
344 if (!((active.startAmplitude == prevEndAmplitude) &&
345 (active.startFrequency == prevEndFrequency))) {
346 constructActiveSegment(pwleBuilder, segmentIdx, 0, active.startAmplitude,
347 active.startFrequency);
348 incrementIndex(segmentIdx);
349 }
350
351 constructActiveSegment(pwleBuilder, segmentIdx, active.duration,
352 active.endAmplitude, active.endFrequency);
353 incrementIndex(segmentIdx);
354
355 prevEndAmplitude = active.endAmplitude;
356 prevEndFrequency = active.endFrequency;
357 totalDuration += active.duration;
358 break;
359 }
360 case PrimitivePwle::braking: {
361 auto braking = e.get<PrimitivePwle::braking>();
362 if (braking.braking > Braking::CLAB) {
363 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
364 }
365 if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
366 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
367 }
368
369 constructBrakingSegment(pwleBuilder, segmentIdx, 0, braking.braking);
370 incrementIndex(segmentIdx);
371
372 constructBrakingSegment(pwleBuilder, segmentIdx, braking.duration, braking.braking);
373 incrementIndex(segmentIdx);
374
375 resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
376 totalDuration += braking.duration;
377 break;
378 }
379 }
380 }
381
382 std::thread([=] {
383 LOG(INFO) << "Starting composePwle on another thread";
384 usleep(totalDuration * 1000);
385 if (callback != nullptr) {
386 LOG(INFO) << "Notifying compose PWLE complete";
387 callback->onComplete();
388 }
389 }).detach();
390
391 return ndk::ScopedAStatus::ok();
392 }
393
394 } // namespace vibrator
395 } // namespace hardware
396 } // namespace android
397 } // namespace aidl
398