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