blob: 5affc3055657698d6a02575f25829ec95a36598a [file] [log] [blame]
Fenglin Wua7baae42020-06-30 15:59:56 +08001/*
Jishnu Prakashfc2ed842021-01-05 13:41:04 +05302 * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
Fenglin Wua7baae42020-06-30 15:59:56 +08003 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#define LOG_TAG "vendor.qti.vibrator"
31
Pige9405a92020-09-20 12:45:49 +080032#include <cutils/properties.h>
Fenglin Wua7baae42020-06-30 15:59:56 +080033#include <dirent.h>
34#include <inttypes.h>
35#include <linux/input.h>
36#include <log/log.h>
37#include <string.h>
38#include <sys/ioctl.h>
39#include <thread>
40
41#include "include/Vibrator.h"
42#ifdef USE_EFFECT_STREAM
43#include "effect.h"
44#endif
45
Fenglin Wua7baae42020-06-30 15:59:56 +080046namespace aidl {
47namespace android {
48namespace hardware {
49namespace vibrator {
50
51#define STRONG_MAGNITUDE 0x7fff
52#define MEDIUM_MAGNITUDE 0x5fff
53#define LIGHT_MAGNITUDE 0x3fff
54#define INVALID_VALUE -1
Fenglin Wuc8f31f12021-04-16 08:54:40 +080055#define CUSTOM_DATA_LEN 3
56#define NAME_BUF_SIZE 32
Fenglin Wua7baae42020-06-30 15:59:56 +080057
58#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8)))
59
Kiran Gundab4935072020-07-19 19:30:14 +053060static const char LED_DEVICE[] = "/sys/class/leds/vibrator";
61
Fenglin Wua7baae42020-06-30 15:59:56 +080062InputFFDevice::InputFFDevice()
63{
64 DIR *dp;
Pige9405a92020-09-20 12:45:49 +080065 FILE *fp = NULL;
Fenglin Wua7baae42020-06-30 15:59:56 +080066 struct dirent *dir;
67 uint8_t ffBitmask[FF_CNT / 8];
68 char devicename[PATH_MAX];
69 const char *INPUT_DIR = "/dev/input/";
Fenglin Wuc8f31f12021-04-16 08:54:40 +080070 char name[NAME_BUF_SIZE];
Fenglin Wua7baae42020-06-30 15:59:56 +080071 int fd, ret;
Pige9405a92020-09-20 12:45:49 +080072 int soc = property_get_int32("ro.vendor.qti.soc_id", -1);
Fenglin Wua7baae42020-06-30 15:59:56 +080073
74 mVibraFd = INVALID_VALUE;
75 mSupportGain = false;
76 mSupportEffects = false;
77 mSupportExternalControl = false;
78 mCurrAppId = INVALID_VALUE;
79 mCurrMagnitude = 0x7fff;
80 mInExternalControl = false;
81
82 dp = opendir(INPUT_DIR);
83 if (!dp) {
84 ALOGE("open %s failed, errno = %d", INPUT_DIR, errno);
85 return;
86 }
87
88 memset(ffBitmask, 0, sizeof(ffBitmask));
89 while ((dir = readdir(dp)) != NULL){
90 if (dir->d_name[0] == '.' &&
91 (dir->d_name[1] == '\0' ||
92 (dir->d_name[1] == '.' && dir->d_name[2] == '\0')))
93 continue;
94
95 snprintf(devicename, PATH_MAX, "%s%s", INPUT_DIR, dir->d_name);
96 fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR));
97 if (fd < 0) {
98 ALOGE("open %s failed, errno = %d", devicename, errno);
99 continue;
100 }
101
Fenglin Wuc8f31f12021-04-16 08:54:40 +0800102 ret = TEMP_FAILURE_RETRY(ioctl(fd, EVIOCGNAME(sizeof(name)), name));
103 if (ret == -1) {
104 ALOGE("get input device name %s failed, errno = %d\n", devicename, errno);
105 close(fd);
106 continue;
107 }
108
109 if (strcmp(name, "qcom-hv-haptics") && strcmp(name, "qti-haptics")) {
110 ALOGD("not a qcom/qti haptics device\n");
111 close(fd);
112 continue;
113 }
114
115 ALOGI("%s is detected at %s\n", name, devicename);
Fenglin Wua7baae42020-06-30 15:59:56 +0800116 ret = TEMP_FAILURE_RETRY(ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffBitmask)), ffBitmask));
117 if (ret == -1) {
118 ALOGE("ioctl failed, errno = %d", errno);
119 close(fd);
120 continue;
121 }
122
123 if (test_bit(FF_CONSTANT, ffBitmask) ||
124 test_bit(FF_PERIODIC, ffBitmask)) {
125 mVibraFd = fd;
126 if (test_bit(FF_CUSTOM, ffBitmask))
127 mSupportEffects = true;
128 if (test_bit(FF_GAIN, ffBitmask))
129 mSupportGain = true;
130
Pige9405a92020-09-20 12:45:49 +0800131 if (soc <= 0 && (fp = fopen("/sys/devices/soc0/soc_id", "r")) != NULL) {
132 fscanf(fp, "%u", &soc);
133 fclose(fp);
134 }
135 if (soc == 400 || soc == 415) {
Fenglin Wua7baae42020-06-30 15:59:56 +0800136 mSupportExternalControl = true;
Pige9405a92020-09-20 12:45:49 +0800137 } else {
Fenglin Wua7baae42020-06-30 15:59:56 +0800138 mSupportExternalControl = false;
Fenglin Wua7baae42020-06-30 15:59:56 +0800139 }
140 break;
141 }
142
143 close(fd);
144 }
145
146 closedir(dp);
147}
148
149/** Play vibration
150 *
151 * @param effectId: ID of the predefined effect will be played. If effectId is valid
152 * (non-negative value), the timeoutMs value will be ignored, and the
153 * real playing length will be set in param@playLengtMs and returned
154 * to VibratorService. If effectId is invalid, value in param@timeoutMs
155 * will be used as the play length for playing a constant effect.
156 * @param timeoutMs: playing length, non-zero means playing, zero means stop playing.
157 * @param playLengthMs: the playing length in ms unit which will be returned to
158 * VibratorService if the request is playing a predefined effect.
159 * The custom_data in periodic is reused for returning the playLengthMs
160 * from kernel space to userspace if the pattern is defined in kernel
161 * driver. It's been defined with following format:
162 * <effect-ID, play-time-in-seconds, play-time-in-milliseconds>.
163 * The effect-ID is used for passing down the predefined effect to
164 * kernel driver, and the rest two parameters are used for returning
165 * back the real playing length from kernel driver.
166 */
167int InputFFDevice::play(int effectId, uint32_t timeoutMs, long *playLengthMs) {
168 struct ff_effect effect;
169 struct input_event play;
170 int16_t data[CUSTOM_DATA_LEN] = {0, 0, 0};
171 int ret;
172#ifdef USE_EFFECT_STREAM
173 const struct effect_stream *stream;
174#endif
175
176 /* For QMAA compliance, return OK even if vibrator device doesn't exist */
177 if (mVibraFd == INVALID_VALUE) {
178 if (playLengthMs != NULL)
179 *playLengthMs = 0;
180 return 0;
181 }
182
183 if (timeoutMs != 0) {
184 if (mCurrAppId != INVALID_VALUE) {
185 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
186 if (ret == -1) {
187 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
188 goto errout;
189 }
190 mCurrAppId = INVALID_VALUE;
191 }
192
193 memset(&effect, 0, sizeof(effect));
194 if (effectId != INVALID_VALUE) {
195 data[0] = effectId;
196 effect.type = FF_PERIODIC;
197 effect.u.periodic.waveform = FF_CUSTOM;
198 effect.u.periodic.magnitude = mCurrMagnitude;
199 effect.u.periodic.custom_data = data;
200 effect.u.periodic.custom_len = sizeof(int16_t) * CUSTOM_DATA_LEN;
201#ifdef USE_EFFECT_STREAM
202 stream = get_effect_stream(effectId);
203 if (stream != NULL) {
204 effect.u.periodic.custom_data = (int16_t *)stream;
205 effect.u.periodic.custom_len = sizeof(*stream);
206 }
207#endif
208 } else {
209 effect.type = FF_CONSTANT;
210 effect.u.constant.level = mCurrMagnitude;
211 effect.replay.length = timeoutMs;
212 }
213
214 effect.id = mCurrAppId;
215 effect.replay.delay = 0;
216
217 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCSFF, &effect));
218 if (ret == -1) {
219 ALOGE("ioctl EVIOCSFF failed, errno = %d", -errno);
220 goto errout;
221 }
222
223 mCurrAppId = effect.id;
224 if (effectId != INVALID_VALUE && playLengthMs != NULL) {
225 *playLengthMs = data[1] * 1000 + data[2];
226#ifdef USE_EFFECT_STREAM
227 if (stream != NULL && stream->play_rate_hz != 0)
228 *playLengthMs = ((stream->length * 1000) / stream->play_rate_hz) + 1;
229#endif
230 }
231
232 play.value = 1;
233 play.type = EV_FF;
234 play.code = mCurrAppId;
235 play.time.tv_sec = 0;
236 play.time.tv_usec = 0;
237 ret = TEMP_FAILURE_RETRY(write(mVibraFd, (const void*)&play, sizeof(play)));
238 if (ret == -1) {
239 ALOGE("write failed, errno = %d\n", -errno);
240 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
241 if (ret == -1)
242 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
243 goto errout;
244 }
245 } else if (mCurrAppId != INVALID_VALUE) {
246 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
247 if (ret == -1) {
248 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
249 goto errout;
250 }
251 mCurrAppId = INVALID_VALUE;
252 }
253 return 0;
254
255errout:
256 mCurrAppId = INVALID_VALUE;
257 return ret;
258}
259
260int InputFFDevice::on(int32_t timeoutMs) {
261 return play(INVALID_VALUE, timeoutMs, NULL);
262}
263
264int InputFFDevice::off() {
265 return play(INVALID_VALUE, 0, NULL);
266}
267
268int InputFFDevice::setAmplitude(uint8_t amplitude) {
269 int tmp, ret;
270 struct input_event ie;
271
272 /* For QMAA compliance, return OK even if vibrator device doesn't exist */
273 if (mVibraFd == INVALID_VALUE)
274 return 0;
275
276 tmp = amplitude * (STRONG_MAGNITUDE - LIGHT_MAGNITUDE) / 255;
277 tmp += LIGHT_MAGNITUDE;
278 ie.type = EV_FF;
279 ie.code = FF_GAIN;
280 ie.value = tmp;
281
282 ret = TEMP_FAILURE_RETRY(write(mVibraFd, &ie, sizeof(ie)));
283 if (ret == -1) {
284 ALOGE("write FF_GAIN failed, errno = %d", -errno);
285 return ret;
286 }
287
288 mCurrMagnitude = tmp;
289 return 0;
290}
291
292int InputFFDevice::playEffect(int effectId, EffectStrength es, long *playLengthMs) {
293 switch (es) {
294 case EffectStrength::LIGHT:
295 mCurrMagnitude = LIGHT_MAGNITUDE;
296 break;
297 case EffectStrength::MEDIUM:
298 mCurrMagnitude = MEDIUM_MAGNITUDE;
299 break;
300 case EffectStrength::STRONG:
301 mCurrMagnitude = STRONG_MAGNITUDE;
302 break;
303 default:
304 return -1;
305 }
306
307 return play(effectId, INVALID_VALUE, playLengthMs);
308}
309
Kiran Gundab4935072020-07-19 19:30:14 +0530310LedVibratorDevice::LedVibratorDevice() {
311 char devicename[PATH_MAX];
312 int fd;
313
314 mDetected = false;
315
316 snprintf(devicename, sizeof(devicename), "%s/%s", LED_DEVICE, "activate");
317 fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR));
318 if (fd < 0) {
319 ALOGE("open %s failed, errno = %d", devicename, errno);
320 return;
321 }
322
323 mDetected = true;
324}
325
326int LedVibratorDevice::write_value(const char *file, const char *value) {
327 int fd;
328 int ret;
329
330 fd = TEMP_FAILURE_RETRY(open(file, O_WRONLY));
331 if (fd < 0) {
332 ALOGE("open %s failed, errno = %d", file, errno);
333 return -errno;
334 }
335
336 ret = TEMP_FAILURE_RETRY(write(fd, value, strlen(value) + 1));
337 if (ret == -1) {
338 ret = -errno;
339 } else if (ret != strlen(value) + 1) {
340 /* even though EAGAIN is an errno value that could be set
341 by write() in some cases, none of them apply here. So, this return
342 value can be clearly identified when debugging and suggests the
343 caller that it may try to call vibrator_on() again */
344 ret = -EAGAIN;
345 } else {
346 ret = 0;
347 }
348
349 errno = 0;
350 close(fd);
351
352 return ret;
353}
354
355int LedVibratorDevice::on(int32_t timeoutMs) {
356 char file[PATH_MAX];
357 char value[32];
358 int ret;
359
360 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "state");
361 ret = write_value(file, "1");
362 if (ret < 0)
363 goto error;
364
365 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "duration");
366 snprintf(value, sizeof(value), "%u\n", timeoutMs);
367 ret = write_value(file, value);
368 if (ret < 0)
369 goto error;
370
371 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate");
372 ret = write_value(file, "1");
373 if (ret < 0)
374 goto error;
375
376 return 0;
377
378error:
379 ALOGE("Failed to turn on vibrator ret: %d\n", ret);
380 return ret;
381}
382
383int LedVibratorDevice::off()
384{
385 char file[PATH_MAX];
386 int ret;
387
388 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate");
389 ret = write_value(file, "0");
390 return ret;
391}
392
Fenglin Wua7baae42020-06-30 15:59:56 +0800393ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
394 *_aidl_return = IVibrator::CAP_ON_CALLBACK;
Kiran Gundab4935072020-07-19 19:30:14 +0530395
396 if (ledVib.mDetected) {
397 *_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK;
398 ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
399 return ndk::ScopedAStatus::ok();
400 }
401
Fenglin Wua7baae42020-06-30 15:59:56 +0800402 if (ff.mSupportGain)
403 *_aidl_return |= IVibrator::CAP_AMPLITUDE_CONTROL;
404 if (ff.mSupportEffects)
405 *_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK;
406 if (ff.mSupportExternalControl)
407 *_aidl_return |= IVibrator::CAP_EXTERNAL_CONTROL;
408
409 ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
410 return ndk::ScopedAStatus::ok();
411}
412
413ndk::ScopedAStatus Vibrator::off() {
414 int ret;
415
416 ALOGD("QTI Vibrator off");
Kiran Gundab4935072020-07-19 19:30:14 +0530417 if (ledVib.mDetected)
418 ret = ledVib.off();
419 else
420 ret = ff.off();
Fenglin Wua7baae42020-06-30 15:59:56 +0800421 if (ret != 0)
422 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
423
424 return ndk::ScopedAStatus::ok();
425}
426
427ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
428 const std::shared_ptr<IVibratorCallback>& callback) {
429 int ret;
430
431 ALOGD("Vibrator on for timeoutMs: %d", timeoutMs);
Kiran Gundab4935072020-07-19 19:30:14 +0530432 if (ledVib.mDetected)
433 ret = ledVib.on(timeoutMs);
434 else
435 ret = ff.on(timeoutMs);
436
Fenglin Wua7baae42020-06-30 15:59:56 +0800437 if (ret != 0)
438 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
439
440 if (callback != nullptr) {
441 std::thread([=] {
442 ALOGD("Starting on on another thread");
443 usleep(timeoutMs * 1000);
444 ALOGD("Notifying on complete");
445 if (!callback->onComplete().isOk()) {
446 ALOGE("Failed to call onComplete");
447 }
448 }).detach();
449 }
450
451 return ndk::ScopedAStatus::ok();
452}
453
454ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength es, const std::shared_ptr<IVibratorCallback>& callback, int32_t* _aidl_return) {
455 long playLengthMs;
456 int ret;
457
Kiran Gundab4935072020-07-19 19:30:14 +0530458 if (ledVib.mDetected)
459 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
460
Fenglin Wua7baae42020-06-30 15:59:56 +0800461 ALOGD("Vibrator perform effect %d", effect);
462
463 if (effect < Effect::CLICK ||
464 effect > Effect::HEAVY_CLICK)
465 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
466
467 if (es != EffectStrength::LIGHT && es != EffectStrength::MEDIUM && es != EffectStrength::STRONG)
468 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
469
470 ret = ff.playEffect((static_cast<int>(effect)), es, &playLengthMs);
471 if (ret != 0)
472 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
473
474 if (callback != nullptr) {
475 std::thread([=] {
476 ALOGD("Starting perform on another thread");
477 usleep(playLengthMs * 1000);
478 ALOGD("Notifying perform complete");
479 callback->onComplete();
480 }).detach();
481 }
482
483 *_aidl_return = playLengthMs;
484 return ndk::ScopedAStatus::ok();
485}
486
487ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
Kiran Gundab4935072020-07-19 19:30:14 +0530488 if (ledVib.mDetected)
489 return ndk::ScopedAStatus::ok();
490
Fenglin Wua7baae42020-06-30 15:59:56 +0800491 *_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
492 Effect::POP, Effect::HEAVY_CLICK};
493
494 return ndk::ScopedAStatus::ok();
495}
496
497ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
498 uint8_t tmp;
499 int ret;
500
Kiran Gundab4935072020-07-19 19:30:14 +0530501 if (ledVib.mDetected)
502 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
503
Fenglin Wua7baae42020-06-30 15:59:56 +0800504 ALOGD("Vibrator set amplitude: %f", amplitude);
505
506 if (amplitude <= 0.0f || amplitude > 1.0f)
507 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
508
509 if (ff.mInExternalControl)
510 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
511
512 tmp = (uint8_t)(amplitude * 0xff);
513 ret = ff.setAmplitude(tmp);
514 if (ret != 0)
515 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
516
517 return ndk::ScopedAStatus::ok();
518}
519
520ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
Kiran Gundab4935072020-07-19 19:30:14 +0530521 if (ledVib.mDetected)
522 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
523
Fenglin Wua7baae42020-06-30 15:59:56 +0800524 ALOGD("Vibrator set external control: %d", enabled);
525 if (!ff.mSupportExternalControl)
526 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
527
528 ff.mInExternalControl = enabled;
529 return ndk::ScopedAStatus::ok();
530}
531
532ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs __unused) {
533 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
534}
535
536ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize __unused) {
537 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
538}
539
540ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported __unused) {
541 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
542}
543
544ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive __unused,
545 int32_t* durationMs __unused) {
546 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
547}
548
549ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite __unused,
550 const std::shared_ptr<IVibratorCallback>& callback __unused) {
551 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
552}
553
554ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return __unused) {
555 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
556}
557
558ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id __unused, Effect effect __unused,
559 EffectStrength strength __unused) {
560 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
561}
562
563ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id __unused) {
564 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
565}
566
567} // namespace vibrator
568} // namespace hardware
569} // namespace android
570} // namespace aidl
571