1 /*
2  * Copyright (C) 2017 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.h"
18 
19 #include <cutils/properties.h>
20 #include <hardware/hardware.h>
21 #include <hardware/vibrator.h>
22 #include <log/log.h>
23 #include <utils/Trace.h>
24 
25 #include <cinttypes>
26 #include <cmath>
27 #include <fstream>
28 #include <iostream>
29 
30 #include "utils.h"
31 
32 namespace android {
33 namespace hardware {
34 namespace vibrator {
35 namespace V1_3 {
36 namespace implementation {
37 
38 static constexpr int8_t MAX_RTP_INPUT = 127;
39 static constexpr int8_t MIN_RTP_INPUT = 0;
40 
41 static constexpr char RTP_MODE[] = "rtp";
42 static constexpr char WAVEFORM_MODE[] = "waveform";
43 
44 // Use effect #1 in the waveform library for CLICK effect
45 static constexpr char WAVEFORM_CLICK_EFFECT_SEQ[] = "1 0";
46 
47 // Use effect #2 in the waveform library for TICK effect
48 static constexpr char WAVEFORM_TICK_EFFECT_SEQ[] = "2 0";
49 
50 // Use effect #3 in the waveform library for DOUBLE_CLICK effect
51 static constexpr char WAVEFORM_DOUBLE_CLICK_EFFECT_SEQ[] = "3 0";
52 
53 // Use effect #4 in the waveform library for HEAVY_CLICK effect
54 static constexpr char WAVEFORM_HEAVY_CLICK_EFFECT_SEQ[] = "4 0";
55 
56 // UT team design those target G values
57 static constexpr std::array<float, 5> EFFECT_TARGET_G = {0.15, 0.15, 0.27, 0.43, 0.57};
58 static constexpr std::array<float, 3> STEADY_TARGET_G = {1.2, 1.145, 0.905};
59 
60 #define FLOAT_EPS 1e-6
61 
62 // Temperature protection upper bound 10°C and lower bound 5°C
63 static constexpr int32_t TEMP_UPPER_BOUND = 10000;
64 static constexpr int32_t TEMP_LOWER_BOUND = 5000;
65 // Steady vibration's voltage in lower bound guarantee
66 static uint32_t STEADY_VOLTAGE_LOWER_BOUND = 90;  // 1.8 Vpeak
67 
freqPeriodFormula(std::uint32_t in)68 static std::uint32_t freqPeriodFormula(std::uint32_t in) {
69     return 1000000000 / (24615 * in);
70 }
71 
convertLevelsToOdClamp(float voltageLevel,uint32_t lraPeriod)72 static std::uint32_t convertLevelsToOdClamp(float voltageLevel, uint32_t lraPeriod) {
73     float odClamp;
74 
75     odClamp = voltageLevel /
76               ((21.32 / 1000.0) *
77                sqrt(1.0 - (static_cast<float>(freqPeriodFormula(lraPeriod)) * 8.0 / 10000.0)));
78 
79     return round(odClamp);
80 }
81 
targetGToVlevelsUnderLinearEquation(std::array<float,4> inputCoeffs,float targetG)82 static float targetGToVlevelsUnderLinearEquation(std::array<float, 4> inputCoeffs, float targetG) {
83     // Implement linear equation to get voltage levels, f(x) = ax + b
84     // 0 to 3.2 is our valid output
85     float outPutVal = 0.0f;
86     outPutVal = (targetG - inputCoeffs[1]) / inputCoeffs[0];
87     if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) {
88         return outPutVal;
89     } else {
90         return 0.0f;
91     }
92 }
93 
targetGToVlevelsUnderCubicEquation(std::array<float,4> inputCoeffs,float targetG)94 static float targetGToVlevelsUnderCubicEquation(std::array<float, 4> inputCoeffs, float targetG) {
95     // Implement cubic equation to get voltage levels, f(x) = ax^3 + bx^2 + cx + d
96     // 0 to 3.2 is our valid output
97     float AA = 0.0f, BB = 0.0f, CC = 0.0f, Delta = 0.0f;
98     float Y1 = 0.0f, Y2 = 0.0f, K = 0.0f, T = 0.0f, sita = 0.0f;
99     float outPutVal = 0.0f;
100     float oneHalf = 1.0 / 2.0, oneThird = 1.0 / 3.0;
101     float cosSita = 0.0f, sinSitaSqrt3 = 0.0f, sqrtA = 0.0f;
102 
103     AA = inputCoeffs[1] * inputCoeffs[1] - 3.0 * inputCoeffs[0] * inputCoeffs[2];
104     BB = inputCoeffs[1] * inputCoeffs[2] - 9.0 * inputCoeffs[0] * (inputCoeffs[3] - targetG);
105     CC = inputCoeffs[2] * inputCoeffs[2] - 3.0 * inputCoeffs[1] * (inputCoeffs[3] - targetG);
106 
107     Delta = BB * BB - 4.0 * AA * CC;
108 
109     // There are four discriminants in Shengjin formula.
110     // https://zh.wikipedia.org/wiki/%E4%B8%89%E6%AC%A1%E6%96%B9%E7%A8%8B#%E7%9B%9B%E9%87%91%E5%85%AC%E5%BC%8F%E6%B3%95
111     if ((fabs(AA) <= FLOAT_EPS) && (fabs(BB) <= FLOAT_EPS)) {
112         // Case 1: A = B = 0
113         outPutVal = -inputCoeffs[1] / (3 * inputCoeffs[0]);
114         if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) {
115             return outPutVal;
116         }
117         return 0.0f;
118     } else if (Delta > FLOAT_EPS) {
119         // Case 2: Delta > 0
120         Y1 = AA * inputCoeffs[1] + 3.0 * inputCoeffs[0] * (-BB + pow(Delta, oneHalf)) / 2.0;
121         Y2 = AA * inputCoeffs[1] + 3.0 * inputCoeffs[0] * (-BB - pow(Delta, oneHalf)) / 2.0;
122 
123         if ((Y1 < -FLOAT_EPS) && (Y2 > FLOAT_EPS)) {
124             return (-inputCoeffs[1] + pow(-Y1, oneThird) - pow(Y2, oneThird)) /
125                    (3.0 * inputCoeffs[0]);
126         } else if ((Y1 > FLOAT_EPS) && (Y2 < -FLOAT_EPS)) {
127             return (-inputCoeffs[1] - pow(Y1, oneThird) + pow(-Y2, oneThird)) /
128                    (3.0 * inputCoeffs[0]);
129         } else if ((Y1 < -FLOAT_EPS) && (Y2 < -FLOAT_EPS)) {
130             return (-inputCoeffs[1] + pow(-Y1, oneThird) + pow(-Y2, oneThird)) /
131                    (3.0 * inputCoeffs[0]);
132         } else {
133             return (-inputCoeffs[1] - pow(Y1, oneThird) - pow(Y2, oneThird)) /
134                    (3.0 * inputCoeffs[0]);
135         }
136         return 0.0f;
137     } else if (Delta < -FLOAT_EPS) {
138         // Case 3: Delta < 0
139         T = (2 * AA * inputCoeffs[1] - 3 * inputCoeffs[0] * BB) / (2 * AA * sqrt(AA));
140         sita = acos(T);
141         cosSita = cos(sita / 3);
142         sinSitaSqrt3 = sqrt(3.0) * sin(sita / 3);
143         sqrtA = sqrt(AA);
144 
145         outPutVal = (-inputCoeffs[1] - 2 * sqrtA * cosSita) / (3 * inputCoeffs[0]);
146         if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) {
147             return outPutVal;
148         }
149         outPutVal = (-inputCoeffs[1] + sqrtA * (cosSita + sinSitaSqrt3)) / (3 * inputCoeffs[0]);
150         if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) {
151             return outPutVal;
152         }
153         outPutVal = (-inputCoeffs[1] + sqrtA * (cosSita - sinSitaSqrt3)) / (3 * inputCoeffs[0]);
154         if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) {
155             return outPutVal;
156         }
157         return 0.0f;
158     } else if (Delta <= FLOAT_EPS) {
159         // Case 4: Delta = 0
160         K = BB / AA;
161         outPutVal = (-inputCoeffs[1] / inputCoeffs[0] + K);
162         if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) {
163             return outPutVal;
164         }
165         outPutVal = (-K / 2);
166         if ((outPutVal > FLOAT_EPS) && (outPutVal <= 3.2)) {
167             return outPutVal;
168         }
169         return 0.0f;
170     } else {
171         // Exception handling
172         return 0.0f;
173     }
174 }
175 
176 using utils::toUnderlying;
177 
178 using Status = ::android::hardware::vibrator::V1_0::Status;
179 using EffectStrength = ::android::hardware::vibrator::V1_0::EffectStrength;
180 
Vibrator(std::unique_ptr<HwApi> hwapi,std::unique_ptr<HwCal> hwcal)181 Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
182     : mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)) {
183     std::string autocal;
184     uint32_t lraPeriod = 0, lpTrigSupport = 0;
185     bool hasEffectCoeffs = false;
186     std::array<float, 4> effectCoeffs = {0};
187 
188     if (!mHwApi->setState(true)) {
189         ALOGE("Failed to set state (%d): %s", errno, strerror(errno));
190     }
191 
192     if (mHwCal->getAutocal(&autocal)) {
193         mHwApi->setAutocal(autocal);
194     }
195     mHwCal->getLraPeriod(&lraPeriod);
196 
197     mHwCal->getCloseLoopThreshold(&mCloseLoopThreshold);
198     mHwCal->getDynamicConfig(&mDynamicConfig);
199 
200     if (mDynamicConfig) {
201         uint8_t i = 0;
202         float tempVolLevel = 0.0f;
203         float tempAmpMax = 0.0f;
204         uint32_t longFreqencyShift = 0;
205         uint32_t shortVoltageMax = 0, longVoltageMax = 0;
206         uint32_t shape = 0;
207 
208         mHwCal->getLongFrequencyShift(&longFreqencyShift);
209         mHwCal->getShortVoltageMax(&shortVoltageMax);
210         mHwCal->getLongVoltageMax(&longVoltageMax);
211 
212         hasEffectCoeffs = mHwCal->getEffectCoeffs(&effectCoeffs);
213         for (i = 0; i < 5; i++) {
214             if (hasEffectCoeffs) {
215                 // Use linear approach to get the target voltage levels
216                 if ((effectCoeffs[2] == 0) && (effectCoeffs[3] == 0)) {
217                     tempVolLevel =
218                         targetGToVlevelsUnderLinearEquation(effectCoeffs, EFFECT_TARGET_G[i]);
219                     mEffectTargetOdClamp[i] = convertLevelsToOdClamp(tempVolLevel, lraPeriod);
220                 } else {
221                     // Use cubic approach to get the target voltage levels
222                     tempVolLevel =
223                         targetGToVlevelsUnderCubicEquation(effectCoeffs, EFFECT_TARGET_G[i]);
224                     mEffectTargetOdClamp[i] = convertLevelsToOdClamp(tempVolLevel, lraPeriod);
225                 }
226             } else {
227                 mEffectTargetOdClamp[i] = shortVoltageMax;
228             }
229         }
230         // Add a boundary protection for level 5 only, since
231         // some devices might not be able to reach the maximum target G
232         if ((mEffectTargetOdClamp[4] <= 0) || (mEffectTargetOdClamp[4] > 161)) {
233             mEffectTargetOdClamp[4] = shortVoltageMax;
234         }
235 
236         mHwCal->getEffectShape(&shape);
237         mEffectConfig.reset(new VibrationConfig({
238             .shape = (shape == UINT32_MAX) ? WaveShape::SINE : static_cast<WaveShape>(shape),
239             .odClamp = &mEffectTargetOdClamp[0],
240             .olLraPeriod = lraPeriod,
241         }));
242 
243         mSteadyTargetOdClamp = longVoltageMax;
244         if ((mHwCal->getSteadyAmpMax(&tempAmpMax)) && (tempAmpMax > STEADY_TARGET_G[0])) {
245             tempVolLevel = round((STEADY_TARGET_G[0] / tempAmpMax) * longVoltageMax);
246             mSteadyTargetOdClamp = (tempVolLevel < STEADY_VOLTAGE_LOWER_BOUND)
247                                        ? STEADY_VOLTAGE_LOWER_BOUND
248                                        : tempVolLevel;
249         }
250         mHwCal->getSteadyShape(&shape);
251         mSteadyConfig.reset(new VibrationConfig({
252             .shape = (shape == UINT32_MAX) ? WaveShape::SQUARE : static_cast<WaveShape>(shape),
253             .odClamp = &mSteadyTargetOdClamp,
254             .olLraPeriod = lraPeriod,
255         }));
256         mSteadyOlLraPeriod = lraPeriod;
257         // 1. Change long lra period to frequency
258         // 2. Get frequency': subtract the frequency shift from the frequency
259         // 3. Get final long lra period after put the frequency' to formula
260         mSteadyOlLraPeriodShift =
261             freqPeriodFormula(freqPeriodFormula(lraPeriod) - longFreqencyShift);
262     } else {
263         mHwApi->setOlLraPeriod(lraPeriod);
264     }
265 
266     mHwCal->getClickDuration(&mClickDuration);
267     mHwCal->getTickDuration(&mTickDuration);
268     mHwCal->getDoubleClickDuration(&mDoubleClickDuration);
269     mHwCal->getHeavyClickDuration(&mHeavyClickDuration);
270 
271     // This enables effect #1 from the waveform library to be triggered by SLPI
272     // while the AP is in suspend mode
273     // For default setting, we will enable this feature if that project did not
274     // set the lptrigger config
275     mHwCal->getTriggerEffectSupport(&lpTrigSupport);
276     if (!mHwApi->setLpTriggerEffect(lpTrigSupport)) {
277         ALOGW("Failed to set LP trigger mode (%d): %s", errno, strerror(errno));
278     }
279 }
280 
on(uint32_t timeoutMs,const char mode[],const std::unique_ptr<VibrationConfig> & config,const int8_t volOffset)281 Return<Status> Vibrator::on(uint32_t timeoutMs, const char mode[],
282                             const std::unique_ptr<VibrationConfig> &config,
283                             const int8_t volOffset) {
284     LoopControl loopMode = LoopControl::OPEN;
285 
286     // Open-loop mode is used for short click for over-drive
287     // Close-loop mode is used for long notification for stability
288     if (mode == RTP_MODE && timeoutMs > mCloseLoopThreshold) {
289         loopMode = LoopControl::CLOSE;
290     }
291 
292     mHwApi->setCtrlLoop(toUnderlying(loopMode));
293     if (!mHwApi->setDuration(timeoutMs)) {
294         ALOGE("Failed to set duration (%d): %s", errno, strerror(errno));
295         return Status::UNKNOWN_ERROR;
296     }
297 
298     mHwApi->setMode(mode);
299     if (config != nullptr) {
300         mHwApi->setLraWaveShape(toUnderlying(config->shape));
301         mHwApi->setOdClamp(config->odClamp[volOffset]);
302         mHwApi->setOlLraPeriod(config->olLraPeriod);
303     }
304 
305     if (!mHwApi->setActivate(1)) {
306         ALOGE("Failed to activate (%d): %s", errno, strerror(errno));
307         return Status::UNKNOWN_ERROR;
308     }
309 
310     return Status::OK;
311 }
312 
313 // Methods from ::android::hardware::vibrator::V1_2::IVibrator follow.
on(uint32_t timeoutMs)314 Return<Status> Vibrator::on(uint32_t timeoutMs) {
315     ATRACE_NAME("Vibrator::on");
316     if (mDynamicConfig) {
317         int usbTemp = 0;
318         mHwApi->getUsbTemp(&usbTemp);
319         if (usbTemp > TEMP_UPPER_BOUND) {
320             mSteadyConfig->odClamp = &mSteadyTargetOdClamp;
321             mSteadyConfig->olLraPeriod = mSteadyOlLraPeriod;
322         } else if (usbTemp < TEMP_LOWER_BOUND) {
323             mSteadyConfig->odClamp = &STEADY_VOLTAGE_LOWER_BOUND;
324             mSteadyConfig->olLraPeriod = mSteadyOlLraPeriodShift;
325         }
326     }
327 
328     return on(timeoutMs, RTP_MODE, mSteadyConfig, 0);
329 }
330 
off()331 Return<Status> Vibrator::off() {
332     ATRACE_NAME("Vibrator::off");
333     if (!mHwApi->setActivate(0)) {
334         ALOGE("Failed to turn vibrator off (%d): %s", errno, strerror(errno));
335         return Status::UNKNOWN_ERROR;
336     }
337     return Status::OK;
338 }
339 
supportsAmplitudeControl()340 Return<bool> Vibrator::supportsAmplitudeControl() {
341     ATRACE_NAME("Vibrator::supportsAmplitudeControl");
342     return (mHwApi->hasRtpInput() ? true : false);
343 }
344 
setAmplitude(uint8_t amplitude)345 Return<Status> Vibrator::setAmplitude(uint8_t amplitude) {
346     ATRACE_NAME("Vibrator::setAmplitude");
347     if (amplitude == 0) {
348         return Status::BAD_VALUE;
349     }
350 
351     int32_t rtp_input =
352         std::round((amplitude - 1) / 254.0 * (MAX_RTP_INPUT - MIN_RTP_INPUT) + MIN_RTP_INPUT);
353 
354     if (!mHwApi->setRtpInput(rtp_input)) {
355         ALOGE("Failed to set amplitude (%d): %s", errno, strerror(errno));
356         return Status::UNKNOWN_ERROR;
357     }
358 
359     return Status::OK;
360 }
361 
362 // Methods from ::android::hardware::vibrator::V1_3::IVibrator follow.
363 
supportsExternalControl()364 Return<bool> Vibrator::supportsExternalControl() {
365     ATRACE_NAME("Vibrator::supportsExternalControl");
366     return false;
367 }
368 
setExternalControl(bool enabled)369 Return<Status> Vibrator::setExternalControl(bool enabled) {
370     ATRACE_NAME("Vibrator::setExternalControl");
371     ALOGE("Not support in DRV2624 solution, %d", enabled);
372     return Status::UNSUPPORTED_OPERATION;
373 }
374 
375 // Methods from ::android.hidl.base::V1_0::IBase follow.
376 
debug(const hidl_handle & handle,const hidl_vec<hidl_string> &)377 Return<void> Vibrator::debug(const hidl_handle &handle,
378                              const hidl_vec<hidl_string> & /* options */) {
379     if (handle == nullptr || handle->numFds < 1 || handle->data[0] < 0) {
380         ALOGE("Called debug() with invalid fd.");
381         return Void();
382     }
383 
384     int fd = handle->data[0];
385 
386     dprintf(fd, "HIDL:\n");
387 
388     dprintf(fd, "  Close Loop Thresh: %" PRIu32 "\n", mCloseLoopThreshold);
389     if (mSteadyConfig) {
390         dprintf(fd, "  Steady Shape: %" PRIu32 "\n", mSteadyConfig->shape);
391         dprintf(fd, "  Steady OD Clamp: %" PRIu32 "\n", mSteadyConfig->odClamp[0]);
392         dprintf(fd, "  Steady OL LRA Period: %" PRIu32 "\n", mSteadyConfig->olLraPeriod);
393     }
394     if (mEffectConfig) {
395         dprintf(fd, "  Effect Shape: %" PRIu32 "\n", mEffectConfig->shape);
396         dprintf(fd,
397                 "  Effect OD Clamp: %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
398                 mEffectConfig->odClamp[0], mEffectConfig->odClamp[1], mEffectConfig->odClamp[2],
399                 mEffectConfig->odClamp[3], mEffectConfig->odClamp[4]);
400         dprintf(fd, "  Effect OL LRA Period: %" PRIu32 "\n", mEffectConfig->olLraPeriod);
401     }
402     dprintf(fd, "  Click Duration: %" PRIu32 "\n", mClickDuration);
403     dprintf(fd, "  Tick Duration: %" PRIu32 "\n", mTickDuration);
404     dprintf(fd, "  Double Click Duration: %" PRIu32 "\n", mDoubleClickDuration);
405     dprintf(fd, "  Heavy Click Duration: %" PRIu32 "\n", mHeavyClickDuration);
406 
407     dprintf(fd, "\n");
408 
409     mHwApi->debug(fd);
410 
411     dprintf(fd, "\n");
412 
413     mHwCal->debug(fd);
414 
415     fsync(fd);
416     return Void();
417 }
418 
perform(V1_0::Effect effect,EffectStrength strength,perform_cb _hidl_cb)419 Return<void> Vibrator::perform(V1_0::Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
420     return performWrapper(effect, strength, _hidl_cb);
421 }
422 
perform_1_1(V1_1::Effect_1_1 effect,EffectStrength strength,perform_cb _hidl_cb)423 Return<void> Vibrator::perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength,
424                                    perform_cb _hidl_cb) {
425     return performWrapper(effect, strength, _hidl_cb);
426 }
427 
perform_1_2(V1_2::Effect effect,EffectStrength strength,perform_cb _hidl_cb)428 Return<void> Vibrator::perform_1_2(V1_2::Effect effect, EffectStrength strength,
429                                    perform_cb _hidl_cb) {
430     return performWrapper(effect, strength, _hidl_cb);
431 }
432 
perform_1_3(Effect effect,EffectStrength strength,perform_cb _hidl_cb)433 Return<void> Vibrator::perform_1_3(Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
434     return performWrapper(effect, strength, _hidl_cb);
435 }
436 
437 template <typename T>
performWrapper(T effect,EffectStrength strength,perform_cb _hidl_cb)438 Return<void> Vibrator::performWrapper(T effect, EffectStrength strength, perform_cb _hidl_cb) {
439     ATRACE_NAME("Vibrator::performWrapper");
440     auto validEffectRange = hidl_enum_range<T>();
441     if (effect < *validEffectRange.begin() || effect > *std::prev(validEffectRange.end())) {
442         _hidl_cb(Status::UNSUPPORTED_OPERATION, 0);
443         return Void();
444     }
445     auto validStrengthRange = hidl_enum_range<EffectStrength>();
446     if (strength < *validStrengthRange.begin() || strength > *std::prev(validStrengthRange.end())) {
447         _hidl_cb(Status::UNSUPPORTED_OPERATION, 0);
448         return Void();
449     }
450     return performEffect(static_cast<Effect>(effect), strength, _hidl_cb);
451 }
452 
performEffect(Effect effect,EffectStrength strength,perform_cb _hidl_cb)453 Return<void> Vibrator::performEffect(Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
454     Status status = Status::OK;
455     uint32_t timeMS;
456     int8_t volOffset;
457 
458     switch (strength) {
459         case EffectStrength::LIGHT:
460             volOffset = 0;
461             break;
462         case EffectStrength::MEDIUM:
463             volOffset = 1;
464             break;
465         case EffectStrength::STRONG:
466             volOffset = 1;
467             break;
468         default:
469             status = Status::UNSUPPORTED_OPERATION;
470             break;
471     }
472 
473     switch (effect) {
474         case Effect::TEXTURE_TICK:
475             mHwApi->setSequencer(WAVEFORM_TICK_EFFECT_SEQ);
476             timeMS = mTickDuration;
477             volOffset = TEXTURE_TICK;
478             break;
479         case Effect::CLICK:
480             mHwApi->setSequencer(WAVEFORM_CLICK_EFFECT_SEQ);
481             timeMS = mClickDuration;
482             volOffset += CLICK;
483             break;
484         case Effect::DOUBLE_CLICK:
485             mHwApi->setSequencer(WAVEFORM_DOUBLE_CLICK_EFFECT_SEQ);
486             timeMS = mDoubleClickDuration;
487             volOffset += CLICK;
488             break;
489         case Effect::TICK:
490             mHwApi->setSequencer(WAVEFORM_TICK_EFFECT_SEQ);
491             timeMS = mTickDuration;
492             volOffset += TICK;
493             break;
494         case Effect::HEAVY_CLICK:
495             mHwApi->setSequencer(WAVEFORM_HEAVY_CLICK_EFFECT_SEQ);
496             timeMS = mHeavyClickDuration;
497             volOffset += HEAVY_CLICK;
498             break;
499         default:
500             _hidl_cb(Status::UNSUPPORTED_OPERATION, 0);
501             return Void();
502     }
503     on(timeMS, WAVEFORM_MODE, mEffectConfig, volOffset);
504     _hidl_cb(status, timeMS);
505     return Void();
506 }
507 
508 }  // namespace implementation
509 }  // namespace V1_3
510 }  // namespace vibrator
511 }  // namespace hardware
512 }  // namespace android
513