• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 COMPOSE_DELAY_MAX_MS = 1000;
28 static constexpr int32_t COMPOSE_SIZE_MAX = 256;
29 static constexpr int32_t COMPOSE_PWLE_SIZE_MAX = 127;
30 
31 static constexpr float Q_FACTOR = 11.0;
32 static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;
33 static constexpr float PWLE_LEVEL_MIN = 0.0;
34 static constexpr float PWLE_LEVEL_MAX = 1.0;
35 static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.0;
36 static constexpr float PWLE_FREQUENCY_MIN_HZ = 140.0;
37 static constexpr float RESONANT_FREQUENCY_HZ = 150.0;
38 static constexpr float PWLE_FREQUENCY_MAX_HZ = 160.0;
39 static constexpr float PWLE_BW_MAP_SIZE =
40         1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
41 
getCapabilities(int32_t * _aidl_return)42 ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
43     LOG(VERBOSE) << "Vibrator reporting capabilities";
44     *_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
45                     IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
46                     IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS |
47                     IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
48                     IVibrator::CAP_GET_Q_FACTOR | IVibrator::CAP_FREQUENCY_CONTROL |
49                     IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
50     return ndk::ScopedAStatus::ok();
51 }
52 
off()53 ndk::ScopedAStatus Vibrator::off() {
54     LOG(VERBOSE) << "Vibrator off";
55     return ndk::ScopedAStatus::ok();
56 }
57 
on(int32_t timeoutMs,const std::shared_ptr<IVibratorCallback> & callback)58 ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
59                                 const std::shared_ptr<IVibratorCallback>& callback) {
60     LOG(VERBOSE) << "Vibrator on for timeoutMs: " << timeoutMs;
61     if (callback != nullptr) {
62         // Note that thread lambdas aren't using implicit capture [=], to avoid capturing "this",
63         // which may be asynchronously destructed.
64         // If "this" is needed, use [sharedThis = this->ref<Vibrator>()].
65         std::thread([timeoutMs, callback] {
66             LOG(VERBOSE) << "Starting on on another thread";
67             usleep(timeoutMs * 1000);
68             LOG(VERBOSE) << "Notifying on complete";
69             if (!callback->onComplete().isOk()) {
70                 LOG(ERROR) << "Failed to call onComplete";
71             }
72         }).detach();
73     }
74     return ndk::ScopedAStatus::ok();
75 }
76 
perform(Effect effect,EffectStrength strength,const std::shared_ptr<IVibratorCallback> & callback,int32_t * _aidl_return)77 ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
78                                      const std::shared_ptr<IVibratorCallback>& callback,
79                                      int32_t* _aidl_return) {
80     LOG(VERBOSE) << "Vibrator perform";
81 
82     if (effect != Effect::CLICK && effect != Effect::TICK) {
83         return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
84     }
85     if (strength != EffectStrength::LIGHT && strength != EffectStrength::MEDIUM &&
86         strength != EffectStrength::STRONG) {
87         return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
88     }
89 
90     constexpr size_t kEffectMillis = 100;
91 
92     if (callback != nullptr) {
93         std::thread([callback] {
94             LOG(VERBOSE) << "Starting perform on another thread";
95             usleep(kEffectMillis * 1000);
96             LOG(VERBOSE) << "Notifying perform complete";
97             callback->onComplete();
98         }).detach();
99     }
100 
101     *_aidl_return = kEffectMillis;
102     return ndk::ScopedAStatus::ok();
103 }
104 
getSupportedEffects(std::vector<Effect> * _aidl_return)105 ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
106     *_aidl_return = {Effect::CLICK, Effect::TICK};
107     return ndk::ScopedAStatus::ok();
108 }
109 
setAmplitude(float amplitude)110 ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
111     LOG(VERBOSE) << "Vibrator set amplitude: " << amplitude;
112     if (amplitude <= 0.0f || amplitude > 1.0f) {
113         return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
114     }
115     return ndk::ScopedAStatus::ok();
116 }
117 
setExternalControl(bool enabled)118 ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
119     LOG(VERBOSE) << "Vibrator set external control: " << enabled;
120     return ndk::ScopedAStatus::ok();
121 }
122 
getCompositionDelayMax(int32_t * maxDelayMs)123 ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) {
124     *maxDelayMs = COMPOSE_DELAY_MAX_MS;
125     return ndk::ScopedAStatus::ok();
126 }
127 
getCompositionSizeMax(int32_t * maxSize)128 ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) {
129     *maxSize = COMPOSE_SIZE_MAX;
130     return ndk::ScopedAStatus::ok();
131 }
132 
getSupportedPrimitives(std::vector<CompositePrimitive> * supported)133 ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
134     *supported = {
135             CompositePrimitive::NOOP,       CompositePrimitive::CLICK,
136             CompositePrimitive::THUD,       CompositePrimitive::SPIN,
137             CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
138             CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
139             CompositePrimitive::LOW_TICK,
140     };
141     return ndk::ScopedAStatus::ok();
142 }
143 
getPrimitiveDuration(CompositePrimitive primitive,int32_t * durationMs)144 ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
145                                                   int32_t* durationMs) {
146     std::vector<CompositePrimitive> supported;
147     getSupportedPrimitives(&supported);
148     if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
149         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
150     }
151     if (primitive != CompositePrimitive::NOOP) {
152         *durationMs = 100;
153     } else {
154         *durationMs = 0;
155     }
156     return ndk::ScopedAStatus::ok();
157 }
158 
compose(const std::vector<CompositeEffect> & composite,const std::shared_ptr<IVibratorCallback> & callback)159 ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
160                                      const std::shared_ptr<IVibratorCallback>& callback) {
161     if (composite.size() > COMPOSE_SIZE_MAX) {
162         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
163     }
164 
165     std::vector<CompositePrimitive> supported;
166     getSupportedPrimitives(&supported);
167 
168     for (auto& e : composite) {
169         if (e.delayMs > COMPOSE_DELAY_MAX_MS) {
170             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
171         }
172         if (e.scale < 0.0f || e.scale > 1.0f) {
173             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
174         }
175         if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
176             return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
177         }
178     }
179 
180     // The thread may theoretically outlive the vibrator, so take a proper reference to it.
181     std::thread([sharedThis = this->ref<Vibrator>(), composite, callback] {
182         LOG(VERBOSE) << "Starting compose on another thread";
183 
184         for (auto& e : composite) {
185             if (e.delayMs) {
186                 usleep(e.delayMs * 1000);
187             }
188             LOG(VERBOSE) << "triggering primitive " << static_cast<int>(e.primitive) << " @ scale "
189                          << e.scale;
190 
191             int32_t durationMs;
192             sharedThis->getPrimitiveDuration(e.primitive, &durationMs);
193             usleep(durationMs * 1000);
194         }
195 
196         if (callback != nullptr) {
197             LOG(VERBOSE) << "Notifying perform complete";
198             callback->onComplete();
199         }
200     }).detach();
201 
202     return ndk::ScopedAStatus::ok();
203 }
204 
getSupportedAlwaysOnEffects(std::vector<Effect> * _aidl_return)205 ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) {
206     return getSupportedEffects(_aidl_return);
207 }
208 
alwaysOnEnable(int32_t id,Effect effect,EffectStrength strength)209 ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
210     std::vector<Effect> effects;
211     getSupportedAlwaysOnEffects(&effects);
212 
213     if (std::find(effects.begin(), effects.end(), effect) == effects.end()) {
214         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
215     } else {
216         LOG(VERBOSE) << "Enabling always-on ID " << id << " with " << toString(effect) << "/"
217                      << toString(strength);
218         return ndk::ScopedAStatus::ok();
219     }
220 }
221 
alwaysOnDisable(int32_t id)222 ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id) {
223     LOG(VERBOSE) << "Disabling always-on ID " << id;
224     return ndk::ScopedAStatus::ok();
225 }
226 
getResonantFrequency(float * resonantFreqHz)227 ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
228     *resonantFreqHz = RESONANT_FREQUENCY_HZ;
229     return ndk::ScopedAStatus::ok();
230 }
231 
getQFactor(float * qFactor)232 ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
233     *qFactor = Q_FACTOR;
234     return ndk::ScopedAStatus::ok();
235 }
236 
getFrequencyResolution(float * freqResolutionHz)237 ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
238     *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ;
239     return ndk::ScopedAStatus::ok();
240 }
241 
getFrequencyMinimum(float * freqMinimumHz)242 ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
243     *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ;
244     return ndk::ScopedAStatus::ok();
245 }
246 
getBandwidthAmplitudeMap(std::vector<float> * _aidl_return)247 ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) {
248     // The output BandwidthAmplitudeMap will be as below and the maximum
249     // amplitude 1.0 will be set on RESONANT_FREQUENCY_HZ
250     // {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,
251     // 0.96, 0.95, 0.94, 0.93, 0.92, 0.91, 0.9}
252     int32_t capabilities = 0;
253     int halfMapSize = PWLE_BW_MAP_SIZE / 2;
254     Vibrator::getCapabilities(&capabilities);
255     if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
256         std::vector<float> bandwidthAmplitudeMap(PWLE_BW_MAP_SIZE, PWLE_LEVEL_MAX);
257         for (int i = 0; i < halfMapSize; ++i) {
258             bandwidthAmplitudeMap[halfMapSize + i + 1] =
259                     bandwidthAmplitudeMap[halfMapSize + i] - 0.01;
260             bandwidthAmplitudeMap[halfMapSize - i - 1] =
261                     bandwidthAmplitudeMap[halfMapSize - i] - 0.01;
262         }
263         *_aidl_return = bandwidthAmplitudeMap;
264         return ndk::ScopedAStatus::ok();
265     } else {
266         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
267     }
268 }
269 
getPwlePrimitiveDurationMax(int32_t * durationMs)270 ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
271     *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS;
272     return ndk::ScopedAStatus::ok();
273 }
274 
getPwleCompositionSizeMax(int32_t * maxSize)275 ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
276     *maxSize = COMPOSE_PWLE_SIZE_MAX;
277     return ndk::ScopedAStatus::ok();
278 }
279 
getSupportedBraking(std::vector<Braking> * supported)280 ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) {
281     *supported = {
282             Braking::NONE,
283             Braking::CLAB,
284     };
285     return ndk::ScopedAStatus::ok();
286 }
287 
resetPreviousEndAmplitudeEndFrequency(float & prevEndAmplitude,float & prevEndFrequency)288 void resetPreviousEndAmplitudeEndFrequency(float &prevEndAmplitude, float &prevEndFrequency) {
289     const float reset = -1.0;
290     prevEndAmplitude = reset;
291     prevEndFrequency = reset;
292 }
293 
incrementIndex(int & index)294 void incrementIndex(int &index) {
295     index += 1;
296 }
297 
constructActiveDefaults(std::ostringstream & pwleBuilder,const int & segmentIdx)298 void constructActiveDefaults(std::ostringstream &pwleBuilder, const int &segmentIdx) {
299     pwleBuilder << ",C" << segmentIdx << ":1";
300     pwleBuilder << ",B" << segmentIdx << ":0";
301     pwleBuilder << ",AR" << segmentIdx << ":0";
302     pwleBuilder << ",V" << segmentIdx << ":0";
303 }
304 
constructActiveSegment(std::ostringstream & pwleBuilder,const int & segmentIdx,int duration,float amplitude,float frequency)305 void constructActiveSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
306                             float amplitude, float frequency) {
307     pwleBuilder << ",T" << segmentIdx << ":" << duration;
308     pwleBuilder << ",L" << segmentIdx << ":" << amplitude;
309     pwleBuilder << ",F" << segmentIdx << ":" << frequency;
310     constructActiveDefaults(pwleBuilder, segmentIdx);
311 }
312 
constructBrakingSegment(std::ostringstream & pwleBuilder,const int & segmentIdx,int duration,Braking brakingType)313 void constructBrakingSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
314                              Braking brakingType) {
315     pwleBuilder << ",T" << segmentIdx << ":" << duration;
316     pwleBuilder << ",L" << segmentIdx << ":" << 0;
317     pwleBuilder << ",F" << segmentIdx << ":" << 0;
318     pwleBuilder << ",C" << segmentIdx << ":0";
319     pwleBuilder << ",B" << segmentIdx << ":"
320                 << static_cast<std::underlying_type<Braking>::type>(brakingType);
321     pwleBuilder << ",AR" << segmentIdx << ":0";
322     pwleBuilder << ",V" << segmentIdx << ":0";
323 }
324 
composePwle(const std::vector<PrimitivePwle> & composite,const std::shared_ptr<IVibratorCallback> & callback)325 ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
326                                          const std::shared_ptr<IVibratorCallback> &callback) {
327     std::ostringstream pwleBuilder;
328     std::string pwleQueue;
329 
330     int compositionSizeMax;
331     getPwleCompositionSizeMax(&compositionSizeMax);
332     if (composite.size() <= 0 || composite.size() > compositionSizeMax) {
333         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
334     }
335 
336     float prevEndAmplitude;
337     float prevEndFrequency;
338     resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
339 
340     int segmentIdx = 0;
341     uint32_t totalDuration = 0;
342 
343     pwleBuilder << "S:0,WF:4,RP:0,WT:0";
344 
345     for (auto &e : composite) {
346         switch (e.getTag()) {
347             case PrimitivePwle::active: {
348                 auto active = e.get<PrimitivePwle::active>();
349                 if (active.duration < 0 ||
350                     active.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
351                     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
352                 }
353                 if (active.startAmplitude < PWLE_LEVEL_MIN ||
354                     active.startAmplitude > PWLE_LEVEL_MAX ||
355                     active.endAmplitude < PWLE_LEVEL_MIN || active.endAmplitude > PWLE_LEVEL_MAX) {
356                     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
357                 }
358                 if (active.startFrequency < PWLE_FREQUENCY_MIN_HZ ||
359                     active.startFrequency > PWLE_FREQUENCY_MAX_HZ ||
360                     active.endFrequency < PWLE_FREQUENCY_MIN_HZ ||
361                     active.endFrequency > PWLE_FREQUENCY_MAX_HZ) {
362                     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
363                 }
364 
365                 if (!((active.startAmplitude == prevEndAmplitude) &&
366                       (active.startFrequency == prevEndFrequency))) {
367                     constructActiveSegment(pwleBuilder, segmentIdx, 0, active.startAmplitude,
368                                            active.startFrequency);
369                     incrementIndex(segmentIdx);
370                 }
371 
372                 constructActiveSegment(pwleBuilder, segmentIdx, active.duration,
373                                        active.endAmplitude, active.endFrequency);
374                 incrementIndex(segmentIdx);
375 
376                 prevEndAmplitude = active.endAmplitude;
377                 prevEndFrequency = active.endFrequency;
378                 totalDuration += active.duration;
379                 break;
380             }
381             case PrimitivePwle::braking: {
382                 auto braking = e.get<PrimitivePwle::braking>();
383                 if (braking.braking > Braking::CLAB) {
384                     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
385                 }
386                 if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
387                     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
388                 }
389 
390                 constructBrakingSegment(pwleBuilder, segmentIdx, 0, braking.braking);
391                 incrementIndex(segmentIdx);
392 
393                 constructBrakingSegment(pwleBuilder, segmentIdx, braking.duration, braking.braking);
394                 incrementIndex(segmentIdx);
395 
396                 resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
397                 totalDuration += braking.duration;
398                 break;
399             }
400         }
401     }
402 
403     std::thread([totalDuration, callback] {
404         LOG(VERBOSE) << "Starting composePwle on another thread";
405         usleep(totalDuration * 1000);
406         if (callback != nullptr) {
407             LOG(VERBOSE) << "Notifying compose PWLE complete";
408             callback->onComplete();
409         }
410     }).detach();
411 
412     return ndk::ScopedAStatus::ok();
413 }
414 
415 }  // namespace vibrator
416 }  // namespace hardware
417 }  // namespace android
418 }  // namespace aidl
419