Loading services/core/java/com/android/server/vibrator/VibrationThread.java +395 −216 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/vibrator/VibratorController.java +16 −8 Original line number Diff line number Diff line Loading @@ -189,12 +189,18 @@ final class VibratorController { * callback to {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. * * @return The positive duration of the vibration started, if successful, zero if the vibrator * do not support the input or a negative number if the operation failed. */ public void on(long milliseconds, long vibrationId) { public long on(long milliseconds, long vibrationId) { synchronized (mLock) { mNativeWrapper.on(milliseconds, vibrationId); long duration = mNativeWrapper.on(milliseconds, vibrationId); if (duration > 0) { notifyVibratorOnLocked(); } return duration; } } /** Loading @@ -203,7 +209,8 @@ final class VibratorController { * * <p>This will affect the state of {@link #isVibrating()}. * * @return The duration of the effect playing, or 0 if unsupported. * @return The positive duration of the vibration started, if successful, zero if the vibrator * do not support the input or a negative number if the operation failed. */ public long on(PrebakedSegment prebaked, long vibrationId) { synchronized (mLock) { Loading @@ -222,7 +229,8 @@ final class VibratorController { * * <p>This will affect the state of {@link #isVibrating()}. * * @return The duration of the effect playing, or 0 if unsupported. * @return The positive duration of the vibration started, if successful, zero if the vibrator * do not support the input or a negative number if the operation failed. */ public long on(PrimitiveSegment[] primitives, long vibrationId) { if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { Loading Loading @@ -327,7 +335,7 @@ final class VibratorController { */ private static native long getNativeFinalizer(); private static native boolean isAvailable(long nativePtr); private static native void on(long nativePtr, long milliseconds, long vibrationId); private static native long on(long nativePtr, long milliseconds, long vibrationId); private static native void off(long nativePtr); private static native void setAmplitude(long nativePtr, float amplitude); private static native int[] getSupportedEffects(long nativePtr); Loading Loading @@ -365,8 +373,8 @@ final class VibratorController { } /** Turns vibrator on for given time. */ public void on(long milliseconds, long vibrationId) { on(mNativePtr, milliseconds, vibrationId); public long on(long milliseconds, long vibrationId) { return on(mNativePtr, milliseconds, vibrationId); } /** Turns vibrator off. */ Loading services/core/java/com/android/server/vibrator/VibratorManagerService.java +100 −68 Original line number Diff line number Diff line Loading @@ -1394,15 +1394,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { while ((nextArg = peekNextArg()) != null) { switch (nextArg) { case "-f": getNextArgRequired(); // consume the -f argument; getNextArgRequired(); // consume "-f" force = true; break; case "-d": getNextArgRequired(); // consume the -d argument; getNextArgRequired(); // consume "-d" description = getNextArgRequired(); break; default: // Not a common option, finish reading. // nextArg is not a common option, finish reading. return; } } Loading Loading @@ -1456,14 +1456,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private int runMono() { CommonOptions commonOptions = new CommonOptions(); VibrationEffect effect = nextEffect(); if (effect == null) { return 0; } CombinedVibrationEffect combinedEffect = CombinedVibrationEffect.createSynced(effect); CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(nextEffect()); VibrationAttributes attrs = createVibrationAttributes(commonOptions); vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combinedEffect, attrs, vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, effect, attrs, commonOptions.description, mToken); return 0; } Loading @@ -1474,10 +1469,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { CombinedVibrationEffect.startSynced(); while ("-v".equals(getNextOption())) { int vibratorId = Integer.parseInt(getNextArgRequired()); VibrationEffect effect = nextEffect(); if (effect != null) { combination.addVibrator(vibratorId, effect); } combination.addVibrator(vibratorId, nextEffect()); } VibrationAttributes attrs = createVibrationAttributes(commonOptions); vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs, Loading @@ -1487,19 +1479,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private int runSequential() { CommonOptions commonOptions = new CommonOptions(); CombinedVibrationEffect.SequentialCombination combination = CombinedVibrationEffect.startSequential(); while ("-v".equals(getNextOption())) { int vibratorId = Integer.parseInt(getNextArgRequired()); int delay = 0; if ("-w".equals(getNextOption())) { delay = Integer.parseInt(getNextArgRequired()); } VibrationEffect effect = nextEffect(); if (effect != null) { combination.addNext(vibratorId, effect, delay); } combination.addNext(vibratorId, nextEffect()); } VibrationAttributes attrs = createVibrationAttributes(commonOptions); vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs, Loading @@ -1512,87 +1496,129 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { return 0; } @Nullable private VibrationEffect nextEffect() { String effectType = getNextArgRequired(); if ("oneshot".equals(effectType)) { return nextOneShot(); VibrationEffect.Composition composition = VibrationEffect.startComposition(); String nextArg; while ((nextArg = peekNextArg()) != null) { if ("oneshot".equals(nextArg)) { addOneShotToComposition(composition); } else if ("waveform".equals(nextArg)) { addWaveformToComposition(composition); } else if ("prebaked".equals(nextArg)) { addPrebakedToComposition(composition); } else if ("primitives".equals(nextArg)) { addPrimitivesToComposition(composition); } else { // nextArg is not an effect, finish reading. break; } if ("waveform".equals(effectType)) { return nextWaveform(); } if ("prebaked".equals(effectType)) { return nextPrebaked(); return composition.compose(); } if ("composed".equals(effectType)) { return nextComposed(); private void addOneShotToComposition(VibrationEffect.Composition composition) { boolean hasAmplitude = false; int delay = 0; getNextArgRequired(); // consume "oneshot" String nextOption; while ((nextOption = getNextOption()) != null) { if ("-a".equals(nextOption)) { hasAmplitude = true; } else if ("-w".equals(nextOption)) { delay = Integer.parseInt(getNextArgRequired()); } return null; } private VibrationEffect nextOneShot() { boolean hasAmplitude = "-a".equals(getNextOption()); long duration = Long.parseLong(getNextArgRequired()); int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired()) : VibrationEffect.DEFAULT_AMPLITUDE; return VibrationEffect.createOneShot(duration, amplitude); composition.addEffect(VibrationEffect.createOneShot(duration, amplitude), delay); } private VibrationEffect nextWaveform() { private void addWaveformToComposition(VibrationEffect.Composition composition) { boolean hasAmplitudes = false; int repeat = -1; int delay = 0; String nextOption = getNextOption(); while (nextOption != null) { getNextArgRequired(); // consume "waveform" String nextOption; while ((nextOption = getNextOption()) != null) { if ("-a".equals(nextOption)) { hasAmplitudes = true; } else if ("-r".equals(nextOption)) { repeat = Integer.parseInt(getNextArgRequired()); } else if ("-w".equals(nextOption)) { delay = Integer.parseInt(getNextArgRequired()); } nextOption = getNextOption(); } List<Long> durations = new ArrayList<>(); List<Integer> amplitudes = new ArrayList<>(); VibrationEffect waveform; String nextArg; while ((nextArg = peekNextArg()) != null && !"-v".equals(nextArg)) { durations.add(Long.parseLong(getNextArgRequired())); while ((nextArg = peekNextArg()) != null) { try { durations.add(Long.parseLong(nextArg)); getNextArgRequired(); // consume the duration } catch (NumberFormatException e) { // nextArg is not a duration, finish reading. break; } if (hasAmplitudes) { amplitudes.add(Integer.parseInt(getNextArgRequired())); } } long[] durationArray = durations.stream().mapToLong(Long::longValue).toArray(); if (!hasAmplitudes) { return VibrationEffect.createWaveform(durationArray, repeat); if (hasAmplitudes) { int[] amplitudeArray = amplitudes.stream().mapToInt(Integer::intValue).toArray(); waveform = VibrationEffect.createWaveform(durationArray, amplitudeArray, repeat); } else { waveform = VibrationEffect.createWaveform(durationArray, repeat); } int[] amplitudeArray = amplitudes.stream().mapToInt(Integer::intValue).toArray(); return VibrationEffect.createWaveform(durationArray, amplitudeArray, repeat); composition.addEffect(waveform, delay); } private void addPrebakedToComposition(VibrationEffect.Composition composition) { boolean shouldFallback = false; int delay = 0; getNextArgRequired(); // consume "prebaked" String nextOption; while ((nextOption = getNextOption()) != null) { if ("-b".equals(nextOption)) { shouldFallback = true; } else if ("-w".equals(nextOption)) { delay = Integer.parseInt(getNextArgRequired()); } } private VibrationEffect nextPrebaked() { boolean shouldFallback = "-b".equals(getNextOption()); int effectId = Integer.parseInt(getNextArgRequired()); return VibrationEffect.get(effectId, shouldFallback); composition.addEffect(VibrationEffect.get(effectId, shouldFallback), delay); } private VibrationEffect nextComposed() { VibrationEffect.Composition composition = VibrationEffect.startComposition(); private void addPrimitivesToComposition(VibrationEffect.Composition composition) { getNextArgRequired(); // consume "primitives" String nextArg; while ((nextArg = peekNextArg()) != null) { int delay = 0; if ("-w".equals(nextArg)) { getNextArgRequired(); // consume the -w option getNextArgRequired(); // consume "-w" delay = Integer.parseInt(getNextArgRequired()); } else if ("-v".equals(nextArg)) { // Starting next vibrator, this composed effect if finished. nextArg = peekNextArg(); } try { composition.addPrimitive(Integer.parseInt(nextArg), /* scale= */ 1, delay); getNextArgRequired(); // consume the primitive id } catch (NumberFormatException | NullPointerException e) { // nextArg is not describing a primitive, leave it to be consumed by outer loops break; } int primitiveId = Integer.parseInt(getNextArgRequired()); composition.addPrimitive(primitiveId, /* scale= */ 1f, delay); } return composition.compose(); } private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) { Loading @@ -1615,38 +1641,44 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" list"); pw.println(" Prints the id of device vibrators. This does not include any "); pw.println(" connected input device."); pw.println(" synced [options] <effect>"); pw.println(" synced [options] <effect>..."); pw.println(" Vibrates effect on all vibrators in sync."); pw.println(" combined [options] (-v <vibrator-id> <effect>)..."); pw.println(" combined [options] (-v <vibrator-id> <effect>...)..."); pw.println(" Vibrates different effects on each vibrator in sync."); pw.println(" sequential [options] (-v <vibrator-id> [-w <delay>] <effect>)..."); pw.println(" sequential [options] (-v <vibrator-id> <effect>...)..."); pw.println(" Vibrates different effects on each vibrator in sequence."); pw.println(" cancel"); pw.println(" Cancels any active vibration"); pw.println(""); pw.println("Effect commands:"); pw.println(" oneshot [-a] <duration> [<amplitude>]"); pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]"); pw.println(" Vibrates for duration milliseconds; ignored when device is on "); pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting "); pw.println(" will be used to scale amplitude."); pw.println(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" If -a is provided, the command accepts a second argument for "); pw.println(" amplitude, in a scale of 1-255."); pw.println(" waveform [-r <index>] [-a] (<duration> [<amplitude>])..."); pw.println(" waveform [-w delay] [-r index] [-a] (<duration> [<amplitude>])..."); pw.println(" Vibrates for durations and amplitudes in list; ignored when "); pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength "); pw.println(" user setting will be used to scale amplitude."); pw.println(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" If -r is provided, the waveform loops back to the specified"); pw.println(" index (e.g. 0 loops from the beginning)"); pw.println(" If -a is provided, the command accepts duration-amplitude pairs;"); pw.println(" otherwise, it accepts durations only and alternates off/on"); pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255."); pw.println(" prebaked [-b] <effect-id>"); pw.println(" prebaked [-w delay] [-b] <effect-id>"); pw.println(" Vibrates with prebaked effect; ignored when device is on DND "); pw.println(" (Do Not Disturb) mode; touch feedback strength user setting "); pw.println(" will be used to scale amplitude."); pw.println(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" If -b is provided, the prebaked fallback effect will be played if"); pw.println(" the device doesn't support the given effect-id."); pw.println(" composed [-w <delay>] <primitive-id>..."); pw.println(" primitives ([-w delay] <primitive-id>)..."); pw.println(" Vibrates with a composed effect; ignored when device is on DND "); pw.println(" (Do Not Disturb) mode; touch feedback strength user setting "); pw.println(" will be used to scale primitive intensities."); Loading services/core/jni/com_android_server_vibrator_VibratorController.cpp +8 −7 Original line number Diff line number Diff line Loading @@ -150,15 +150,16 @@ static jboolean vibratorIsAvailable(JNIEnv* env, jclass /* clazz */, jlong ptr) return wrapper->hal()->ping().isOk() ? JNI_TRUE : JNI_FALSE; } static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeoutMs, static jlong vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeoutMs, jlong vibrationId) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorOn failed because native wrapper was not initialized"); return; return -1; } auto callback = wrapper->createCallback(vibrationId); wrapper->hal()->on(std::chrono::milliseconds(timeoutMs), callback); auto result = wrapper->hal()->on(std::chrono::milliseconds(timeoutMs), callback); return result.isOk() ? timeoutMs : (result.isUnsupported() ? 0 : -1); } static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong ptr) { Loading Loading @@ -234,7 +235,7 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, j aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength); auto callback = wrapper->createCallback(vibrationId); auto result = wrapper->hal()->performEffect(effectType, effectStrength, callback); return result.isOk() ? result.value().count() : -1; return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1); } static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, Loading @@ -252,7 +253,7 @@ static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlon } auto callback = wrapper->createCallback(vibrationId); auto result = wrapper->hal()->performComposedEffect(effects, callback); return result.isOk() ? result.value().count() : -1; return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1); } static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong ptr) { Loading Loading @@ -311,7 +312,7 @@ static const JNINativeMethod method_table[] = { (void*)vibratorNativeInit}, {"getNativeFinalizer", "()J", (void*)vibratorGetNativeFinalizer}, {"isAvailable", "(J)Z", (void*)vibratorIsAvailable}, {"on", "(JJJ)V", (void*)vibratorOn}, {"on", "(JJJ)J", (void*)vibratorOn}, {"off", "(J)V", (void*)vibratorOff}, {"setAmplitude", "(JF)V", (void*)vibratorSetAmplitude}, {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect}, Loading services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java +2 −1 Original line number Diff line number Diff line Loading @@ -74,11 +74,12 @@ final class FakeVibratorControllerProvider { } @Override public void on(long milliseconds, long vibrationId) { public long on(long milliseconds, long vibrationId) { mEffectSegments.add(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, /* frequency= */ 0, (int) milliseconds)); applyLatency(); scheduleListener(milliseconds, vibrationId); return milliseconds; } @Override Loading Loading
services/core/java/com/android/server/vibrator/VibrationThread.java +395 −216 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/vibrator/VibratorController.java +16 −8 Original line number Diff line number Diff line Loading @@ -189,12 +189,18 @@ final class VibratorController { * callback to {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. * * @return The positive duration of the vibration started, if successful, zero if the vibrator * do not support the input or a negative number if the operation failed. */ public void on(long milliseconds, long vibrationId) { public long on(long milliseconds, long vibrationId) { synchronized (mLock) { mNativeWrapper.on(milliseconds, vibrationId); long duration = mNativeWrapper.on(milliseconds, vibrationId); if (duration > 0) { notifyVibratorOnLocked(); } return duration; } } /** Loading @@ -203,7 +209,8 @@ final class VibratorController { * * <p>This will affect the state of {@link #isVibrating()}. * * @return The duration of the effect playing, or 0 if unsupported. * @return The positive duration of the vibration started, if successful, zero if the vibrator * do not support the input or a negative number if the operation failed. */ public long on(PrebakedSegment prebaked, long vibrationId) { synchronized (mLock) { Loading @@ -222,7 +229,8 @@ final class VibratorController { * * <p>This will affect the state of {@link #isVibrating()}. * * @return The duration of the effect playing, or 0 if unsupported. * @return The positive duration of the vibration started, if successful, zero if the vibrator * do not support the input or a negative number if the operation failed. */ public long on(PrimitiveSegment[] primitives, long vibrationId) { if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { Loading Loading @@ -327,7 +335,7 @@ final class VibratorController { */ private static native long getNativeFinalizer(); private static native boolean isAvailable(long nativePtr); private static native void on(long nativePtr, long milliseconds, long vibrationId); private static native long on(long nativePtr, long milliseconds, long vibrationId); private static native void off(long nativePtr); private static native void setAmplitude(long nativePtr, float amplitude); private static native int[] getSupportedEffects(long nativePtr); Loading Loading @@ -365,8 +373,8 @@ final class VibratorController { } /** Turns vibrator on for given time. */ public void on(long milliseconds, long vibrationId) { on(mNativePtr, milliseconds, vibrationId); public long on(long milliseconds, long vibrationId) { return on(mNativePtr, milliseconds, vibrationId); } /** Turns vibrator off. */ Loading
services/core/java/com/android/server/vibrator/VibratorManagerService.java +100 −68 Original line number Diff line number Diff line Loading @@ -1394,15 +1394,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { while ((nextArg = peekNextArg()) != null) { switch (nextArg) { case "-f": getNextArgRequired(); // consume the -f argument; getNextArgRequired(); // consume "-f" force = true; break; case "-d": getNextArgRequired(); // consume the -d argument; getNextArgRequired(); // consume "-d" description = getNextArgRequired(); break; default: // Not a common option, finish reading. // nextArg is not a common option, finish reading. return; } } Loading Loading @@ -1456,14 +1456,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private int runMono() { CommonOptions commonOptions = new CommonOptions(); VibrationEffect effect = nextEffect(); if (effect == null) { return 0; } CombinedVibrationEffect combinedEffect = CombinedVibrationEffect.createSynced(effect); CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(nextEffect()); VibrationAttributes attrs = createVibrationAttributes(commonOptions); vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combinedEffect, attrs, vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, effect, attrs, commonOptions.description, mToken); return 0; } Loading @@ -1474,10 +1469,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { CombinedVibrationEffect.startSynced(); while ("-v".equals(getNextOption())) { int vibratorId = Integer.parseInt(getNextArgRequired()); VibrationEffect effect = nextEffect(); if (effect != null) { combination.addVibrator(vibratorId, effect); } combination.addVibrator(vibratorId, nextEffect()); } VibrationAttributes attrs = createVibrationAttributes(commonOptions); vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs, Loading @@ -1487,19 +1479,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private int runSequential() { CommonOptions commonOptions = new CommonOptions(); CombinedVibrationEffect.SequentialCombination combination = CombinedVibrationEffect.startSequential(); while ("-v".equals(getNextOption())) { int vibratorId = Integer.parseInt(getNextArgRequired()); int delay = 0; if ("-w".equals(getNextOption())) { delay = Integer.parseInt(getNextArgRequired()); } VibrationEffect effect = nextEffect(); if (effect != null) { combination.addNext(vibratorId, effect, delay); } combination.addNext(vibratorId, nextEffect()); } VibrationAttributes attrs = createVibrationAttributes(commonOptions); vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs, Loading @@ -1512,87 +1496,129 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { return 0; } @Nullable private VibrationEffect nextEffect() { String effectType = getNextArgRequired(); if ("oneshot".equals(effectType)) { return nextOneShot(); VibrationEffect.Composition composition = VibrationEffect.startComposition(); String nextArg; while ((nextArg = peekNextArg()) != null) { if ("oneshot".equals(nextArg)) { addOneShotToComposition(composition); } else if ("waveform".equals(nextArg)) { addWaveformToComposition(composition); } else if ("prebaked".equals(nextArg)) { addPrebakedToComposition(composition); } else if ("primitives".equals(nextArg)) { addPrimitivesToComposition(composition); } else { // nextArg is not an effect, finish reading. break; } if ("waveform".equals(effectType)) { return nextWaveform(); } if ("prebaked".equals(effectType)) { return nextPrebaked(); return composition.compose(); } if ("composed".equals(effectType)) { return nextComposed(); private void addOneShotToComposition(VibrationEffect.Composition composition) { boolean hasAmplitude = false; int delay = 0; getNextArgRequired(); // consume "oneshot" String nextOption; while ((nextOption = getNextOption()) != null) { if ("-a".equals(nextOption)) { hasAmplitude = true; } else if ("-w".equals(nextOption)) { delay = Integer.parseInt(getNextArgRequired()); } return null; } private VibrationEffect nextOneShot() { boolean hasAmplitude = "-a".equals(getNextOption()); long duration = Long.parseLong(getNextArgRequired()); int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired()) : VibrationEffect.DEFAULT_AMPLITUDE; return VibrationEffect.createOneShot(duration, amplitude); composition.addEffect(VibrationEffect.createOneShot(duration, amplitude), delay); } private VibrationEffect nextWaveform() { private void addWaveformToComposition(VibrationEffect.Composition composition) { boolean hasAmplitudes = false; int repeat = -1; int delay = 0; String nextOption = getNextOption(); while (nextOption != null) { getNextArgRequired(); // consume "waveform" String nextOption; while ((nextOption = getNextOption()) != null) { if ("-a".equals(nextOption)) { hasAmplitudes = true; } else if ("-r".equals(nextOption)) { repeat = Integer.parseInt(getNextArgRequired()); } else if ("-w".equals(nextOption)) { delay = Integer.parseInt(getNextArgRequired()); } nextOption = getNextOption(); } List<Long> durations = new ArrayList<>(); List<Integer> amplitudes = new ArrayList<>(); VibrationEffect waveform; String nextArg; while ((nextArg = peekNextArg()) != null && !"-v".equals(nextArg)) { durations.add(Long.parseLong(getNextArgRequired())); while ((nextArg = peekNextArg()) != null) { try { durations.add(Long.parseLong(nextArg)); getNextArgRequired(); // consume the duration } catch (NumberFormatException e) { // nextArg is not a duration, finish reading. break; } if (hasAmplitudes) { amplitudes.add(Integer.parseInt(getNextArgRequired())); } } long[] durationArray = durations.stream().mapToLong(Long::longValue).toArray(); if (!hasAmplitudes) { return VibrationEffect.createWaveform(durationArray, repeat); if (hasAmplitudes) { int[] amplitudeArray = amplitudes.stream().mapToInt(Integer::intValue).toArray(); waveform = VibrationEffect.createWaveform(durationArray, amplitudeArray, repeat); } else { waveform = VibrationEffect.createWaveform(durationArray, repeat); } int[] amplitudeArray = amplitudes.stream().mapToInt(Integer::intValue).toArray(); return VibrationEffect.createWaveform(durationArray, amplitudeArray, repeat); composition.addEffect(waveform, delay); } private void addPrebakedToComposition(VibrationEffect.Composition composition) { boolean shouldFallback = false; int delay = 0; getNextArgRequired(); // consume "prebaked" String nextOption; while ((nextOption = getNextOption()) != null) { if ("-b".equals(nextOption)) { shouldFallback = true; } else if ("-w".equals(nextOption)) { delay = Integer.parseInt(getNextArgRequired()); } } private VibrationEffect nextPrebaked() { boolean shouldFallback = "-b".equals(getNextOption()); int effectId = Integer.parseInt(getNextArgRequired()); return VibrationEffect.get(effectId, shouldFallback); composition.addEffect(VibrationEffect.get(effectId, shouldFallback), delay); } private VibrationEffect nextComposed() { VibrationEffect.Composition composition = VibrationEffect.startComposition(); private void addPrimitivesToComposition(VibrationEffect.Composition composition) { getNextArgRequired(); // consume "primitives" String nextArg; while ((nextArg = peekNextArg()) != null) { int delay = 0; if ("-w".equals(nextArg)) { getNextArgRequired(); // consume the -w option getNextArgRequired(); // consume "-w" delay = Integer.parseInt(getNextArgRequired()); } else if ("-v".equals(nextArg)) { // Starting next vibrator, this composed effect if finished. nextArg = peekNextArg(); } try { composition.addPrimitive(Integer.parseInt(nextArg), /* scale= */ 1, delay); getNextArgRequired(); // consume the primitive id } catch (NumberFormatException | NullPointerException e) { // nextArg is not describing a primitive, leave it to be consumed by outer loops break; } int primitiveId = Integer.parseInt(getNextArgRequired()); composition.addPrimitive(primitiveId, /* scale= */ 1f, delay); } return composition.compose(); } private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) { Loading @@ -1615,38 +1641,44 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" list"); pw.println(" Prints the id of device vibrators. This does not include any "); pw.println(" connected input device."); pw.println(" synced [options] <effect>"); pw.println(" synced [options] <effect>..."); pw.println(" Vibrates effect on all vibrators in sync."); pw.println(" combined [options] (-v <vibrator-id> <effect>)..."); pw.println(" combined [options] (-v <vibrator-id> <effect>...)..."); pw.println(" Vibrates different effects on each vibrator in sync."); pw.println(" sequential [options] (-v <vibrator-id> [-w <delay>] <effect>)..."); pw.println(" sequential [options] (-v <vibrator-id> <effect>...)..."); pw.println(" Vibrates different effects on each vibrator in sequence."); pw.println(" cancel"); pw.println(" Cancels any active vibration"); pw.println(""); pw.println("Effect commands:"); pw.println(" oneshot [-a] <duration> [<amplitude>]"); pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]"); pw.println(" Vibrates for duration milliseconds; ignored when device is on "); pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting "); pw.println(" will be used to scale amplitude."); pw.println(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" If -a is provided, the command accepts a second argument for "); pw.println(" amplitude, in a scale of 1-255."); pw.println(" waveform [-r <index>] [-a] (<duration> [<amplitude>])..."); pw.println(" waveform [-w delay] [-r index] [-a] (<duration> [<amplitude>])..."); pw.println(" Vibrates for durations and amplitudes in list; ignored when "); pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength "); pw.println(" user setting will be used to scale amplitude."); pw.println(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" If -r is provided, the waveform loops back to the specified"); pw.println(" index (e.g. 0 loops from the beginning)"); pw.println(" If -a is provided, the command accepts duration-amplitude pairs;"); pw.println(" otherwise, it accepts durations only and alternates off/on"); pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255."); pw.println(" prebaked [-b] <effect-id>"); pw.println(" prebaked [-w delay] [-b] <effect-id>"); pw.println(" Vibrates with prebaked effect; ignored when device is on DND "); pw.println(" (Do Not Disturb) mode; touch feedback strength user setting "); pw.println(" will be used to scale amplitude."); pw.println(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" If -b is provided, the prebaked fallback effect will be played if"); pw.println(" the device doesn't support the given effect-id."); pw.println(" composed [-w <delay>] <primitive-id>..."); pw.println(" primitives ([-w delay] <primitive-id>)..."); pw.println(" Vibrates with a composed effect; ignored when device is on DND "); pw.println(" (Do Not Disturb) mode; touch feedback strength user setting "); pw.println(" will be used to scale primitive intensities."); Loading
services/core/jni/com_android_server_vibrator_VibratorController.cpp +8 −7 Original line number Diff line number Diff line Loading @@ -150,15 +150,16 @@ static jboolean vibratorIsAvailable(JNIEnv* env, jclass /* clazz */, jlong ptr) return wrapper->hal()->ping().isOk() ? JNI_TRUE : JNI_FALSE; } static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeoutMs, static jlong vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeoutMs, jlong vibrationId) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorOn failed because native wrapper was not initialized"); return; return -1; } auto callback = wrapper->createCallback(vibrationId); wrapper->hal()->on(std::chrono::milliseconds(timeoutMs), callback); auto result = wrapper->hal()->on(std::chrono::milliseconds(timeoutMs), callback); return result.isOk() ? timeoutMs : (result.isUnsupported() ? 0 : -1); } static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong ptr) { Loading Loading @@ -234,7 +235,7 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, j aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength); auto callback = wrapper->createCallback(vibrationId); auto result = wrapper->hal()->performEffect(effectType, effectStrength, callback); return result.isOk() ? result.value().count() : -1; return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1); } static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, Loading @@ -252,7 +253,7 @@ static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlon } auto callback = wrapper->createCallback(vibrationId); auto result = wrapper->hal()->performComposedEffect(effects, callback); return result.isOk() ? result.value().count() : -1; return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1); } static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong ptr) { Loading Loading @@ -311,7 +312,7 @@ static const JNINativeMethod method_table[] = { (void*)vibratorNativeInit}, {"getNativeFinalizer", "()J", (void*)vibratorGetNativeFinalizer}, {"isAvailable", "(J)Z", (void*)vibratorIsAvailable}, {"on", "(JJJ)V", (void*)vibratorOn}, {"on", "(JJJ)J", (void*)vibratorOn}, {"off", "(J)V", (void*)vibratorOff}, {"setAmplitude", "(JF)V", (void*)vibratorSetAmplitude}, {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect}, Loading
services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java +2 −1 Original line number Diff line number Diff line Loading @@ -74,11 +74,12 @@ final class FakeVibratorControllerProvider { } @Override public void on(long milliseconds, long vibrationId) { public long on(long milliseconds, long vibrationId) { mEffectSegments.add(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, /* frequency= */ 0, (int) milliseconds)); applyLatency(); scheduleListener(milliseconds, vibrationId); return milliseconds; } @Override Loading