blob: 1420bf560404ff9f1e6320540e27db20ec548fa5 [file] [log] [blame]
Lais Andrade9e9fcc92020-04-07 20:13:08 +01001/*
2 * Copyright (C) 2020 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#define LOG_TAG "VibratorHalWrapper"
18
19#include <android/hardware/vibrator/1.3/IVibrator.h>
20#include <android/hardware/vibrator/BnVibratorCallback.h>
21#include <android/hardware/vibrator/IVibrator.h>
22#include <hardware/vibrator.h>
23
24#include <utils/Log.h>
25
Lais Andrade10d9dc72020-05-20 12:00:49 +000026#include <vibratorservice/VibratorCallbackScheduler.h>
Lais Andrade9e9fcc92020-04-07 20:13:08 +010027#include <vibratorservice/VibratorHalWrapper.h>
28
29using android::hardware::vibrator::CompositeEffect;
30using android::hardware::vibrator::Effect;
31using android::hardware::vibrator::EffectStrength;
32
33using std::chrono::milliseconds;
34
35namespace V1_0 = android::hardware::vibrator::V1_0;
36namespace V1_1 = android::hardware::vibrator::V1_1;
37namespace V1_2 = android::hardware::vibrator::V1_2;
38namespace V1_3 = android::hardware::vibrator::V1_3;
39namespace Aidl = android::hardware::vibrator;
40
41namespace android {
42
43namespace vibrator {
44
45// -------------------------------------------------------------------------------------------------
46
47template <class T>
Lais Andraded39ff7d2020-05-19 10:42:51 +000048HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) {
49 if (cache.has_value()) {
Lais Andrade10d9dc72020-05-20 12:00:49 +000050 // Return copy of cached value.
51 return HalResult<T>::ok(*cache);
Lais Andraded39ff7d2020-05-19 10:42:51 +000052 }
53 HalResult<T> ret = loadFn();
54 if (ret.isOk()) {
Lais Andrade10d9dc72020-05-20 12:00:49 +000055 // Cache copy of returned value.
Lais Andraded39ff7d2020-05-19 10:42:51 +000056 cache.emplace(ret.value());
57 }
58 return ret;
59}
60
61template <class T>
Lais Andrade9e9fcc92020-04-07 20:13:08 +010062bool isStaticCastValid(Effect effect) {
63 T castEffect = static_cast<T>(effect);
64 auto iter = hardware::hidl_enum_range<T>();
65 return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end());
66}
67
Lais Andrade9e9fcc92020-04-07 20:13:08 +010068// -------------------------------------------------------------------------------------------------
69
70template <typename T>
71HalResult<T> HalResult<T>::ok(T value) {
72 return HalResult(value);
73}
74
75template <typename T>
76HalResult<T> HalResult<T>::failed() {
77 return HalResult(/* unsupported= */ false);
78}
79
80template <typename T>
81HalResult<T> HalResult<T>::unsupported() {
82 return HalResult(/* unsupported= */ true);
83}
84
85template <typename T>
86HalResult<T> HalResult<T>::fromStatus(binder::Status status, T data) {
87 if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
88 return HalResult<T>::unsupported();
89 }
90 if (status.isOk()) {
91 return HalResult<T>::ok(data);
92 }
93 return HalResult<T>::failed();
94}
95
96template <typename T>
97HalResult<T> HalResult<T>::fromStatus(V1_0::Status status, T data) {
98 switch (status) {
99 case V1_0::Status::OK:
100 return HalResult<T>::ok(data);
101 case V1_0::Status::UNSUPPORTED_OPERATION:
102 return HalResult<T>::unsupported();
103 default:
104 return HalResult<T>::failed();
105 }
106}
107
108template <typename T>
109template <typename R>
110HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
111 return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed();
112}
113
114template <typename T>
115template <typename R>
116HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
117 return ret.isOk() ? HalResult<T>::fromStatus(status, data) : HalResult<T>::failed();
118}
119
120// -------------------------------------------------------------------------------------------------
121
122HalResult<void> HalResult<void>::ok() {
123 return HalResult();
124}
125
126HalResult<void> HalResult<void>::failed() {
127 return HalResult(/* failed= */ true);
128}
129
130HalResult<void> HalResult<void>::unsupported() {
131 return HalResult(/* failed= */ false, /* unsupported= */ true);
132}
133
134HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
135 if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
136 return HalResult<void>::unsupported();
137 }
138 if (status.isOk()) {
139 return HalResult<void>::ok();
140 }
141 return HalResult<void>::failed();
142}
143
144HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) {
145 switch (status) {
146 case V1_0::Status::OK:
147 return HalResult<void>::ok();
148 case V1_0::Status::UNSUPPORTED_OPERATION:
149 return HalResult<void>::unsupported();
150 default:
151 return HalResult<void>::failed();
152 }
153}
154
155template <typename R>
156HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
157 return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed();
158}
159
160// -------------------------------------------------------------------------------------------------
161
162class HalCallbackWrapper : public Aidl::BnVibratorCallback {
163public:
Lais Andrade10d9dc72020-05-20 12:00:49 +0000164 HalCallbackWrapper(std::function<void()> completionCallback)
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100165 : mCompletionCallback(completionCallback) {}
166
167 binder::Status onComplete() override {
168 mCompletionCallback();
169 return binder::Status::ok();
170 }
171
172private:
173 const std::function<void()> mCompletionCallback;
174};
175
176// -------------------------------------------------------------------------------------------------
177
178HalResult<void> AidlHalWrapper::ping() {
179 return IInterface::asBinder(mHandle)->pingBinder() ? HalResult<void>::ok()
180 : HalResult<void>::failed();
181}
182
183HalResult<void> AidlHalWrapper::on(milliseconds timeout,
184 const std::function<void()>& completionCallback) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000185 HalResult<Capabilities> capabilities = getCapabilities();
186 bool supportsCallback = capabilities.isOk() &&
187 static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK);
188 auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
189
190 auto ret = HalResult<void>::fromStatus(mHandle->on(timeout.count(), cb));
191 if (!supportsCallback && ret.isOk()) {
192 mCallbackScheduler->schedule(completionCallback, timeout);
193 }
194
195 return ret;
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100196}
197
198HalResult<void> AidlHalWrapper::off() {
199 return HalResult<void>::fromStatus(mHandle->off());
200}
201
202HalResult<void> AidlHalWrapper::setAmplitude(int32_t amplitude) {
203 float convertedAmplitude = static_cast<float>(amplitude) / std::numeric_limits<uint8_t>::max();
204 return HalResult<void>::fromStatus(mHandle->setAmplitude(convertedAmplitude));
205}
206
207HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) {
208 return HalResult<void>::fromStatus(mHandle->setExternalControl(enabled));
209}
210
211HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
212 return HalResult<void>::fromStatus(mHandle->alwaysOnEnable(id, effect, strength));
213}
214
215HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) {
216 return HalResult<void>::fromStatus(mHandle->alwaysOnDisable(id));
217}
218
219HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
Lais Andraded39ff7d2020-05-19 10:42:51 +0000220 std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
Lais Andrade10d9dc72020-05-20 12:00:49 +0000221 return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this),
222 mCapabilities);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100223}
224
225HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() {
Lais Andraded39ff7d2020-05-19 10:42:51 +0000226 std::lock_guard<std::mutex> lock(mSupportedEffectsMutex);
Lais Andrade10d9dc72020-05-20 12:00:49 +0000227 return loadCached<std::vector<Effect>>(std::bind(&AidlHalWrapper::getSupportedEffectsInternal,
228 this),
229 mSupportedEffects);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100230}
231
232HalResult<milliseconds> AidlHalWrapper::performEffect(
233 Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000234 HalResult<Capabilities> capabilities = getCapabilities();
235 bool supportsCallback = capabilities.isOk() &&
236 static_cast<int32_t>(capabilities.value() & Capabilities::PERFORM_CALLBACK);
237 auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
238
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100239 int32_t lengthMs;
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100240 auto result = mHandle->perform(effect, strength, cb, &lengthMs);
Lais Andrade10d9dc72020-05-20 12:00:49 +0000241 milliseconds length = milliseconds(lengthMs);
242
243 auto ret = HalResult<milliseconds>::fromStatus(result, length);
244 if (!supportsCallback && ret.isOk()) {
245 mCallbackScheduler->schedule(completionCallback, length);
246 }
247
248 return ret;
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100249}
250
251HalResult<void> AidlHalWrapper::performComposedEffect(
252 const std::vector<CompositeEffect>& primitiveEffects,
253 const std::function<void()>& completionCallback) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000254 // This method should always support callbacks, so no need to double check.
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100255 auto cb = new HalCallbackWrapper(completionCallback);
256 return HalResult<void>::fromStatus(mHandle->compose(primitiveEffects, cb));
257}
258
Lais Andrade10d9dc72020-05-20 12:00:49 +0000259HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
260 int32_t capabilities = 0;
261 auto result = mHandle->getCapabilities(&capabilities);
262 return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
263}
264
265HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() {
266 std::vector<Effect> supportedEffects;
267 auto result = mHandle->getSupportedEffects(&supportedEffects);
268 return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
269}
270
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100271// -------------------------------------------------------------------------------------------------
272
273HalResult<void> HidlHalWrapperV1_0::ping() {
274 auto result = mHandleV1_0->ping();
275 return HalResult<void>::fromReturn(result);
276}
277
Lais Andrade10d9dc72020-05-20 12:00:49 +0000278HalResult<void> HidlHalWrapperV1_0::on(milliseconds timeout,
279 const std::function<void()>& completionCallback) {
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100280 auto result = mHandleV1_0->on(timeout.count());
Lais Andrade10d9dc72020-05-20 12:00:49 +0000281 auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
282 if (ret.isOk()) {
283 mCallbackScheduler->schedule(completionCallback, timeout);
284 }
285 return ret;
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100286}
287
288HalResult<void> HidlHalWrapperV1_0::off() {
289 auto result = mHandleV1_0->off();
290 return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
291}
292
293HalResult<void> HidlHalWrapperV1_0::setAmplitude(int32_t amplitude) {
294 auto result = mHandleV1_0->setAmplitude(static_cast<uint8_t>(amplitude));
295 return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
296}
297
298HalResult<void> HidlHalWrapperV1_0::setExternalControl(bool) {
299 ALOGV("Skipped setExternalControl because Vibrator HAL does not support it");
300 return HalResult<void>::unsupported();
301}
302
303HalResult<void> HidlHalWrapperV1_0::alwaysOnEnable(int32_t, Effect, EffectStrength) {
304 ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available");
305 return HalResult<void>::unsupported();
306}
307
308HalResult<void> HidlHalWrapperV1_0::alwaysOnDisable(int32_t) {
309 ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available");
310 return HalResult<void>::unsupported();
311}
312
313HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilities() {
Lais Andraded39ff7d2020-05-19 10:42:51 +0000314 std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
315 return loadCached<Capabilities>(std::bind(&HidlHalWrapperV1_0::getCapabilitiesInternal, this),
316 mCapabilities);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100317}
318
319HalResult<std::vector<Effect>> HidlHalWrapperV1_0::getSupportedEffects() {
320 ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available");
321 return HalResult<std::vector<Effect>>::unsupported();
322}
323
Lais Andrade10d9dc72020-05-20 12:00:49 +0000324HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
325 Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100326 if (isStaticCastValid<V1_0::Effect>(effect)) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000327 return performInternalV1_0(effect, strength, completionCallback);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100328 }
329
330 ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
331 Aidl::toString(effect).c_str());
332 return HalResult<milliseconds>::unsupported();
333}
334
335HalResult<void> HidlHalWrapperV1_0::performComposedEffect(const std::vector<CompositeEffect>&,
336 const std::function<void()>&) {
337 ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
338 return HalResult<void>::unsupported();
339}
340
Lais Andraded39ff7d2020-05-19 10:42:51 +0000341HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilitiesInternal() {
342 hardware::Return<bool> result = mHandleV1_0->supportsAmplitudeControl();
343 Capabilities capabilities =
344 result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
345 return HalResult<Capabilities>::fromReturn(result, capabilities);
346}
347
Lais Andrade10d9dc72020-05-20 12:00:49 +0000348template <class I, class T>
349HalResult<milliseconds> HidlHalWrapperV1_0::performInternal(
350 perform_fn<I, T> performFn, sp<I> handle, T effect, EffectStrength strength,
351 const std::function<void()>& completionCallback) {
352 V1_0::Status status;
353 int32_t lengthMs;
354 auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
355 status = retStatus;
356 lengthMs = retLengthMs;
357 };
358
359 V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
360 auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
361 milliseconds length = milliseconds(lengthMs);
362
363 auto ret = HalResult<milliseconds>::fromReturn(result, status, length);
364 if (ret.isOk()) {
365 mCallbackScheduler->schedule(completionCallback, length);
366 }
367
368 return ret;
369}
370
371HalResult<milliseconds> HidlHalWrapperV1_0::performInternalV1_0(
372 Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
373 V1_0::Effect e = static_cast<V1_0::Effect>(effect);
374 return performInternal(&V1_0::IVibrator::perform, mHandleV1_0, e, strength, completionCallback);
375}
376
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100377// -------------------------------------------------------------------------------------------------
378
Lais Andrade10d9dc72020-05-20 12:00:49 +0000379HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
380 Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100381 if (isStaticCastValid<V1_0::Effect>(effect)) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000382 return performInternalV1_0(effect, strength, completionCallback);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100383 }
384 if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000385 return performInternalV1_1(effect, strength, completionCallback);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100386 }
387
388 ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
389 Aidl::toString(effect).c_str());
390 return HalResult<milliseconds>::unsupported();
391}
392
Lais Andrade10d9dc72020-05-20 12:00:49 +0000393HalResult<milliseconds> HidlHalWrapperV1_1::performInternalV1_1(
394 Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
395 V1_1::Effect_1_1 e = static_cast<V1_1::Effect_1_1>(effect);
396 return performInternal(&V1_1::IVibrator::perform_1_1, mHandleV1_1, e, strength,
397 completionCallback);
398}
399
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100400// -------------------------------------------------------------------------------------------------
401
Lais Andrade10d9dc72020-05-20 12:00:49 +0000402HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(
403 Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100404 if (isStaticCastValid<V1_0::Effect>(effect)) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000405 return performInternalV1_0(effect, strength, completionCallback);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100406 }
407 if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000408 return performInternalV1_1(effect, strength, completionCallback);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100409 }
410 if (isStaticCastValid<V1_2::Effect>(effect)) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000411 return performInternalV1_2(effect, strength, completionCallback);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100412 }
413
414 ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
415 Aidl::toString(effect).c_str());
416 return HalResult<milliseconds>::unsupported();
417}
418
Lais Andrade10d9dc72020-05-20 12:00:49 +0000419HalResult<milliseconds> HidlHalWrapperV1_2::performInternalV1_2(
420 Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
421 V1_2::Effect e = static_cast<V1_2::Effect>(effect);
422 return performInternal(&V1_2::IVibrator::perform_1_2, mHandleV1_2, e, strength,
423 completionCallback);
424}
425
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100426// -------------------------------------------------------------------------------------------------
427
428HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
429 auto result = mHandleV1_3->setExternalControl(static_cast<uint32_t>(enabled));
430 return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
431}
432
Lais Andrade10d9dc72020-05-20 12:00:49 +0000433HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
434 Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100435 if (isStaticCastValid<V1_0::Effect>(effect)) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000436 return performInternalV1_0(effect, strength, completionCallback);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100437 }
438 if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000439 return performInternalV1_1(effect, strength, completionCallback);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100440 }
441 if (isStaticCastValid<V1_2::Effect>(effect)) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000442 return performInternalV1_2(effect, strength, completionCallback);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100443 }
444 if (isStaticCastValid<V1_3::Effect>(effect)) {
Lais Andrade10d9dc72020-05-20 12:00:49 +0000445 return performInternalV1_3(effect, strength, completionCallback);
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100446 }
447
448 ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
449 Aidl::toString(effect).c_str());
450 return HalResult<milliseconds>::unsupported();
451}
452
Lais Andraded39ff7d2020-05-19 10:42:51 +0000453HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
454 HalResult<Capabilities> parentResult = HidlHalWrapperV1_2::getCapabilitiesInternal();
455 if (!parentResult.isOk()) {
456 // Loading for previous HAL versions already failed, so propagate failure.
457 return parentResult;
458 }
459
460 Capabilities capabilities = parentResult.value();
461 auto result = mHandleV1_3->supportsExternalControl();
462 capabilities |= result.withDefault(false) ? Capabilities::EXTERNAL_CONTROL : Capabilities::NONE;
463 return HalResult<Capabilities>::fromReturn(result, capabilities);
464}
465
Lais Andrade10d9dc72020-05-20 12:00:49 +0000466HalResult<milliseconds> HidlHalWrapperV1_3::performInternalV1_3(
467 Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
468 V1_3::Effect e = static_cast<V1_3::Effect>(effect);
469 return performInternal(&V1_3::IVibrator::perform_1_3, mHandleV1_3, e, strength,
470 completionCallback);
471}
472
Lais Andrade9e9fcc92020-04-07 20:13:08 +0100473// -------------------------------------------------------------------------------------------------
474
475}; // namespace vibrator
476
477}; // namespace android